Execute: Dispatching to the UI thread - canton7/Stylet GitHub Wiki

Summary

Execute is a little static helper, which makes it easier to dispatch delegates to be run on the UI thread. It wraps Application.Current.Dispatcher, and provides methods to make it easier and terser to use.

It also provides a helper property, Execute.InDesignMode. This is true if and only if the Visual Studio or Expression Blend designer is active, and code is being executed to supply dummy data for design-time display.

A high-level summary of the methods it offers is given in the table below, with more in-depth explanations given after.

Method Inline if possible Waits for Completion
Execute.OnUIThread
Execute.OnUIThreadSync ✔ (Blocks)
Execute.OnUIThreadAsync ✔ (Task)
Execute.PostToUIThread
Execute.PostToUIThreadAsync ✔ (Task)

Inline if possible: The method will check if the current thread is the UI thread. If it is, the delegate will be run synchronously. If it isn't, then it will be dispatched to the UI thread in some form.

Waits for completion: Either blocks until the delegate has finished executing, or returns a Task which completes when the delegate has finished executing.

Details

Execute.OnUIThread

Checks to see whether the current thread is the UI thead. If it is, the delegate will be run synchronously. If it is not, the delegate will be dispatched to the UI thread, to be run at some point in the future. In this case, Execute.OnUIThread will not wait for the delegate to complete.

This mirrors the traditional pattern of:

public static void InvokeIfRequired(Action action)
{
    if (Application.Current.Dispatcher.CheckAccess())
        action();
    else
        Application.Current.Dispatcher.BeginInvoke(action);
}

Execute.OnUIThreadSync

Checks to see whether the current thread is the UI thread. If it is, then it will run the delegate synchronously. If it is not, then it will dispatch the delegate to be run on the UI thread, and block until it has finished executing.

It is therefore very similar to Execute.OnUIThread, except that it will only return after the delegate has finished executing.

Execute.OnUIThreadAsync

Checks to see whether the current thread is the UI thread. If it is, then it will run the delegate synchronously, and return a completed Task. If it is not, then it will dispatch the delegate to be run on the UI thread at some point in the future, and return a Task which completes when the delegate has finished executing.

It is therefore effectively the async version of Execute.OnUIThreadSync.

Execute.PostToUIThread

Regardless of whether the current thread is the UI thread, will post the delegate to be run on the UI thread at some point in the future.

Execute.PostToUIThreadAsync

Regardless of whether the current thread is the UI thread, will post the delegate to be run on the UI thread at some point in the future, and returns a Task which completes when the delegate has finished executing.

BEWARE you must never do something like Execute.PostToUIThreadAsync(() => something(foo)).Wait(). If you do that from the UI thread, you will cause a deadlock. This approach does not make sense with the Execute.PostXXX methods - use Execute.OnUIThreadSync or Execute.OnUIThreadAsync instead.

Advanced: Unit Testing

The Dispatcher

Execute actually has a level of abstraction from Application.Current.Dispatcher. Execute.Dispatcher is a static property of type IDispatcher, and is used by Execute to dispatch delegates. This property can never be null, and defaults to an IDispatcher implementation which executes everything synchronously. It is then overridden in BootstrapperBase to be a wrapper around Application.Current.Dispatcher.

This behaviour means that methods using one of the Execute methods can be unit-tested, or used at design-time. In unit testing, all Execute methods will run their delegate synchronously (since a Dispatcher isn't available).

If you need to, you can also set Execute.Dispatcher to a custom IDispatcher implementation for your unit tests.

Design Mode

Execute.InDesignMode is also settable, and this will override the "actual" value. It is anticipated that you will almost never need to actually do this, but sometimes it's unavailable in order to unit test queer little edge cases (there are a couple of such cases in Stylet).