TheBoubou
TheBoubou

Reputation: 19903

BackgroundWorker - Cross-thread operation not valid

I have a winform application (one form), on this form there is a RichTextBox. In the constructor of this form I create an instance of the class MyClass. In the “Form_Load” I call the method Initialisation from MyClass instance.

In the form constructor

myClass = new MyClass(RichTextBox richTextBox);

In the Form_Load

myClass.Initialisation();

In the Initialisation method, in a loop, I read some parmeters do other stuffs. To not freeze the application (because some operation can take a while, some seconds), I use a BackgroundWorker. I use it like this (see code below).

When I execute, I get this error : Cross-thread operation not valid: Control ‘richTextBox’ accessed from a thread other than the thread it was created on.

Could you tell me how solve this ? Work perfect when I don't access the richTextBox

public Class MyClass
{
    static BackgroundWorker _bw;
    public MyClass()
    {
        _bw = new BackgroundWorker
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };
        _bw.DoWork += bw_DoWork;
        _bw.ProgressChanged += bw_ProgressChanged;
        _bw.RunWorkerCompleted += bw_RunWorkerCompleted;
    }
    static void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        foreach (....)
        {
            if (....)
            {
                richtextBox.Text.AppendText("MyText");
            }
        }
        e.Result = true;
    }
    static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){}
    static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e){}
}

Upvotes: 20

Views: 33426

Answers (7)

Kent Aguilar
Kent Aguilar

Reputation: 5338

You can also try this.

this.Invoke(new MethodInvoker(delegate()
{
    richtextBox.Text.Add("MyText");
}));

Upvotes: 4

coloboxp
coloboxp

Reputation: 504

To make it cleaner and based on Jon Skeet's suggestion, I made an extension method which does the same, you can change the "this Label control" to this TextBox control or simply use "this Control control" (and basically allow every control to be updated easily):

internal static class ExtensionMethods
{
    /// <summary>
    /// Updates the label text while being used in a multithread app.
    /// </summary>
    /// <param name="control">The control.</param>
    /// <param name="text">The text.</param>
    internal static void UpdateThreadedText(this Label control, string text)
    {
        Action action = () => control.Text = text;
        control.Invoke(action);
    }

    /// <summary>
    /// Refreshes the threaded.
    /// </summary>
    /// <param name="control">The control.</param>
    internal static void RefreshThreaded(this Label control)
    {
        Action action = control.Refresh;
        control.Invoke(action);
    }
}

And then the usage is quite simple:

this.yourLabelName.UpdateThreadedText("This is the text");
this.yourTextBoxName.UpdateThreadedText("This is the text");
this.yourControlName.UpdateThreadedText("This is the text");

or

this.yourLabelName.RefreshThreaded();

Works for me nicely :)

Upvotes: 3

ispiro
ispiro

Reputation: 27673

Add:

e.Result = "MyText";

In your bw_DoWork And:

richTextBox1.AppendText((string)e.Result);

In your bw_RunWorkerCompleted

(Alter it to fit your code)

EDIT:

If it's done many times during the BackgroundWorker's work, you can add:

_bw.ReportProgress(0, "MyText");

to the bw_DoWork and:

richTextBox1.AppendText((string)e.UserState);

to the bw_ProgressChanged.

Upvotes: 0

S2S2
S2S2

Reputation: 8502

Using BackgroundWorker component, only the ProgressChanged and RunWorkerCompleted events allow you to invoke methods/properties on UI controls (which should be always done on UI thread). As you are updating the UI in DoWork event which runs on a non-UI thread you are getting this error, you should probably update you UI controls using Invoke or BeginInvoke methods in DoWork event if you want to.

Upvotes: 4

Chamika Sandamal
Chamika Sandamal

Reputation: 24312

try this code,

BeginInvoke((MethodInvoker)delegate
{
    richtextBox.Text.Add("MyText");
});

Upvotes: 13

John Woo
John Woo

Reputation: 263723

i think the error stops on this line:

richtextBox.Text.Add("MyText");

your question i similar to this:

BackgroundWorker OnWorkCompleted throws cross-thread exception

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500595

Using BackgroundWorker doesn't exempt you of the normal threading rules - such as that only the UI thread can access UI components.

If you want to update the UI from a BackgroundWorker other than using the progress/completion events (which are raised on the UI thread) you need to use Control.Invoke / Control.BeginInvoke just as you would in other situations. For example:

if (....)
{
    Action action = () => richtextBox.Text.Add("MyText");
    richtextBox.Invoke(action); // Or use BeginInvoke
}

Upvotes: 39

Related Questions