最近学习了一下Windows 程序设计这本书中的鼠标章节,现在总结一下学习的内容。大家可以一起参考。我将分为两个部分进行介绍。有关鼠标更具体的学习可以参考微软官方文档鼠标输入
1.鼠标的基础知识
1.0 定义:鼠标带有一个或者多个按钮的定位设备。区别于键盘:输入和管理文本功能。鼠标主要用来绘制和处理图形对象。我们常用的鼠标是两个按钮+一个滚轮,其实滚轮中间也有一个按钮。
1.1 操作系统中与鼠标有关的API函数:
①获取电脑是否连接鼠标:
fMouse = GetSystemMetrics(SM_MOUSEPRESENT)
// fMouse:连接了鼠标时为TRUE,否则为0。
②确定鼠标按钮数
cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS)
// 如果没有鼠标则cButtons 为0。
③设置鼠标的其他信息
BOOL
WINAPI
SystemParametersInfoW(
_In_ UINT uiAction,
_In_ UINT uiParam,
_Pre_maybenull_ _Post_valid_ PVOID pvParam,
_In_ UINT fWinIni);
例如获取鼠标滚轮信息:
SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,&uScrollLine,0);
1.2 一些和鼠标有关的术语:
①鼠标指针:位图格式的小图标。具有一个单像素精度的"热点"(hot spot),它在显示设备上指示了一个精确位置。我们平时知道的鼠标位置,说的就是热点的位置了。
Windows 在Winuser.h中为我们提供了几种常用的指针。最常用的是斜向箭头,称为IDC_ARROW。然后还有IDC_CROSS(➕ 状)、IDC_WAIT(⌛状,表示等待)等,具体查看Winuser.h头文件。我们的创建窗口类时,默认指定为IDC_ARROW。
ws.hCursor = LoadCursor(NULL, IDC_ARROW);
②单击:按下鼠标按钮,然后松开。
③双击:连续两次快速按下按钮并且松开。
④拖动:保持按下按钮,并且移动鼠标。
2.客户区鼠标消息
2.0 介绍:与键盘不同的是键盘只会把消息发送到具有指定焦点的窗口上。但是鼠标不同,当鼠标被单击或者经过窗口时,即使该窗口是非活动窗口或不带输入焦点,窗口过程依然会处理鼠标消息。
Windows 定义了21种鼠标消息。但是其中11种消息与客户区无关,称为"非客户区消息"。Windows应用程序经常忽略这类消息。
2.1 客户区鼠标消息:
按钮 | 按下 | 释放 | 第二次按下按钮 |
---|---|---|---|
左键 | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_LBUTTONDBLCLK |
中键 | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_MBUTTONDBLCLK |
右键 | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_RBUTTONDBLCLK |
注意:只有当窗口定义成了接受鼠标消息时,窗口过程才能处理DBLICK。
对于所有的客户区消息来说:
参数LParam:包含了鼠标的位置信息。LOWORD(LParam):低位字表示x坐标,HIWORD(LParam)表示y坐标。
参数wParam:表示鼠标按钮、SHIFT键和CTRL键的状态。在WinUser.h头文件中定义了位掩码来测试参数wParam。前缀MK_代表"鼠标键"(Mouse Key).
/*
* Key State Masks for Mouse Messages
*/
#define MK_LBUTTON 0x0001
#define MK_RBUTTON 0x0002
#define MK_SHIFT 0x0004
#define MK_CONTROL 0x0008
#define MK_MBUTTON 0x0010
#if(_WIN32_WINNT >= 0x0500)
#define MK_XBUTTON1 0x0020
#define MK_XBUTTON2 0x0040
#endif /* _WIN32_WINNT >= 0x0500 */
例如可以这样使用判断当出现WM_LBUTTONDOWN消息时,是否同时按下SHIFT键:
case WM_LBUTTONDOWN:
{
if(wParam & MK_SHIFT)
// Do Something...
MessageBox(hWnd, TEXT("鼠标左键 + SHIFT键 "), TEXT("提示"), MB_OK);
break;
}
Windows规定:如果在非活动窗口内按下鼠标左键,Windows会将这个窗口变成活动窗口。并且向该窗口发送WM_LBUTTONDOWN消息。窗口过程再收到WM_LBUTTONDOWN消息时,就能保证安全的保证该窗口是活动窗口。另外即使窗口没有WM_LBUTTONDOWN消息时,也能够处理WM_LBUTTONUP消息。
下面是一个简单例子:
#include <windows.h>
#define MAXPOINTS 1000
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Connect") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Connect-the-Points Mouse Demo"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static POINT pt[MAXPOINTS] ;
static int iCount ;
HDC hdc ;
int i, j ;
PAINTSTRUCT ps ;
switch (message)
{
case WM_LBUTTONDOWN:
iCount = 0 ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON && iCount < 1000)
{
pt[iCount ].x = LOWORD (lParam) ;
pt[iCount++].y = HIWORD (lParam) ;
hdc = GetDC (hwnd) ;
SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0) ;
ReleaseDC (hwnd, hdc) ;
}
return 0 ;
case WM_LBUTTONUP:
InvalidateRect (hwnd, NULL, FALSE) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
ShowCursor (TRUE) ;
for (i = 0 ; i < iCount - 1 ; 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_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
2.2 处理Shift键:
在上面的程序中在处理WM_MOUSEMOVE消息时有如下处理: if (wParam & MK_LBUTTON && iCount < 1000)判断是否按下鼠标左键。如果在处理鼠标消息时,需要用判断Shift键和Ctrl键的状态时,可以这样处理:
if(wParam & MK_SHIFT)
{
if (wParam & MK_CONTROL)
{
// 按下Shift + Ctrl 键
MessageBeep(0);
MessageBox(hWnd,TEXT("Ctrl+Shift 键被按下"),TEXT("提示"),MB_OK);
}
else if (wParam & MK_SHIFT)
{
// 按下Shift键
MessageBeep(0);
MessageBox(hWnd, TEXT("Shift 键被按下"), TEXT("提示"), MB_OK);
}
}
else
{
if (wParam & MK_CONTROL)
{
MessageBox(hWnd, TEXT("Ctrl 键被按下"), TEXT("提示"), MB_OK);
}
else
{
MessageBox(hWnd, TEXT("Ctrl 和 shift键都没有被按下"), TEXT("提示"), MB_OK);
}
}
利用虚拟键VK_LBUTTON、VK_RBUTTON、VK_MBUTTON、VK_SHIFT、VK_CONTROL和GetKeyState()函数也能够返回鼠标按钮的状态。
2.3 鼠标双击:
要做到鼠标双击需要达到两个条件:
①物理位置上十分靠近。
②必须发生在特定的时间间隔内。(双击速度)
前面提到窗口过程要处理鼠标双击消息时,窗口类需要包含CS_DBLCLKS风格:
ws.style = CS_HREDRAW | CS_VREDRAW| CS_DBLCLKS;
如果窗口过程没有包含CS_DBLCLKS风格,则窗口过程处理鼠标双击消息顺序如下:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDOWN
WM_LBUTTONUP
可以使用GetMessageTime函数来获取WM_LBUTTONDOWN消息之间的时间间隔。
包含CS_DBLCLKS风格时,处理顺序如下:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
3.非客户区鼠标消息
到目前为止我们说的10种鼠标消息都是发生在窗口客户区内的移动或者单击。如果鼠标位于窗口客户区以外的区域,鼠标就会产生非客户区消息。窗口的非封闭区域包括其边框、菜单栏、标题栏、滚动条、窗口菜单、最小化按钮和最大化按钮。具体可以参考微软官方文档鼠标输入
系统一般不需要用户处理非客户区消息。只需要将消息发送给DefWindowProc函数,由系统来处理。这与键盘中的WM_SYSKEYDOWN、WM_SYSKEYUP、WM_CHAR等消息相似。非客户区消息与客户区消息基本上是对应的。表识符包含了"NC"字样,表示"非客户"(NonClient).
非客户区消息如下:
按钮 | 按下 | 释放 | 第二次按下按钮 |
---|---|---|---|
左键 | WM_NCLBUTTONDOWN | WM_NCLBUTTONUP | WM_NCLBUTTONDBLCLK |
中键 | WM_NCMBUTTONDOWN | WM_NCMBUTTONUP | WM_NCMBUTTONDBLCLK |
右键 | WM_NCRBUTTONDOWN | WM_NCRBUTTONUP | WM_NCRBUTTONDBLCLK |
非客户区消息的wParam参数与客户区的有一些不同。wParam参数表示非客户区鼠标移动或者单击的位置。它的值被设定成表示一个HT为首的标识符,其中HT表示"击中测试"。
LParam则相同都是表示坐标。
3.1 击中测试消息
我们来学习最后一个鼠标消息叫做WM_NCHITTEST。这个消息的优先级高于其他所有的客户区和非客户区消息。Windows 应用程序通常会把这个消息发送给DefWindowProc函数,然后系统利用WM_NCHITTEST产生其他所有和鼠标位置有关的消息。DefWindowProc函数返回一个可用于鼠标参数的wParam值。这个值可以是任何非客户区消息的wParam值。也可以是如下的值。
HTBORDER
18
在没有大小调整边框的窗口的边框中。
HTBOTTOM
15
在可调整大小的窗口的下水平边框 (用户可以单击鼠标以垂直调整窗口大小) 。
HTBOTTOMLEFT
16
在可调整大小的窗口边框的左下角, (用户可以单击鼠标以) 对角线调整窗口大小。
HTBOTTOMRIGHT
17
在可调整大小的窗口边框的右下角 (用户可以单击鼠标以) 对角线调整窗口大小。
HTCAPTION
2
在标题栏中。
HTCLIENT
1
在工作区中。
HTCLOSE
20
在 “关闭 ”按钮中。
HTERROR
-2
在屏幕背景或窗口之间的分隔线上, (与 HTNOWHERE 相同,只不过 DefWindowProc 函数会发出系统蜂鸣声以指示) 错误。
HTGROWBOX
4
在大小框中, (与 HTSIZE) 相同。
HTHELP
21
在 “帮助 ”按钮中。
HTHSCROLL
6
在水平滚动条中。
HTLEFT
10
在可调整大小的窗口的左边框 (用户可以单击鼠标以水平调整窗口大小) 。
HTMENU
5
在菜单中。
HTMAXBUTTON
9
在 “最大化 ”按钮中。
HTMINBUTTON
8
在 “最小化 ”按钮中。
HTNOWHERE
0
在屏幕背景上或窗口之间的分隔线上。
HTREDUCE
8
在 “最小化 ”按钮中。
HTRIGHT
11
在可调整大小的窗口的右边框 (用户可以单击鼠标以水平调整窗口大小) 。
HTSIZE
4
在大小框中 (与 HTGROWBOX) 相同。
HTSYSMENU
3
在窗口菜单或子窗口的 “关闭 ”按钮中。
HTTOP
12
在窗口的上水平边框中。
HTTOPLEFT
13
在窗口边框的左上角。
HTTOPRIGHT
14
在窗口边框的右上角。
HTTRANSPARENT
-1
在同一线程 (当前由另一个窗口覆盖的窗口中,消息将发送到同一线程中的基础窗口,直到其中一个窗口返回不是 HTTRANSPARENT) 的代码。
HTVSCROLL
7
在垂直滚动条中。
HTZOOM
9
在 “最大化 ”按钮中。文章来源:https://www.toymoban.com/news/detail-834205.html
欢迎大家一起学习和交流。文章来源地址https://www.toymoban.com/news/detail-834205.html
到了这里,关于Windows 程序设计--- 鼠标(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!