Skip to content

WPF ShowDialogAsync

jbe2277 edited this page Jun 21, 2018 · 2 revisions

Note: The open-source application Waf DotNetApiBrowser shows the techniques described in this article.

This article shows how to implement an async variant of ShowDialog and why this has an advantage in some scenarios.

Scenario

Show a WPF dialog and then fill it with asynchronously loaded data. During loading the dialog should show some progress indication and when the loading is completed the dialog will be filled with the data.

Straight forward implementation

Show the WPF dialog and then load the data within the Loaded event handler.

Better design

The disadvantage of the straight forward implementation is that loading of the data will be done in the code-behind part of the View (e.g. Loaded event handler). ShowDialogAsync is an alternative which allows a class (e.g. Controller) to be responsible for showing the dialog and to load the data.

Sample: Using ShowDialogAsync

The following sample starts to download some content from the internet and shows a WPF dialog at the same time. When the download completes then the dialog is updated with the content. If the user closes the dialog before the download has completed, then the CancellationTokenSource is used to cancel the download.

private async Task ShowDialogAndLoadDataAsync(object ownerWindow)
{
    var cancellationSource = new CancellationTokenSource();
    var downloadTask = DownloadAsync(cancellationSource.Token);
    var customDialog = new CustomDialog();
    var dialogTask = customDialog.ShowDialogAsync(ownerWindow);

    var firstCompletedTask = await Task.WhenAny(downloadTask, dialogTask);
    if (firstCompletedTask == downloadTask)
    {
        customDialog.DataContext = await downloadTask;
    }
    else
    {
        cancellationSource.Cancel();
    }

    try
    {
        await Task.WhenAll(downloadTask, dialogTask);
    }
    catch (OperationCanceledException)
    {
        // Ignore this exception
    }
}

private async Task<string> DownloadAsync(CancellationToken cancellationToken)
{
    var httpClient = new HttpClient();
    var httpClientTask = httpClient.GetAsync("http://www.google.com", 
        cancellationToken);

    await Task.Delay(2000, cancellationToken); // Simulate some visible delay

    var httpResult = await httpClientTask;
    return await httpResult.Content.ReadAsStringAsync();
}

Sample: Implementation of ShowDialogAsync

This part shows the implementation of ShowDialogAsync.

public partial class CustomDialog : Window
{
    private TaskCompletionSource<object> showDialogCompletionSource;

    public CustomDialog()
    {
        InitializeComponent();
        Closed += OnClosed;
    }

    public Task ShowDialogAsync(object owner)
    {
        if (showDialogCompletionSource != null) 
            throw new InvalidOperationException("The dialog is already shown.");
        showDialogCompletionSource = new TaskCompletionSource<object>();
        Dispatcher.BeginInvoke(DispatcherPriority.Send, (Action)(() =>
        {
            Owner = owner as Window;
            ShowDialog();
        }));
        return showDialogCompletionSource.Task;
    }

    private void OnClosed(object sender, EventArgs e)
    {
        showDialogCompletionSource.SetResult(null);
        showDialogCompletionSource = null;
    }
}

Further readings

  1. WPF Dispatcher BeginInvoke vs. InvokeAsync
Clone this wiki locally