65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (15投票s)

2005 年 2 月 10 日

2分钟阅读

viewsIcon

102638

downloadIcon

5798

这个动态调整大小的控件不会闪烁。本文介绍了问题以及用于解决闪烁的技术。

Sample Image

引言

我阅读了每一篇关于无闪烁绘图的文章,但我仍然无法使其适用于我的可调整大小的自定义控件。有一些已知问题,但也有一个很少被提及的微妙之处。在本文中,我将讨论所有涉及的问题以及如何处理它们。

本文中包含的示例是椭圆形按钮控件的实现。 绘图使用 GDI+ 完成。圆形按钮会随着鼠标光标悬停而改变外颜色。 当鼠标单击按钮时,内部颜色会发生变化。 当拖动对话框时,控件会拉伸和缩小。

背景

关于这个主题的许多文章建议:

  1. 使用双缓冲
  2. 重写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。 这将使您的控件无闪烁!

关注点

下载演示并拖动它的角落。 看看按钮改变形状而没有任何闪烁。

无闪烁的可调整大小的自定义控件 - CodeProject - 代码之家
© . All rights reserved.