三、Windows鼠标操作之非显示区域鼠标消息
在窗口的显示区域内移动或按下鼠标按键时,将产生10种消息。如果鼠标在窗口的显示区域之外但还在窗口内,Windows就给窗口消息处理程序发送一条「非显示区域」鼠标消息。窗口非显示区域包括标题列、菜单和窗口滚动条。
通常,您不需要处理非显示区域鼠标消息,而是将这些消息传给DefWindowProc,从而使Windows执行系统功能。就这方面来说,非显示区域鼠标消息类似于系统键盘消息WM_SYSKEYDOWN、WM_SYSKEYUP和WM_SYSCHAR。
非显示区域鼠标消息几乎完全与显示区域鼠标消息相对应。消息中含有字母「NC」以表示是非显示区域消息。如果鼠标在窗口的非显示区域中移动,那么窗口消息处理程序会接收到WM_NCMOUSEMOVE消息。鼠标按键产生如表7-2所示的消息。
表7-2 |
键 |
按下 |
释放 |
按下(双击) |
左 |
WM_NCLBUTTONDOWN |
WM_NCLBUTTONUP |
WM_NCLBUTTONDBLCLK |
中 |
WM_NCMBUTTONDOWN |
WM_NCMBUTTONUP |
WM_NCMBUTTONDBLCLK |
右 |
WM_NCRBUTTONDOWN |
WM_NCRBUTTONUP |
WM_NCRBUTTONDBLCLK |
对非显示区域鼠标消息,wParam和lParam参数与显示区域鼠标消息的wParam和lParam参数不同。wParam参数指明移动或者按鼠标按键的非显示区域。它设定为WINUSER.H中定义的以HT开头的标识符之一(HT表示「命中测试」)。
lParam参数的低位word为x坐标,高位word为y坐标,但是,它们是屏幕坐标,而不是像显示区域鼠标消息那样指的是显示区域坐标。对屏幕坐标,显示器左上角的x和y的值为0。当往右移时x的值增加,往下移时y的值增加(见图7-2)。
您可以用两个Windows函数将屏幕坐标转换为显示区域坐标或者反之:
ScreenToClient (hwnd, &pt) ; ClientToScreen (hwnd, &pt) ;
这里pt是POINT结构。这两个函数转换了保存在结构中的值,而且没有保留以前的值。注意,如果屏幕坐标点在窗口显示区域的上面或者左边,显示区域坐标x或y值就是负值。
命中测试消息
如果您数一下,就可以知道我们已经介绍了21个鼠标消息中的20个,最后一个消息是WM_NCHITTEST,它代表「非显示区域命中测试」。此消息优先于所有其它的显示区域和非显示区域鼠标消息。lParam参数含有鼠标位置的x和y屏幕坐标,wParam参数没有用。
Windows应用程序通常把这个消息传送给DefWindowProc,然后Windows用WM_NCHITTEST消息产生与鼠标位置相关的所有其它鼠标消息。对于非显示区域鼠标消息,在处理WM_NCHITTEST时,从DefWindowProc传回的值将成为鼠标消息中的wParam参数,这个值可以是任意非显示区域鼠标消息的wParam值再加上以下内容:
HTCLIENT HTNOWHERE HTTRANSPARENT HTERROR |
显示区域 不在窗口中 窗口由另一个窗口覆盖 使DefWindowProc产生警示用的哔声 |
如果DefWindowProc在其处理WM_NCHITTEST消息后传回HTCLIENT,那么Windows将把屏幕坐标转换为显示区域坐标并产生显示区域鼠标消息。
如果您还记得我们如何通过拦截WM_SYSKEYDOWN消息来停用所有的系统键盘功能,那么您可能会想我们可否通过拦截鼠标消息完成类似的事情。完全可以!只要您在窗口消息处理程序中包含以下几条叙述:
case WM_NCHITTEST: return (LRESULT) HTNOWHERE ;
就可以有效地禁用您窗口中的所有显示区域和非显示区域鼠标消息。这样一来,当鼠标在您的窗口(包括系统菜单图标、缩放按钮以及关闭按钮)中时,鼠标按键将会失效。
从消息产生消息
Windows用WM_NCHITTEST消息产生所有其它鼠标消息,这种由消息引出其它消息的想法在Windows中是很普遍的。让我们来举个例子。您知道,如果您在一个Windows程序的系统菜单图标上双击一下,那么程序将会终止。双击产生一系列的WM_NCHITTEST消息。由于鼠标定位在系统菜单图标上,因此DefWindowProc将传回HTSYSMENU的值,并且Windows把wParam等于HTSYSMENU的WM_NCLBUTTONDBLCLK消息放在消息队列中。
窗口消息处理程序通常把鼠标消息传递给DefWindowProc,当DefWindowProc接收到wParam参数等于HTSYSMENU的WM_NCLBUTTONDBLCLK消息时,它就把wParam参数等于SC_CLOSE的WM_SYSCOMMAND消息放入消息队列中(这个WM_SYSCOMMAND消息是在使用者从系统菜单中选择「Close」时产生的)。同样地,窗口消息处理程序也把这个消息传给DefWindowProc。DefWindowProc通过给窗口消息处理程序发送WM_CLOSE消息来处理该消息。
如果一个程序在终止之前要求来自使用者的确认,那么窗口消息处理程序就需要拦截WM_CLOSE,否则,DefWindowProc呼叫DestroyWindow函数来处理WM_CLOSE。除了其它处理,DestroyWindow还给窗口消息处理程序发送一个WM_DESTROY消息。窗口消息处理程序通常用下列程序代码来处理WM_DESTROY消息:
caseWM_DESTROY: PostQuitMessage (0) ; return 0 ;
PostQuitMessage使得Windows把WM_QUIT消息放入消息队列中,此消息永远不会到达窗口消息处理程序,因为它使GetMessage传回0,并终止消息循环,从而也终止了程序。