在 C# 中为计时器使用 Graphics.DrawString 时如何减少闪烁?
How to reduce flickering when using Graphics.DrawString in C#, for a timer?
下面是在进度条上绘制文本的 C# 代码。它用于向进度条添加文本,显示剩余时间倒计时。我使用 Graphics class 来绘制字符串而不是 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 ) ) );
}
}
我已经尝试按照 Stack Overflow 上相关问题的建议在此方法中设置 DoubleBuffered = true
,但它不能防止闪烁。我无法减少文本更新的次数,因为文本是一个必须每秒更新一次的倒计时时钟。有没有办法用双缓冲来防止闪烁,或者有其他可能的解决方案吗?
ProgressBar 似乎是那些试图隐藏绘画的控件之一。所以我们需要自己动手。只需将您的 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);
}
}
}
然后只需在您的计时器事件中更改 ProgressBar 的 ForegroundText。
myLovelyProgressBar1.ForegroundText = "A constantly changing lovely text";
下面是在进度条上绘制文本的 C# 代码。它用于向进度条添加文本,显示剩余时间倒计时。我使用 Graphics class 来绘制字符串而不是 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 ) ) );
}
}
我已经尝试按照 Stack Overflow 上相关问题的建议在此方法中设置 DoubleBuffered = true
,但它不能防止闪烁。我无法减少文本更新的次数,因为文本是一个必须每秒更新一次的倒计时时钟。有没有办法用双缓冲来防止闪烁,或者有其他可能的解决方案吗?
ProgressBar 似乎是那些试图隐藏绘画的控件之一。所以我们需要自己动手。只需将您的 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);
}
}
}
然后只需在您的计时器事件中更改 ProgressBar 的 ForegroundText。
myLovelyProgressBar1.ForegroundText = "A constantly changing lovely text";