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); } } } }