ProgressBar在Windows窗体中很慢

时间:2011-05-20 12:04:25

标签: c# .net winforms

我正在使用Windows Vista和Visual Studio 2010.创建.Net 4 Windows窗体应用程序。删除默认表单上的进度条,添加代码以处理表单加载事件并在那里执行progressBar1.Value = 100;

开始调试,你会看到动画在大约半秒内将进度条移动到100。

我的项目中需要2个进度条。一个用于“全局进展”,第二个用于“当前步进”,因此第二个从0到100,并且回到0以进行下一步。问题是,进度条对于某些快速步骤来说速度很慢,它永远不会达到100而且看起来很奇怪。

有没有办法摆脱那个动画?在WPF中没关系,但我宁愿继续使用Windows Forms。

5 个答案:

答案 0 :(得分:15)

这就是Vista / 7进度条的设计方式。当您更改进度条的值时,该栏会逐渐动画化为该值。

我知道避免此问题的唯一方法是在更新进度条时倒退,如下所示:

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

有关更完整的讨论,请参阅Disabling .NET progressbar animation when changing value?

答案 1 :(得分:13)

在相关问题中使用进度条和Heffernan's tip向前推进Reinhart's extension method approach时,我想出了自己的解决方案。

该解决方案非常无缝,并成功处理了值Maximum时您将遇到的问题。这种ProgressBar的扩展方法可以缓解在Windows Vista上运行时 WinForms ProgressBar控件中出现的渐进式动画样式导致的延迟 7(我还没有在Windows 8上测试过。)

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

样本使用:

private void backgroundWorker_ProgressChanged(object sender, 
                                                  ProgressChangedEventArgs e)
{
     progressBar.SetProgressNoAnimation(e.ProgressPercentage);
}

答案 2 :(得分:2)

您可以轻松编写自定义进度条以显示其值而无需动画。以下是一个简单的实现,用于显示从0到100的进度并恢复为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);
    }
}

答案 3 :(得分:0)

我喜欢Derek W's answer,并且设法找到了一种支持数据绑定的解决方案。我从System.Windows.Forms.ProgressBar继承并创建了新的可绑定属性。否则相同:

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

您可以像这样绑定到属性(viewModel具有一个名为Value的int属性):

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

答案 4 :(得分:0)

要做一个更简单的答案,如here所示:

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

说明:

它会在PB增大时对其进行动画处理,但在其减小时不会进行动画处理。这就是为什么上述黑客会“解决”问题的原因。