Jake Gearhart
Jake Gearhart

Reputation: 307

updating datagridview from within a thread causes error c# winform

I am writing a C# winform that uses a datagridview object. At a certain point in the program I need to update said datagridview from within a thread. This causes the program to throw an unhandled exception saying that object reference not set to an instance of an object, and makes the datagridview turn into the error image of a white box with an red X in it.

Specifically what I am trying to do in this case is update values in the rows, by removing the rows and updating the statistical values and reinserting the rows.

I do this in a separate function that the thread calls and it updates two rows in the datagridview every time.

Here is the code for my update function:

private void updateStats(int binNumber)
    {
        binNumber *= 2;//has to multiply by 2 because every bin has two rows in this table
        if (statsData.Rows.Count >= (binNumber + 1))
        {
            statsData.Rows.RemoveAt(binNumber);
            //number does not change because the index was just decreased by one
            statsData.Rows.RemoveAt(binNumber);//because every bin requires two rows
        }
        Bin bin = bins[binNumber / 2];
        List<double> realETempData = new List<double>();
        List<double> imagETempData = new List<double>();
        List<double> realAlphaTempData = new List<double>();
        List<double> imagAlphaTempData = new List<double>();
        //updates average and std dev values
        foreach (MinSearchData dataPoint in bin.binData)
        {
            realETempData.Add(dataPoint.RealE);
            imagETempData.Add(dataPoint.ImagE);
            realAlphaTempData.Add(dataPoint.RealAlpha);
            imagAlphaTempData.Add(dataPoint.ImagAlpha);
        }
        bin.AverageRealE = realETempData.Average();
        bin.AverageImagE = imagETempData.Average();
        bin.StdDevRealE = calculateStandardDeviation(realETempData);
        bin.StdDevImagE = calculateStandardDeviation(imagETempData);
        bin.AverageRealAlpha = realAlphaTempData.Average();
        bin.AverageImagAlpha = imagAlphaTempData.Average();
        bin.StdDevRealAlpha = calculateStandardDeviation(realAlphaTempData);
        bin.StdDevImagAlpha = calculateStandardDeviation(imagAlphaTempData);
        realETempData.Clear();
        imagETempData.Clear();
        realAlphaTempData.Clear();
        imagAlphaTempData.Clear();
        DataRow myRow = statsData.NewRow();

        myRow[0] = bin.BinName;
        myRow[1] = "Real";
        myRow[2] = bin.AverageRealAlpha;
        myRow[3] = bin.StdDevRealAlpha;
        myRow[4] = bin.AverageRealE;
        myRow[5] = bin.StdDevRealE;
        statsData.Rows.InsertAt(myRow, binNumber);

        DataRow myRow2 = statsData.NewRow();
        myRow2[0] = "";
        myRow2[1] = "Imaginary";
        myRow2[2] = bin.AverageImagAlpha;
        myRow2[3] = bin.StdDevImagAlpha;
        myRow2[4] = bin.AverageImagE;
        myRow2[5] = bin.StdDevImagE;
        statsData.Rows.InsertAt(myRow2, binNumber + 1);
    }

The stranger part of this is that I seem to be unable to catch the exception, and it happens inconsistently, as in sometime it fails the first time it tries to update, and other times it fails on the first try.

Any help would be greatly appreciated,

Thanks,

-Jake

Upvotes: 0

Views: 2012

Answers (1)

Alireza
Alireza

Reputation: 10476

This may cause due to race problem. In other words, one thread (likely the main thread) tries to paint the grid based on its current list of rows while the other thread infers and manipulates it. Moreover, I guess you have turned off CheckForIllegalCrossThreadCalls to be able to manipulate the grid directly over threads. If it is, this is the main cause of the problem. Anyway, the possible solution is to use BeginInvoke to indirectly work with the control:

private void updateStats(int binNumber)
{
  datagridview1.BeginInvoke(new MethodInvoker(() =>
  {
        binNumber *= 2;//has to multiply by 2 because every bin has two rows in this table
        if (statsData.Rows.Count >= (binNumber + 1))
          ....
          ....
          ....        
        myRow2[5] = bin.StdDevImagE;
        statsData.Rows.InsertAt(myRow2, binNumber + 1);
   }
}

EDIT:

I thought statsData is a DataGridView. Based on OP's comment it is a DataTable. So, I tailored the answer to reflect this fact.

Upvotes: 2

Related Questions