开发者

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.


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.


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);
}


You can fire an event to the main, UI, thread and there have:

if (this.InvokeRequired)
{
    this.Invoke(...);
}

so you are testing on the main Window itself.


BackgroundWorkers are easy to implement if you are able to given the requirements.

Define a DoWork method that runs on a background thread such as saves to the database. The RunWorkerCompleted method is called when DoWork finishes. RunWorkerCompleted runs on the UI thread, and you can update the view's list with no problems.

// on the UI thread
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += RunWorkerCompleted;
worker.RunWorkerAsync("argument");

Events:

static void DoWork(object sender, DoWorkEventArgs e)
{
    e.Result = "4";
}

static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
        string a = (string)e.Result;
        Console.WriteLine(a);
    }
    else
    {
        Console.WriteLine(e.Error.Message);
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜