简体   繁体   中英

RunWorkerCompleted updates UI before DoWork has finished

I'm curious to know why the RunWorkerCompleted actually happens to update the UI before the progress bar has finished, with the textBox1 text "Done". Why is this happening?

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";
    }

If I use

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

then it does what I expect

If I try this below, the textbox updates as expected but not the progressbar

 public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {


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

A little lag is possible and to be expected.

The ReportProgress() mechanism probably uses BeginInvoke() so updates are queued. That would give a little delay but usually not noticeable.

The ProgressBar has its own internal Update mechanism. This seems to purposely delay feedback with a few hundred msec. Some User scaled tuning I guess.

A very simple solution to this problem (which is essentially about the ProgressBar control) has been devised by Derek Will and at the time of writing is available here: https://derekwill.com/2014/06/24/combating-the-lag-of-the-winforms-progressbar/

The trick is to adjust the ProgressBar's value a little higher than necessary, then back down to the required value. This eliminates the lag because it only occurs (one must assume by design) when the bar's value is increased, not when it is decreased.

The following extension method will update the ProgressBar's value without any lag. All credit goes to Derek Will for this solution; I've merely simplified the code a little and bound the value within the limits of the control's range, and I'm posting my version here for posterity. Lag-less versions of the Increment and PerformStep methods may be defined using the same pattern.

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;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM