I have a project coded in .NET Winforms. I need to implement a data-mining operation, print the text to TextBox and update the progress.
I tried to use BackgroundWorker to do, but it throws a InvalidOperationException (Cross-thread operation not valid: Control ‘xxxxx’ accessed from a thread other than the thread it was created on)
To narrow down the potential causes of the problem, I started a new project, included the following: Button - To start the BackgroundWorker Label - to print the text. And ProgressBar.
However, the result is the same. I searched on SOF, and was told to use a delegate, but I am not familiar with it.
This is the code sample that throws the error:
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace TestProject
{
public partial class Form1 : Form
{
private readonly BackgroundWorker _bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
_bw.DoWork += RosterWork;
_bw.ProgressChanged += BwProgressChanged;
_bw.RunWorkerCompleted += BwRunWorkerCompleted;
_bw.WorkerReportsProgress = true;
_bw.WorkerSupportsCancellation = false;
}
private void RosterWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
for (int i = 0; i < 1000; i++)
{
label1.Text = i.ToString();
_bw.ReportProgress(Convert.ToInt32((i * (100 / 1000))));
}
}
private void BwProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void btnStart_Click(object sender, EventArgs e)
{
progressBar1.Show();
_bw.RunWorkerAsync();
}
private void BwRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Hide();
}
}
}
Update: I follow Jon Skeet's answer, it really work on my test project, but back to my real project,
The Layout of my Form:
Form - TabControl - Tab1 -Tab1Panel -TextBox1
When reach this line :
TextBox txtbox1 = new TextBox();
Tab1Panel.Controls.Add(txtbox1);
The error still occur when i add Textbox to Panel Control Programmatically.
Finally,I replace by this:
if (Tab1Panel.InvokeRequired)
Tab1Panel.Invoke((MethodInvoker)delegate { Tab1Panel.Controls.Add(txtbox1); });
else
Tab1Panel.Controls.Add(txtbox1);
Everything is work. How to determine the control is InvokeRequired, Is it control specified?
This is the problem:
label1.Text = i.ToString();
You're trying to change the label text within the BackgroundWorker
, which is not running on a UI thread. The point of BackgroundWorker
is to do all the non-UI work there, using ReportProgress
to periodically "go back" to the UI thread and update the UI with the progress you're making.
So either you need to change label1.Text
in BwProgressChanged
as well, or you need to use Control.Invoke
/BeginInvoke
just as you would from any other background thread:
// Don't capture a loop variable in a lambda expression...
int copy = i;
Action updateLabel = () => label1.Text = copy.ToString();
label1.BeginInvoke(updateLabel);
For more about the copying part, see Eric Lippert's blog post, "Closing over the loop variable considered harmful". In this particular case it's only an issue because I'm using BeginInvoke
. This could be changed to just:
Action updateLabel = () => label1.Text = i.ToString();
label1.Invoke(updateLabel);
... but now the background worker would always be waiting for the UI to catch up before it kept going, which in real life is usually not what you want. I generally prefer BeginInvoke
over Invoke
.
use this code iw will work
BeginInvoke((MethodInvoker)delegate
{
TextBox1.Text += "your text here";
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With