在一个Dialog的OnPaint按照下面的方法实现:
void CMyDialog::OnPaint()
{
CWnd* pWnd = GetDlgItem(IDC_STATIC1);
CDC* pDC = pWnd->GetDC();
pWnd->Invalidate(); //??????????
pWnd->UpdateWindow(); //????????????
pDC->SelectStockObject(BLACK_BRUSH);
pDC->Rectangle(0,0,10,10);
pWnd->ReleaseDC(pDC);
}
按照MSDN上的说明,通过 UpdateWindow 会向窗口发送 WM_PAINT 的消息,
那么相应改消息的时候是否又会调用 OnPaint 函数呢?
上面这段函数执行的顺序到底是怎么样的呢?
MSDN的解释如下:如果刷新区域不为空,UpdateWindow 会绕过消息队列直接发送 WM_PAINT 消息给子窗口。之所以要首先调用 pWnd->Invalidate,是因为首先要设置刷新区域。刷新之后,再开始static的自身绘制.
关于控件自身的重绘还包括很多,例如处理 WM_ERASEBKND,同时在每次绘制时,最好要重新用双缓冲绘制背景,而不是调用 Invalidate(TRUE),因为这样,仍然会引起轻微的闪烁。
但是有时候,我们会发现 UpdateWindow 会引起子窗口的更新。要避免这一点,最好把父窗口的属性加上 WS_CLIPCHILDREN.
WS_CLIPCHILDREN 和 WS_CLIPSIBLINGS 的作用还有很多。
例如这样一个问题。创建一个基本的对话框程序,在界面上先放置一个按钮,再次放置一个 ClistCtrl 的对象,其中按钮和 ClistCtrl 部分重叠在一起。
在VC或者VS中提供的对话框编辑器中,可以看到:
运行程序的时候,看到按钮和 ClistCtrl 重叠的部分被遮住,在 ClistCtrl 对象内鼠标点击按钮和此对象重叠的部分,按钮就出现了,可以看到效果是点击到了按钮。
如果我们交换按钮和 ClistCtrl 的tab顺序,就会发现按钮可以看见,但是却不能响应了。这说明 ClistCtrl 的z-order更靠前,但是显示的顺序却恰恰相反。
原因在于:Windows 的资源文件里并没有存储 Tab Order 的值,而是以控件出现的先后顺序体现,此先后顺序决定了对话框管理器创建控件的顺序(也顺便决定了兄弟窗口之间的初始 Z-Order)。各个控件依次添加到一个内部的管理链表中,而发生鼠标事件时,会从链表头开始逐项检查鼠标事件发生的位置是否处于某控件的窗口范围内,一旦测试命中,显然就不会再往后遍历了。而显示时则不同,显然所有的控件都是要显示的,后绘制的控件如果与前绘制的有重叠,显然最终会是后绘制的控件的样子。
当然,我们可以通过转发消息来实现,即在 PreTranslateMessage 中来进行控制。不过实际上利用WS_CLIPSIBLINGS可以很巧妙的解决这个问题。我们仍然按最先放置的顺序来看。这种情况下,按钮是可以被响应的,只是被遮住了。因此我们把listctrl的风格中加入WS_CLIPSIBLINGS,这样它就可以不刷新绘制按钮区域,按钮就可以被看见,也可以响应了。
参考:
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.
WS_CLIPSIBLINGS Clips child windows relative to each other; that is, when a particular child window receives a paint message, the WS_CLIPSIBLINGS style clips all other overlapped child windows out of the region of the child window to be updated. (If WS_CLIPSIBLINGS is not given and child windows overlap, when you draw within the client area of a child window, it is possible to draw within the client area of a neighboring child window.) For use with the WS_CHILD style only.
WS_CLIPCHILDREN Excludes the area occupied by child windows when you draw within the parent window. Used when you create the parent window.
评论