19

I'm using Windows Vista and Visual Studio 2010. Create a .Net 4 Windows Forms Application. Drop a progress bar on the default form, add code to handle the form load event and do a progressBar1.Value = 100; there.

Start debugging and you see an animation moving the progress bar to 100 in about half a second.

I need 2 progress bars in my project. One is for "global progress" and the second is for "current step progress" so the second goes from 0 to 100 and hen back to 0 for the next step. The problem is that with the progress bar being slow for some of the quick steps it never reaches 100 and it looks weird.

Is there a way to get rid of that animation? In WPF it's OK but I'd rather stay with Windows Forms.

Derek W
  • 9,708
  • 5
  • 58
  • 67
user755327
  • 445
  • 1
  • 3
  • 10
  • try calling Application.DoEvents() method – Umesh CHILAKA May 20 '11 at 12:13
  • I don't think it's Windows Forms problem. I used progress bar many times and it was always fast. Check if something else is slowing your application performance. – Vale May 20 '11 at 12:13
  • It's a problem only with the progress bar. I'm using a backgroud worker that is reporting progress looping between 0 and 100 with a System.Threading.Thread.Sleep(10) in between each value. The event handler for report progress displays the value of the progress in a text box and in the progress bar. The text box goes from 0 to 100, the progress bar goes from 0 to about 70. – user755327 May 20 '11 at 12:16
  • Have you tried using background worker? – Vale May 20 '11 at 12:17
  • Umh, are you able to use Markee style instead normal style in the progress? – JTorrecilla May 20 '11 at 12:18
  • @Chilaka, Application.DoEvents() method must be avoided. See http://blogs.msdn.com/b/jfoscoding/archive/2005/08/06/448560.aspx – Alireza Maddah May 20 '11 at 12:28
  • 4
    @AlirezaMaddah - it's times like this when I wish comments could be downvoted. Application.DoEvents() IS EVIL. – Jonathon Reinhart Jun 07 '12 at 20:26
  • @JonathonReinhart I do agree. If the guy knows what actually this method does under the hoods, he'll never recommend it without considering the cons. – Alireza Maddah Jul 20 '12 at 10:20
  • possible duplicate of [Disabling .NET progressbar animation when changing value?](http://stackoverflow.com/questions/5332616/disabling-net-progressbar-animation-when-changing-value) – Jonathon Reinhart Apr 08 '14 at 19:01

5 Answers5

20

This is just how the Vista/7 progress bar is designed. When you change the value of the progress bar, the bar is animated to that value progressively.

The only way I know of avoiding this problem is to go backwards when updating the progress bar, as follows:

progressBar1.Value = n;
if (n>0)
    progressBar1.Value = n-1;

For a more complete discussion see Disabling .NET progressbar animation when changing value?

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I'll have to check but I think the progress bar was slower than the text box in Windows XP too. So WPF or cope with it, OK. – user755327 May 20 '11 at 13:15
  • @user I'm 100% sure that his is the answer. WPF draws all its own controls and doesn't use Win32 controls. WinForms wraps up the Win32 controls. See http://stackoverflow.com/questions/5332616/disabling-net-progressbar-animation-when-changing-value – David Heffernan May 20 '11 at 13:17
  • It's OK. I just meant I'll have to check with Windows XP, I remember the progress bar being "slower" than the text box. – user755327 May 20 '11 at 13:21
  • @user Right, I misunderstood. I think you'll find XP behaves as you like, but in any case the trick above will still work fine there. If I were you I would make the max value of my progress bar 1001 and then use code like Value := 10*n+1; Value := 10*n; so that your backwards step is never noticeable. – David Heffernan May 20 '11 at 13:24
  • Just tested on Windows XP and it behaves "normally". I remember seeing something like that on XP but it must have been the application. – user755327 May 20 '11 at 13:45
  • I decided to use this to make it 'jump' but only when setting to 100% complete. It looked weird to *not* have the animation for most of the progress, but by using this trick when setting 100% complete, it ensured that at least the progress bar popped over to 'done' before the next thing happened. – Daryn Mar 19 '13 at 17:52
  • this works nicely, but you need to multiply your values by 1000 like in your other answer, especially if you're using a very small range, like from 0 to 2. – Dave Cousineau Jun 29 '14 at 02:33
18

Building off of Heffernan's tip on going backwards with the progress bar and Reinhart's extension method approach in a related question, I came up with my own solution.

The solution is pretty seamless and successfully handles the issue you will encounter when the value is at Maximum. This extension method to ProgressBar alleviates the lagging that is caused from the progressive animation style present in the WinForms ProgressBar control when running on Windows Vista and 7 (I haven't tested on Windows 8 yet).

public static class ExtensionMethods
{
    /// <summary>
    /// Sets the progress bar value, without using 'Windows Aero' animation.
    /// This is to work around a known WinForms issue where the progress bar 
    /// is slow to update. 
    /// </summary>
    public static void SetProgressNoAnimation(this ProgressBar pb, int value)
    {
        // To get around the progressive animation, we need to move the 
        // progress bar backwards.
        if (value == pb.Maximum)
        {
            // Special case as value can't be set greater than Maximum.
            pb.Maximum = value + 1;     // Temporarily Increase Maximum
            pb.Value = value + 1;       // Move past
            pb.Maximum = value;         // Reset maximum
        }
        else
        {
            pb.Value = value + 1;       // Move past
        }
        pb.Value = value;               // Move to correct value
    }
}

Sample usage:

private void backgroundWorker_ProgressChanged(object sender, 
                                                  ProgressChangedEventArgs e)
{
     progressBar.SetProgressNoAnimation(e.ProgressPercentage);
}
Community
  • 1
  • 1
Derek W
  • 9,708
  • 5
  • 58
  • 67
  • Honestly, the `pb.Value = value;` at the end can be inside the `else`; reducing the maximum while the value is at maximum automatically reduces the value anyway. – Nyerguds Aug 27 '14 at 10:41
2

You can easily write a custom progress bar to show its value without animation. The following is a simple implementation to show the progress from 0 to 100 and revert to 0.

public class ProgressBarDirectRender : UserControl
{
    private int _value;
    public int Value
    {
        get { return _value; }
        set
        {
            if (value < 0 || value > 100)
                throw new ArgumentOutOfRangeException("value");
            _value = value;
            const int margin = 1;
            using (var g = CreateGraphics())
            {
                if (_value == 0)
                    ProgressBarRenderer.DrawHorizontalBar(g, ClientRectangle);
                else
                {
                    var rectangle = new Rectangle(ClientRectangle.X + margin,
                                                  ClientRectangle.Y + margin,
                                                  ClientRectangle.Width * _value / 100 - margin * 2,
                                                  ClientRectangle.Height - margin * 2);
                    ProgressBarRenderer.DrawHorizontalChunks(g, rectangle);
                }
            }
        }
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        ProgressBarRenderer.DrawHorizontalBar(e.Graphics, ClientRectangle);
    }
}
fujieda
  • 91
  • 1
  • 4
  • Good idea, but doesnt work if you want to go "backwards" (Maybe because this was not needed in the question) Would suggest to enable doublebuffering, move all drawing to OnPaint and just invalidate in the setter. But this was a good hint for me and now everything works perfect :) – drvolcano Nov 29 '15 at 11:56
2

A much simpler answer, as shown here, is to do this:

pbar.Value = value;
pbar.Value = value - 1;
pbar.Value = value;

Explanation:

It animates the PB as it increases, but not while it decreases. And that is why the above hack sppears to 'fix' the problem.

serg06
  • 2,027
  • 1
  • 18
  • 26
0

I liked Derek W's answer and I managed to find a solution which supports data binding. I inherited from System.Windows.Forms.ProgressBar and created new bindable property. Otherwise it's the same:

[DefaultBindingProperty("ValueNoAnimation")]
public class NoAnimationProgressBar : ProgressBar
{
    /// <summary>
    /// Sets the progress bar value, without using 'Windows Aero' animation.
    /// This is to work around (hack) for a known WinForms issue where the progress bar 
    /// is slow to update. 
    /// </summary>
    public int ValueNoAnimation
    {
        get => Value;
        set
        {
            // To get around the progressive animation, we need to move the 
            // progress bar backwards.
            if (value != Maximum)
                Value = value + 1; // Move past
            else
            {
                // Special case as value can't be set greater than Maximum.
                Maximum = value + 1;
                Value = value + 1;
                Maximum = value;
            }

            Value = value; // Move to correct value
        }
    }
}

You can bind to the property like this (viewModel has an int property called Value):

var dataSource = new BindingSource { DataSource = _viewModel };
progressBarBindingHack.DataBindings.Add("ValueNoAnimation", dataSource, "Value");
kwitee
  • 172
  • 4
  • 13