BackgroundWorker和Dispatcher.Invoke

时间:2013-11-03 02:44:57

标签: wpf backgroundworker dispatch

与许多应用程序一样,我想在应用程序进行长时间计算时更新我的​​状态文本。我读过有关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);

我希望将更新放在另一个线程中会起作用。但它没有。

如何确保状态反映正确的程序状态?

2 个答案:

答案 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 */ }));