Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross-thread operation exception when worker thread adds to BindingList

I have a worker thread that needs to add items to a BindingList. However, the BindingList is databound to a DataGridView. So, when I try to add to the list, I get an InvalidOperationException (Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.)

Normally for this exception you would do:

if(winformControl.InvokeRequired) {
    winformControl.Invoke(MethodDelegate);
}

However, the databinding confuses things, as there is no Winform control in sight. All I have is the following line, which throws the exception:

ClassInstance.MyBindingList.Add(myObject);

If you have a solution specifically for this scenario, great.

If not, how can I get the worker thread to tell my main thread to perform a particular method (with several parameters supplied by the worker thread)? This may be a preferable option, since my worker thread is actually doing a bunch of stuff at the moment (like writing to the database), and I'm not sure if everything is thread-safe. I'm a student, and new to multithreading, and it really is not my forte yet.

like image 742
C Walker Avatar asked Aug 12 '10 20:08

C Walker


2 Answers

One option here is to tell BindingList<T> to use the sync-context, like this - however, this is arguably not the best approach. I wonder if you could expose your data via an event or similar (rather than adding to the list directly) - then have your UI handle the event by sending to the right thread and adding to the UI model.

like image 104
Marc Gravell Avatar answered Sep 30 '22 11:09

Marc Gravell


In your worker class constructor, try this:

private System.Threading.SynchronizationContext mContext = null;

/// <summary>
/// Constructor for MyBackgroundWorkerClass
/// </summary>
public MyBackgroundWorkerClass(System.Threading.SynchronizationContext context)
{
    mContext = context;
}

Then, when you need to invoke something on the UI thread:

private void CallOnTheUiThread(object dataToPassToUiThread)
{
    // Make sure the code is run on the provided thread context.
    // Make the calling thread wait for completion by calling Send, not Post.
    mContext.Send(state =>
        {
            // Change your UI here using dataToPassToUiThread.  
            // Since this class is not on a form, you probably would 
            // raise an event with the data.
        }
    ), null);
}

When creating your worker class from a form on the UI thread, this is what you would pass as the synchronization context.

private void Form1_Load(object sender, EventArgs e)
{
    var worker = new MyBackgroundWorkerClass(SynchronizationContext.Current);
}
like image 30
bporter Avatar answered Sep 30 '22 12:09

bporter