hossein zarei
hossein zarei

Reputation: 71

How run async Task on WPF without hanging gui

I Have WPF App for Monitoring data and I write Task.Run in Loaded method to do work and show data in GUI.

The code looks like below:

private async void Window_Loaded(object sender, RoutedEventArgs e)
{

        await Task.Run(() => DoAddAsync());
}

void DoAddAsync()
{

  while (true)

  {
       this.Dispatcher.Invoke(() =>
       {
              //Do Some
       });
  }
}

It works fine and I can change component in GUI without hanging.

But if I execute a method in this.Dispatcher.Invoke, GUI is hanging and I can't do anything until that method closed.

Anybody can help me to run method in this.Dispatcher.Invoke without hanging GUI?

Upvotes: 2

Views: 3018

Answers (1)

Ivan Salo
Ivan Salo

Reputation: 821

You can use an asynchronous loop. Even though this looks like an infinite loop, its really not. Because of the await of the delay Task, this method yields control back to the its caller periodically. If this weren’t an async method with a valid asynchronous operation, it would be a synchronous method and would deadlock the UI when we called it.

But since it is not, it works as needed and gives you both a very clear intent of what the code is supposed to do as well as keeps us on the UI thread the whole time (the magic of await!) so we don't have multithreaded issues or multiple delegates/callbacks.

private async Task DoAddAsync()
{
    while (true)
    {
        // do the work in the loop
        // ....
        // don't run again for at least 200 milliseconds
        await Task.Delay(200);
    }
}

The real trick to making this work though, is not in the DoAddAsync method. Its how we call it. You do not await the call to it.

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
     DoAddAsync()
}

You can also update your way but it introduces multiple delegates, potential multithreading complexities depending on the work being done and we still have to get back onto the UI thread to update the UI.

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    await Task.Run(async () =>
    {
        while (true)
        {
            // do the work in the loop

            // update the UI on the UI thread
            Dispatcher.Invoke(() => // do some );

            // don't run again for at least 200 milliseconds
            await Task.Delay(200);
        }
    });
}

Upvotes: 1

Related Questions