1

I have a MainWindow with a TextBlock and a Button controls on it, by clicking on which the RunAsync(int) method is called. It was doing some calculations, so the process took quite a long time and blocked the UI. I was able to move it to an asynchronous thread, but I am completely stuck on how best to implement updating the interface from this method every iteration of the For loop. Using the progress bar, for example.

At the moment i have the following code:

public partial class MainWindow : Window
{
    public MainWindow() => InitializeComponent();

    private async Task<List<int>> RunAsync(int par)
    {
        return await Task.Run(() =>
        {
            List<int> list = new List<int>();
            for (int i = 0; i < par;  i++)
            {
                // some code, that takes quite a long time
                Thread.Sleep(500); 
                list.Add(i);
            }
            return list;
        });
    }
    
    private async void StartBtn_Click(object sender, RoutedEventArgs e)
    {
        int count = (await RunAsync(5)).Count;
        label.Text = count.ToString();
    }
}
  • 2 things: 1 instead of Thread.Sleep... you should use Task.Delay. 2: use the Dispatcher to update the UI from a different thread – Jonathan Alfaro Apr 06 '21 at 12:35
  • ... or simply use binding ... bind property from some class which implements INotyfiPropertyChanged and set it's instance as DataContext of MainWindow ... then from worker thread update this property ... that's it – Selvin Apr 06 '21 at 12:38
  • You can use the current SynchronizationContext to update UI. Or simply use BackgroundWorker – Pawel Maga Apr 06 '21 at 12:40
  • 2
    You may not even need to call the Dispatcher. Just move the async call into the loop body, i.e. run and await an async method and update the UI in each loop iteration. – Clemens Apr 06 '21 at 12:42
  • 1
    You may want to take a look at the [`Progress`](https://learn.microsoft.com/en-us/dotnet/api/system.progress-1) class, and [how it's used](https://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html). Another helpful tutorial is [here](https://devblogs.microsoft.com/dotnet/async-in-4-5-enabling-progress-and-cancellation-in-async-apis/). – Theodor Zoulias Apr 06 '21 at 12:46
  • 1
    The awaiting thread has a SynchronizationContext, so it will return to that thread when the Task.Run is ready. no need to invoke anything. – Jeroen van Langen Apr 06 '21 at 13:11
  • possible duplicate : https://stackoverflow.com/questions/29872663/making-a-progress-bar-update-in-real-time-in-wpf/29872887 – yu yang Jian Apr 06 '21 at 14:02

1 Answers1

5

how best to implement updating the interface from this method every iteration of the For loop. Using the progress bar, for example.

This is what IProgress<T> is for.

On a side note, you'll find your code is cleaner if you call methods using Task.Run instead of *implementingthem usingTask.Run`.

public partial class MainWindow : Window
{
  private List<int> Run(int par, IProgress<int> progress)
  {
    List<int> list = new List<int>();
    for (int i = 0; i < par;  i++)
    {
      // some code, that takes quite a long time
      Thread.Sleep(500); 
      list.Add(i);
      progress?.Report(i);
    }
    return list;
  }
    
  private async void StartBtn_Click(object sender, RoutedEventArgs e)
  {
    var progress = new Progress<int>(report => /* Do something with report */);
    var list = await Task.Run(() => Run(5, progress));
    label.Text = list.Count.ToString();
  }
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810