尽管双重缓冲,但自动收报机仍在闪烁

有没有人知道如何摆脱闪烁? 我研究了SO,网络,并试了很多不同的东西,比如把TickerControl放到双缓冲面板和双缓冲时没有在OnPaint()中绘图:为什么它不起作用? 除了许多其他的东西。 它仍然闪烁,不是每次重复,而是每秒几次。

此外,即使在OnPaint中删除“g.Clear(BackColor)”之后,仍然必须清除背景,因为文本继续可读地滚动。

这里是我的TickerControl类的相关部分:

class TickerControl : Control { private static readonly StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap); private const int padding = 40; private const int scrollSleep = 10; private const int scrollAdvancePixels = 1; private float textWidth; private float currentX; private static readonly Timer scrollTimer = new Timer(); public TickerControl() { this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer , true); scrollTimer.Tick += AdvanceText; scrollTimer.Interval = scrollSleep; scrollTimer.Enabled = true; } private void AdvanceText(object sender, EventArgs e) { if (IsDisposed) return; currentX -= scrollAdvancePixels; if (currentX <= -textWidth) currentX = 0; Invalidate(); } protected override void OnPaintBackground(PaintEventArgs pevent) { } protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(BackColor); using (SolidBrush brush = new SolidBrush(ForeColor)) { g.DrawString(Text, Font, brush, currentX, 0, stringFormat); g.DrawString(Text, Font, brush, currentX + textWidth, 0, stringFormat); } } 

有任何想法吗?

更新:

正如sallushan建议手动双缓冲,让我补充说我以前尝试过,使用下面的代码。 但是在屏幕上它看起来与上面完全一样,所以问题似乎并不在我的OnPaint方法中。 我想它必须在我的Control的设置中的某个地方。

  private Bitmap backBuffer; protected override void OnPaint(PaintEventArgs e) { if (backBuffer == null) backBuffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height); Graphics g = Graphics.FromImage(backBuffer); g.Clear(BackColor); using (SolidBrush brush = new SolidBrush(ForeColor)) { g.DrawString(Text, Font, brush, currentX, 0, stringFormat); g.DrawString(Text, Font, brush, currentX + textWidth, 0, stringFormat); } g.Dispose(); e.Graphics.DrawImageUnscaled(backBuffer, 0, 0); } protected override void OnPaintBackground(PaintEventArgs pevent) { // Don't call base! } protected override void OnSizeChanged(EventArgs e) { if (backBuffer != null) { backBuffer.Dispose(); backBuffer = null; } base.OnSizeChanged(e); } 

在表单中设置此代码。 它会消除闪烁

  protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; return cp; } } 

希望能帮助到你

创建一个常量位图作为帧。 然后在该位图上进行更改,然后在控件上呈现该位图。 这应该消除闪烁。

 class MyControl : System.Windows.Forms.Control { Bitmap bmp; Graphics g; int x = 100; public MyControl() { this.SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint | System.Windows.Forms.ControlStyles.UserPaint | System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer , true); bmp = new Bitmap(100, 100); g = Graphics.FromImage(bmp); } protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { this.g.FillRectangle(Brushes.White, new Rectangle(0, 0, 100, 100)); this.g.DrawString("Hello", this.Font, Brushes.Black, (float)x, 10); e.Graphics.DrawImage(bmp, new Point(0, 0)); x--; } } 

尝试使用Invalidate(矩形)或Invalidate(Region)方法将重新绘制的数量限制为实际需要完成的重绘数量。 您正在重新绘制每个OnPaint事件期间的完整控件。

好吧,除了三重缓冲的想法 – 坦率地说我从未在我的自定义控件中使用过,而且我有相当复杂的想法 – 我会说每10毫秒使控件失效与它有关。 那是每秒100次。 25fps的电影在运动的流动性方面对于人眼来说是舒适的,但是你重新控制你的控制4倍。

尝试更高的价值:说40。

此外,当您重新绘制控件时,您可以只重绘它的一个区域,即更改的部分:形成旧文本位置的两个区域的联合和新文本位置。 你不需要重新绘制任何仍然存在的周围区域(无论是背景还是诸如此类)。

与TextRenderer的DrawText相比,DrawString()相当慢 – 大约6次,你可能想要使用它。 你将失去一些function(使用GDI而不是GDI +),但它可能适合你的场景。

哦,我还会微量优化并拉出您在每个OnPaint()外部重新创建的SolidBrush画笔作为类成员,仅在ForeColor更改时更新它。

看起来你正在绘制彼此相邻的2个字符串:

 g.DrawString(Text, Font, brush, currentX, 0, stringFormat); g.DrawString(Text, Font, brush, currentX + textWidth, 0, stringFormat); 

它应该只是仅仅使Invalidate ‘控制的那部分已经改变的情况。 最简单的方法是缓存/计算字符串的高度并执行以下操作:

  Invalidate(new Rectangle((int)currentX, 0, (int)textWidth*2, (int)textHeight)); 

在我的机器上,这比使整个控件无效更加平滑。

将Hans Passant的评论转化为答案,以便我接受它:

这种效果不称为闪烁,它被称为撕裂。 您会看到旧位图的一部分和新位图的一部分。 在移动物体上变得非常明显,它们显得紧张。 在Winforms中无法修复,google“垂直消隐间隔”。 – 汉斯帕斯特