ContentDialog with time-consuming task does not allow me to use the App until it is finished

75 Views Asked by At

Is there a way to use the app while the task is still running?

With my current code, the app is unresponsive while the task executed from the ContentDialog does not finish.

private async void FooContentDialog(object sender, EventArgs e)
{
    ContentDialog fooDialog = new ContentDialog
    {
    // XamlRoot must be set in the case of a ContentDialog running in a Desktop app
    XamlRoot = this.Content.XamlRoot,
    Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, /* fade effect*/
    Title = "Run time-consuming fooTask?",
    PrimaryButtonText = "Yes",
    CloseButtonText = "No",
    DefaultButton = ContentDialogButton.Primary,
    Content = "An extremely time-consuming task will run. Sit back and have a coffee in the meantime."
    };
    ContentDialogResult result = await fooDialog.ShowAsync();
    
    if (result == ContentDialogResult.None)
    {            
        return;
    }
    
    //time-consuming fooTask
    fooTask();
}
1

There are 1 best solutions below

0
On

If the function you are invoking (fooTask) is a void returning function, then it means that it is doing CPU bound work.

This kind of functions blocks the executing thread, which in your case is the UI thread. Because you invoked the function from the UI thread, it's the UI thread which is in charge to execute the whole code of the function and while doing so it is completely stuck, unable to do any other activity: that's why your user interface is frozen and not responsive, basically the UI thread is unable to respond to user interactions and to move or resize the user interface accordingly to user clicks.

To summarize, your problem is the following:

I want to run some heavy CPU-bound work from the UI thread, but I don't want to freeze the user interface while doing that

The common solution to this problem is invoking the CPU-bound method by using Task.Run. Your code will be changed this way:

private async void FooContentDialog(object sender, EventArgs e)
{
    // previous code omitted for brevity
    
    await Task.Run(() => 
    {
      //executes the time-consuming fooTask
      fooTask();
    });

    // keep on working in the UI thread
}

By doing so, you are asking the .NET thread pool to execute the time consuming CPU-bound work for you and you are asynchronously waiting for the task to be completed (notice that this way you are not blocking the UI thread, which is free to keep on responding to user interactions).

Notice that, with a very few exceptions, this is the only proper usage of the Task.Run method.

If you want to learn more about the usage of Task.Run to execute CPU-bound work from the UI thread I highly recommend to read a series of articles availabe on the Stephen Cleary blog. You can start from the first article of the series.