7

I'm performing some tests with an scrollbar in Winforms. I have the following code, that seems to be correct, but I never see the scrollbar completely fill. When the scrollbar value reaches 100, the scroll draw is as shown in this picture (about 50%).

enter image description here

This is the example code that I'm using:

    private void animate_scroll(object sender, EventArgs e)
    {
        progressBar1.Minimum = 0;
        progressBar1.Maximum = 100;
        progressBar1.Step = 1;

        ThreadPool.QueueUserWorkItem(new WaitCallback(AnimateScroll));
    }

    private void AnimateScroll(object state)
    {
        while (true)
        {
            mValue = (mValue + 1) % 101;
            this.Invoke((MethodInvoker)delegate()
            {
                progressBar1.Value = mValue;
                System.Diagnostics.Debug.WriteLine(progressBar1.Value);
            });
            System.Threading.Thread.Sleep(10);
        }
    }

    private int mValue = 0;
halfer
  • 19,824
  • 17
  • 99
  • 186
Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
  • Your loop in your example never ends. Even though mValue reaches 100, it will immediately go back to zero and start over. – Nathan A May 05 '14 at 16:19
  • 1
    ProgressBar interpolates to make progress look smooth. Since it doesn't have a time machine to look into the future, it needs to make interpolation work by intentionally delaying updates. Which works fine if you use the control for normal usage. There's nothing normal about this code, real programs that require a progress indicator don't behave this way. A hack is to intentionally move progress back. – Hans Passant May 05 '14 at 16:31
  • @HansPassant "real programs that require a progress indicator don't behave this way" don't they? everytime I use a progress bar in my program I have this problem (on windows 7+) – Dave Cousineau Apr 05 '17 at 18:56

5 Answers5

18

According to the following David Heffernan's answer, the issue is caused by the animation added in Windows 7. The issue gets fixed doing the following trick:

                progressBar1.Value = mValue;
                progressBar1.Value = mValue - 1;
Community
  • 1
  • 1
Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
  • 1
    Too bad this was down-voted. It's a simple solution and worked perfectly. – Marc Clifton Jun 18 '15 at 21:49
  • Yeah, ugly but effective ;-) – Daniel Peñalba Jun 19 '15 at 07:05
  • 1
    this works, although if you're trying to max it out, and the the max is too smal, then 7/8ths for example will be a fairly large gap missing at the end. you can get around that by setting a huge maximum like 1,000,000 and then setting the value to 1 less, and then the missing 1/1,000,000th is not noticible. – Dave Cousineau Apr 05 '17 at 18:58
1

This is happening because there is animation built into the WinForms ProgressBar control. To see this for yourself, just change your animate_scroll method like so:

    private void animate_scroll(object sender, EventArgs e)
    {
        progressBar1.Minimum = 0;
        progressBar1.Maximum = 100;
        progressBar1.Step = 1;
        // ThreadPool.QueueUserWorkItem(new WaitCallback(AnimateScroll));

        // here's the change:
        progressBar1.Value = 100;
    }

As you'll see, when you programmatically change the value from 0 (the default) to 100, the ProgressBar control animates itself to expand its content to the width that corresponds to the value. Your original code is sending Value updates faster than the control can animate between the values.

There doesn't seem to be a way to change the way this animation works. Maybe your use case would be satisfied just by setting the Style property to Marquee, which scrolls a highlighted section across the control.

Michael Gunter
  • 12,528
  • 1
  • 24
  • 58
1

I am doing the same type of thing to test my progress bar before the actual function gets implemented. I estimated my final function will take around 5 seconds to complete, so I set up the progress bar to have a maximum of 100 and a step of 1. Then I created a loop to 100 and did a Thread.Sleep(50) each loop for about 5 seconds of time.

for( int ii = 0; ii < 110; ++ii)
{
    Thread.Sleep(50);
    form.statusOutputMainChannel(ii.ToString());
    form.progressBarVerifyUpdate();
}

I found that I had to call the bar.PerformStep() method another 10 times in order for the animation to catch up to the value.

This is another "ugly" way to get it to work, but it's not clear to me that the other answers really got to a "this fixes it" either.

  • Rob
rallen911
  • 148
  • 9
0

Try this. It works for me.

If ProgressBar1.Value = ProgressBar1.Maximum Then
    wait(500)
    ProgressBar1.Value = ProgressBar1.Maximum
End If

And the Wait sub is:

Public Sub wait(ByVal interval As Integer)
    ' Loops for a specificied period of time (milliseconds)
    Dim sw As New Stopwatch
    sw.Start()
    Do While sw.ElapsedMilliseconds < interval
        ' Allows UI to remain responsive
        Application.DoEvents()
    Loop
    sw.Stop()
End Sub
Cristian
  • 654
  • 1
  • 13
  • 30
amdoty
  • 1
0

C# answer: Create a method to finish and update the progress bar:

public async Task<bool> DoProgressbarFinishStep()
{
    myProgress.progressBar1.Value = myProgress.progressBar1.Maximum;
    await Task.Delay(5000);
    return true;
}

then when your other routine is finished doing its loop, call the method:

bool result = await DoProgressbarFinishStep();

The ProgressBar control will then update correctly to 100% during those 5000 milliseconds (or whatever time works for you).

From what I can tell, the ProgressBar appears to exhibit this behaviour on purpose, and needs asynchronous programming to work well. Let's face it, if you are performing such a long task that it requires a progress bar, then you should be programming asynchronously anyway to avoid locking up UI threads.

Read more (2021) at https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

Peter Wilson
  • 377
  • 3
  • 5