//button is clicked
//worker starts
private void processWorker_DoWork(object sender, DoWorkEventArgs e)
{
string code = DoLongWorkAndReturnCode();
if (code != 0)
{
MessageBox.Show("Error!");
EnableAllButtons(); // this is defined in the other thread and it's where i run into the error.
}
else
{
string code = DoAnotherLongProcessAndReturnCode();
if (code != 0)
{
MessageBox.Show("Error 2!");
EnableAllButtons(); // again, this is defined in the other thread
}
}
}
我遇到了交叉线程错误,因为“EnableAllButtons()”是在不同的线程中定义的。
如何从一个不同的线程启用一个线程中的所有按钮?
答案 0 :(得分:2)
根据您的姓名,我假设您使用的是BackgroundWorker。您必须在ProgressChanged,RunWorkerCompleted,Control.Invoke或Control.BeginInvoke中更新控件(您无需为Control的BeginInvoke触发EndInvoke)。
我的建议是在后台工作程序中抛出异常(如果你没有使用除检查0之外的代码,如果不使用Result方法。)并检查传入的参数的Error属性。 RunWorkerCompleted事件。从那里你可以启动另一个后台工作人员或停止。您将在主线程上运行,因此您可以在不调用的情况下更改按钮。
示例:通过BeginInvoke启用按钮
使用此示例,您可以使用您的代码,只需修改EnableAllButtons
即可private void EnableAllButtons()
{
//See if the main form needs invoke required, assuming the buttons are on the same thread as the form
if(this.InvokeRequired)
{
//Calls EnableAllButtons a seccond time but this time on the main thread.
//This does not block, it is "fire and forget"
this.BeginInvoke(new Action(EnableAllButtons));
}
else
{
btnProcessImages.Enabled = true;
btnBrowse.Enabled = true;
btnUpload.Enabled = true;
btnExit.Enabled = true;
ControlBox = true;
}
}
示例:通过结果返回数据
private void processWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string code = DoLongWorkAndReturnCode();
e.Result = code;
}
private void processWorkers_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (code != 0)
{
MessageBox.Show("Error!");
EnableAllButtons(); // this is defined in the other thread and it's where i run into the error.
}
else
{
doAnotherLongProcessAndReturnCodesBackgroundWorker.RunWorkerAsync();
}
}
示例:通过异常+重用事件处理程序返回数据。
private void processWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string code = DoLongWorkAndReturnCode();
if (code != 0)
{
thow new MyCustomExecption(code);
}
}
private void processWorker2_DoWork(object sender, DoWorkEventArgs e)
{
string code = DoAnotherLongProcessAndReturnCode();
if (code != 0)
{
thow new MyCustomExecption(code);
}
}
private void processWorkers_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
string message;
//See if it was our error
if(e.Error.GetType() == typeOf(MyCustomExecption))
{
//Choose which error message to send
if(sender.Equals(processWorker2))
message = "Error2!";
else
message = "Error!";
}
else
{
//Handle other types of thrown exceptions other than the ones we sent
message = e.Error.ToString();
}
//no matter what, show a dialog box and enable all buttons.
MessageBox.Show(message);
EnableAllButtons();
}
else
{
//Worker completed successfully.
//If this was called from processWorker1 call processWorker2
//Here is the code that was returned from the function
if(sender.Equals(processWorker1))
processWorker2.RunWorkerAsync();
}
}
答案 1 :(得分:0)
为了访问另一个线程中的控件,您需要使用Control.Invoke,如下所述: Control.Invoke Method (Delegate)
答案 2 :(得分:0)
如果您必须直接从另一个线程访问UI控件,请查看这个最有用的扩展方法
https://stackoverflow.com/a/3588137/141172
BackgroundWorker提供an event for progress updates。如果您对UI的更改具有此类性质,请考虑使用该事件。