60 鼠标基础
用GetSystemMetrics函数来确定鼠标是否存在:
fMouse = GetSystemMetrics(SM_MOUSEPRESENT);
要确定所安装鼠标上键的个数,可使用:
cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); //如果没有安装鼠标,返回0.
当Windows用户移动鼠标时,Windows在显示屏上移动一个称为“鼠标光标”的小位图。鼠标光标有一个指向显示屏上精确位置的单像素的“热点”。
Windows支持几种预定义的鼠标光标,程序可以使用这些光标。最常见的是称为IDC_ARROW的斜箭头。热点在箭头的顶端。IDC_CROSS光标的热点在十字交叉线的中心。IDC_WAIT光标是一个沙漏,用于指示程序正在运行。
鼠标键动作的术语:
① 单击 按下并放开一个鼠标键
② 双击 快速按下并放开鼠标键两次
③ 拖曳 按住鼠标键并移动鼠标
对于三键鼠标,三个键分别被称为左键、中键、右键。在Windows头文件中定义的与鼠标有关的标识符使用缩写LBUTTON、MBUTTON、RBUTTON。
61 客户区鼠标消息
Windows只把键盘消息发送给拥有输入焦点的窗口。鼠标消息与此不同,只要鼠标跨越窗口或者在某窗口中按下鼠标键,那么窗口过程就会收到鼠标消息,而不管该窗口是否活动或者拥有输入焦点。
当鼠标移过窗口的客户区时,窗口过程收到WM_MOUSEMOVE消息。当在窗口的客户区按下或者释放一个鼠标键时,窗口过程会收到如下消息:
键 |
按下 |
释放 |
双击键 |
左 |
WM_LBUTTONDOWN |
WM_LBUTTONUP |
WM_LBUTTONDBLCLK |
中 |
WM_MBUTTONDOWN |
WM_MBUTTONUP |
WM_MBUTTONDBLCLK |
右 |
WM_RBUTTONDOWN |
WM_RBUTTONUP |
WM_RBUTTONDBLCLK |
仅当定义的窗口类能接收DBLCLK消息之后,窗口过程才能接收到双击消息。
对于所有这些消息来说,其lParam值均含有鼠标的位置:低位是x坐标,高位是y坐标。这两个坐标是相对于窗口客户区左上角的位置。
wParam的值指示鼠标键和Shift和Ctrl键的状态。MK_前缀代表“鼠标键”。
MK_LBUTTON 按下左键
MK_MBUTTON 按下中键
MK_RBUTTON 按下右键
MK_SHIFT 按下Shift键
MK_CONTROL 按下Ctrl键
如果接收到WM_LBUTTONDOWN消息,而且值wParam & MK_SHIFT是TRUE,就知道当左键按下时,也按下了右键。
当把鼠标移过窗口的客户区时,Windows并不为鼠标的每个可能的像素位置都产生一条WM_MOUSEMOVE消息。程序接收到WM_MOUSEMOVE消息的次数取决于鼠标硬件以及窗口过程处理鼠标移动消息的速度。
62 处理Shift键
要判断鼠标移动时,是否按下了Shift键,可以通过wParam进行判断,具体方法如下:
if (wParam & MK_SHIFT)
{
if (wParam & MK_CONTROL)
{
//按下了Shift和Ctrl键
}
else
{
//按下了Shift键
}
}
63 双击鼠标键
要确定为双击,这两次单击必须发生在其相互的物理位置十分接近的状况下,默认时范围是一个平均系统字体字符的宽,半个字符的高,并且发生在指定的时间间隔内。
如果希望窗口过程能够接收到双击键的鼠标消息,那么在调用RegisterClass初始化窗口类结构时,必须在窗口风格中包含CS_DBCLKS标识符。
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBCLKS;
如果在窗口风格中未包含CS_DBCLKS,那么用户在短时间内双击了鼠标键,窗口过程将接收到如下消息:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDOWN
WM_LBUTTONUP
如果窗口类风格中包含了CS_DBCLKS,那么双击鼠标键时窗口过程将接收到如下消息:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBCLK
WM_LBUTTONUP
64 非客户区鼠标消息
在窗口的客户区内移动或按下鼠标键时,将产生10个消息。如果鼠标在窗口的客户区之外,但在窗口内,Windows将给窗口过程发生一个“非客户区”鼠标消息。窗口非客户区包括标题栏、菜单、和窗口滚动条。
通常不需要处理非客户区鼠标消息,而将这些消息传给DefWindowProc,从而使Windows执行系统功能。
消息中包含字母“NC”以表示是非客户区消息。
如果鼠标在窗口的非客户区移动,那么窗口过程接收到WM_NCMOUSEMOVE消息。其他动作产生如下消息:
键 |
按下 |
释放 |
双击 |
左 |
WM_NCLBUTTONDOWN |
WM_NCLBUTTONUP |
WM_NCLBUTTONDBCLK |
中 |
WM_NCMBUTTONDOWN |
WM_NCMBUTTONUP |
WM_NCMBUTTONDBCLK |
右 |
WM_NCRBUTTONDOWN |
WM_NCRBUTTONUP |
WM_NCRBUTTONDBCLK |
非客户区鼠标消息的wParam和lParam参数意义如下:
wParam:指明移动或者单击鼠标键的非客户区位置。以HT开头。
lParam:包含低位字的x坐标和高位字的y坐标,但是它们都是屏幕坐标,而不是客户区坐标。
使用以下两个函数将屏幕坐标和客户区坐标互换。
ScreenToClient(hwnd, &pt);
ClientToScreen(hwnd, &pt);
如果屏幕坐标点在窗口客户区的上面或者左边,客户区坐标x或y值就是负值。
65 命中测试
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将把屏幕坐标转换为客户区坐标并产生客户区鼠标消息。
66 从消息产生消息
如果在一个Windows程序的系统菜单图标上双击一下,那么程序将会终止。双击产生一些了的WM_NCHITTEST消息。由于鼠标定位在系统菜单图标上,所以DefWindowProc将返回HTSYSMENU的值,并且Windows把wParam等于HTSYSMENU的WM_NCLBUTTONDBCLK消息放在消息队列中。
当DefWindowProc接收到wParam参数为HTSYSMENU的WM_NCLBUTTONDBCLK消息时,就把wParam参数为SC_CLOSE的WM_SYSCOMMAND消息放入消息队列中。同样,窗口过程也把这个消息传给DefWindowProc。DefWindowProc通过给窗口过程发生WM_CLOSE消息来处理该消息。
如果一个程序在终止前需要来自用户的确认,那么窗口过程就必须捕获WM_CLOSE,否则,DefWindowProc将调用DestroyWindow函数来处理WM_CLOSE。
67 捕获鼠标
捕获鼠标,只要调用SetCapture(hwnd);在这个函数调用之后,Windows将所有鼠标消息发给窗口句柄为hwnd的窗口过程。鼠标消息总是客户区消息,即使鼠标正在窗口的非客户区。lParam参数将指示鼠标在客户区坐标中的位置。不过,当鼠标位于客户区的左边或者上方的时候,这些x和y坐标可以是负的。
当需要释放鼠标时,调用ReleaseCapture();就可恢复正常。
如果鼠标被捕获,而鼠标键并没有被按下,并且鼠标光标移到了另一个窗口上,那么将不是由捕获鼠标的那个窗口而是由光标下面的窗口来接收鼠标消息。
68 鼠标轮
鼠标轮的转动产生一个WM_MOUSEWHEEL消息。
lParam参数将获得鼠标的位置,坐标是相对于屏幕左上角的,不是客户区的。
wParam参数低字包含一系列的标识,用于表明鼠标键和Shift与Ctrl键的状态。
wParam的高字中有一个“delta”值,该值默认可以是120或-120,这取决于滚轮是向前转动还是向后转动。值120或-120表明文档将分别向上或向下滚动三行。