如何在C#中使用Graphics.DrawString输出计时器时减少闪烁现象?

3
以下是能够在进度条上绘制文本的C#代码。它用于向进度条添加文本,显示剩余时间倒计时。我使用了Graphics类来绘制字符串,而不是Windows表单标签,因为当放置在进度条上时,标签背景无法设置为透明。
然而,使用这个代码时,每次更新文本时都会闪烁(这个方法在定时器中被调用,每秒钟触发一次),因此它几乎不停地闪烁,无法使用。
/// <summary>
    /// Adds time remaining text into a System.Windows.Forms.ProgressBar
    /// </summary>
    /// <param name="target">The target progress bar to add text into</param>
    /// <param name="remainingTimeText">The text to add into the progress bar.</param>
    private void set_progress_bar_text( System.Windows.Forms.ProgressBar target, string remainingTimeText )
    {
        // Make sure we do not have a null progress bar.
        if( target == null )
        {
            throw new ArgumentException( "Null Target" );
        }

        // Use the progress bar label font and size.
        Font textFont = new Font( labelProgress.Font.Name, labelProgress.Font.Size );

        // gr will be the graphics object we use to draw on the progress bar.
        using( Graphics gr = target.CreateGraphics() )
        {
            gr.DrawString( remainingTimeText,
                textFont,
                new SolidBrush( Color.Black ), // The brush we will use to draw the string, using a black colour.

                // The position on the progress bar to put the text.
                new PointF(
                // X location of text, to be placed at the centre of the progress bar.
                    progressBar.Width / 2 - ( gr.MeasureString( remainingTimeText,
                    textFont ).Width / 2.0F ),
                // Y Location
                progressBar.Height / 2 - ( gr.MeasureString( remainingTimeText,
                    textFont ).Height / 2.0F ) ) );
        }
    }

我已经尝试在该方法中设置DoubleBuffered = true,如Stack Overflow上相关问题所建议的那样,但它并不能防止闪烁。由于文本是一个必须每秒更新一次的倒计时时钟,因此我无法减少文本更新的次数。有没有办法通过双缓冲来防止闪烁,或者还有其他潜在的解决方案?


3
您应该在进度条的绘图事件内调用set_progress_bar_text,并通过EventArgs参数从给定的Graphics对象上进行绘制。这样可以使优化的绘图方法生效。计时器只需触发进度条的重绘即可。 - Ralf
1
还可以参考http://stackoverflow.com/questions/18418670/anyway-to-remove-text-flickering-in-this-custom-progressbar-class - Matthew Watson
据我所知,进度条没有自己的绘制事件,@Ralf。 - user2283597
看了其他问题后,那里的解决方案只是完全停止了我的文本显示。CreateParams会阻止所有文本在进度条上显示,我是否漏掉了某些步骤?谢谢@MatthewWatson - user2283597
3
你需要在paint事件中完成绘画,而不是像你现在这样做。 - Matthew Watson
1个回答

5
进度条似乎是那些试图隐藏绘制的控件之一。因此,我们需要自己编写代码来实现进度条。只需将您的ProgressBar替换为此控件即可。
根据评论中提到的内容,添加了CreateParams重写以减少闪烁。
public class MyLovelyProgressBar : ProgressBar
{
    public MyLovelyProgressBar()
    {
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    }

    private string foregroundText;
    public string ForegroundText 
    {
        get { return foregroundText;  }
        set 
        {
            if (foregroundText != value)
            {
                Invalidate();
                foregroundText = value;
            }
        } 
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams result = base.CreateParams;
            result.ExStyle |= 0x02000000; // WS_EX_COMPOSITED 
            return result;
        }
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg)
        {
            case 15: //WmPaint
                using (var graphics = Graphics.FromHwnd(Handle))
                    PaintForeGroundText(graphics);
                break;
        }
    }

    private void PaintForeGroundText(Graphics graphics)
    {
        if (!string.IsNullOrEmpty(ForegroundText))
        {
            var size = graphics.MeasureString(ForegroundText, this.Font);
            var point = new PointF(Width / 2.0F - size.Width / 2.0F, Height / 2.0F - size.Height / 2.0F);
            graphics.DrawString(ForegroundText, this.Font, new SolidBrush(Color.Black), point);
        }
    }
}

在计时器事件中,只需更改该进度条的 ForegroundText 即可。
myLovelyProgressBar1.ForegroundText = "A constantly changing lovely text";

这很适合在进度条上添加文本,但是除非也使用链接中提到的方法,否则仍然存在闪烁问题。现在完美地运作了,谢谢。 - user2283597
1
如果您有声望,请编辑我的答案并添加您的更改。如果没有,您是在谈论设置WS_EX_COMPOSITED样式吗? - Ralf
我似乎无法编辑您添加的代码,但是需要的是WS_EX_COMPOSITED,如下所示:protected override CreateParams CreateParams { get { CreateParams result = base.CreateParams; result.ExStyle |= 0x02000000; // WS_EX_COMPOSITED return result; } } - user2283597

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接