Search code examples
c#winformsthread-synchronization

Control 'x' accessed from a thread other than the thread it was created on


winform app, i have a grid view and the datasource populate (on bind function) by delegate begin invoke sapareted thread, but the gridView DataSource cannot get the generated value from the new thread because the gridview was created on Main Thread:

Here i invoke new thread

    private void button_selectFile_Click(object sender, EventArgs e)
    {

        if (resultLoadingFile == DialogResult.OK)
        {
            filename = openFileDialog_logLoader.FileName;
            string name = System.IO.Path.GetFileName(filename);
            label_selectFileStatus.Text = name;

            readDelegate parseAndSplit = new readDelegate(ReadLogFileAndDrawTable);
            AsyncCallback cb = new AsyncCallback(doneReadFile);
            IAsyncResult ar = parseAndSplit.BeginInvoke(filename, cb, dataGridView_mainTable);
        }
    }

Here i call bind:

    private void doneReadFile(IAsyncResult ar)
    {
        Bind();
    }

And this is Bind():

private void Bind(){
        TableLoadMgr.ItemsLoaded = TableModelListFiltered.Count();
        updateLoadedStatus();
        //The following line throw exception:
        dataGridView_mainTable.DataSource = TableModelListFiltered;
    }

enter image description here enter image description here The exacly question is: How do i fire Bind() on the Main Thread since the callback function fire on the new delegate thread.

Remarks:

  1. Duplicate subject question i saw didn't answer winform and the constraints
  2. Timer is not an option
  3. new user trigger (such button "show" after thread complete) is no an option

Solution

  • Your AsyncResult will have an AsyncState that holds a reference to your DataGridView. So you can use that control to check whether a context switch is needed for Bind() and if yes, use the Invoke of the control to switch threads:

    private void doneReadFile(IAsyncResult ar)
    {
        var ctl = ar.AsyncState as System.Windows.Forms.Control; // the control
        if (ctl != null && ctl.InvokeRequired) { // is Invoke needed?
            // call this method again, but now on the UI thread.
            ctl.Invoke(new Action<IAsyncResult>(doneReadFile), ar);
        } else {
           Bind();
        }
    }