views:

328

answers:

4

I am getting this error when I try to read a property from a custom panel control. The property returns the value of a textbox within the panel. How do I read the property that returns the value of the textbox control from another thread? Sample of my property code is below. I am not worried about the setter.

Here is the eaxct error message: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.

public string Header
{
get
{
   return _HeaderComboBox.Text;
}
set
{
   _HeaderComboBox.Text = value;
}
}
+2  A: 

You need to marshal the call back to the UI thread in order to access the property.

Before .NET 2.0, you had to call the Invoke method on the Control class in order to marshal the call to the Text proprety.

In .NET 2.0 and after, if your background thread has access to the SynchronizationContext for the UI thread, then you can call the Send method to marshal the call back to the UI.

Note, that if you didn't have to wait for the result of the calls (as you do here, since you want the result of the call to the Text property), you could call BeginInvoke and Post on Control and SynchronizationContext respectively.

casperOne
A: 

MSDN sample using BeginInvoke

This is how I would implement the sample based on the getter snippet you posted:

public string Header
{
    get
    {
        string text = string.Empty;
        _HeaderComboBox.BeginInvoke(new MethodInvoker(delegate
        {
            text = _HeaderComboBox.Text;
        }));
        return text;
    }
    set
    {
        _HeaderComboBox.Text = value;
    }
}

There are more elegant methods, however, this is a general example for you.

dboarman
He doesn't say which platform he's on. This link works for WinForms; for WPF, use Dispatcher.BeginInvoke instead.
itowlson
Thanks. I think this solves my problem. The only problem is that it keeps returning an empty string. Is using Invoke instead of BeginInvoke the right way to do it?
John Sheares
if it is returning an empty string then you are not setting the text property. I did this in a test project to make sure it worked - and it does return the text property.
dboarman
I changed it to Invoke and it appears to work while BeginInvoke returns an empty string. I am using the exact code in your answer.
John Sheares
That's interesting...good to know. I'll do some playing around with it a little later and expand the example a bit...
dboarman
A: 

You have to outsource your cross-thread code into a separate method, make a delegate of it and then Invoke it on a Control in the Thread you want to change. You could also use a closure instead of a delegate+method.

HalloDu
+2  A: 

You cannot access WinForms controls on any thread other than the UI thread, A.K.A. the one it was created on, because of cross-threading issues, race conditions, etc. To solve this, you have to run any commands you want to run on the UI thread. This can be done by using the Invoke method:

public void InvokeExample()
{
    if (InvokeRequired)
    {
        // Invoke this method on the UI thread using an anonymous delegate
        Invoke(new MethodInvoker(() => InvokeExample()));
        return;
    }

    string header = Control.Header;
}
nasufara