Tuesday, October 19, 2010

Automatic MVVM Command Binding

When working with MVVM it can be quite a lot of work to wire up all commands with the view, bind them and make sure the right method was called.

Furthermore, you would need a property in your ViewModel that would bind to the view, wouldn’t it be nice to just have some methods in your ViewModel that would just like that respond to the buttons you click.

I came across this technique while looking at a MIX talk by Rob Eisenberg. The following example is an easy to use simple framework. It allows you to implement this quickly.  And use it without needing to change a lot of your already in place MVVM screens.  It is based on a great MVVM framework that Rob has been working on : Caliburn , which definitely is worth a look.

The concept that we are working with here, is to have some convention in our application in regards to using ViewModels and Commands.  The convention in this example is that all methods used for handling commands start with “Execute” and end with “Command”.  This is a very intuitive way to write commands, and is actually the way I was declaring then before I started to use this setup.

Lets say for a moment, we want to create new MVVM screen using a ViewModel, in this example we want to declare our ViewModel inside our View by calling an extension method “Bind<TViewModel>()” and binding the ViewModel to it.  You could eventually extract this into a Bootstrapper or so… But for now we will just keep it this way.

Our first class to do this, is our Extension class called “MvvmBindingExtension”.  It accepts a TViewModel generic type and reflects all Command methods in it.  Then it binds them to the View so they can be invoked.

Lets have a look at the implementation of this extension.

public static class MvvmBindingExtension
{
private const string COMMAND_METHOD_PREFIX = "Execute";
private const string COMMAND_METHOD_SUFFIX = "Command";

/// <summary>
/// Bind a ViewModel to a View, and auto bind commands
/// </summary>
/// <typeparam name="TViewModel">The ViewModel that should be </typeparam>
/// <param name="element">The view we want to bind our viewmodel to</param>
public static void Bind<TViewModel>(this FrameworkElement view) where TViewModel : new()
{
//Create an instance of our viewModel
object viewModel = new TViewModel();

//Get the type of the viewmodel for reflection purposes.
Type viewModelType = viewModel.GetType();

//Get all command methods obbeying the convention we set up for command handler methods
IEnumerable<MethodInfo> commandMethods = viewModelType.GetMethods()
.Where(method => method.Name.StartsWith(COMMAND_METHOD_PREFIX))
.Where(method => method.Name.EndsWith(COMMAND_METHOD_SUFFIX));

foreach (MethodInfo commandMethod in commandMethods)
{
//Make sure we get the right name of the method
int lengtOfSubString = commandMethod.Name.Length - COMMAND_METHOD_PREFIX.Length - COMMAND_METHOD_SUFFIX.Length;
string commandName = commandMethod.Name.Substring(COMMAND_METHOD_PREFIX.Length, lengtOfSubString);

//Get button by name
ButtonBase button = view.FindName(commandName) as ButtonBase;
if (button != null)
{
//Set binding to reflection command
button.SetBinding(ButtonBase.CommandProperty, new Binding { Source = new ReflectionCommand(commandMethod, viewModel) });
}
}
view.DataContext = viewModel;
}
}


You can see that we are using a “ReflectiveCommand” here,  this command takes the MethodInfo we have extracted from the ViewModel, and invokes it when executing the command on the ViewModel. 
We are binding this directly to the Source, so we don’t have to create any properties in our ViewModel to bind to.



public class ReflectionCommand : ICommand
{
private object target;
private MethodInfo method;

public ReflectionCommand(MethodInfo method, object target)
{
this.method = method;
this.target = target;
}

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

public event EventHandler CanExecuteChanged;

public void Execute(object parameter)
{
method.Invoke(target, new[] { parameter });
}
}


This is command is then bound to a FrameworkElement that has the same name as the Command method without it’s PRE- and SUFFIX. So basically for a “Save” method this would be “ExecuteSaveCommand” where Save would then be set as the name of the element.



<Grid x:Name="LayoutRoot" Background="White">
<Button x:Name="Save" Content="Save"/>
</Grid>


Finally we would have a ViewModel that would look something like this, no more Command properties, no more RelayCommands being initialized. Just simply one public command method we can use.



  public class ViewModel
{
public void ExecuteSaveCommand(object parameter)
{
MessageBox.Show("Hello world!");
}
}

No comments:

Post a Comment