Thursday, 25 September 2008

Hooking up Commands to Events in WPF

A question came up on the Stack Overflow website a little while back about how to execute a command when an event is raised. The questioner, Brad Leach, is using a Model-View-ViewModel architecture for WPF (of which I'm also a fan) and wanted to know how he could hook up a Command on his ViewModel to the TextChanged event of a TextBox in his View. In his case, it turned out that he got what he needed by Data-binding the Text property of the TextBox to a property on the ViewModel; in my answer to his question I outlined an approach that can be used in other cases where properties are not involved, so that the data-binding trick doesn't work. Now I've gone one better, and created a a utility class, EventBehaviourFactory, that makes it easy to hook up any WPF event to a Command using Data Binding.

Using EventBehaviourFactory you define new attached properties, one for each kind of event you want to hook a Command to. Then you set the value of the property on a WPF element, specifying the command that you want to be executed. When the event is raised, the command will be executed, with the EventArgs being passed through as the command parameter.

Using the EventBehaviourFactory

First of all, you need to include my EventBehaviourFactory class in your project. You can either copy the code from the bottom of this post, or get the whole project from MSDN Code Gallery.

Then you need to define a static class to hold a new Attached property that you will attach to an object to specify which command to execute when a particular event is raised. You need to call EventBehaviourFactory.CreateCommandExecutionEventBehaviour and pass it the routed event that you want to handle - note you must pass the static field holding the RoutedEvent object (usually the same name as the event itself, but ending in "Event") not the event property. Just as when creating a standard attached property, you also need to pass the name of the property as a string (this should match the name of the static field that you store the DependencyProperty in), and the type of the class that is defining the property.

As an example, I'll create a property that will execute a Command when the TextChanged event of a TextBox is raised:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace FunctionalFun.Wpf
{
    public static class TextBoxBehaviour
    {
        public static readonly DependencyProperty TextChangedCommand = EventBehaviourFactory.CreateCommandExecutionEventBehaviour(TextBox.TextChangedEvent, "TextChangedCommand", typeof (TextBoxBehaviour));
        
        public static void SetTextChangedCommand(DependencyObject o, ICommand value)
        {
            o.SetValue(TextChangedCommand, value);
        }

        public static ICommand GetTextChangedCommand(DependencyObject o)
        {
            return o.GetValue(TextChangedCommand) as ICommand;
        }
    }
}

The important part is in line 9, where I'm calling EventBehaviourFactory. This is the part that creates the attached behaviour. In the download, I've included a Resharper template to mostly automate this step. To make use of the property you just set a value for it on the object whose events interest you, binding it to the appropriate command. Here's an example:

<Window x:Class="FunctionalFun.Wpf.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ff="clr-namespace:FunctionalFun.Wpf"
    Title="Window1" Height="300" Width="300">
    <Window.DataContext>
        <ff:ViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBox ff:TextBoxBehaviour.TextChangedCommand="{Binding TextChanged}"/>
    </Grid>
</Window>

The ViewModel that this Window uses (initialised in line 7) is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace FunctionalFun.Wpf
{
    public class ViewModel
    {
        public ICommand TextChanged
        {
            get
            {
				//  this is very lazy: I should cache the command!
                return new TextChangedCommand();
            }
        }

        private class TextChangedCommand : ICommand
        {
            public event EventHandler CanExecuteChanged;
            public void Execute(object parameter)
            {
                MessageBox.Show("Text Changed");
            }

            public bool CanExecute(object parameter)
            {
                return true;
            }
        }
    }
}

How EventBehaviourFactory works

EventBehaviourFactory uses the attached behaviour technique. Basically, it creates new Attached properties, one for each kind of event that you ask it to handle. The attached property lets you specify which command you want to be executed when the event is raised. The attached property is configured with a PropertyChanged handler (wrapped up in the private ExecuteCommandOnRoutedEventBehaviour class) that starts listening to the appropriate event on an object whenever the attached property is set on the object - and stops listening if the attached property is set to null.

As you read the code below, you might wonder why I have factored out the ExecuteCommandBehaviour class. That was because I was trying to include a way of handling non-routed events in this Factory as well. I ran into a couple of problems, however, so I took it. I'm not sure how useful it would have been anyway, because I couldn't find terribly many non-routed events in WPF. If this would be useful to anybody, let me know, and I'll have another stab at it.

The whole project is available on MSDN Code Gallery. There are a couple of example properties in there, and even a trio of unit tests. As a bonus, you get a Resharper template

using System;
using System.Windows;
using System.Windows.Input;

namespace FunctionalFun.Wpf
{
    public static class EventBehaviourFactory
    {
        public static DependencyProperty CreateCommandExecutionEventBehaviour(RoutedEvent routedEvent, string propertyName, Type ownerType)
        {
            DependencyProperty property = DependencyProperty.RegisterAttached(propertyName, typeof (ICommand), ownerType,
                                                               new PropertyMetadata(null, 
                                                                   new ExecuteCommandOnRoutedEventBehaviour(routedEvent).PropertyChangedHandler));

            return property;
        }

        /// <summary>
        /// An internal class to handle listening for an event and executing a command,
        /// when a Command is assigned to a particular DependencyProperty
        /// </summary>
        private class ExecuteCommandOnRoutedEventBehaviour : ExecuteCommandBehaviour
        {
            private readonly RoutedEvent _routedEvent;

            public ExecuteCommandOnRoutedEventBehaviour(RoutedEvent routedEvent)
            {
                _routedEvent = routedEvent;
            }

            /// <summary>
            /// Handles attaching or Detaching Event handlers when a Command is assigned or unassigned
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="oldValue"></param>
            /// <param name="newValue"></param>
            protected override void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue)
            {
                UIElement element = sender as UIElement;
                if (element == null) { return; }

                if (oldValue != null)
                {
                    element.RemoveHandler(_routedEvent, new RoutedEventHandler(EventHandler));
                }

                if (newValue != null)
                {
                    element.AddHandler(_routedEvent, new RoutedEventHandler(EventHandler));
                }
            }

            protected void EventHandler(object sender, RoutedEventArgs e)
            {
                HandleEvent(sender, e);
            }
        }

        internal abstract class ExecuteCommandBehaviour
        {
            protected DependencyProperty _property;
            protected abstract void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue);

            protected void HandleEvent(object sender, EventArgs e)
            {
                DependencyObject dp = sender as DependencyObject;
                if (dp == null)
                {
                    return;
                }

                ICommand command = dp.GetValue(_property) as ICommand;

                if (command == null)
                {
                    return;
                }

                if (command.CanExecute(e))
                {
                    command.Execute(e);
                }
            }

            /// <summary>
            /// Listens for a change in the DependencyProperty that we are assigned to, and
            /// adjusts the EventHandlers accordingly
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            public void PropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
                // the first time the property changes,
                // make a note of which property we are supposed
                // to be watching
                if (_property == null)
                {
                    _property = e.Property;
                }

                object oldValue = e.OldValue;
                object newValue = e.NewValue;

                AdjustEventHandlers(sender, oldValue, newValue);
            }
        }
    } 
}

35 comments:

Anonymous said...

That is what is was looking for. Great example that helps in complete decoupling between View and ViewModel.

Great Thanks

Anonymous said...

This is great. Just started a new project using the Model-View-ViewModel pattern and this has been a great helper class. Thanks!

Benjamin Schleimer said...

humm, this is really interesting.
After playing around with the example a little, I realized that now the dependency graph is
Model <--- ViewModel ---> View.

So all of code for extracting and converting the data from the view (for non-trival view-model data mappings) is now done inside the ViewModel.

Since the View already has knowledge of ViewModel (what properties to bind to), wouldn't it be better if the ViewModel presented an interface that was extremely similar to whats in the view (usually trival to extract and present) and then pass that data to the ViewModel?

Or is it better to always have the ViewModel do 100% of the work and keep the View as dumb as possible?

Another thought I had was why you have the SetXXXCommand in the XXXBehavior class? Doesn't the binding pretty much limit which ICommand can be used by any given behavior?

Thanks for the code!
Ben
bensch (turn the words into digits) one two eight at yahoo dot com

Sam said...

Ben,
Glad you found it interesting.

Have you drawn your dependency diagram correctly? It seems to indicate that the ViewModel depends on the View, which is not the case.

You're right that this example does help with putting even more of the behaviour inside the ViewModel. I try to do that as much as possible. I think it's fair to say that none of the Views in my current project have any code-behind.

By the way, if you want to see even more powerful examples of hooking up events in the View to methods in the ViewModel, have a look at the Caliburn framework:
http://tinyurl.com/cg8tlc

Anonymous said...

I'm now able to handle events easily by combing your class with Josh Smith's version of MVVM.

Thanks for this post...

eekmo said...

Sam,
This looks really promising but I ran into this:

public static readonly DependencyProperty DropDownClosedCommand = EventBehaviorFactory.CreateCommandExecutionEventBehavior(ComboBox.DropDownClosed, "DropDownClosedCommand", typeof(ComboBoxBehavior));

ComboBox does not expose the DropDownClosedEvent.

Any Ideas?

Thanks in advance,
Eric.

Sam said...

Eric,
I think the problem is that DropDownClosed is a standard event rather than a routed event - my code is only designed to work with routed events. You would need to create a custom version of the behaviour specially for handling that event on ComboBoxes. Sorry I can't be more help than that at the moment!

Ryan Vice said...

This is fantastic. Very useful and provides a good way of understanding the power of dependency properties and attached properties

Sam said...

Ryan. Glad you like it - and thanks for putting the link on your blog!

Anonymous said...

Thanks a lot for the posting..... I was struggling to get it done using MVVM before.... I really appreciate it .....

VK

Игорь said...

Thanks, great thing. WPF is not totally suited for MVVM. Such additions are great help.

Ákos said...

Thanks for the post! It's really great!

But I have a problem. I need the service of commands which binds the commands CanExecute() value to the enability of the related control.

Do You have any idea how to improve your example to reach my goal?

Thank you very much!

Sam said...

@Ákos,
I'm not sure exactly what you're asking here.

When you bind a command to a Button, it automatically gets disabled when CanExecute returns false, or enabled if it returns true. One thing to make sure of, when you implement ICommand directly, is to raise the CanExecuteChanged event when CanExecute is likely to return a different value to before.

If you don't do that, the button will be enabled or disabled depending on the value returned by CanExecute at the time you bound the command to it, and it will never change.

Hope that helps.

Ákos said...

@Sam,

For example, I have a TextBox, and I want to hook up (using your code) aCommand to TextBox.TextChanged event. (I don't want to implement ICommand, so I use Josh Smith's RelayCommand as it has all the functionalities I need.) And this way I would like to see, that when aCommand.CanExecute() == false, then aTextBox.IsEnabled == false.

Is it clear now?

Thanks!

Sam said...

@Ákos,
Thanks for the clarification. I understand what you're asking now.

It wouldn't be straightforward to do what you want using my Event->Command binding - and it probably wouldn't be the best way, either.

I would suggest that you create something like an IsTextBoxEnabled property on your ViewModel, make sure your ViewModel raises the appropriate PropertyChanged events, then bind the TextBox.IsEnabled directly to that property.

Ákos said...

@Sam
That's the way I already do. I just thought that it would be more confortable to use the command-like solution.

Thanks for the post, and thanks for the answers!

Richard said...

That's awesome! Keep up the good work.

Anonymous said...

if i have an event (mouse click)to transfer control from one window to another , and i now have to do the same without using an event handler, how can i do this ?

Ashraf said...

Good article.
Another implementation for generic solution using reflection emit for emitting the event handler at runtime when you supply in XAML the event name for any control with the command binding

Игорь said...

Thanks, great thing. WPF is not totally suited for MVVM. Such additions are great help.

Ryan Vice said...

This is fantastic. Very useful and provides a good way of understanding the power of dependency properties and attached properties

Anonymous said...

That is what is was looking for. Great example that helps in complete decoupling between View and ViewModel.

Great Thanks

Bryan said...

This is awesome but it does not work inside a DataTemplate which is where it would be most powerful.  Can anyone help me figure out how to do it?

bryan said...

I figured it out.. Very cool Idea here!!
  

Kevin said...

Nice work!
But how could you handle an event with arguments, say TextBox.PreviewKeyUpCommand in which I need to access its argments. Is there a way to specify something like CommandParameter="..."?

Samuel Jack said...

Kevin, The EventArgs from the event get passed through to the command as its parameter when it is executed.

Vishal said...

Hi Sam, Thank you for a really good post.
I am stumped by a problem. For example, How do I access method or properties defined in ViewModel class in the 'Execute' method of 'TextChangedCommand' class (of course without creating the new object of ViewModel class).

Vishal said...

Hello again, got my answer by doing a little googling.. View Model object needs to pass while creating the instance of  'TextChangedCommand' class.. e.g. return new TextChangedCommand(this);  :-)

Samuel Jack said...

@Vishal,
That's certainly one way of doing it. These days, I tend to write all my commands using a variation on Josh Smith's RelayCommand. My take on it is here .

T20t said...

Thank you for a good post!!

Thomas said...

Great work !

Your commands as a variation on Josh Smith's RelayCommand would be the ultimative solution. I would really apreciate this !

Narayana said...

Excellent Article

Cklepeis said...

Great article, but I'm having issues binding to the FrameworkElement.Unloaded event. Please see my question here:  
http://stackoverflow.com/questions/10923481/wpf-binding-an-icommand-to-an-event-frameworkelement-unloaded/10928763#10928763

Alternator said...

Hey thanks for this, so far it is working well.

I found sometimes I want to pass the datacontext rather than the eventargs (so my commands don't have to understand eventargs if it isn't relevant to their function).

Fortunately your factory was easily extendable to allow defining a solitary commandparameters attached property, which I altered the HandleEvent function to use in place of the args if it is available (I do still need to figure out how to correctly handle the null scenerio vs a not defined scenerio though, although it isn't a major).

Anindya Chatterjee said...

nice article

Post a Comment