'ProgressBar is slow in Windows Forms

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.



Solution 1:[1]

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

Solution 2:[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);
    }
}

Solution 3:[3]

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.

Solution 4:[4]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2 fujieda
Solution 3 serg06
Solution 4 kwitee