Going Async in UWP and WPF, Keep Your Main Thread Free

Don't leave your users hanging, C# code that won't block your UI.

Async programming is similar to highway ramps.

Async in UWP and WPF is an important tool to keep your apps responsive. Especially for any long running code. For example, web requests, file I/O or database query.

It’s common practise in apps on all platforms with a user interface, to load data in a separate thread, display some kind of “busy” indicator then callback to the main thread (UI thread) to update the UI. This is a best practise that all modern programming languages support.

A Windows app requires a bit of extra code to get this working. There are also several nuances such as error handling and exceptions. Furthermore, it’s not immediately obvious how the Task object throws exceptions.

In this post I’ll demonstrate how to fetch data in an async way while maintaining the MVVM pattern. Furthermore we’ll ensure our user knows the app is loading data in the background with a visual indicator.

I’ve written the sample for UWP but you can of course adapt this to WPF or WinForms.

MVVM

Our sample uses some commonly used nuget packages to facilitate the MVVM pattern.

You can, of course, roll your own MVVM and AsyncCommand classes but for most smaller projects these packages are perfect and save us some time.

Next, let’s organize our business logic and async API calls into service classes. Async in UWP and WPF is relatively easy to grasp with the wonderful Task class introduced in .NET Framework 4.

Async Task, Services and IoC

It’s good practice to encapsulate your business logic into services. MVVM Light has a SimpleIoC container which makes it easy to register your interfaces for dependency injection.

This example service method will simulate a long running task then return some data. The operation will execute on a separate thread and therefore not block the main thread. Note the task delay.

				
					public class PostService : IPostService
{
    public async Task<ObservableCollection<Post>> GetPostsAsync()
    {
        await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);

        ObservableCollection<Post> posts = new ObservableCollection<Post>();
        for (int i = 1; i <= 20; i++)
        {
            Post p = new Post()
            {
                Title = "Post " + i,
                Content = "The quick brown fox jumps over the lazy dog",
                PostDate = DateTime.Now
            };
            posts.Add(p);
        }
        return posts;
    }
}
				
			

Any method you declare as async must return Task, Task or void. Be sure to avoid async void methods since they’re problematic and a pain to debug. Async void methods should be returning Task instead.

Another thing I’d recommend is to avoid using Task.Result which blocks synchronously until the task is complete. You should be using await which waits asynchronously till the task is complete.

There are other async best practices to follow but these are the main ones to abide.

Async Commands in the View Model

In the ViewModel we’ll leverage the abstract class provided by MVVM Light: ViewModelBase. This class extends ObservableObject which in turn implements INotifyPropertyChanged. All we need to do it use the Set method in our setters and it will raise the PropertyChanged event.

The AsyncAwaitBestPractices.MVVM package contains a convenient and functional AsyncCommand class. Typically, we only need to supply 2 arguments to the constructor, the async method to execute and a canExecute method. The supplied Task can wrap several other async/await calls.

				
					public class MainViewModel : ViewModelBase
{
    private readonly IPostService _postService;

    public MainViewModel(IPostService postService)
    {
        _postService = postService;
        RefreshCommand = new AsyncCommand(ExecuteRefreshCommand, _ => !IsListRefreshing);
    }

    public IAsyncCommand RefreshCommand { get; }

    private ObservableCollection<Post> _posts = new ObservableCollection<Post>();
    public ObservableCollection<Post> Posts
    {
        get => _posts;
        set => Set(ref _posts, value);
    }

    private bool _isListRefreshing;
    public bool IsListRefreshing
    {
        get => _isListRefreshing;
        set => Set(ref _isListRefreshing, value);
    }

    private async Task ExecuteRefreshCommand()
    {
        Debug.WriteLine("Refreshing list...");
        IsListRefreshing = true;

        try
        {
            // Use ConfigureAwait(true) to callback to the main thread
            Posts = await _postService.GetPostsAsync().ConfigureAwait(true);

        }
        catch (Exception e)
        {
            Debug.WriteLine(e.ToString());
        }
        finally
        {
            IsListRefreshing = false;
            Debug.WriteLine("Refreshing list complete!");
        }
    }
}
				
			

You will notice the call to GetPostsAsync has an attached ConfigureAwait(true). Calling this method with true configures the task so that continuation after the await is run in the caller context. Effectively allowing us to update the UI. On the other hand, ConfigureAwait(false) should be used everywhere else in most cases.

If ConfigureAwait(bool) is passed false then the async task will complete on a separate thread. In this case, you’ll need to get back onto the main thread to update the UI. One such solution is the Dispatcher.

				
					await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => 
{
    //Update UI code here
});
				
			

Async in UWP and WPF behave relatively the same in this regard. WPF uses Dispatcher.BeginInvoke.

It’s also important to note in the ViewModel we can use try/catch blocks to catch exceptions and handle them gracefully in one place. Alternatively, you can catch some exceptions in the service then rethrow others or just return null (as a last resort).

Async Web Request

All web requests should be asynchronous, period. Sending a request to a server can be slow of flat out fail for a variety of reasons. Consequently, the Task object is perfectly suited for web requests.

In the sample, I use the Windows.Web.Http package for HTTPClient. This is a modern API built into UWP that uses native code to increase performance.

I recommend using the popular Newtonsoft.Json NuGet package to handle object deserialization. As long as your model’s properties match the JSON attributes from the server, you can deserialize in one line. Check out this code snippet.

				
					public async Task<ObservableCollection<Post>> GetPostsHttpAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);

    using (var client = new HttpClient())
    {
        string json = await client.GetStringAsync(new Uri("http://localhost:3000/posts"));
        var posts = JsonConvert.DeserializeObject<ObservableCollection<Post>>(json);
        return posts;
    }
}
				
			

To enable web requests, you must enable the “Internet (Client)” capability, though it should be enabled by default. Refer to \Assets\Test\posts-db.json for a JSON object to run with json-server.

Async File Read

File input and output is far more predictable than a web request for example. Unless you are saving or reading a ton of data, the user probably won’t notice a difference between synchronous and asynchronous.

If you are interfacing a network share then I recommend using async. Conversely, if you are reading or writing a small chunk of data directly to a local hard drive then it doesn’t matter as much. Although it doesn’t hurt using async APIs. This code snippet shows how to read a json array from file then deserialize to an object.

				
					public async Task<ObservableCollection<Post>> GetPostsFileAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);

    var installedLocation = Package.Current.InstalledLocation; //AsyncMVVM\bin\x86\Debug\AppX
    var file = await installedLocation.GetFileAsync("posts.json");
    string json = await FileIO.ReadTextAsync(file);
    var posts = JsonConvert.DeserializeObject<ObservableCollection<Post>>(json);
    return posts;
}
				
			

File and folder access in UWP is far more restrictive than WPF and for good reason. For example, access outside the application folder, data folder or user directory is heavily regulated. This post explains in detail how to handle and access files and folders on Windows in a modern fashion. The Windows.Storage package provides an easy to use async file API.

Copy \Assets\Test\posts.json from the sample app to \bin\x86\Debug\AppX to test this code snippet.

Async Database Access

Like a web request, database calls have several point of failures so it’s beneficial to do them asynchronously. The Microsoft.Data.SqlClient package, available on NuGet, will be the flagship data access driver for SQL Server going forward.

To do a fully async database call you’ll need to use the async APIs method provided. OpenAsync, ExecuteReadAsync and ReadAsync. Access to column data is synchronous in this code snippet.

To enable database calls, you must enable the “Private Networks (Client & Server)” capability. And secondly, to use “Integrated Security=SSPI;” in your connection string you must enable the “Enterprise Authentication” capability. This is done within Visual Studio.

				
					public async Task<ObservableCollection<Post>> GetPostsDbAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);

    string connectionString = "Data Source=SQL-TEST;Initial Catalog=TEST_DB;Integrated Security=SSPI;";
    using (var connection = new SqlConnection(connectionString))
    {
        await connection.OpenAsync().ConfigureAwait(false);

        string commandString = "sp_TestGetPosts";
        using (var command = new SqlCommand(commandString, connection))
        {
            using (var reader = await command.ExecuteReaderAsync().ConfigureAwait(false))
            {
                var posts = new ObservableCollection<Post>();
                while (await reader.ReadAsync().ConfigureAwait(false))
                {
                    var post = new Post
                    {
                        Title = (string)reader["Title"],
                        Content = (string)reader["Content"],
                        PostDate = (DateTime)reader["PostDate"],
                        // Use reader.GetString(int) to optimize large data sets.
                    };
                    posts.Add(post);
                }
                return posts;
            }
        }
    }
}
				
			

For a simple stored procedure to test the app out, check in \Assets\Test\posts-sp.sql.

XAML Behaviors and the User Interface

The XAML behaviors package allows invoking Commands from XAML almost anywhere. As a result, we will maintain our MVVM pattern for interactivity. Basically any UI component that doesn’t have the Command property can benefit from this. For example, there are several ways to invoke a Command on Page load. In particular, the EventTriggerBehavior fills the gap. There are dozens of events to hook into. Not only does this help maintain the MVVM, it keeps your code organized.

The Microsoft.Xaml.Behaviors .Uwp.Managed package is now open source and you must add it to your project via nuget.

Once you’ve added the package you’ll need to declare a couple XML namespaces for interactivity.

				
					<Page
    xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
    xmlns:interactivitycore="using:Microsoft.Xaml.Interactions.Core">
    ...
</Page>
				
			

The XAML code below is a child of Page. It will simply call the RefreshCommand when the Page has loaded. No fusing around.

				
					<interactivity:Interaction.Behaviors>
        <interactivitycore:EventTriggerBehavior EventName="Loaded">
            <interactivitycore:InvokeCommandAction Command="{Binding RefreshCommand}"/>
        </interactivitycore:EventTriggerBehavior>
    </interactivity:Interaction.Behaviors>
				
			

The good thing about behaviors is that you’ll probably need more than one so this helps keeps your code consistent and clean.

In the sample app I use a ProgressRing on top of the ListView to clearly show the user the app is loading data and that we haven’t forgotten about them. If we encounter an error, it’s simple to show the user what went wrong.

Sample of async in UWP.

Wrapping Up

Using async in UWP and WPF is a learning curve. Once you’ve figured it out, you’ll always be thinking async first. Asynchronous programming isn’t something you should just drop into your app, you need to plan for it and design the UI. Of course it’s possible to overdo it. Though using async across the board usually has more benefits than issues.

You can download the sample on GitHub here.

Back to top