-2

Getting cross thread error trying to update a row in datagridview from Backgroundworker process RunWorkerCompleted event.

I have a separate class where i am doing long running work in the backgroundworker and when complete trying to update the datagridview from the result. Event fires but get a cross tread exception.

Fails when i try to update the gridview here DataGVhome.Rows[rowIndex].Cells["AlertInfo"].Value = alertMsg.SensorAlert;

From reading numerouos articles and others having issues this is supposed to work i.e. handling the DGV row update once the backgroundworker completed event fires.

    private void MSbackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            string hitfile = (string)e.Argument;
            e.Result = _MassSpec.ParseMassFile(hitfile);
        }
        catch(Exception ex)
        {
            log.Error("Error in MDShomeForm:MSbackgroundWorker_DoWork - " + e.Result.ToString() + " " + ex.Message + Environment.NewLine);
        }
    }

    private void MSbackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // check error, check cancel, then use result
        if (e.Error != null)
        {
            // handle the error
            MetroMessageBox.Show(this, "Error in Mass RunWorker = " + e.Error.Message);
        }
        else if (e.Cancelled)
        {
            // handle cancellation
            MetroMessageBox.Show(this, "Mass RunWorker was Cancelled = " + e.Error.Message);
        }
        else
        {
            AlertMsg alertMsg = (AlertMsg)e.Result;
            // Test it for hit and update grid in the UI thread
            try
            {
                string searchValue = "";
                int rowIndex = -1;
                //update the gridview for this sensor
                searchValue = alertMsg.SensorType;
                foreach (DataGridViewRow row in DataGVhome.Rows)
                {
                    if (row.Cells[2].Value.ToString().Equals(searchValue))
                    {
                        rowIndex = row.Index;
                        break;
                    }
                }
                if (rowIndex > -1)
                {
                    // update the L1 Alert for this sensor at rowIndex

                    DataGVhome.Rows[rowIndex].Cells["AlertInfo"].Value = alertMsg.SensorAlert;
                    //dataGVhome.Rows[rowIndex].Cells["AlertIndicator"].Value = alertMsg.SensorAlert;
                    switch (alertMsg.SensorAlertInd)
                    {
                        case (int)StandardVals.AlertInds.Green:
                            DataGVhome.Rows[rowIndex].Cells["AlertIndicator"].Value = "Green";
                            DataGVhome["AlertIndicator", rowIndex].Style.BackColor = Color.LightGreen;
                            break;
                        case (int)StandardVals.AlertInds.Yellow:
                            DataGVhome.Rows[rowIndex].Cells["AlertIndicator"].Value = "Yellow";
                            DataGVhome["AlertIndicator", rowIndex].Style.BackColor = Color.Yellow;
                            break;
                        case (int)StandardVals.AlertInds.Red:
                            DataGVhome.Rows[rowIndex].Cells["AlertIndicator"].Value = "Red";
                            DataGVhome["AlertIndicator", rowIndex].Style.BackColor = Color.Red;
                            break;
                    }
                    DataGVhome.Update();
                }
            }
            catch (Exception ex)
            {
                log.Error("Error in MDShomeForm:MSBackgroundWorkerCompleted - " + ex.Message + Environment.NewLine);
            }
        }
        // general cleanup code, runs when there was an error or not.
    }

I log the exception here 2019-06-26 17:16:18,564 ERROR MDS_Command_Application.MDShomeForm - Error in MDShomeForm:MSBackgroundWorkerCompleted - Cross-thread operation not valid: Control 'DataGVhome' accessed from a thread other than the thread it was created on.

richgso
  • 3
  • 2
  • You are **100% sure** the error is coming from one of the lines of code you have shown us? Does the string `MDShomeForm:MSBackgroundWorkerCompleted` exist elsewhere in your code? – mjwills Jun 26 '19 at 22:34
  • I usually use a Progress Event and pass the datatable as a state object to the main thread (the form). The error you are getting is due to cross threading because the BackGroundWork is a different process from the the Form. – jdweng Jun 26 '19 at 23:38
  • It returns the AlartMsg Class and that is what i am using to try and update the datagrid. – richgso Jun 27 '19 at 19:17
  • It returns the AlertMsg Class and that is what i am using to try and update the datagrid. The data is correct in that returned class. And Yes that is the line when i run debugger the first time it tries to update the datagrid column it throws exception. Only MSBackgroundWorkerCompleted used to catch the event of worker competed. Very puzzling since i believe this is how MS wants us to do it if you use the backgroundworker process. Thanks for looking. Have been banging my head against this for a while now. – richgso Jun 27 '19 at 19:26
  • My guess is you're assigning `MSbackgroundWorker_RunWorkerCompleted` to a background thread, but since you won't show that code we'll never know. – Dour High Arch Jun 27 '19 at 23:40
  • As mentioned it is in the e.result and assigned in the else statement. Am i missing what you are looking for me to show? I have all the code for the dowork and runCompleted in original above. Please let me know if you need something else. My understanding is that the RunCompleted event is back on the main thread and where we can then update controls in the main form. – richgso Jun 30 '19 at 14:52
  • else { AlertMsg alertMsg = (AlertMsg)e.Result; – richgso Jun 30 '19 at 14:52
  • Somewhere in your code is something like `WeNeedThis += MSbackgroundWorker_RunWorkerCompleted`; we need to see the `WeNeedThis` code; I'm betting it's in a background thread. – Dour High Arch Jul 01 '19 at 17:02
  • So sorry you were looking for where events were wired up. – richgso Jul 01 '19 at 20:04
  • In the initializecomponent main form: MSbackgroundWorker.DoWork += new DoWorkEventHandler(MSbackgroundWorker_DoWork); MSbackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler( MSbackgroundWorker_RunWorkerCompleted); – richgso Jul 01 '19 at 20:04

1 Answers1

-1

Well this does indeed appear to still be an MS bug (i am using VS 2017 community FYI) since it does not allow updating of the main UI thread form the runworkercompleted event even though the docs say it should. Below is how i was able to get it to update the UI from this event without the cross thread exception. Wasted a lot of time trying all different ways but hope this helps. Had to use Invoke to bypass this exception. I had also switched to using a datatable in and fill in hopes it would clear but same issue until i did the below.

Replaced the else code in the RunworkerCompleted event and all good now.

else
    {
        AlertMsg alertMsg = e.Result as AlertMsg;
        // Don't do anything if the form's handle hasn't been created 
        // or the form has been disposed.
        if (!this.IsHandleCreated || this.IsDisposed) return;
        // Invoke an anonymous method on the thread of the form.
        this.Invoke((MethodInvoker)delegate
        {            
            this.l1SensorAlertsTableAdapter.Fill(aGE_MDS_DevDataSet3.L1SensorAlerts);
        });
     }
richgso
  • 3
  • 2