views:

335

answers:

2

I recently updated an application from VS2003 to VS2008 and I knew I would be dealing with a host of "Cross-thread operation not valid: Control 'myControl' accessed from a thread other than the thread it was created on" I am handling this in what I beleive is the correct way (see code sample below). I am running into numerous controls that are going to need a similar fix. Not wanting to have similar code for every label, textbox etc.. that are being accessed by a non UI thread. What are the ramifications of just setting the CheckForIllegalCrossThreadCalls = false for the entire app?

I found a CodeProject article with various workarounds and a warning at the bottom to NOT set the property. I am looking for other opinions/experiences on this issue.

private void ShowStatus(string szStatus)
{
    try
    {
        if (this.statusBar1.InvokeRequired) { BeginInvoke(new MethodInvoker(delegate() { ShowStatus(szStatus); })); }
        else { statusBar1.Panels[0].Text = szStatus; }
    }
  catch (Exception ex)
  {
    LogStatus.WriteErrorLog(ex, "Error", "frmMNI.ShowStatus()");
  }
}

I found another article with some possible solutions SO Question 2367718

+2  A: 

The remarks section of the documentation for CheckForIllegalCrossThreadCalls makes it pretty clear that doing so is not a good idea

When a thread other than the creating thread of a control tries to access one of that control's methods or properties, it often leads to unpredictable results. A common invalid thread activity is a call on the wrong thread that accesses the control's Handle property. Set CheckForIllegalCrossThreadCalls to true to find and diagnose this thread activity more easily while debugging. Note that illegal cross-thread calls will always raise an exception when an application is started outside the debugger.

Brian Rasmussen
+3  A: 

When you're not debugging, you'll still have problems.

From the documentation of Control.CheckForIllegalCrossThreadCalls:

Note that illegal cross-thread calls will always raise an exception when an application is started outside the debugger.

You'll need to correct the problems.

That being said, you mentioned:

Not wanting to have similar code for every label, textbox etc.. that are being accessed by a non UI thread.

I would reconsider this stance. You should try to move the logic running on a separate thread into separate methods or classes, which will in turn make marshaling the calls back into the UI much simpler. Over time, this will make your code much more reliable and maintainable.

Note that you can use Control.Invoke to marshal a whole set of calls to the UI in one call, too, instead of doing each single set operation individually. There really shouldn't be that many of them, when you finish.

Edit:

For example, it sounds like you're loading the data. Say you have (on your background thread), your data loading method:

var myData = LoadData();
this.Invoke( new Action( () =>
    {
        // Just set all of your data in one shot here...
        this.textBox1.Text = myData.FirstName;
        this.textBox2.Text = myData.LastName;
        this.textBox3.Text = myData.NumberOfSales.ToString();
    }));
Reed Copsey
+1 - I had that very quote on my clipboard, when I refreshed the page before posting an answer...
Fredrik Mörk
Control.Invoke seems to be what I am looking for. I have some data coming back on a NON UI thread and setting a dozen or so textboxes, labels etc.. If I could marshal it in one sweep that would be great.
Ron Skufca
@Ron: Yes, you can marshal all of the UI code in one sweep. Using lambda syntax (since you're in VS2008) allows you to do it "in line" in your method, and not need to make a weird delegate to pass, too...
Reed Copsey
@Ron: Just edited to show you how easy it is in 2008, since you have lambdas...
Reed Copsey
Yes there is a method in the WinForm that processes the data when it comes back from a asynch web service call. It is in that returning asych method that the code attempts to set textboxes, etc... and where I get the exceptions.
Ron Skufca