BindingList.Clear performed Async causes DataGridView System.IndexOutOfRangeException

485 views Asked by At

I need some help. I have a DataGridView that is just a readonly one bind to a BindingList. I first set the DataSource for the new list and them I add the Items to the list. the Columns are generated automatically. when I need to reload the page I just clear the list and add the items again.

if I perform the reload of the page sync with the UI it works fine but if I start a task and do it, calling Clear() on the BindingList fails with an exception:

System.IndexOutOfRangeException was unhandled
  HResult=-2146233080
  Message=Index 0 does not have a value.
  Source=System.Windows.Forms
  StackTrace:
       at System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
       at InCare.UserControls.DataGridViewItemWrapper.DataGridViewOnRowsAdded(Object sender, DataGridViewRowPostPaintEventArgs args) in C:\ws\Source\Repos\incare\Src\InCare.UserControls\DataGridViewItemWrapper.cs:line 40
       at System.Windows.Forms.DataGridViewRowPostPaintEventHandler.Invoke(Object sender, DataGridViewRowPostPaintEventArgs e)
       at System.Windows.Forms.DataGridView.OnRowPostPaint(DataGridViewRowPostPaintEventArgs e)
       at System.Windows.Forms.DataGridView.PaintRows(Graphics g, Rectangle boundingRect, Rectangle clipRect, Boolean singleHorizontalBorderAdded)
       at System.Windows.Forms.DataGridView.PaintGrid(Graphics g, Rectangle gridBounds, Rectangle clipRect, Boolean singleVerticalBorderAdded, Boolean singleHorizontalBorderAdded)
       at System.Windows.Forms.DataGridView.OnPaint(PaintEventArgs e)
       at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer)
       at System.Windows.Forms.Control.WmPaint(Message& m)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
1

There are 1 answers

1
Dennis On

This is multi-threading issue.

BindingList is not thread-safe. When you're attempting to clear it from task, which works on worker thread, DGV attempts to read it from from UI thread.

You didn't post code, but the basic approach is to build some collection inside task, and return this collection to replace BindingList content.

Do not try to change BindingList from background thread:

private async Task<IList<SomeDataItem> GetDataItemsAsync()
{
    // do some work in background, e.g. call web service or database
    // ...
    return dataItems;
}

pirvate async void HandleRefreshButtonClick(object sender, EventArgs e)
{
    var dataItems = await GetDataItemsAsync();

    // since we didn't call ConfigureAwait(false) for task,
    // the rest of method will run on UI thread
    bindingList.Clear();

    foreach (var item in dataItems)
    {
        bindingList.Add(item);
    }
}