【问题标题】:ProgressBar is slow in Windows FormsWindows 窗体中的 ProgressBar 很慢
【发布时间】:2011-05-20 12:04:24
【问题描述】:

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

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

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

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

【问题讨论】:

  • 尝试调用 Application.DoEvents() 方法
  • 我不认为这是 Windows 窗体的问题。我多次使用进度条,它总是很快。检查是否有其他因素降低了您的应用程序性能。
  • 只有进度条有问题。我正在使用一个后台工作者,它报告在 0 到 100 之间循环的进度,每个值之间都有一个 System.Threading.Thread.Sleep(10) 。报告进度的事件处理程序在文本框和进度条中显示进度值。文本框从 0 到 100,进度条从 0 到大约 70。
  • 你试过使用后台工作者吗?
  • @AlirezaMaddah - 像这样的时候,我希望 cmets 可以被否决。 Application.DoEvents() 是邪恶的。

标签: c# .net winforms


【解决方案1】:

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

我知道避免这个问题的唯一方法是在更新进度条时向后退,如下:

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

更完整的讨论见Disabling .NET progressbar animation when changing value?

【讨论】:

  • 我必须检查一下,但我认为进度条也比 Windows XP 中的文本框慢。所以WPF还是应付吧,OK。
  • @user 我 100% 确定他就是答案。 WPF 绘制所有自己的控件并且不使用 Win32 控件。 WinForms 封装了 Win32 控件。见stackoverflow.com/questions/5332616/…
  • 没关系。我只是说我必须检查一下 Windows XP,我记得进度条比文本框“慢”。
  • @user 是的,我误解了。我想你会发现 XP 的行为如你所愿,但无论如何上面的技巧在那里仍然可以正常工作。如果我是你,我会将进度条的最大值设为 1001,然后使用像 Value := 10*n+1; 这样的代码值:= 10*n;这样你的后退一步就不会被注意到。
  • 刚刚在 Windows XP 上测试过,它的行为“正常”。我记得在 XP 上看到过类似的东西,但它一定是应用程序。
【解决方案2】:

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

该解决方案非常无缝,并成功处理了您在值为Maximum 时遇到的问题。 ProgressBar 的这种扩展方法缓解了在 Windows Vista 和 7 上运行时由 WinForms ProgressBar 控件中存在的渐进动画样式引起的滞后(我没有'尚未在 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);
}

【讨论】:

  • 老实说,末尾的pb.Value = value; 可以在else 内;当值处于最大值时减小最大值会自动减小值。
【解决方案3】:

您可以轻松编写自定义进度条以在没有动画的情况下显示其值。下面是一个简单的实现,展示从 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);
    }
}

【讨论】:

  • 好主意,但如果你想“倒退”则不起作用(可能是因为问题中不需要这样做)建议启用双缓冲,将所有绘图移动到 OnPaint 并在设置器中无效.但这对我来说是一个很好的提示,现在一切都很完美:)
【解决方案4】:

here 所示,一个更简单的答案是这样做:

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

解释:

它会在 PB 增加时为它设置动画,但在它减少时不会。这就是为什么上述 hack spears 来“解决”问题的原因。

【讨论】:

    【解决方案5】:

    我喜欢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");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-18
      • 2011-05-15
      • 1970-01-01
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多