无闪烁可调整大小的自定义控件






4.76/5 (15投票s)
2005 年 2 月 10 日
2分钟阅读

102638

5798
这个动态调整大小的控件不会闪烁。本文介绍了问题以及用于解决闪烁的技术。
引言
我阅读了每一篇关于无闪烁绘图的文章,但我仍然无法使其适用于我的可调整大小的自定义控件。有一些已知问题,但也有一个很少被提及的微妙之处。在本文中,我将讨论所有涉及的问题以及如何处理它们。
本文中包含的示例是椭圆形按钮控件的实现。 绘图使用 GDI+ 完成。圆形按钮会随着鼠标光标悬停而改变外颜色。 当鼠标单击按钮时,内部颜色会发生变化。 当拖动对话框时,控件会拉伸和缩小。
背景
关于这个主题的许多文章建议:
- 使用双缓冲
- 重写
OnEraseBkgnd
并返回TRUE
。
好吧,我同时实现了这两个,但它仍然闪烁。 当调整大小(当整个对话框被重绘时)时,问题最明显。 问题是对话框在擦除其自身背景时会擦除我的自定义控件。 因此,每次我调整对话框的大小时,我都会遇到闪烁,因为对话框会在控件重新绘制自身之前,先用灰色覆盖我的控件。
使用代码
剪裁解决方案:告诉对话框擦除所有内容,除了圆形控件占据的区域。实现此目的的最简单方法是将 Windows 样式设置为WS_CLIPCHILDREN
。您可以在“OnInitDialog
”方法中以编程方式执行此操作(或者您可以在 Visual Studio 的设计视图中设置此对话框属性)
BOOL CNoFlickerDlg::OnInitDialog() { .... // Tell the dialog NOT to erase children controls ModifyStyle(0, WS_CLIPCHILDREN); return TRUE; // return TRUE unless you set the focus to a control }
由于我的控件具有自定义(圆形)区域,因此每次控件改变形状或大小时都必须重新计算。 为了使此功能正常工作,调用ReCalculateDimensions
函数的最佳位置是从对话框的OnEraseBkgnd
,像这样:
BOOL CNoFlickerDlg::OnEraseBkgnd(CDC* pDC)
{
m_cButtonControl.ReCalculateDimensions();
return CDialog::OnEraseBkgnd(pDC);
}
您必须在此处进行调用的原因是重要的。对话框将根据控件区域的设置,从其表面(正在被擦除)中排除控件区域。 因此,通过在CDialog::OnEraseBkgnd
之前调用它,您可以确保控件区域在被剪裁之前是最新的。
双缓冲:由于所有绘图都在 GDI+ 中完成,我使用CachedBitmap
类来帮助我进行双缓冲。 代码归结为以下几行:
void CButtonControl::OnPaint() { // device context for painting CPaintDC dc(this); Graphics graphics(dc.m_hDC); // Only redraw the control if the buffer is dirty if (m_bDirtyBuffer) { DrawButton(graphics); m_bDirtyBuffer = FALSE; } graphics.DrawCachedBitmap(m_pCachedBitmap, 0, 0); }
您可以从上面的代码中看出,只有当m_bDirtyBuffer
标志设置为 true 时,才会进行实际绘制。 其他时间,绘画是使用缓存的位图完成的。 每当调整控件大小时(即,在OnSize
方法中),您都会将该标志设置为 true。
最后,请确保控件本身已覆盖OnEraseBkgnd
方法并返回TRUE
。 这将使您的控件无闪烁!
关注点
下载演示并拖动它的角落。 看看按钮改变形状而没有任何闪烁。