In my windows form I have a text box and a button, the text box “tb_LogBox” is multiline text box I am trying to create a background worker that is supposed to call a function i.e. “LogTimer.DnT()” when I compile is and run it Visual studio throws InvalidOperationException.
The actual Error I am getting Cross-thread operation not valid: Control 'tb_LogBox' ac开发者_Python百科cessed from a thread other than the thread it was created on. The following sample code illustrate what I am trying to do
private void button1_Click(object sender, EventArgs e)
{
try
{
var bw = new BackgroundWorker();
bw.DoWork += ExecuteOperations ;
bw.RunWorkerAsync();
}
catch (Exception ex)
{
tb_LogBox.AppendText(Environment.NewLine + " =@= " + ex.Message+" "+ex.Source);
}
}
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
var FuncCall = new LogTimer();
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); // the line i am getting the error. on
}
public class LogTimer
{
public string DnT()
{
const string datePat = @"d/MM/yyyy";
var dateTime = DateTime.Now();
return dateTime.ToString(datePat);
}
}
Try to use the begin invoke method :
BeginInvoke(new Action(() =>
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT());
}));
This would be smoother than Invoke.
you need to marshall the Ui change onto the UI thread. This can be performed by using an invoke/begininvoke call around your tb_LogBox.AppendText
in a Winforms Application:
this.BeginInvoke((MethodInvoker)delegate
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
in a WPF application:
this.Dispatcher.BeginInvoke(
(Action)delegate()
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
Hope this helps!
Do this in your ExecuteOperations:
tb_LogBox.Invoke((MethodInvoker)delegate() { tb_LogBox.AppendText(...) }));
You cannot use other threads (BackgroundWorker uses a .NET threadpool thread) to make changes to UI components. This is a major hurdle you will have to get used to in WinForms programming.
The BackgroundWorker is executing on its own thread and all operations related to WinForms GUI elements must run on the thread they were created on. They way you currently use the BackgroundWorker is identical to just Queuing the operation using ThreadPool.QueueUserWorkItem(). For communication back to the GUI using a BackgroundWorker, use ReportProgess or set the DoWorkEventArgs.Result property in the worker method, and react to the corresponding events on the GUI thread. You can also use Invoke/BeginInvoke on a WinForms control to execute arbitrary code directly on the GUI thread. In your case that would mean replacing the line accessing the tb_LogBox with:
tb_LogBox.Invoke(new Action(() =>
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
));
You need to invoke the control's method on the UI thread:
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
var FuncCall = new LogTimer();
tb_LogBox.Invoke((MethodInvoker)delegate{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
}
I don't know what LogTimer
does, but it may very well be that you should create that inside the delegate as well:
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
tb_LogBox.Invoke((MethodInvoker)delegate{
var FuncCall = new LogTimer();
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
}
You can't access the host thread from the background worker's execution thread. You can use the ReportProgress method of the BackgroundWorker to send information to the host thread.
private void button1_Click(object sender, EventArgs e)
{
try
{
var bw = new BackgroundWorker();
bw.DoWork += ExecuteOperations;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerAsync();
}
catch (Exception ex)
{
tb_LogBox.AppendText(Environment.NewLine + " =@= " + ex.Message + " " + ex.Source);
}
}
private static void ExecuteOperations(object sender, DoWorkEventArgs e)
{
var FuncCall = new LogTimer();
string text = Environment.NewLine + FuncCall.DnT();
(sender as BackgroundWorker).ReportProgress(0, text);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
tb_LogBox.AppendText(e.UserState as string);
}
public class LogTimer
{
public string DnT()
{
const string datePat = @"d/MM/yyyy";
var dateTime = DateTime.Now;
return dateTime.ToString(datePat);
}
}
精彩评论