Reputation: 19903
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
Reputation: 5338
You can also try this.
this.Invoke(new MethodInvoker(delegate()
{
richtextBox.Text.Add("MyText");
}));
Upvotes: 4
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
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
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
Reputation: 24312
try this code,
BeginInvoke((MethodInvoker)delegate
{
richtextBox.Text.Add("MyText");
});
Upvotes: 13
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
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