CommandBindings in MVVM

December 10, 2008

Anyone who has tried to implement RoutedCommands in WPF using M-V-VM has undoubtedly run into issues. Commands (non-UI commands that is) should be implemented in the ViewModel. For instance if I needed to save a CustomerViewModel then I would implement that as a command directly on my CustomerViewModel. However if I wanted to pop up a window to show the users addresses I would implement a ShowCustomerAddress command directly in the view since this a UI specific function.

The problem lies in the way that RoutedCommands find their associated CommandBindings. In order for a command to be executed it has to have a CommandBinding which tells it how to handle the Executed and CanExecute events. When you associate a control with a command it walks up the logical tree to find the first command binding associated with it. A typical scenario would look like this:

<UserControl>
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Save" CanExecute="Save_CanExecute" Executed="Save_Executed" />
</UserControl.CommandBindings>

<Button Command=”ApplicationCommands.Save” Content=”Save” />
</UserControl>

This approach forces us to define the behavior of of the command directly in our view, when really that logic should be in the ViewModel. So how do we get the the View to look for the CommandBindings in the ViewModel instead of the baked in behavior of searching up the logical tree? Well, we can’t. So instead, we need a way to get the CommandBindings out of the ViewModel and into the View. The first thing we need is a CommandBindingCollection property in our ViewModel:

private readonly CommandBindingCollection _CommandBindings;
public CommandBindingCollection CommandBindings
{
   get
   {
      return _CommandBindings;
   }
}

Now we create our CommandBindings and add them to the CommandBinding property we created:

public CustomerViewModel(Customer model)
{
//Create a command binding for the Save command
CommandBinding saveBinding = new CommandBinding(ApplicationCommands.Save, SaveExecuted, SaveCanExecute);

//Register the binding to the class
CommandManager.RegisterClassCommandBinding(typeof(CustomerViewModel), saveBinding);

//Adds the binding to the CommandBindingCollection
CommandBindings.Add(saveBinding);
}

Now with a little Attached Property goodness we add the commandbindings of our ViewModel to our View:

public static DependencyProperty RegisterCommandBindingsProperty =  DependencyProperty.RegisterAttached("RegisterCommandBindings", typeof(CommandBindingCollection), typeof(AttachedProperties), new PropertyMetadata(null, OnRegisterCommandBindingChanged));

public static void SetRegisterCommandBindings(UIElement element, CommandBindingCollection value)
{
if(element != null)
element.SetValue(RegisterCommandBindingsProperty, value);
}
public static CommandBindingCollection GetRegisterCommandBindings(UIElement element)
{
return (element != null ? (CommandBindingCollection)element.GetValue(RegisterCommandBindingsProperty) : null);
}
private static void OnRegisterCommandBindingChanged
(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
UIElement element = sender as UIElement;
if (element != null)
{
CommandBindingCollection bindings = e.NewValue as CommandBindingCollection;
if (bindings != null)
{
element.CommandBindings.AddRange(bindings);
}
}
}

This is how you would use it in the view:

<UserControl local:AttachedProperties.
RegisterCommandBindings="Binding CommandBindings}" >

image

Conclusion

There are few other examples of how to do this like Josh Smith posted on CodePlex, but I think this is a much more straightforward approach and requires less code. I really hope this helps someone dealing with the same issue. If you think there is a better way, please let me know.

Code

There is a full example that you can download here. Be sure to change the file extension from .doc to .zip.
kick it on DotNetKicks.com