How can I make the progress bar update fast enough?

后端 未结 9 1588
礼貌的吻别
礼貌的吻别 2021-02-08 20:40

I\'m using a progress bar to show the user how far along the process is. It has 17 steps, and it can take anywhere from ~5 seconds to two or three minutes depending on the weath

相关标签:
9条回答
  • 2021-02-08 20:42

    I use Mark Lansdown's excellent answer as an Extension method for the ProgressBar control.

    public static void ValueFast(this ProgressBar progressBar, int value)
    {
        progressBar.Value = value;
    
        if (value > 0)    // prevent ArgumentException error on value = 0
        {
            progressBar.Value = value - 1;
            progressBar.Value = value;
        }
    
    }
    

    Or you can also do it this way which only sets the ProgressBar value property twice instead of three times:

    public static void ValueFast(this ProgressBar progressBar, int value)
    {
        if (value < 100)    // prevent ArgumentException error on value = 100
        {
            progressBar.Value = value + 1;    // set the value +1
        }
    
        progressBar.Value = value;    // set the actual value
    
    }
    

    Simply call it on any ProgressBar control using the extension method:

    this.progressBar.ValueFast(50);
    

    If you really wanted to you could also check the current Windows Environment and only do the hack section of code for Windows Vista+ since Windows XP's ProgressBar does not have the slow progress animation.

    0 讨论(0)
  • 2021-02-08 20:46

    It sounds like you're doing everything on the UI thread and thus not releasing the message pump. Have you tried using smoething like BackgroundWorker and the ProgressChanged event? See MSDN for an example.

    BackgroundWorker is ideal for loading external data - but note that you shouldn't do any data-binding etc until you get back to the UI thread (or just use Invoke/BeginInvoke to push work to the UI thread).

    0 讨论(0)
  • 2021-02-08 20:46

    Did you try Application.DoEvents(); ?

    0 讨论(0)
  • 2021-02-08 20:52

    Expanding on the answer given by Silas Hansen, this one seems to give me perfect results every time.

    protected void UpdateProgressBar(ProgressBar prb, Int64 value, Int64 max)
    {
        if (max < 1)
            max = 1;
        if (value > max)
            value = max;
        Int32 finalmax = 1;
        Int32 finalvalue = 0;
        if (value > 0)
        {
            if (max > 0x8000)
            {
                // to avoid overflow when max*max exceeds Int32.MaxValue.
                // 0x8000 is a safe value a bit below the actual square root of Int32.MaxValue
                Int64 progressDivideValue = 1;
                while ((max / progressDivideValue) > 0x8000)
                    progressDivideValue *= 0x10;
                finalmax = (Int32)(max / progressDivideValue);
                finalvalue = (Int32)(value / progressDivideValue);
            }
            else
            {
                // Upscale values to increase precision, since this is all integer division
                // Again, this can never exceed 0x8000.
                Int64 progressMultiplyValue = 1;
                while ((max * progressMultiplyValue) < 0x800)
                    progressMultiplyValue *= 0x10;
                finalmax = (Int32)(max * progressMultiplyValue);
                finalvalue = (Int32)(value * progressMultiplyValue);
            }
        }
        if (finalvalue <= 0)
        {
            prb.Maximum = (Int32)Math.Min(Int32.MaxValue, max);
            prb.Value = 0;
        }
        else
        {
            // hacky mess, but it works...
            // Will pretty much empty the bar for a split second, but this is normally never visible.
            prb.Maximum = finalmax * finalmax;
            // Makes sure the value will DEcrease in the last operation, to ensure the animation is skipped.
            prb.Value = Math.Min(prb.Maximum, (finalmax + 1));
            // Sets the final values.
            prb.Maximum = (finalmax * finalmax) / finalvalue;
            prb.Value = finalmax;
        }
    }
    
    0 讨论(0)
  • 2021-02-08 20:58

    The reason for this whole mess is the interpolating animation effect introduced by Vista and W7. It has aboslutely nothing to do with thread blocking issues. Calling setProgress() or setting the Value property driectly, triggers an animation effect to occur, which I will explain how to cheat:

    I came up with a hack of setting the maximum according to a fixed value. The maximum property does not trigger the effect, thus you get to freely move the progress around with instant response.

    Remember that the actual shown progress is given by: ProgressBar.Value / ProgressBar.Maximum. With this in mind, the example below will move the progress from 0 to 100, repensented by i:

    ProgressBar works like this:  
    progress = value / maximum
    
    therefore:
    maximum = value / progress
    

    I added some scaling factors needed, should be self explanatory:

    progressBar1.Maximum *= 100;
    progressBar1.Value = progressBar1.Maximum / 100;
    for (int i = 1; i < 100; i++)
    {
        progressBar1.Maximum = (int)((double)progressBar1.Value / (double)(i + 1) * 100);
        Thread.Sleep(20);
    }
    
    0 讨论(0)
  • 2021-02-08 21:00

    First. I'd never turn off the CheckForIllegalCrossThreadCalls option.

    Second. Add a Refresh() after you update the progress. Just because you're doing work in a different thread doesn't mean your GUI thread is going to get around to updating.

    0 讨论(0)
提交回复
热议问题