-1

I have read many explanations on Threading but it is still quite hard for me to grasp. I tried it in my code and I get an error like so: "Additional information: Cross-thread operation not valid: Control 'dgvLocDatabase' accessed from a thread other than the thread it was created on". Below is the code I have created. It must add data which comes from different sources to 2 different datagridview.

private void btnCountRecord_Click(object sender, EventArgs e)
{
    CountRecord();

    Parallel.Invoke(
        GetDevData,
        GetPcData
        );
}



private void GetDevData()
{

    if (bIsConnected == false)
    {
        MessageBox.Show(@"Please connect the device first", @"Error");
        return;
    }

    var sdwEnrollNumber = "";
    var idwTMachineNumber = 0;
    var idwEMachineNumber = 0;
    var idwVerifyMode = 0;
    var idwInOutMode = 0;
    var idwYear = 0;
    var idwMonth = 0;
    var idwDay = 0;
    var idwHour = 0;
    var idwMinute = 0;
    var idwSecond = 0;
    var idwWorkcode = 0;

    var idwErrorCode = 0;
    var iGlCount = 0;
    var iIndex = 0;

    dgvDevDatabase.Rows.Clear();


    axCZKEM1.EnableDevice(iMachineNumber, false); //disable the device
    if (axCZKEM1.ReadGeneralLogData(iMachineNumber)) //read all the attendance records to the memory
    {
        lblProgressBar.Text = @"Loading data. Please wait...";
        progBar.Visible = true;
        progBar.Maximum = _gRecCount;
        progBar.Minimum = 1;
        progBar.Value = 1;
        progBar.Step = 1;

        while (axCZKEM1.SSR_GetGeneralLogData(iMachineNumber, out sdwEnrollNumber, out idwVerifyMode,
                   out idwInOutMode, out idwYear, out idwMonth, out idwDay, out idwHour, out idwMinute, out idwSecond, ref idwWorkcode))//get records from the memory
        {
            iGlCount++;
            var bioId = sdwEnrollNumber;
            var timeLog = idwYear + "-" + idwMonth + "-" + idwDay + " " + idwHour + ":" 
                + idwMinute + ":" + idwSecond;
            dgvDevDatabase.Rows.Add(bioId, timeLog);
            progBar.PerformStep();
        }
        if (progBar.Value == _gRecCount) lblProgressBar.Text = @"";
    }
    else
    {
        Cursor = Cursors.Default;
        axCZKEM1.GetLastError(ref idwErrorCode);

        if (idwErrorCode != 0)
        {
            MessageBox.Show(@"Reading data from device failed. ErrorCode: " + idwErrorCode, @"Error");
        }
        else
        {
            MessageBox.Show(@"No data from terminal to return", @"Error");
        }
    }
    axCZKEM1.EnableDevice(iMachineNumber, true);//enable the device
}

private void GetPcData()
        {
            var tda = new TimeLogDataAccess();
            _rdr = tda.GetAllLogs();
            while (_rdr.HasRows && _rdr.Read())
            {
                dgvLocDatabase.Rows.Add(_rdr["bio_id"].ToString(), _rdr["time_log"].ToString());
            }
        }

If I remove the "GetPcData" from the Parallel.Invoke and substitute it with a Messagebox.Show("Call me too");, I don't get errors and if I just run the "GetPcData" on it's own, I don't get errors too. So I think it's the "GetPcData" in the Parallel.Invoke that is causing the issue.

Can you please tell me what I'm doing wrong and if you can explain more to me this parallel.invoke or threading. Thank you.

Ibanez1408
  • 4,550
  • 10
  • 59
  • 110

1 Answers1

0

The problem is, that you're trying to modify the UI in a thread other than the thread, the UI was created from. Have a look at the SynchronizationContext class (https://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext(v=vs.110).aspx) and try the following: Before you invoke a new thread assign SynchronizationContext.Current to a variable. Then start the new thread and if you want to update the UI do that by calling

myContext.Post(cb => { doSomethingOnUi(); }, null);

and see if that fixes your problem.

samwise
  • 1,907
  • 1
  • 21
  • 24