vc技术内幕,有一节关于在对话框上控件画图(6.8节)的代码:
void CMyDlg::OnPaint()
{
CWnd *pWnd = GetDlgItem(IDC_STATIC1);
RECT rect;
pWnd->GetClientRect(&rect);
CDC *pControlDC = pWnd->GetDC();
pWnd->Invalidate();
pWnd->UpdateWindow();
pControlDC->SelectStockObject(BLACK_BRUSH);
pControlDC->Rectangle(rect.left, rect.top, rect.right,rect.bottom);
pWnd->ReleaseDC(pControlDC);
}
这里连用了 pWnd->Invalidate(); pWnd->UpdateWindow();
原文解释:“这里面的一个技巧就在于我们既要在控件窗口内绘图,又要防止Windows对它进行重复绘制。”
就是这两句代码(pWnd->Invalidate(); pWnd->UpdateWindow(); )让许多人摸不着头脑。
归纳起来有两种问题:
1、认为这是死循环
这是初学者提的问题,这里需指出:pWnd是IDC_STATIC控件的指针,并不是对当前窗体
2、对Invalidate、UpdateWindow均会引起WM_PAINT事件的不解
“系统会在多个不同的时机发送WM_PAINT消息:当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,这些动作都是由系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。”最后一句话指出了问题所在,Invalidate使客户区无效,但并没有及时触发WM_PAINT,WM_PAINT是由系统控制的,只有当在消息队列没有其他消息时才触发WM_PAINT
系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?
"这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽可能地推后做,这样有利于提高绘制的效率:在两个WM_PAINT消息之间多个Invalidate调用使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送WM_PAINT消息而不管Update Region是否为空等"
这也就解释了代码中去掉pWnd->UpdateWindow() 后不能出现所绘图的现象,其实图像已经绘制,只不过被Invalidate滞后触发的WM_PAINT刷了
MSDN的解释
pWnd->Invalidate();
Specifies whether the background within the update region is to be erased
pWnd->UpdateWindow();
Updates the client area by sending aWM_PAINT message if the update region is not empty. The UpdateWindow member function sends a WM_PAINT message directly, bypassing the application queue. If the update region is empty, WM_PAINT is not sent.