鼠标的使用同样是通过获取Windows鼠标消息来获取用户当前的鼠标状态的。
一、鼠标的介绍
鼠标是计算机的输入设备之一, 在图形化的操作系统上, 鼠标的使用使一些复杂的操作变得简单, 随着科技的进步, 鼠标的种类也越来越多, 按接口类型可分为串行鼠标、PS/2鼠标、总线鼠标、USB鼠标(多为光电鼠标)四种。按其工作原理及其内部结构的不同可以分为机械式,光机式和光电式。
这里我们不讨论鼠标的硬件构造, 更多关于鼠标的硬件知识请自行查阅相关资料。
1>. 鼠标所在的位置
在Windows系统下, 用户移动鼠标时, 在屏幕上一般会以一个斜式的箭头来表示鼠标当前的位置, 这个箭头实际上是一个位图格式的小图标, 称为"鼠标指针", 鼠标指针具有一个单像素精度的"热点"(hot spot), 当鼠标样式为箭头时, 这个"热点"就是鼠标箭头的顶点, 还有一些样式是"十"字样式, 这样的指针"热点"位于"十"字的中心位置, 热点在显示设备上指示了一个精确的位置。 当我们去捕获鼠标指针的位置时, 实际上是指鼠标指针的这个"热点"所在的像素单元的位置。
2>. 鼠标的术语
①. 单击 : 按下鼠标按键, 然后松开;
②. 双击 : 连续快速的按下鼠标同一个按键然后松开;
③. 拖动 : 保持按键按下状态, 并移动鼠标。
现在我们常见的三键鼠标, 三个按键分布称为左键、中键和右键, 其中左键的标识符简写为LBUTTON, 中键的标识符为MBUTTON, 右键的标识符为RBUTTON。 双键鼠标只有左键和右键, 单键鼠标只有左键。
3>. 鼠标的样式
Windows系统为鼠标提供了几种默认的鼠标样式, 如: 箭头、沙漏、十字瞄准等, 在以前学习的过程中实际上我们已经接触了使用默认的鼠标样式, 回忆这行代码:
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ;
这样就是使用一个默认的斜式箭头作为鼠标的指针样式, 斜式箭头样式的标识符为 IDC_ARROW, 这些标识符定义在 WINUSER.H 头文件中, 此外还有以下标识符及其对应的样式:
二、客户区鼠标消息
与键盘消息不同, 在键盘消息中, Windows只把键盘消息发送到当前具有输入焦点的窗口, 而鼠标消息无论窗口是否获取焦点, 只要鼠标经过客户区, 或者在客户区内被单击窗口过程都会收到鼠标消息, 被点击(包括双击/单击/拖动)的窗口将变成活动窗口。与客户区消息相对应的称为非客户区消息, 非客户区消息是指鼠标指针在窗口内并在在客户区外的移动或单击/双击等, 非客户区包括窗口的标题栏、菜单栏、滚动条、窗口的边框, 这些将在后面进行讨论, 这里先说客户区鼠标消息。
1>. 鼠标单击
鼠标在客户区单击时各个鼠标按键所产生的消息如下:
2>. wParam参数中的内容
参数wParam中的值表示了鼠标按钮、Shift键和Ctrl键的状态。 将wParam与"鼠标键"标识符进行按位与(&)运算可以得到鼠标按键与鼠标键的状态, 以前缀MK_为开头的标识符称为"鼠标键", 有如下鼠标键:
#define MK_LBUTTON 0x0001 //按下左键
#define MK_RBUTTON 0x0002 //按下右键
#define MK_MBUTTON 0x0010 //按下中键
#define MK_SHIFT 0x0004 //按下Shift键
#define MK_CONTROL 0x0008 //按下Ctrl键
例如, 当接收到 WM_LBUTTONDOWN 消息时, 若
wParam & MK_SHIFT
的值为TRUE(非零), 则表示按下左键的同时也按下了Shift键。
例如:
case WM_LBUTTONDOWN:
if( wParam & MK_CONTROL )
{
MessageBox( hwnd, TEXT("Ctrl键与鼠标左键同时被按下!"), TEXT("鼠标动作"), MB_OK ) ;
return 0 ;
}
return 0 ;
只有当鼠标左键与键盘的Ctrl键同时被按下时我们弹出个对话框说明"Ctrl键与鼠标左键同时
3>. 鼠标双击
双击对两次击中的位置以及时间间隔都有一定要求, 只有当两次快速的单击在物理位置上靠的很近并且时间间隔很短的情况下才算双击。
如果想让窗口过程接收鼠标双击消息, 需要在注册窗口类(RegisterClass)时, 初始化wndclass中的style成员的属性中再加上CS_DBLCLKS标识符:被按下!", 否则什么也不做。
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;
如果在窗口类的style成员中没有包含 CS_DBLCLKS 标识符, 那么即使当用户双击时不会产生双击消息, 而是产生一串如下的消息:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDOWN
WM_LBUTTONUP
由于用户在连续两次按下鼠标左键时需要一定时间, 即使这个时间比较短暂, 但是在这个过程中程序还是有可能收到其他消息的, 比如用户在快速的两次单击中手的微微抖动就会在其中插入一个WM_MOUSEMOVE的消息, 这里暂时忽略其中插入的消息, 假设消息就是连续的这些。
当窗口类的style成员只中包含CS_DBLCLKS标识符后, 用户再次双击就会产生这样的一串消息:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
可以看到, 在加入 CS_DBLCLKS 标识符后, 第三个消息 WM_LBUTTONDOWN 只是被简单的替换成了 WM_LBUTTONDBLCLK 消息。
鼠标各个按键双击时第三个消息所对应替换的消息如下:
#define WM_LBUTTONDBLCLK 0x0203 //左键
#define WM_MBUTTONDBLCLK 0x0209 //中键
#define WM_RBUTTONDBLCLK 0x0206 //右键
首先捕获 鼠标左键在非客户区的单击事件, 然后再通过 wParam 判断鼠标在窗口的位置, 这里获取鼠标位置是通过 LOWORD 和 HIWORD 宏完成的, 还有两个功能相同的宏也可以用来获取lParam中的鼠标信息, 他们是 GET_X_LPARAM 宏和 GET_Y_LPARAM, 不过这两个宏是定义在 WINDOWSX.H 头文件中的, 如果要使用这两个宏需要将 WINDOWSX.H 包含进来。
代码实例
按住鼠标左键,移动左键,生成多个点,让后连接各个点,产生图形
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
回调函数
参数:
hwnd : 窗口句柄
message : 消息ID
wParam和lParam:消息参数
HDC hdc;//定义设备环境句柄
static TCHAR szBuffer[128];
PAINTSTRUCT ps;
static int iCount, cyChar, cxClient, cyClient;
static POINT pt[MAX];
switch (message)
{
//case WM_SIZE://窗体大小改变
// hdc = GetDC(hWnd);
// //GetClientRect(hwnd, &rect);
// cxClient = LOWORD(lParam);
// cyClient = HIWORD(lParam);
// ReleaseDC(hWnd, hdc);
// return 0;
case WM_PAINT://窗口绘画:点
{
//int x, xLast;
int i, j;
HDC hdc = BeginPaint(hWnd, &ps);//函数为指定窗口进行绘画作准备,并用将和绘画有关的信息填充到一个 PAINTSTRUCT 结构中。
SetCursor(LoadCursor(NULL, IDC_WAIT));//设置箭头样式的标识符为wait
ShowCursor(TRUE); //显示光标。
for (i = 0; i < iCount; i++)//对记录的点进行连线
{
for (j = i + 1; j < iCount; j++)
{
MoveToEx(hdc, pt[i].x, pt[i].y, NULL);
LineTo(hdc, pt[j].x, pt[j].y);
}
}
ShowCursor(FALSE);
SetCursor(LoadCursor(NULL, IDC_ARROW));//设置箭头样式的标识符为箭头
EndPaint(hWnd, &ps);
return 0;
}
case WM_LBUTTONDOWN:
iCount = 0;
InvalidateRect(hWnd, NULL, TRUE);//按下鼠标左键时,不重绘背景
return 0;
case WM_LBUTTONUP:
InvalidateRect(hWnd, NULL, FALSE);//抬起鼠标左键时,重绘背景
return 0;
case WM_MOUSEMOVE://鼠标移动时记录点的位置,画出点
if (wParam & MK_LBUTTON && iCount < MAX)
{
hdc = GetDC(hWnd);
pt[iCount].x = GET_X_LPARAM(lParam);
pt[iCount].y = GET_Y_LPARAM(lParam);
SetPixelV(hdc, pt[iCount].x, pt[iCount].y, 0);
iCount++;
ReleaseDC(hWnd, hdc);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}