Trying to learn Windows Phone development at the same time as trying to understand how to implement the MVVM pattern is a bit challenging. So my focus is on the following objectives of MVVM:
- View has a single ViewModel.
- View binds to the ViewModel’s Notifiable properties and Observable collections for data.
- View binds its behaviors to ViewModel’s ICommand implementations.
- View subscribes to ViewModel messages for navigation.
- ViewModel is unaware of view.
- ViewModel is responsible for state persistance.
I looked at a number of canned solutions for MVVM such as “MvvmLight”, “Prism” and others. I’m in no position to judge which is best for me until I can actually grasp what it is they do. And to do that I need to try to work out solutions to these objective myself, with a little help-by-example.
So to start, the following is my implementation of “commanding”. The objective is to use a control’s behaviors rather than events and to implement these behaviors in the view model rather than the code-behind. There are a number of reasons to do this, least of which is testing your logic without the need for a view.
The following is the CommandHandler class. It implements the ICommand interface needed by a controls Command property (or an Interaction’s Trigger).
public class CommandHandler : ICommand
{
private Action<object> execAction;
private Func<object, bool> canExecFunc;
public CommandHandler(Action<object> execAction)
{
this.execAction = execAction;
}
public CommandHandler(Action<object> execAction, Func<object, bool> canExecFunc)
{
this.execAction = execAction;
this.canExecFunc = canExecFunc;
}
public bool CanExecute(object parameter)
{
if (canExecFunc != null)
return canExecFunc.Invoke(parameter);
else
return true;
}
public event System.EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (execAction != null)
execAction.Invoke(parameter);
}
public void OnCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
foreach (var v in CanExecuteChanged.GetInvocationList())
{
try
{
v.DynamicInvoke(this, new EventArgs());
}
catch { }
}
}
}
}
To use this class the view model needs to provide the following implementation:
private CommandHandler someButtonCommand;
public ICommand SomeButtonCommand
{
get
{
if (someButtonCommand == null)
someButtonCommand = new CommandHandler(new Action<object>((object parm) =>
{
// execute actions for this command...
}), new Func<object,bool>((object parm)=>
{
// optionally provide feeback to the control
// as to the state of this command.
return true;
}));
return someButtonCommand;
}
}
In this example the command handler implements both the execute action and the optional canExecute function. In the XAML the binding looks like this:
<Button Content="Do It" Command="{Binding SomeButtonCommand, Mode=OneWay}" />
By binding the control’s Command property to the ICommand property of the view model you delegate command execution to the view model. In addition, the button control automatically subscribes to the CanExecuteChanged event of the handler making is very simple to disable the control when ever the view model decides that the command is not appropriate. This can be accomplished when the control invokes the CanExecute delegate or when the view model proactively invokes the OnCanExecuteChanged method of the handler. In either case, the CanExecute will return either true or false indicating the state of the command and ultimately the enabled state of the bound control.
For controls that don’t have the Command property you can still bind their behaviors by using Interaction Triggers. The following is an example of how to bind the SelectionChanged event of a ListBox to the view model using the same implementation approach as before.
<ListBox Name="listBox1" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding ItemSelectedCmd, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>