Testing with Dispatcher

The threading model in the Windows Presentation Foundation (WPF) is based on the concept of a “dispatcher”. Every thread has a Dispatcher object associated with it. Dispatcher is responsible for policing cross-thread operations and provides a way for threads to marshal method invocations to each other, via its BeginInvoke and Invoke methods.

Dispatcher contains the WPF version of a Windows message queue and message pump. It provides a prioritized message queue, from which the message pump can pull messages and process them. Most WPF classes have an affinity for the thread on which they are instantiated. That thread affinity is based on an association between the object and the thread’s Dispatcher.

Using IDispatcher in your view models

 /// <summary>
 /// The Dispatcher interface.
 /// </summary>
 public interface IDispatcher
 {
 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 void Invoke(Delegate callback);

 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="object"/>.
 /// </returns>
 object Invoke(Delegate method, DispatcherPriority priority, params object[] args);

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation BeginInvoke(Delegate callback);

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation BeginInvoke(Delegate method, params object[] args);

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation InvokeAsync(Action callback);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority);
 }

The real dispatcher

public class DispatcherFactory
 {
 public static IDispatcher GetDispatcher()
 {
 return Application.Current != null ? new AppDispatcher(Application.Current.Dispatcher) : new AppDispatcher();
 }

 private class AppDispatcher : IDispatcher
 {
 private readonly Dispatcher _safeDispatcher;
 private readonly Dispatcher _currentDispatcher;

 public AppDispatcher(Dispatcher dispatcher):this()
 {
 this._safeDispatcher = dispatcher ?? this._currentDispatcher;
 }

 public AppDispatcher()
 {
 this._currentDispatcher = Dispatcher.CurrentDispatcher;
 this._safeDispatcher = this._currentDispatcher;
 }

 private Dispatcher Dispatcher
 {
 get { return this._currentDispatcher.CheckAccess() ? this._currentDispatcher : this._safeDispatcher; }
 }

 void IDispatcher.Invoke(Delegate callback)
 {
 this.Dispatcher.Invoke(callback);
 }

 object IDispatcher.Invoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 return this.Dispatcher.Invoke(method, priority, args);
 }

 DispatcherOperation IDispatcher.BeginInvoke(Delegate callback)
 {
 return this.Dispatcher.BeginInvoke(callback);
 }

 DispatcherOperation IDispatcher.BeginInvoke(Delegate method, params object[] args)
 {
 return this.Dispatcher.BeginInvoke(method, args);
 }

 DispatcherOperation IDispatcher.BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 return this.Dispatcher.BeginInvoke(method, priority, args);
 }


 DispatcherOperation IDispatcher.InvokeAsync(Action callback)
 {
 return this.Dispatcher.InvokeAsync(callback);
 }

 DispatcherOperation IDispatcher.InvokeAsync(Action callback, DispatcherPriority priority)
 {
 return this.Dispatcher.InvokeAsync(callback, priority);
 }

 DispatcherOperation<TResult> IDispatcher.InvokeAsync<TResult>(Func<TResult> callback)
 {
 return this.Dispatcher.InvokeAsync(callback);
 }

 DispatcherOperation<TResult> IDispatcher.InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority)
 {
 return this.Dispatcher.InvokeAsync(callback, priority);
 }
 }
 }

The testing dispatcher

 /// <summary>
 /// The stub dispatcher.
 /// </summary>
 public class TestingDispatcher : IDispatcher
 {
 private readonly Dispatcher currentDispatcher;

 /// <summary>
 /// Initializes a new instance of the <see cref="TestingDispatcher"/> class.
 /// </summary>
 public TestingDispatcher()
 {
 this.currentDispatcher = Dispatcher.CurrentDispatcher;
 }

 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 public void Invoke(Delegate callback)
 {
 try
 {
 this.currentDispatcher.Invoke(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="object"/>.
 /// </returns>
 public object Invoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 try
 {
 return this.currentDispatcher.Invoke(method, priority, args);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation BeginInvoke(Delegate callback)
 {
 try
 {
 return this.currentDispatcher.BeginInvoke(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation BeginInvoke(Delegate method, params object[] args)
 {
 try
 {
 return this.currentDispatcher.BeginInvoke(method, args);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 try
 {
 return this.currentDispatcher.BeginInvoke(method, priority, args);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation InvokeAsync(Action callback)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback, priority);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback, priority);
 }
 finally
 {
 this.DoEvents();
 }
 }

 private void DoEvents()
 {
 DispatcherFrame frame = new DispatcherFrame();
 this.currentDispatcher.BeginInvoke(new Action(() => frame.Continue = false), DispatcherPriority.ContextIdle);
 Dispatcher.PushFrame(frame);
 }
 }

2 thoughts on “Testing with Dispatcher

  1. This seems like a nice solution, but isn’t it incomplete?

    The factory doesn’t seem aware of the TestDispatcher… how does it ever get used? Do you set the Dispatcher.CurrentDispatcher to an instance of TestDispatcher in your test setup?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s