Introduction
After struggling with switching context between threads using Dispatcher
I'm happy to be able to convey the following solution using a Task
instead.
Using the code
I wrap the task in an execute method that takes an Action
.
private void Execute(Action action)
{
try
{
var task = Task.Factory.StartNew(() =>
{
if (action != null)
action();
}, CancellationToken.None, TaskCreationOptions.None, taskScheduler);
if (task.IsFaulted)
throw new AggregateException(task.Exception);
}
catch (Exception e)
{
if (Debugger.IsAttached) Debugger.Break();
}
}
To use the method you need to set the task scheduler within the UI thread and the SynchronizationContext
must be set.
taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
private void InvokeLoginStatusChanged(Object sender, EventArgs e)
{
if (LogonStatusChanged == null) return;
Execute(() => LogonStatusChanged(this, e));
}
Points of Interest
Using a Task
to switch context between threads is tricky when it comes to Unit tests. In those cases you can create you own Synchronization context using the following code.
[TestInitialize]
private void TestSetup()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
Note that the Synchronization Context is not created right away when you start your application.
If you reference TaskScheduler.FromCurrentSynchronizationContext()
in the App.cs constructor it will always be null
.
In stead move any initialization to OnStartup
.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
}
History
2013-12-6: The initial version.
2013-12-8: Added an example.