以下是本人经过实践的几个能有效解决MFC窗体刷新闪烁,MFC自绘时闪烁的方法,在这里整理出来供大家参考。

方法1:减少绘制时间

减少绘制时间,尽量快的绘制出图像。在ON_WM_PAINT消息中尽量不要做与绘图无关或者大量冗余的事情,例如数值运算,for循环等,尽量在最短的时间内完成绘图。

方法2:剪裁重叠区域

剪裁控件之间或父窗体与子控件之间的重叠区域。

复杂的界面有多层个控件组成,他们之间可能重叠。在Windows窗体改变大小或者刷新时,从父窗体到窗体上的控件逐个收到ON_WM_PAINT消息等重绘消息,所以会先刷新并重绘父窗口,然后重绘子控件,子父窗口重画的过程一般无法在一个刷新周期内完成,所以会出现闪烁。这时候父窗口被子控件挡住的区域是没必要重绘的,可以给父窗口加上WS_CLIPCHILDREN样式,让父窗口重绘时剪裁所有的子控件区域。如果统计控件发生重叠,可以加上WS_CLIPCHILDREN样式。

方法3:不绘制背景

不绘制背景,重载ON_WM_ERASEBKGND消息,不做任何操作,直接返回。如果需要绘制背景可以在OnPaint函数中一起绘制背景。

BOOL CTimeLineEx::OnEraseBkgnd(CDC* pDC)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    return FALSE;
}

方法4:GDI双缓存技术

  1. 先使用CreateCompatibleDC创建兼容DC;
  2. 使用CreateCompatibleBitmap为兼容DC创建画布;
  3. 最后使用BitBlt将在内存中的图拷贝到屏幕上显示。
PAINTSTRUCT ps;
::BeginPaint(m_hWnd, &ps);
HDC hDC = ps.hdc;

// 获取控件矩形
CRect rcWnd;
GetClientRect(&rcWnd);

// 设置剪切区域
HRGN hrgn = CreateRectRgn(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
::SelectClipRgn(hDC, hrgn);

// 创建兼容DC
HDC hMemDC = ::CreateCompatibleDC(hDC);
if (hMemDC == NULL)
{
    // 创建兼容DC失败
    ::DeleteObject(hrgn);
    ::EndPaint(&ps);
    return;
}
::SelectClipRgn(hMemDC, hrgn);
HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, rcWnd.Width(), rcWnd.Height());
::SelectObject(hMemDC, hMemBmp);
::SetBkMode(hMemDC, TRANSPARENT);
    
// 在这里使用hMemDC绘制
    
// 将在内存中的图拷贝到屏幕上显示
::BitBlt(hDC, rcWnd.left, rcWnd.top, rcWnd.Width(), rcWnd.Height(), hMemDC, 0, 0, SRCCOPY);
    
// 释放资源
::DeleteObject(hMemBmp);
::DeleteDC(hMemDC);
::DeleteObject(hrgn);
::EndPaint(m_hWnd, &ps);

方法5:使用缓存

如果重绘的时候每次绘制的内容都是相同的,可以在第一次重绘时把图片缓存起来,下次重绘的时候直接把图片贴出来即可。

注意:如果使用CreateSolidBrushCreatePenCreateRectRgn等API创建GDI对象,在使用结束后一定要调用相应的DeleteObject删除GDI对象,否则会造成GDI泄露。

标签: MFC, GDI+, 双缓冲, 双缓存, 控件闪烁, 自绘闪烁, CDC

添加新评论