Brian Genisio's House of Bilz

  Home  |   Contact  |   Syndication    |   Login
  72 Posts | 0 Stories | 186 Comments | 0 Trackbacks

News

Locations of visitors to this page

Archives

Post Categories

Who am I?

More Adventures in MVVM  Shout it kick it on DotNetKicks.com

There are several examples on the web that describe the “Attached Behavior” pattern in Silverlight and WPF.  This pattern works really well for binding commands in the ViewModel to controls in the View.  The problem with this is that for every behavior, there is a LOT of boilerplate code that goes along with it.  Because the DepencencyProperties need to be static, they cannot be easily abstracted into a common class. 

If you want to attach a MouseEnterBehavior to a control, you need to create two or three static Dependency Properties in the MouseEnter class.  They are MouseEnter.Command, MouseEnter.MouseEnterBehavior and optionally, MouseEnter.CommandParameter.

    public class MouseEnter 
    {
        private static readonly DependencyProperty BehaviorProperty =
            DependencyProperty.RegisterAttached(
                "MouseEnterBehavior",
                typeof(MouseEnterBehavior),
                typeof(Control),
                null);

        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                "Command",
                typeof(ICommand),
                typeof(MouseEnter),
                new PropertyMetadata(OnSetCommand));

        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached(
                "CommandParameter",
                typeof(object),
                typeof(MouseEnter),
                new PropertyMetadata(OnSetParameter))

And then the "Change Handlers"

        private static void OnSetCommand(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            var target = dependencyObject as Control;
            if (target == null)
                return;

            GetOrCreateBehavior(target).Command = args.NewValue as ICommand;
        }

        private static void OnSetParameter(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            var target = dependencyObject as Control;
            if (target != null)
            {
                GetOrCreateBehavior(target).CommandParameter = args.NewValue;
            }
        }

        protected static MouseEnterBehavior GetOrCreateBehavior(Control control)
        {
            var behavior = control.GetValue(BehaviorProperty) as MouseEnterBehavior;
            if (behavior == null)
            {
                behavior = new MouseEnterBehavior(control);
                control.SetValue(BehaviorProperty, behavior);
            }

            return behavior;
        }

Since the Dependency Properties are static, Silverlight also requires you to in include Get* and Set* static methods:

        public static void SetCommand(Control control, ICommand command) { control.SetValue(CommandProperty, command); }
        public static ICommand GetCommand(Control control) { return control.GetValue(CommandProperty) as ICommand; }
        public static void SetCommandParameter(Control control, object parameter) { control.SetValue(CommandParameterProperty, parameter); }
        public static object GetCommandParameter(Control buttonBase) { return buttonBase.GetValue(CommandParameterProperty); }

This is a classic case of reuse via “Copy and Paste”.  The problem is that there are several places in this code where you need to change three different types and many strings.  If you don’t invoke the magic incantation properly, nothing works.  It will compile but it won’t work (or you will get an obscure XAML parse error).

I cringe whenever I have to employ copy/paste reuse.  In the cases where it is absolutely necessary (such as this), I believe the risk can be reduced proportionately to the complexity of the modification after you paste.  This is why I came up with an Attachment base class to generalize all of this boilerplate code.  The previous code can be reduced to:

    public class MouseEnter : Attachment<Control, MouseEnterBehavior, MouseEnter>
    {
        private static readonly DependencyProperty BehaviorProperty = Behavior();
        public static readonly DependencyProperty CommandProperty = Command(BehaviorProperty);
        public static readonly DependencyProperty CommandParameterProperty = Parameter(BehaviorProperty);

        public static void SetCommand(Control control, ICommand command) { control.SetValue(CommandProperty, command); }
        public static ICommand GetCommand(Control control) { return control.GetValue(CommandProperty) as ICommand; }
        public static void SetCommandParameter(Control control, object parameter) { control.SetValue(CommandParameterProperty, parameter); }
        public static object GetCommandParameter(Control buttonBase) { return buttonBase.GetValue(CommandParameterProperty); }
    }

The only modifications to this copied code exist on the first line. 

What is the class name?  MouseEnter (2 places)
What type of control can the behavior attach to? Control
What type of behavior do you want to attach? MouseEnterBehavior

In addition to the decreased configuration complexity, the actual code that needs to be copied goes from 58 lines of boilerplate code to 11 lines of boilerplate code.  This is a big win, in my opinion.

In this code, I am using the CommandBehaviorBase class from the Prism framework.  It is part of the generic constraints.  If you use something else for your behaviors, replace it as you see fit.  Your own base class for command behaviors would slip in nicely, I am sure.

Here is the Attachment base class:

    public class Attachment<TargetT, BehaviorT, AttachmentT>
        where TargetT : Control
        where BehaviorT : CommandBehaviorBase<TargetT>
    {
        public static DependencyProperty Behavior()
        {
            return DependencyProperty.RegisterAttached(
                typeof(BehaviorT).Name,
                typeof(BehaviorT),
                typeof(TargetT),
                null);
        }

        public static DependencyProperty Command(DependencyProperty behaviorProperty)
        {
            return DependencyProperty.RegisterAttached(
                "Command",
                typeof (ICommand),
                typeof(AttachmentT),
                new PropertyMetadata((target, args) => OnSetCommandCallback(target, args, behaviorProperty)));
        }

        public static DependencyProperty Parameter(DependencyProperty behaviorProperty)
        {
            return DependencyProperty.RegisterAttached(
                "CommandParameter",
                typeof (object),
                typeof (AttachmentT),
                new PropertyMetadata((target, args) => OnSetParameterCallback(target, args, behaviorProperty)));
        }

        protected static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e, DependencyProperty behaviorProperty)
        {
            var target = dependencyObject as TargetT;
            if (target == null)
                return;

            GetOrCreateBehavior(target, behaviorProperty).Command = e.NewValue as ICommand;
        }

        protected static void OnSetParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e, DependencyProperty behaviorProperty)
        {
            var target = dependencyObject as TargetT;
            if (target != null)
            {
                GetOrCreateBehavior(target, behaviorProperty).CommandParameter = e.NewValue;
            }
        }

        protected static BehaviorT GetOrCreateBehavior(Control control, DependencyProperty behaviorProperty)
        {
            var behavior = control.GetValue(behaviorProperty) as BehaviorT;
            if (behavior == null)
            {
                behavior = Activator.CreateInstance(typeof(BehaviorT), control) as BehaviorT;
                control.SetValue(behaviorProperty, behavior);
            }

            return behavior;
        }
    }

And finally, just to complete the example, here is what the MouseEnterBehavior looks like:

    public class MouseEnterBehavior : CommandBehaviorBase<Control>
    {
        public MouseEnterBehavior(Control selectableObject)
            : base(selectableObject)
        {
            selectableObject.MouseEnter += (sender, args) => ExecuteCommand();
        }
    }

And to use it in your XAML, it would look like this:

<Button Behaviors:MouseEnter.Command="{Binding MouseEnter}" Behaviors:MouseEnter.CommandParameter="Optional Paremeter"/> 
  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati
posted on Friday, August 21, 2009 2:43 PM

Feedback

# re: Adventures in MVVM – Generalized Command Behavior Attachments 8/24/2009 5:31 PM Chad
Awesome, I'd given up on having event handlers handled by my ViewModel, I figured having the event in the View code behind that was just one line pointing to the ViewModel method was much better than ~60 odd lines of cut and paste cruft needed before this.

# re: Adventures in MVVM – Generalized Command Behavior Attachments 8/25/2009 12:37 AM Rob
As an alternative to this approach, I would encourage you to look at Caliburn. You can find the docs here: http://caliburn.codeplex.com/Wiki/View.aspx?title=Table%20Of%20Contents In particular, take a look at the documentation on Actions.

# re: Adventures in MVVM – Generalized Command Behavior Attachments 8/29/2009 3:05 PM Nyubi
Great tutorial here, just a little bit confuse for newbie like me :) thx and keep sharing :)

# re: Adventures in MVVM – Generalized Command Behavior Attachments 9/15/2009 9:20 PM Ciaran Murphy
Beautiful clean work Brian easy to understand and works nice with Prism. Gets rid of the need for codesnippets. Any reason why MouseEnter & Attachment are not static? Obviously they work because everything inside is static but most of the samples I've seen declare the class as static too.

# re: Adventures in MVVM – Generalized Command Behavior Attachments 9/15/2009 10:02 PM Brian Genisio
@Ciaran: Thanks for the comments :) Good question. The class doesn't need to be static, just the DependencyProperties and their handlers. People make the class static because it is, essentially, a static class. You can't inherit from a static class, though. If you want to inherit the Attachment base, it can't be static.

One possible workaround for this would be to make Attachment static, and don't inherit... just call with the Attachment. prefix every time you use the base methods. I found the code to be messier that way, but it might be a bit more pure.

I think you can really go either way.

# re: Adventures in MVVM – Generalized Command Behavior Attachments 7/13/2010 8:27 PM Nunung
Nice post brother

# re: Adventures in MVVM – Generalized Command Behavior Attachments 10/7/2010 4:04 AM Scott Anderson
Thanks for this. I'm a new Silverlight developer using Prism, and the boilerplate work for settings up custom commands was getting really old. This rocks and has saved me a lot of time. Cheers to you!

# re: Adventures in MVVM – Generalized Command Behavior Attachments 11/3/2010 2:16 AM Scotsman Ice Machine
I am an idiot when it comes to attached behavior. Thanks for clearing a few issues up for me!

# re: Adventures in MVVM – Generalized Command Behavior Attachments 12/29/2010 8:43 AM Chris
This is some sweet code! Thanks!!! Worked like a charm on my first try. It is much more condensed and clean than other samples.

# re: Adventures in MVVM – Generalized Command Behavior Attachments 4/12/2011 6:27 AM Niraj
thanks for this article

# re: Adventures in MVVM – Generalized Command Behavior Attachments 11/10/2011 10:06 PM Richard Hill
Excellent idea and one that I really appreciate you posting.

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: