在我的Windows窗体中,我有一个文本框和一个按钮,文本框“tb_LogBox”是多行文本框我正在尝试创建一个应该调用函数的后台工作者,即“LogTimer.DnT()”时我编译并运行它Visual Studio抛出InvalidOperationException。
我得到的实际错误 跨线程操作无效:控制'tb_LogBox'从其创建的线程以外的线程访问。以下示例代码说明了我要做的事情
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);
}
}
答案 0 :(得分:4)
尝试使用begin invoke方法:
BeginInvoke(new Action(() =>
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT());
}));
这比Invoke更顺畅。
答案 1 :(得分:2)
您需要将Ui更改编组到UI线程上。这可以通过使用tb_LogBox.AppendText周围的invoke / begininvoke调用来执行
在Winforms应用程序中:
this.BeginInvoke((MethodInvoker)delegate
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
在WPF应用程序中:
this.Dispatcher.BeginInvoke(
(Action)delegate()
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
希望这有帮助!
答案 2 :(得分:1)
在ExecuteOperations中执行此操作:
tb_LogBox.Invoke((MethodInvoker)delegate() { tb_LogBox.AppendText(...) }));
您不能使用其他线程(BackgroundWorker使用.NET线程池线程)来更改UI组件。这是您在WinForms编程中习惯的主要障碍。
答案 3 :(得分:0)
您需要在UI线程上调用控件的方法:
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
var FuncCall = new LogTimer();
tb_LogBox.Invoke((MethodInvoker)delegate{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
}
我不知道LogTimer
的作用,但很可能你也应该在委托中创建它:
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
tb_LogBox.Invoke((MethodInvoker)delegate{
var FuncCall = new LogTimer();
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
}
答案 4 :(得分:0)
BackgroundWorker在其自己的线程上执行,与WinForms GUI元素相关的所有操作必须在创建它们的线程上运行。它们当前使用的方式BackgroundWorker与使用ThreadPool.QueueUserWorkItem()对操作进行排队相同。要使用BackgroundWorker回传到GUI,请使用ReportProgess或在worker方法中设置DoWorkEventArgs.Result属性,并对GUI线程上的相应事件做出反应。您还可以在WinForms控件上使用Invoke / BeginInvoke直接在GUI线程上执行任意代码。在你的情况下,这意味着用以下代码替换访问tb_LogBox的行:
tb_LogBox.Invoke(new Action(() =>
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
));
答案 5 :(得分:0)
您无法从后台工作程序的执行线程访问主机线程。您可以使用BackgroundWorker的ReportProgress方法将信息发送到主机线程。
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);
}
}