-4

Using Winforms, C#, FW 4.5. I have a simple form with a textbox and a gridView. When I start a new task, it's able to access the cells of the grid and change the text. When I try to change the textbox text i get this error "cross thread operation not valid..."

Both the textbox and the gridView are on the same form. Why the behavior is different?

enter image description here

enter image description here

Itay.B
  • 3,991
  • 14
  • 61
  • 96
  • 3
    Please include the code *as text*, ideally as a [mcve], rather than showing a screenshot. Fundamentally, you simply shouldn't be accessing a UI control on a different thread. The fact that it happens to work for the textbox is surprising but you should still not do it... – Jon Skeet Dec 15 '15 at 15:57
  • 1
    Read on this here. Textboxes and plenty of other controls can not be written to from outside their main thread. [This question and answer](http://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread) should give you some inspiration on how to solve your problem. – MicroParsec Dec 15 '15 at 15:58
  • 1
    Avoid manipulating controls from non-UI thread. In fact, you can't even do that with some controls. Make some form of intermediate list or object to which you can store data ( you'll still need to lock that object ) and update form when you're done. Also consider using BackgroundWorker if the task is time demanding and you want to keep the form updated. – Transcendental Dec 15 '15 at 16:01
  • 2
    Possible duplicate of [Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on](http://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the) – C. Knight Dec 15 '15 at 16:13

1 Answers1

2

It is generally a bad idea to access your UI from a different thread than the one which started the UI. It is possible to avoid the exception by setting the static property Control.CheckForIllegalCrossThreadCalls to false, but that is a very dangerous way.

You should rather use Control.BeginInvoke to defer execution back to the UI thread.

So you could replace your line txtSql.Text = sQuery to something like that:

void RunChecks()
{
    ...
    SetQueryText(sQuery); // instead of txtSql.Text = sQuery;
    ...
}

delegate void SetTextDelegate(string text);
void SetQueryText(string query)
{
    if (txtSql.InvokeRequired)
    {
        txtSql.BeginInvoke(new SetTextDelegate(SetQueryText), query);
        return;
    }
    txtSql.Text = query;
}

So SetQueryText checks if it is necessary to call Invoke (or BeginInvoke) because it was called from another thread. If this is the case, it calls BeginInvoke to defer execution to the UI thread and returns.

Unfortunatly this still uses the delegate syntax instead of lambdas, but maybe there's a better syntax that I just don't know.

You need to do this for all controls you are accessing from different threads.

René Vogt
  • 43,056
  • 14
  • 77
  • 99