使用BackgroundWorker更新GUI

时间:2011-03-04 09:53:36

标签: wpf multithreading user-interface backgroundworker

我一直在寻找并发现执行后台工作和更新GUI的好方法是使用后台工作程序。但是,执行这个(愚蠢的)小任务(从1到10000计数)它不会更新标签内容,而是打印到调试! (当然,这只是另一个项目的尖峰解决方案......)

以下是代码:

public partial class MainWindow : Window
{
    BackgroundWorker bw = new BackgroundWorker();

    public MainWindow()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {

        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
        bw.WorkerReportsProgress = true;
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        bw.RunWorkerAsync();

    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("DONE");

    }

    void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        label1.Content = "going here: "+e.ProgressPercentage;
        Debug.WriteLine(e.ProgressPercentage);
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i=0; i < 10000; i++)
        {
            bw.ReportProgress((i*100)/10000);
        }
    }

}

3 个答案:

答案 0 :(得分:12)

在UI线程上引发ProgressChanged事件,而不是工作线程。在你的代码中,工作线程几乎什么也没做(只需从0到10000循环并调用ReportProgress),大部分工作都是在UI线程上完成的。基本上,您发送了太多进度通知。因此,UI线程几乎总是很忙,没有时间呈现标签的新内容。

当您更改控件的属性时,不会立即执行WPF中的渲染,而是在单独的调度程序帧上执行,该调度程序帧在调度程序根据任务的优先级执行时不再需要执行时处理。用于渲染的优先级值为7(DispatcherPriority.Render); ProgressChanged事件被编组到UI线程,优先级为9(DispatcherPriority.Normal),as specified on MSDN。因此ProgressChanged通知始终具有比呈现更高的优先级,并且由于它们不断进入,因此调度程序永远没有时间处理呈现任务。

如果您只是降低通知的频率,您的应用应该可以正常工作(目前您为每个百分比值发送100个通知,这是无用的):

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 0; i < 10000; i++)
        {
            if (i % 100 == 0)
                bw.ReportProgress(i / 100);
        }
    }

答案 1 :(得分:3)

this.Dispatcher.BeginInvoke( (Action) delegate(){
   label1.Content = "going here: "+e.ProgressPercentage;
});

答案 2 :(得分:0)

尝试使用以下内容更改标签:

string Text = "going here: " + e.ProgressPercentage;
this.Invoke((MethodInvoker)delegate {
    label1.Content = newText;
});

请注意,我不确定它是否有效。我现在无法测试它。如果它不起作用,请告诉我,我将删除答案。

如果您需要一种规范的方式来完成您想要的操作,请查看此帖子中的Hath答案:How do I update the GUI from another thread?