Task Commands revisited

Posted on Feb 23, 2012 in and tagged , , , ,

Half a year ago I blogged about using TPL with MVVM in a test-friendly way. My solution turned out to be pretty handy, though it was far from being perfect. In this post I will describe what was wrong with the initial implementation and can we slightly improve it.

Looking at that solution right now, I wonder why did I decide to use event async pattern (providing the ExecuteCompleted event) in the first place. The intention was to notify the caller about completion of the asynchronous operation. That is what System.Threading.Task is used for this days, isn’t it? So the obvious solution is to expose the ExecuteAsync() method returning the Task instance on the command class. Here is the source of the updated AsyncRelayCommand:

public class AsyncRelayCommand<T> : ICommand {
    private readonly RelayCommand<T> _internalCommand;
    private readonly Func<T, Task> _executeMethod;
 
    public event EventHandler CanExecuteChanged
    {
        add { _internalCommand.CanExecuteChanged += value; }
        remove { _internalCommand.CanExecuteChanged -= value; }
    }
 
    public AsyncRelayCommand(Func<T, Task> executeMethod, Predicate<T> canExecuteMethod)
    {
        if (executeMethod == null)
        {
            throw new ArgumentNullException("executeMethod");
        }
 
        _executeMethod = executeMethod;
        _internalCommand = new RelayCommand<T>(_ => { }, canExecuteMethod);
    }
 
    public AsyncRelayCommand(Func<T, Task> executeMethod)
        : this(executeMethod, _ => true) { }
 
    void ICommand.Execute(object parameter)
    {
        if (parameter is T)
        {
            ExecuteAsync((T)parameter);
        }
 
        else throw new ArgumentException("Parameter should be of type " + typeof(T));
    }
 
    public Task ExecuteAsync(T parameter)
    {
        return _executeMethod(parameter);
    }
 
    public bool CanExecute(object parameter)
    {
        return _internalCommand.CanExecute((T)parameter);
    }
 
    public void RaiseCanExecuteChanged()
    {
        _internalCommand.RaiseCanExecuteChanged();
    }
}

The code is pretty straightforward, here are the key points:

  • Create an inner command that responds to CanExecute, RaiseCanExecute calls and handles subscriptions to CanExecuteChanged event (optional, since I am using MVVM Light with most of my projects, that’s an acceptable solution for me. However, you may easily implement your own commanding logic).
  • Introduce new ExecuteAsync method that returns Task instance.
  • Implement ICommand.Execute explicitly (I find it more consistent not to have Execute method in the AsyncRelayCommand class).

Side note: as I mentioned in the beginning, I find this command extension rather useful. It was successfully used in 5 own projects (both WP7 and WPF), and I hope you enjoy it too.

As usual, the source code is available at bitbucket (TaskCommands.v2.zip).

Leave a Reply

Your email address will not be published. Required fields are marked *