Generic Command Binding with WPF and Prism

by Brent 15. December 2009 16:17

I’ve been working a lot with WPF lately for a client project. I really enjoy working with WPF and this project is giving me the chance to really flex my WPF skillz. To make our app as testable as possible, I made the push to move to an MVVM architecture. I’m not going to discuss the details of MVVM, but I will say that is a pretty cool way to keep your functionality independent from your presentation.

To really leverage MVVM, we needed a way bind events to our view models. Luckily, Prism offers functionality that does just that. They take the concept of Command Binding and elaborate on it allowing you to bind events to commands on your view model. Command binding with Prism is reasonably easy, but it does require the creation of several classes for every event that you want to bind to. As it turns out, most of the code required for those classes is repetitive and can be generalized to work with all events on all controls.  In this post I will show you the classes I created, based on the blueprints laid out by the Prism team, which allows such binding to happen.

First up, let’s take a look at the class that actually executes the command, CommandBehaviorBinder:

/// <summary>
/// Behavior that allows controls that derive from <see cref="Control"/>
/// to hook up with <see cref="System.Windows.Input.ICommand"/> objects.
/// </summary>
/// <typeparam name="C">
/// The type of <see cref="Control"/> to hook up.
/// </typeparam>
public class CommandBehaviorBinder<C>
: CommandBehaviorBase<C> where C : Control 
{
	/// <summary>
	/// Initializes a new instance of the <see cref="CommandBehaviorBinder"/> class.
	/// </summary>
	/// <param name="control">The control.</param>
	public CommandBehaviorBinder(C control)
		: base(control)
	{
	}

	/// <summary>
	/// Executes the command.
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	public void Execute(Object sender, EventArgs e)
	{
		ExecuteCommand();
	}
}

A couple of things to take note of here:

1. CommandBehaviorBase is a class (provided by Prism) which associates an ICommand, the command’s parameter (if any) and the target object which the command is attached.

2. The generic type associated with CommandBehaviorBinder must be of type Control.

3. The Execute method just calls the ExecuteCommand method provided by CommandBehaviorBase.

Next up is the class which contains the actual Dependency Properties for a Command and an optional CommandParameter. This class is called Commander:

/// <summary>
/// Class that holds all Dependency Properties and Shared methods to allow an
/// event of a DependencyObject class to be attached to a Command.
/// </summary> public class Commander { private static readonly DependencyProperty EventCommandBehaviorProperty =
DependencyProperty.RegisterAttached( "EventCommandBehavior", typeof(CommandBehaviorBinder<Control>), typeof(Commander), null); /// <summary> /// Command to execute on when the event is fired. /// </summary> public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached( "Command", typeof(ICommand), typeof(Commander), new PropertyMetadata(OnSetCommandCallback)); /// <summary> /// Command parameter to supply on command execution. /// </summary> public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached( "CommandParameter", typeof(Object), typeof(Commander), new PropertyMetadata(OnSetCommandParameterCallback)); /// <summary> /// Event name to bind command to. /// </summary> public static readonly DependencyProperty EventNameProperty =
DependencyProperty.RegisterAttached( "EventName", typeof(String), typeof(Commander), null); /// <summary> /// Sets the <see cref="ICommand"/> to execute when the event is fired. /// </summary> /// <param name="control">DependencyObject to attach Command.</param> /// <param name="command">Command to attach.</param> public static void SetCommand(Control control, ICommand command) { control.SetValue(CommandProperty, command); } /// <summary> /// Retrieves the <see cref="ICommand"/> attached to the <see cref="Control"/>. /// </summary> /// <param name="control">
/// DependencyObject containing the Command dependency property.
/// </param>
/// <returns>The value of the command attached.</returns> public static ICommand GetCommand(Control control) { return control.GetValue(CommandProperty) as ICommand; } /// <summary> /// Sets the value for the CommandParameter attached property on the provided
/// <see cref="Control"/>.
/// </summary> /// <param name="control">DependencyObject to attach CommandParameter.</param> /// <param name="parameter">Parameter value to attach.</param> public static void SetCommandParamter(Control control, Object parameter) { control.SetValue(CommandParameterProperty, parameter); } /// <summary> /// Gets the value in CommandParameter attached property on the provided
/// <see cref="Control"/>.
/// </summary> /// <param name="control">DependencyObject that has the CommandParameter.</param> /// <returns>The value of the property.</returns> public static Object GetCommandParameter(Control control) { return control.GetValue(CommandParameterProperty); } /// <summary> /// Sets the value for the EventName attached property on the provided <see cref="Control"/>. /// </summary> /// <param name="control">DependencyObject to attach the EventName to.</param> /// <param name="eventName">The name of the event to attach.</param> public static void SetEventName(Control control, String eventName) { control.SetValue(EventNameProperty, eventName); } /// <summary> /// Gets the value in EventName attached property on the provided <see cref="Control"/>. /// </summary> /// <param name="control">The DependencyObject that has the EventName.</param> /// <returns>The value of the property.</returns> public static String GetEventName(Control control) { return control.GetValue(EventNameProperty).ToString(); } private static void OnSetCommandCallback(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e) { var control = dependencyObject as Control; if (control != null) { var behavior = GetOrCreateBehavior(control); behavior.Command = e.NewValue as ICommand; } } private static void OnSetCommandParameterCallback(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e) { var control = dependencyObject as Control; if (control != null) { var behavior = GetOrCreateBehavior(control); behavior.CommandParameter = e.NewValue; } } private static CommandBehaviorBase<Control> GetOrCreateBehavior(Control control) { var behavior = control.GetValue(EventCommandBehaviorProperty)
as CommandBehaviorBase<Control>; ; if (behavior == null) { behavior = CreateCommandBehavior(control,
control.GetValue(EventNameProperty).ToString()); control.SetValue(EventCommandBehaviorProperty, behavior); } return behavior; } private static CommandBehaviorBase<Control> CreateCommandBehavior(
Control control,
String eventName)
{
var type = control.GetType();
var behavior = new CommandBehaviorBinder<Control>(control);
var info = type.GetEvent(eventName);

if (info != null)
{
var methodInfo = behavior.GetType().GetMethod("Execute");
var handler = Delegate.CreateDelegate(
info.EventHandlerType, behavior, methodInfo, true);
info.AddEventHandler(control, handler);
}
else { throw new ArgumentException(String.Format(
"Target object '{0}' doesn't have the event '{1}'.", type.Name, eventName));
}

return behavior;
}
}

It looks like there’s a lot to digest here, but it’s really not so bad. Here are the key things to notice with this class:

1. All the methods are static. This is to allow for binding from XAML.

2. There are three public Dependency Properties: Command, CommandParameter and EventName. These are what we will actually be binding to from XAML.

3. The middle chunk of the class is devoted to getters and setters to allow for setting the properties from XAML.

4. The last method in the class, CreateCommandBehavior, uses reflection on the control to find the event and add an event handler to it.

Now that our infrastructure is set up, we just need to do some set up to get this thing moving. First of all, create your view model with a property for each command you want to bind to (using the type ICommand):

public ICommand MoveRightCommand { get; set; }
public ICommand MoveLeftCommand { get; set; }

Next, set up the callback methods that will be called when the commands are executed:

private void MoveRight(Object data)
{
…
}

private void MoveLeft(Object data)
{
	…
}

Then instantiate the commands in the view model’s constructor, passing the callback methods as parameters:

MoveRightCommand = new DelegateCommand<Object>(MoveRight);
MoveLeftCommand = new DelegateCommand<Object>(MoveLeft);

Your view model is now all set; time to move over to the XAML. First include the namespaces at the root of your view for the view model and the commanding stuff:

xmlns:vm="clr-namespace:CommandingExample.ViewModels"
xmlns:commands="clr-namespace:CommandingExample.Commands"

Then, set your view model as the view’s DataContext:

<Window.DataContext>
	<vm:ViewModel />
</Window.DataContext>

Finally, add the controls and bind the commands on the new view model to the events of choice:

<Button
	Content="&gt;&gt;"
	commands:Commander.Command="{Binding MoveRightCommand}"
	commands:Commander.EventName="Click" />
<Button
	Content="&lt;&lt;"
	commands:Commander.Command="{Binding MoveLeftCommand}"
	commands:Commander.EventName="Click" />

Here we are binding the commands on our view models to the Click events of these buttons. If we had CommandParameters to bind to, we would do so here as well.

Note that Buttons actually have a Command property that can be bound to more easily than this for the click event, but that is the only control that has such a property and it is easy to illustrate what we are trying to accomplish by using the Button as an example.

That’s it!

As with anything, there are pros and cons to this approach. For pros, the biggie is that no more additional classes need to be written to bind to new events. For cons, you lose the type-safety and design-time intellisense that is inherent with the approach shown in the Prism example projects. In my opinion, the pros outweigh the cons.

If you put this to use, let me know how it goes!

If you want to play around with this, here’s a sample project to tinker with:

Comments

2/10/2010 11:29:37 PM #

Radoslav

Hi Brent,

Tank’s for this excellent article. May be a best pattern for generic command binding but I have one question.
How I can pass a command parameter in WPW for example MouseWheelEventArgs on MouseWheel event to the commander.


commands:Commander.Command="{Binding MoveLeftCommand}"
commands:Commander.EventName="Click"
commands:Commander.CommandParamter= ?????????

Best regards

Radoslav

Radoslav Bulgaria

2/11/2010 2:03:26 PM #

Admin

@Radoslav

CommandParameter is a DependencyProperty, which means that it is bindable using the {binding ...} syntax.  However, if you are trying to get the MouseWheelEventArgs that typically get passed into the event handler, I think you are out of luck using this approach.

If you happen to be using the business object framework, CSLA, you will have better luck because CSLA 3.8 has some new features that do the same thing that the above approach does, but makes it even more user friendly.  In addition, you will find that the optional event args that CSLA passes in will have your MouseWheelEventArgs rolled in.

Hope this helps!

Admin United States

Comments are closed

About Me

Brent Edwards

Senior Consultant - Magenic

Owner - Edwards Digital Technologies LLC

Twitter :: LinkedIn