RunWorkerCompleted在DoWork完成之前更新了UI

本文关键字:更新 UI DoWork RunWorkerCompleted | 更新日期: 2023-09-27 18:25:58

我很想知道为什么RunWorkerCompleted在进度条完成之前更新了UI,textBox1文本为"完成"。为什么会发生这种情况?

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 1; i <= 100; i++)
        {
            Thread.Sleep(10);
            backgroundWorker1.ReportProgress(i);
        }
    }
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        textBox1.Text = "Done";
    }

如果我使用

    for (int i = 1; i <= 100; i++)
        {
            Thread.Sleep(20);
            backgroundWorker1.ReportProgress(i);
            MessageBox.Show(i.ToString());
        }

然后它就达到了我所期望的

如果我在下面尝试,文本框会按预期更新,但进度条不会更新

 public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {

        progressBar1.Value = e.ProgressPercentage;
        textBox1.Text = e.ProgressPercentage.ToString();
        progressBar1.Refresh();
    }

RunWorkerCompleted在DoWork完成之前更新了UI

一点滞后是可能的,也是意料之中的。

ReportProgress()机制可能使用BeginInvoke(),因此对更新进行排队。这会造成一点延迟,但通常不会引起注意。

ProgressBar有自己的内部更新机制。这似乎有意将反馈延迟几百毫秒。我想是一些用户规模的调整。

Derek Will设计了一个非常简单的解决方案来解决这个问题(本质上是关于ProgressBar控件),在撰写本文时,可以在这里找到:https://derekwill.com/2014/06/24/combating-the-lag-of-the-winforms-progressbar/

诀窍是将ProgressBar的值调整得比必要的值高一点,然后回落到所需的值。这消除了滞后,因为它只在条形图的值增加时发生(必须根据设计进行假设),而不是在条形图值减少时发生。

以下扩展方法将更新ProgressBar的值,不会有任何延迟。所有的功劳都归功于Derek Will的解决方案;我只是稍微简化了代码,并将值限制在控件的范围内,我将在这里发布我的版本,以供后人使用。Increment和PerformStep方法的无滞后版本可以使用相同的模式来定义。

using System;
using System.Windows.Forms;
public static class ProgressBarEM
{
    public static void ValueNoLag(this ProgressBar bar, int value)
    {
        value = Math.Max(bar.Minimum, Math.Min(value, bar.Maximum));
        bar.Maximum += 1;
        bar.Value = value + 1;
        bar.Value = value;
        bar.Maximum -= 1;
    }
}