在一个Dialog的OnPaint按照下面的方法实现:
voidCMyDialog::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,这样它就可以不刷新绘制按钮区域,按钮就可以被看见,也可以响应了。
GetDlgItem(IDC_LIST1)->ModifyStyle(0,WS_CLIPSIBLINGS);