与许多应用程序一样,我想在应用程序进行长时间计算时更新我的状态文本。我读过有关Dispatcher和BackgroundWorker的文章。我知道我肯定需要确保更新发生在UI线程中。我的第一次尝试是:
MyView.UpdateStatus( "Please wait" );
LongComputation();
MyView.UpdateStatus( "Ready" );
这不起作用,因为(我认为)LongComputation会阻止状态更新。
所以我试着这样做:
BackgroundWorker worker = new BackgroundWorker();
MyView.UpdateStatus( "Please wait");
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
LongComputation();
}
worker.RunWorkerAsync();
MyView.UpdateStatus( "Ready" );
我正在跳跃,额外的线程将为UpdateStatus提供更新状态文本的机会。它也不起作用。其中一个原因是LongComputation的结果显示在Windows窗体中。只要我将LongComputation放在BackgroundWorker中,结果就不会显示出来。
所以我第三次尝试使用流动的代码:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
Dispatcher.Invoke(((Action)(() => Status.Text = args.Argument as string)));
};
worker.RunWorkerAsync(newStatus);
我希望将更新放在另一个线程中会起作用。但它没有。
如何确保状态反映正确的程序状态?
答案 0 :(得分:1)
BackgroundWorker使用RunWorkerCompleted和ReportProgress事件与主线程进行通信。 RunWorkerCompleted应该做你需要做的事情,因为一旦后台工作完成它就会在UI线程上执行。
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
LongComputation();
};
// RunWorkerCompleted will fire on the UI thread when the background process is complete
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{
// an exception occurred on the background process, do some error handling
}
MyView.UpdateStatus("Ready");
};
MyView.UpdateStatus("Please wait");
worker.RunWorkerAsync();
此外,您可以使用RunWorkerCompleted使用DoWorkerEventArgs的Result属性将结果编组回主线程。
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
args.Result = LongComputation();
};
worker.rep
// RunWorkerCompleted will fire on the UI thread when the background process is complete
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{
// an exception occurred on the background process, do some error handling
}
var result = args.Result;
// do something on the UI with your result
MyView.UpdateStatus("Ready");
};
最后,您可以使用ReportProgress事件,在后台进程的逻辑步骤中更新UI:
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
FirstHalfComputation();
// you can report a percentage back to the UI thread, or you can send
// an object payload back
int completedPercentage = 50;
object state = new SomeObject();
worker.ReportProgress(completedPercentage , state);
SecondHalfComputation();
};
worker.WorkerReportsProgress = true; // this is important, defaults to false
worker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args)
{
int completedPercentage = args.ProgressPercentage;
SomeObject state = args.UserState as SomeObject
// update a progress bar or do whatever makes sense
progressBar1.Step = completedPercentage;
progressBar1.PerformStep();
};
答案 1 :(得分:0)
我解决了以下问题:从主线程在窗体的Load中存储分派器,然后从成员变量中调用分派器,而我从BackgroundWorker线程将其存储在其中:
在表格的开头声明成员变量:
Dispatcher mDispatcherMain = null;
将调度程序存储在以下表单的加载函数中:
mDispatcherMain = Dispatcher.CurrentDispatcher;
从BackgroundWorker的DoWork函数中调用主线程:
mDispatcherMain.Invoke(new Action(() => { /* What you want to do */ }));