闪烁的自定义 ProgressBar

Flickering custom ProgressBar

对于我正在制作的应用程序,我根据 William Daniel 制作的自定义 ProgressBar:How to change the color of progressbar in C# .NET 3.5?

在用较低的百分比值进行一些测试后,我注意到这个奇怪的闪烁:

我尝试将 DoubleBuffered 设置为 true 但它们仍然出现。

有谁知道为什么会出现这些奇怪的线条?

那不是闪烁,那是撕裂的一种形式。自定义控件并不总是完全绘制;如果您有一个不允许窗体正确重绘自身及其子控件的闭环,就会发生这种情况。
可能,使填充 ProgressBar 的过程异步。

的一些修改建议使其更流畅:

  • 使用浮点值进行计算(不要转换为 int),并使用 RectagleF 定义绘图的边界。

  • 删除位图并设置 ControlStyles.OptimizedDoubleBuffer:

    If true, the control is first drawn to a buffer rather than directly to the screen, which can reduce flicker. If you set this property to true, you should also set the AllPaintingInWmPaint to true.

  • 那么当然是设置ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint:

    AllPaintingInWmPaint: If true, the control ignores the window message WM_ERASEBKGND to reduce flicker. This style should only be applied if the UserPaint bit is set to true.
    UserPaint: If true, the control paints itself rather than the operating system doing so. If false, the Paint event is not raised. This style only applies to classes derived from Control.

  • ControlStyles.Opaque 删除背景并让 ProgressBarRenderer 完成其工作,以填充 ProgressBar 的基本图形。

根据当前Value设置计算ProgressBar彩色部分的当前宽度:
float width = (this.Width - 3) * ((float)this.Value / this.Maximum)
如果 width > 0,则使用 Forecolor 和 BackColor 属性绘制 ProgressBar 颜色。当然,您可以使用一组不同的属性来定义这些颜色。

为绘图添加一些抗锯齿,设置 Graphics.SmoothingMode, to make the color transition generated by the LinearGradientBrush smoother. This is more useful when you set LinearGradientMode.Horizontal

导致:


public class ProgressBarCustom : ProgressBar
{
    public ProgressBarCustom()
    {
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | 
            ControlStyles.AllPaintingInWmPaint | 
            ControlStyles.UserPaint | 
            ControlStyles.Opaque, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        ProgressBarRenderer.DrawHorizontalBar(e.Graphics, ClientRectangle);

        float width = (Width - 3) * ((float)Value / Maximum);
        if (width > 0) {
            var rect = new RectangleF(1, 1, width, Height - 3);
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            using (var brush = new LinearGradientBrush(rect, BackColor, ForeColor, LinearGradientMode.Horizontal)){
                e.Graphics.FillRectangle(brush, rect);
            }
        }
    }
}

作为一个小改进,您可以在 ProgressBar 的顶部绘制一条 9 像素高、半透明颜色的线,以模拟原始 反射 .
更改代码以添加 Graphics.DrawLine():

// [...]
using (var brush = new LinearGradientBrush(rect, BackColor, ForeColor, LinearGradientMode.Horizontal))
using (var pen = new Pen(Color.FromArgb(40, 240,240,240), 9)) {
    e.Graphics.FillRectangle(brush, rect);
    e.Graphics.DrawLine(pen, 1.0f, 1.0f, width, 1.0f);
}