MVVM Light ActivityCommand
In my last post I introduced the idea of Caliburn-style coroutines with MVVM Light. I touched on the idea of an ActivityCommand
class that could be used in place of RelayCommand
to allow for tighter integration with activities. Let's take a look at how that could work.
The ActivityCommand Class
First, ActivityCommand
is effectively a direct copy of RelayCommand
. The only things that change are the type of the "execute" lambda and the actual Execute method. Here's the gist:
private readonly IMessenger _messenger;
private readonly Func<IEnumerable<IActivity>> _execute;
private readonly Func<bool> _canExecute;
public ActivityCommand(IMessenger messenger, Func<IEnumerable<IActivity>> execute, Func<bool> canExecute)
{
_messenger = messenger;
_execute = execute;
_canExecute = canExecute;
}
Since we need an instance of IMessenger
later on, we have no choice but to pass one into the constructor. That's a shame, but I'd rather be passing an instance around than relying on the Messenger.Default
singleton.
Here's the Execute method, which should look reasonably familiar if you've read the last post:
public void Execute(object parameter)
{
var context = new ActivityContext(_messenger);
var enumerator = _execute().GetEnumerator();
bool done = !enumerator.MoveNext();
if (done) return;
do
{
var activity = enumerator.Current;
activity.Completed += (sender, e) =>
{
context.Cancel = e.Cancel;
context.Error = e.Error;
done = e.Cancel || e.Error != null || !enumerator.MoveNext();
};
activity.Execute(context);
} while (!done);
}
So when the command executes, we loop through all the activities returned from the _execute
lambda and kick them off one by one until the series is cancelled or an exception is thrown.
Now it becomes really easy to wire up our ICommand
properties to a series of activities:
ShowMessageCommand = new ActivityCommand(MessengerInstance, ShowMessage);
IEnumerable<IActivity> ShowMessage()
{
yield return new MessageBoxActivity("Hello from an activity!", "Hello World");
}
Obviously there's also a generic ActivityCommand<T>
that lets you pass a parameter to the "execute" lambda, just like RelayCommand<T>
.
There's one more helper class that I've introduced to simplify things slightly. There are times when, as part of a series of activities, you want to do something that's simple enough not to need its own IActivity
implementation. That's where RelayActivity
comes in:
The RelayActivity Class
RelayActivity
is a class that implements IActivity but does nothing except executes the Action
it's passed. Here's the implementation:
public class RelayActivity : IActivity
{
public RelayActivity(Action execute)
{
_execute = execute;
}
Action _execute;
public void Execute(ActivityContext context)
{
try
{
_execute();
Completed(this, new ActivityCompletedEventArgs());
}
catch (Exception ex)
{
Completed(this, new ActivityCompletedEventArgs(ex));
}
}
public event EventHandler<ActivityCompletedEventArgs> Completed = delegate { };
}
Pretty straight forward. Here's a way we might use it:
IEnumerable<IActivity> ShowMessage()
{
yield return new MessageBoxActivity("Hello from an activity!", "Hello World");
yield return new RelayActivity(() => this.Status = "Just showed a message!");
}
So we show our message, and then immediately after we set a local property somewhere. The setting of that property is such a simple task that there's no reason to define a dedicated SetStatusActivity
class for it.
So Where Are We?
Right now we have a way to define "asynchronous" tasks and execute them sequentially, and we have a way to tie that execution to an ICommand
really simply.
Questions? Comments? Criticisms? Please leave me a comment!
Trackbacks
- Windows Client Developer Roundup 053 for 1/6/2011 · All About Computer | http://ispey.com/computer/computer/windows-client-developer-roundup-053-for-162011/
- MVVM Light ActivityCommand « Mas-Tool's Favorites | http://mas-tool.com/?p=2774