3

I hit a problem and I thought it might be due to the complexity of my classes passing objects to each other so I minimised it and the problem persists:

I've got a default winform project created in VS2017 Community

On the form I've added a textbox, a richtextbox, a backgroundworker and a button to activate the background worker.

I've put the following code in the form to populate the text boxes and to run the worker on button click:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        textBox1.Text = "Hello";
        richTextBox1.Text = "World!";
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (backgroundWorker1.IsBusy != true)
        {
            backgroundWorker1.RunWorkerAsync();
        }
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        MessageBox.Show(textBox1.Text);
        MessageBox.Show(richTextBox1.Text);
    }
}

I run the program and I don't understand what happens next.

textBox1.Text is accessible from the form so the MessageBox shows fine. richTextBox1.Text is NOT accessible and gives me this error:

Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.

WHY?

I'm assuming richTextBox has more routing and wrapping but is the .Text property not exactly the same?! What's going on here?

EDIT: I don't think this is a duplicate to the marked question because his wasn't working for TextBox.Text whereas mine is. I'm asking about the DIFFERENCE BETWEEN TextBox and RichTextBox .Text properties.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
jamheadart
  • 5,047
  • 4
  • 32
  • 63
  • 1
    Never access UI thread elements from a background thread. Neither `TextBox` nor `RichTextBox`. The code in your `DoWork` handler is definitely wrong. You should use `ReportProgress` and do your things there. – Uwe Keim Feb 20 '19 at 08:25
  • Yes I'm aware, and I actually pass things via arguments as I run the async worker. This is old code I'm looking at and I'd still like an answer to my question! – jamheadart Feb 20 '19 at 08:28
  • 1
    Possible duplicate of [Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on](https://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the) – Sinatr Feb 20 '19 at 08:29
  • 2
    The answer is UI controls aren't thread safe, as to how they react, its unpredictable, there is nothing more to be said. – TheGeneral Feb 20 '19 at 08:31
  • 2
    `TextBox` *works* by coincidence, you are *lucky* what current implementation doesn't throw. You shouldn't be relying on such *tests*. All UI elements have to be accessed from same thread they were created with. – Sinatr Feb 20 '19 at 08:31
  • You can [see in the Reference Source](https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,f12a8fdec6c1bb36) that the underlying `WindowText` _seems_ to be somewhat thread-safe. – Uwe Keim Feb 20 '19 at 08:33

2 Answers2

8

They have been implemented differently.

TextBox.Text basically returns Control.Text which calls WindowText which uses GetWindowText. in the code comments clearly mentioned it's okay to call GetWindowText cross-thread. So they've turned off checking for cross-thread on purpose by setting a flag inCrossThreadSafeCall.

But for ReachTextBox.Text it doesn't rely on Control.Text. It sends EM_STREAMOUT and uses the result. So no flag has been set to except this from cross-thread exception.

Note: You should ignore such cases and you should never try to access UI element from another thread. Always use Invoke method of the control when you are trying to interact with UI thread from another thread.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • THANK YOU. I already knew what everyone was telling me about not referring to UI elements in threads. I either pass the UI threads as arguments or in some cases have used invoke. I'm glad its confirmed that `.Text` properties do not come from the same place. – jamheadart Feb 20 '19 at 08:53
  • Check for cross-thread operation happens in `Handle` of control by checking if `checkForIllegalCrossThreadCalls && !inCrossThreadSafeCall && InvokeRequired` is true, then throws the exception. For `WindowText` property it has been turned off by setting `inCrossThreadSafeCall` to true. – Reza Aghaei Feb 20 '19 at 09:32
1

You have to Invoke UI code (you can't run UI in the thread other than UI):

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   // What to do with UI
   Action action = () => {
      MessageBox.Show(textBox1.Text);
      MessageBox.Show(richTextBox1.Text);
   }

   if (InvokeRequired)  // We are in some background thread, Invoke required
     Invoke(action);
   else                 // We are in UI (main) thread, just call the action
     action(); 
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215