Geeks With Blogs

News Locations of visitors to this page
Brian Genisio's House of Bilz

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"/> 
Posted on Friday, August 21, 2009 2:43 PM | Back to top


Comments on this post: Adventures in MVVM – Generalized Command Behavior Attachments

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
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.
Left by Chad on Aug 24, 2009 5:31 PM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
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.
Left by Rob on Aug 25, 2009 12:37 AM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
Great tutorial here, just a little bit confuse for newbie like me :) thx and keep sharing :)
Left by Nyubi on Aug 29, 2009 3:05 PM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
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.
Left by Ciaran Murphy on Sep 15, 2009 9:20 PM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
@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.
Left by Brian Genisio on Sep 15, 2009 10:02 PM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
Nice post brother
Left by Nunung on Jul 13, 2010 8:27 PM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
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!
Left by Scott Anderson on Oct 07, 2010 4:04 AM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
I am an idiot when it comes to attached behavior. Thanks for clearing a few issues up for me!
Left by Scotsman Ice Machine on Nov 03, 2010 2:16 AM

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

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
thanks for this article
Left by Niraj on Apr 12, 2011 6:27 AM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
Excellent idea and one that I really appreciate you posting.
Left by Richard Hill on Nov 10, 2011 10:06 PM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
Hi,
Great Post! I am trying to use this code for the double click even and I have it working but I require access to the MouseButtonEventArgs argument that is passed into the Event function? How would I got about doing that? Thank You!
Left by Lukasz on May 27, 2012 2:21 PM

# re: Adventures in MVVM – Generalized Command Behavior Attachments
Requesting Gravatar...
awesome article you've describe here, ill bookmark it
Left by Acnes on Sep 11, 2012 4:45 AM

Your comment:
 (will show your gravatar)


Copyright © Brian Genisio's House Of Bilz | Powered by: GeeksWithBlogs.net