0

Trying to perform a heavy task and keep UI updated. Using async/await, I would like to refresh some controls once the processing is complete. Basically code looks like this:

private async void btn_Click(object sender, EventArgs e)
{
    // progress reporter
    var progressHandler = new Progress<string>(value =>
    {
        lblProgress.Text = value.ToString();
    });
    var progress = (IProgress<string>)progressHandler;

    // async method call
    await MyTask(progress); // this will update a list with data (myList)

    // custom method that sets grid data source and rebinds the grid:
    // executed but grid NOT refreshed although this runs in Main thread
    // -> why?
    gridHelper.Reload(myGrid, myList); 

    //   Reload(myGrid) above basically does this:
    //   grid.DataSource = myList;
    //   grid.ResetBindings();

    lblProgress.Text = string.Empty; // works!
    SetControlsEnabled(true);        // works! (updates some buttons status)
}

private async Task MyTask(IProgress<string> progress)
{
    await Task.Run(() =>
    {
        // some pre-processing
        // ...

        while (condition)
        {
            // time-consuming processing
            // ...
           
            // report progress
            progress.Report("some progress done...");

        }
    }).ConfigureAwait(false);
}

Most of it works. Task is performed, labels are updated, progress is reported and UI is not blocked. But the grid is not updated, although it is running on Main thread and after async task completes. If I call that exact same grid refresh code in another event handler, it works.

I already read extensive documentation about this (special thanks to Stephen Stephen Cleary), and I think I could get this working using Thread classes. However I wonder why isn't the grid updated?

Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
evilmandarine
  • 4,241
  • 4
  • 17
  • 40
  • `Refresh` doesn't do what you think it does. Try wrapping the list in [a BindingSource](https://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource(v=vs.110).aspx?f=255&mspperror=-2147217396#Anchor_8) – Crowcoder Dec 12 '17 at 13:40
  • Wondering if you need to capture the synchronization context of main thread and ensure that context is running within the scope of the progress report. – Botonomous Dec 12 '17 at 13:42
  • @Crowcoder: I thought it forced the control to repaint (invalidate and redraw), isn't that what it does? – evilmandarine Dec 12 '17 at 13:48
  • @Botonomous: then I need to Post a message, something like this? https://stackoverflow.com/a/12029045/2983568 – evilmandarine Dec 12 '17 at 13:48
  • @supafly ok, you do know what it does. But that does not mean that any data updates have happened. Your data must be databinding aware, like a BindingList or BindingSource. – Crowcoder Dec 12 '17 at 13:49
  • where is gridHelper instance? – Botonomous Dec 12 '17 at 13:52
  • @Botonomous: it's a MainForm member: private readonly GridHelper gridHelper = new GridHelper(); – evilmandarine Dec 12 '17 at 13:53
  • I don't see where you are doing anything with myGrid. I see you call refresh on gridHelper passing in myGrid, but where do you manipulate it? Seems the refresh is happening, but there is nothing to update. – Botonomous Dec 12 '17 at 13:55
  • @Botonomous: Ok edited the code. The Refresh is not important. The awaited method updates a list that is also a MainForm member. Once that is done, the Reload takes the grid and the list and performs the operation described in the comments (= set grid datasource then reset bindings). – evilmandarine Dec 12 '17 at 14:00
  • Unbinding the DGV before running threaded code that updates the data source is very important. Bound data updates are not thread-safe and don't produce an InvalidOperationException. – Hans Passant Dec 12 '17 at 14:18

1 Answers1

1

Thanks to the insightful comments, a working fix to this issue is the following:

// add this to unbind data source before modifying data
myGrid.DataSource = null;

// async method call that modifies data
await MyTask(progress);

Note that just clearing the data bindings did not work in my case:

myGrid.DataBindings.Clear(); // not working
evilmandarine
  • 4,241
  • 4
  • 17
  • 40