7.3 非客户区鼠标消息

        摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P230

        到目前为止,所有讨论的 10 种鼠标消息都是发生在窗口客户区内的移动或单击。如果鼠标位于窗口内部除客户区外的其他区域,Windows 就会向窗口过程发送一个“非客户区”鼠标消息。窗口的非客户区包括标题栏、菜单和窗口滚动条

        系统一般不需要用户处理非客户区鼠标消息。取而代之的是,用户只需将这些消息发送给 DefWindowProc,从而使 Windows 执行系统函数。从这个角度来看,非客户区鼠标消息与 WM_SYSKEYDOWN、WM_SYSKEYUP 和 WM_SYSCHAR 这样的系统键盘消息很相似。

        非客户区鼠标消息几乎与客户区鼠标消息完全对应。消息的标识符包含了字母“NC”,表示“非客户”(nonclient)。如果鼠标在窗口的非客户区内移动,窗口过程就会接收 WM_NCMOUSEMOVE 消息。鼠标按钮产生的消息如下表所示。

按  钮 按  下 释  放 第二次按下按钮
 左键  WM_NCLBUTTONDOWN  WM_NCLBUTTONUP  WM_NCLBUTTONDBLCLK
 中键  WM_NCMBUTTONDOWN  WM_NCMBUTTONUP  WM_NCMBUTTONDBLCLK
 右键  WM_NCRBUTTONDOWN  WM_NCRBUTTONUP  WM_NCRBUTTONDBLCLK

        非客户区鼠标消息的参数 wParam 和 lParam 与客户区鼠标消息的参数有些不同。参数 wParam 表示非客户区鼠标移动或单击的位置。它的值被设定成一个以 HT 为首的标识符。其中 HT 表示“击中测试”(hit-test)。这些标识符都定义在 WINUSER.H 头文件中。

        参数 lParam 的低位字包含 x 坐标,高位字包含 y 坐标。但是,这些坐标都是屏幕坐标,而不是前面的客户坐标。对屏幕坐标来说,显示区域左上角的 x 和 y 都是 0。向右表示 x 值增加的方向,而沿屏幕向下表示 y 值增加的方向。(如图 7-3 所示。)

7.3 非客户区鼠标消息_第1张图片

        利用下面两个 Windows 函数,可以将屏幕坐标与客户区坐标相互转换:

ScreenToClient (hwnd, &pt);
ClientToScreen (hwnd, &pt);
其中 pt 是一个 POINT 结构。这两个函数转换 POINT 结构保存的坐标值,且不保留过去的值。

        注意,如果一个屏幕坐标点位于窗口客户区的上方或者左方,那么转换成客户区坐标后,x 值或 y 值会是负数。

7.3.1  击中测试消息

        数一数,对于所有 21 个鼠标消息,到现在我们已经学习了其中 20 个。最后一个消息是 WM_NCHITTEST,表示“非客户区击中测试”(nonclient hit test)。这个消息的优先级高于其他所有的客户区和非客户区鼠标消息。参数 lParam 包含鼠标位置的屏幕坐标 x 和 y。参数 wParam 没有用到。

        Windows 应用程序通常会把这个消息发送给 DefWindowProc。然后 Windows 会利用 WM_NCHITTEST 消息来产生所有其他和鼠标位置相关的鼠标消息。对非客户区消息来说,DefWindowProc 处理 WM_NCHITTEST 消息后返回一个可用于鼠标消息参数 wParam 的值。这个返回值可以是任何一个非客户区鼠标消息的 wParam 参数的值,也可以是如下所示的一些值:

HTCLIENT                 客户区
HTNOWHERE                不在任何窗口
HTTRANSPARENT            被另一个窗口覆盖的窗口
HTERROR                  使函数 DefWindowProc 产生一个警示声
如果 DefWindowProc 在处理 WM_NCHITEST 消息之后 返回 HTCLIENT,则 Windows 会将屏幕坐标转换成客户区坐标,并产生一个客户区鼠标消息

        你可能记得如何捕捉 WM_SYSKEYDOWN 消息,使所有的系统键盘函数失效。也许你会想,可不可以利用鼠标消息来实现类似的功能呢?当然可以!如果在窗口过程中包含下面几行语句:

case WM_NCHITTEST:
     return (LRESULT) HTNOWHERE;
那么 程序就能够有效地阻止系统向窗口发送所有客户区和非客户区鼠标消息。此时,无论鼠标位于窗口的任何位置,包括系统菜单图标、调整大小按钮和关闭按钮,鼠标按钮操作都将失效。

7.3.2  消息引发消息

        Windows 利用 WM_NCHITTEST 消息来产生其他所有的鼠标消息。这种消息引发消息的思想在 Windows 中很常见。举一个例子来说,你可能知道,双击 Windows 程序的系统菜单图标可以关闭这个这个窗口。双击产生了一系列 WM_NCHITTEST 消息。鼠标位于系统菜单图标之上,所以 DefWindowProc 返回 HTSYSMENU,这时 Windows 在消息队列中添加了一个 WM_NCLBUTTONDBLCLK 消息,其中参数 wParam 等于 HTSYSMENU。

        窗口过程一般将这个消息发送给 DefWindowProc。当 DefWindowProc 接收到参数 wParam 为 HTSYSMENU 的 WM_NCLBUTTONDBLCLK 消息时,系统会在消息队列中添加一个 WM_SYSCOMMAND 消息,其中参数 wParam 等于 SC_CLOSE。(当用户选择系统菜单中的关闭按钮时,也产生 WM_SYSCOMMAND 消息。)然后,通常窗口过程再将这个消息发送给 DefWindowProc。DefWindowProc 处理这个消息,并向窗口发送 WM_CLOSE 消息。

        如果想在结束程序之前等待用户的确认,窗口过程可以捕捉 WM_CLOSE 消息。否则,DefWindowProc 会调用 DestroyWindow 函数来处理 WM_CLOSE 消息。除了其他处理,DestroyWindow 还会向窗口过程发送一个 WM_DESTROY 消息。在正常情况下,窗口过程处理 WM_DESTROY 消息的代码如下。

case WM_DESTROY:
    PostQuitMessage (0);
    reutrn 0;
PostQuitMessage 函数使 Windows 在消息队列中添加一个 WM_QUIT 消息。窗口过程不会接收到这个消息,因为它会导致 GetMessage 函数返回 0,从而结束了消息循环,整个程序也就退出了。


你可能感兴趣的:(《Windows,程序设计》学习之旅)