使用BackgroundWorker更新GUI

Mig*_*iro 5 wpf user-interface multithreading 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);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

Tho*_*que 12

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

当您更改控件的属性时,不会立即执行WPF中的渲染,而是在单独的调度程序帧上执行,该调度程序帧在调度程序根据任务的优先级执行时不再需要执行时处理.用于渲染的优先级值为7(DispatcherPriority.Render); 该ProgressChanged事件被编组到UI线程与图9(a优先DispatcherPriority.Normal),对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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 当然,绑定到属性并从工作线程更新它更容易. (2认同)