0

I am loading docs in a datagridview from a parallel thread using the code below: inside a block Task.Run(() => {});

When I close the form I get ObjectDisposedException Cannot access a disposed object. Looks like the if (IsDisposed || Disposing) fails and the code attempts to add a row and in the meantime Disposing has became true.

I could catch this exception but it is dirty.

Adding a flag enabled when closing the form to check if the form has been closed does not work as well. It seems to behave the same way as this.Disposing

Adding a Thread.Sleep(5); before if (this.IsDisposed || this.Disposing) seems enough to avoid the exception to be raised though I am not sure if it could be raised if unlucky enough.

What would probably be the best practice to avoid this issue ?

  foreach (var doc in docList)
  {
     if (this.InvokeRequired)
     {
         if (IsDisposed || Disposing || dgv.IsDisposed || dgv.isposing) return;
         this.Invoke(new MethodInvoker(() => { dgv.Rows.Add(doc.Name); }));
     }
  }
losange
  • 335
  • 3
  • 14
  • Maybe you just want to hide form? – Iluvatar Nov 17 '17 at 12:01
  • You aren't addding rows asynchronously to the gridview. You *can't* add rows asynchronously to the gridview or any other UI element. `Invoke` means "run on the UI thread". This code simply wastes a background thread, to add rows in the slowest way possible on the UI thread, forcing a redraw each time a row is added. – Panagiotis Kanavos Nov 17 '17 at 12:26
  • You should remove *all* of this code and simply bind the gridview to the collection or datatable that contains the data. How much data is there anyway? Humans can't see 100s of rows at once, nor can you display them. If you have a lot of data use paging and virtualized scrolling to load only what's needed – Panagiotis Kanavos Nov 17 '17 at 12:27
  • 1
    Furthermore, don't use `async void` except in event handlers. The signature for asynchronous methods that don't return results is `async Task`. You can't await or stop or handle anything that runs with `async void` – Panagiotis Kanavos Nov 17 '17 at 12:29
  • What is your *actual* problem? Why are you trying to "load a grid asynchronously"? Did you encounter a performance problem? Are you adding messages as soon as they arrive? Are you trying to display too many things at once? Something else? Whatever it is, there are techniques to deal with it since .NET 1.0 came out 15 years ago. None involves `async` – Panagiotis Kanavos Nov 17 '17 at 12:31
  • For example, all you need to load the data is to assign the list to the [DataSource property](https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-bind-data-to-the-windows-forms-datagridview-control), eg `dgv.DataSource = docList`. No need for loops or `Rows.Add` – Panagiotis Kanavos Nov 17 '17 at 12:34
  • I stopped using datasource long ago because of poor maintainability. I am letting a parallel thread to add the row to avoid freezing the UI. Connection is not so fast because it is requesting objects having System.Data.Linq.Binary field. – losange Nov 17 '17 at 15:23
  • Sorry about the async I removed this from the question it's actually not being used, rows are indeed added from a parallel thread using Task.Run() – losange Nov 17 '17 at 15:26

2 Answers2

0

Don't know what you want to do with form after closing. If it's okay to just hide it use this:

private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        e.Cancel = true;
        this.Hide();
    }
Iluvatar
  • 642
  • 7
  • 19
0

Have you tried adding conditions inside Invoke:

if (this.InvokeRequired)
{
    this.Invoke(new MethodInvoker(() => 
    {
        foreach (var doc in docList)
        {
            if (IsDisposed || Disposing || dgv.IsDisposed || dgv.isposing)  return;
            dgv.Rows.Add(doc.Name); 
        }));
    }
}
Pablo notPicasso
  • 3,031
  • 3
  • 17
  • 22