Asynchronous WPF Commands

Programming applications for Windows has always been and event-driven affair: we’d place a control on a form, attach an event handler to it and whack some code behind to handle it. Job done. Unfortunately this approach is not very testable and as you’ve probably gathered by now, we’re very much interested in testability around here. There are couple of possible solutions to this problem and one of them (which works particularly well in WPF) is to replace event handlers with commands. This allows the view to be loosely coupled to the underlying “view model” and Darius Collins blogged about this approach using delegate WPF commands. There is also an excellent article by Josh Smith available on MSDN which discusses this pattern (Model-View-ViewModel) in detail.

To cut the long story short, the version of self-updateable DelegateCommand<T> (or RelayCommand<T> as Josh Smith calls it) is now part of the Sharpfellows.Toolkit but as it is being discussed extensively in the posts I have already mentioned I will not repeat myself here. Having said that, I feel I need to mention a minor deficiency of the UpdateableCommand<T> (as we call it): there are sadly some rare cases when the command does not work as expected when it comes to enabling/disabling the associated UI element. This is simply due to the RequerySuggested event is being raised in rather mysterious circumstances when the command manager “thinks” something may require refresh. This may not always work well with your app, so you may need to call CommandManager.InvalidaterequerySuggested() method to force it. Luckily the behaviour is repeatable and easy to spot so you do not have to worry about the app working erratically.

Asynchronous command

As it often happens, in order to keep the UI responsive, some of the commands need to execute the handler on a background thread. This is usually done using BackgroundWorker which provides convenient completion callback executing on the UI thread. There are however two major problems with this approach: first of all the threading code creates unnecessary and repeatable noise, cluttering the view model with “how” rather than letting it focus on the “what”, and secondly the multithreading introduces serious complexity into unit tests of the view model. To solve both of those issues I came up with UpdateableAsyncCommand<T> which, as the name implies, is capable of executing the command asynchronously. The clever bit about it is the fact that instead of directly using the threading API to execute the handler, the async command delegates the task of executing it to an object implementing IThreadingService interface (see below).

   1:  /// <summary>
   2:  /// Provides facilities for background tasks
   3:  /// </summary>
   4:  public interface IThreadingService
   5:  {
   6:      /// <summary>
   7:      /// Queues the execution of a task
   8:      /// </summary>
   9:      /// <param name="work"></param>
  10:      void QueueExecution(Action work);
  11:   
  12:      /// <summary>
  13:      /// Queues the execution of a task and calls back when it is complete
  14:      /// </summary>
  15:      /// <param name="work"></param>
  16:      /// <param name="completionCallback"></param>
  17:      void QueueExecution(Action work, Action completionCallback);
  18:   
  19:      /// <summary>
  20:      /// Queues the execution of a task with callback for successful completion and failure
  21:      /// </summary>
  22:      /// <param name="work"></param>
  23:      /// <param name="completionCallback"></param>
  24:      /// <param name="errorCallback"></param>
  25:      void QueueExecution(Action work, Action completionCallback, Action<Exception> errorCallback);
  26:  }

You may be wondering why on earth would we introduce an abstraction to be put on top of relatively simple threading API, but the reasons become clear when it comes to unit testing. UpdateableAsyncCommand<T> allows us to inject it with an implementation of the IThreadingService which executes the method synchronously. This fake implementation (provided for your convenience in the form of SynchronousThreadingService) allows the unit tests to run synchronously and shifts their focus to the business flow and interaction of components, rather than obscuring the picture with complex synchronisation code. The async command exposes IsBusy property which is set to true whenever the command handler is executing. As the UpdateableAsyncCommand<T> implements INotifyPropertyChanged you can even use the IsBusy property to drive the state of your UI. The following code fragment illustrates the most important aspects of the UpdateableAsyncCommand<T>

   1:  /// <summary>
   2:  /// Implements an asynchronous command
   3:  /// </summary>
   4:  /// <typeparam name="T"></typeparam>
   5:  public class UpdateableAsyncCommand<T> : NotifyPropertyChangedBase, IAsyncCommand
   6:  {
   7:      private readonly IThreadingService _threadingService;
   8:      private readonly Action<T> _executeMethod;
   9:      private readonly Func<T, bool> _canExecuteMethod;
  10:      private readonly Action _completionCallback;
  11:      private readonly Action<Exception> _errorCallback;
  12:      private bool _isBusy;
  13:   
  14:      /// <summary>
  15:      /// Initializes a new instance of the <see cref="UpdateableAsyncCommand&lt;T&gt;"/> class.
  16:      /// </summary>
  17:      /// <param name="threadingService">The threading service </param>
  18:      /// <param name="executeMethod">The execute method.</param>
  19:      /// <param name="canExecuteMethod">The method which determines if the command can be executed.</param>
  20:      /// <param name="completionCallback">The method to be executed when the command handler completes</param>
  21:      /// <param name="errorCallback">The error callback.</param>
  22:      public UpdateableAsyncCommand(IThreadingService threadingService, Action<T> executeMethod, Func<T, bool> canExecuteMethod, Action completionCallback, Action<Exception> errorCallback)
  23:      {
  24:          // Sanity checks omitted...
  25:          _threadingService = threadingService;
  26:          _executeMethod = executeMethod;
  27:          _canExecuteMethod = canExecuteMethod;
  28:          _completionCallback = completionCallback;
  29:          _errorCallback = errorCallback;
  30:      }
  31:   
  32:      /// <summary>
  33:      /// Defines the method that determines whether the command can execute in its current state.
  34:      /// </summary>
  35:      /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
  36:      /// <returns>
  37:      /// true if this command can be executed; otherwise, false.
  38:      /// </returns>
  39:      public bool CanExecute(object parameter)
  40:      {
  41:          return !IsBusy && _canExecuteMethod((T) parameter);
  42:      }
  43:   
  44:      /// <summary>
  45:      /// Occurs when changes occur that affect whether or not the command should execute.
  46:      /// </summary>
  47:      event EventHandler ICommand.CanExecuteChanged
  48:      {
  49:          add { CommandManager.RequerySuggested += value; }
  50:          remove { CommandManager.RequerySuggested -= value; }
  51:      }
  52:   
  53:      /// <summary>
  54:      /// Executes the command with given parameter.
  55:      /// </summary>
  56:      /// <param name="parameter">The parameter.</param>
  57:      public void Execute(object parameter)
  58:      {
  59:          if ( IsBusy )
  60:              throw new InvalidOperationException("Concurrent execution of the command is not supported.");
  61:   
  62:          IsBusy = true;
  63:   
  64:          _threadingService.QueueExecution(() => _executeMethod((T) parameter), CommandFinished, CommandError );
  65:      }
  66:   
  67:      /// <summary>
  68:      /// Gets or sets a value indicating whether this instance is busy.
  69:      /// </summary>
  70:      /// <value><c>true</c> if this instance is busy; otherwise, <c>false</c>.</value>
  71:      public bool IsBusy
  72:      {
  73:          get { return _isBusy; }
  74:          private set
  75:          {
  76:              if (_isBusy == value)
  77:                  return;
  78:   
  79:              _isBusy = value;
  80:              // As the IsBusy may be set from the background thread, we ned to "poke" the 
  81:              // command manager to let it know it may be a good idea to check the command's CanExecute
  82:              CommandManager.InvalidateRequerySuggested();
  83:              OnPropertyChanged(() => IsBusy);
  84:          }
  85:      }
  86:   
  87:      private void CommandFinished()
  88:      {
  89:          _completionCallback();
  90:          IsBusy = false;
  91:      }
  92:   
  93:      private void CommandError(Exception ex)
  94:      {
  95:          _errorCallback(ex);
  96:          IsBusy = false;
  97:      }
  98:  }

 

More on the IThreadingService

The contract for the threading service is quite similar to that of IScheduler (found in reactive extensions) and for a while I was thinking if we should not use it instead. It became clear however that more often than not we need to call back to UI thread when the command handler is finished. This in turn forces use of SynchronisationContext, which is not available when running unit tests on the background thread (as some test runners do). As already mentioned we use SynchronousThreadingService in unit tests which alleviates the need to use SynchronizationContext altogether.

The concept of the IThreadingService has one more benefit: as the service is usually injected by an IoC container, it is very easy to switch from asynchronous to synchronous execution mode simply by replacing the implementation. This may be very helpful when debugging an application when we suspect the issue may be caused a misbehaving thread.

PS: To pay credit where it is due, I feel I need to mention that the threading service code included in the toolkit has been authored by John Rayner. The original idea however came from Dave Hanson who since then patented it and made enough money in royalties to leave his job and embark on a year long journey across Australia :)

Putting it all together

To illustrate the use of asynchronous commands I developed a simple (and rather silly) Web search application called Bingo. The app uses Microsoft’s Bing engine API and the search, as you would expect, is being executed using async command. For what it is, the sample may seem seriously over-engineered but I wanted to illustrate how the async command fits into the big picture of MVVM application.

image

As you can see from the screenshot above, in spite of all the years spent mastering Microsoft technologies, our website still does not deserve #1 spot in the Bing search results for “sharpfellows”… How disappointing ;) 

PS: The sample is obviously available as part of the toolkit.

September 1 2010
blog comments powered by Disqus