4.7 KiB
Await a UI task sent from a background thread
Enables code running on a background thread to await a task that must run on the UI thread.
Normally, you can update your UI from a background thread
by calling Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => /* update the UI */)
.
However, this merely schedules the work on the UI thread and returns immediately,
even if the task is to await user input in a popup box.
It also does not provide a way for the task to return a result to the caller.
RunTaskAsync provides an alternative that uses TaskCompletionSource in combination with RunAsync to return a Task that you can await from your background thread, thereby pausing execution until the UI task completes.
Because RunTaskAsync is an extension method, you call it as if it were a method on Dispatcher:
var result = await Dispatcher.RunTaskAsync(async () => { ...; return value; });
using System;
using System.Threading.Tasks;
using Windows.UI.Core;
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher dispatcher,
Func<Task<T>> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
{
var taskCompletionSource = new TaskCompletionSource<T>();
await dispatcher.RunAsync(priority, async () =>
{
try
{
taskCompletionSource.SetResult(await func());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
return await taskCompletionSource.Task;
}
// There is no TaskCompletionSource<void> so we use a bool that we throw away.
public static async Task RunTaskAsync(this CoreDispatcher dispatcher,
Func<Task> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>
await RunTaskAsync(dispatcher, async () => { await func(); return false; }, priority);
}
Usage
public static async Task MyMethod()
{
System.Diagnostics.Debug.WriteLine("MyMethod() is running");
var buttonLabel = await CoreWindow.GetForCurrentThread().Dispatcher.RunTaskAsync(async () =>
{
var popup = new Windows.UI.Popups.MessageDialog("Alert",
"MyMethod() is paused. Choose a button to continue running MyMethod()");
popup.Commands.Add(new Windows.UI.Popups.UICommand("Button 1"));
popup.Commands.Add(new Windows.UI.Popups.UICommand("Button 2"));
popup.CancelCommandIndex = 0;
var command = await popup.ShowAsync();
// The value returned by the lambda expression will be the RunTaskAsync return value.
return command.Label;
});
System.Diagnostics.Debug.WriteLine($"MyMethod() is running again. User clicked {buttonLabel}");
}
One situation where RunTaskAsync is handy is when you want to render a software bitmap to the screen from a non-ui thread, which requires using the dispatcher to run async code on the UI thread.
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
public static async Task RenderToImageElement(
Image imageElement, SoftwareBitmap softwareBitmap)
{
// Changes to imageElement must happen on the UI thread.
await imageElement.Dispatcher.RunTaskAsync(async () =>
{
if (softwareBitmap != null)
{
var imageSource = new SoftwareBitmapSource();
await imageSource.SetBitmapAsync(softwareBitmap);
imageElement.Source = imageSource;
}
else
{
// Clear the image element.
imageElement.Source = null;
}
});
}
See also
CoreDispatcher class
CoreDispatcher.RunAsync method
TaskCompletionSource class
Lambda expressions (anonymous methods using the "=>" syntax)
Extension methods
Interpolated strings (strings with a $ prefix)
The UWP Community Toolkit includes an extended version of this task snippet in the form of a DispatcherHelper class (source, docs).