赞
踩
关于鼠标的一些细节知识:
通常,我们发消息时,都是对一个特定的窗口,但是对于鼠标消息却不然:只要鼠标跨越窗口或者在某窗口中按下鼠标按键,那么窗口消息处理程序就会收到鼠标消息,而不管该窗口是否活动或者是否拥有输入焦点。鼠标消息一个有21种:10个显示区域消息,11个非显示区域消息
显示区域鼠标消息
当鼠标移过窗口的显示区域时,窗口消息处理程序收到WM_MOUSEMOVE消息。
当在窗口的显示区域中按下或者释放一个鼠标按键时,窗口消息处理程序会接收到下面这些消息:
键 | 按下 | 释放 | 按下(双键) |
左 | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_LBUTTONDBLCLK |
中 | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_MBUTTONDBLCLK |
右 | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_RBUTTONDBLCLK |
只有对三键鼠标,窗口消息处理程序才会收到MBUTTON消息。
对于这些消息,其lParam值均含有鼠标的位置:低字字节为x坐标,高字节为y坐标,这两个坐标是相对于窗口显示区域左上角的位置。您可以用LOWORD和HIWORD宏来提取这些值:
- x = LOWORD (lParam) ;
- y = HIWORD (lParam) ;
wParam的值指示鼠标按键以及Shift和Ctrl键的状态:
MK_LBUTTON | 按下左键 |
MK_MBUTTON | 按下中键 |
MK_RBUTTON | 按下右键 |
MK_SHIFT | 按下Shift键 |
MK_CONTROL | 按下Ctrl键 |
举个例子,如果收到了WM_LBUTTONDOWN且wparam & MK_SHIFT为真,则说明左键按下时也按下了Shift键。
这里要强调一下:
1.WM_MOUSEMOVE消息:当您把鼠标移过窗口的显示区域时,Windows并不为鼠标的每个可能的图素位置都产生一个WM_MOUSEMOVE消息。您的程序接收到WM_MOUSEMOVE消息的次数,依赖于鼠标硬件,以及您的窗口消息处理程序在处理鼠标移动消息时的速度。换句话说,Windows不能用未处理的WM_MOUSEMOVE消息来填入消息队列。
2.对于WM_LBUTTONDOWN和WM_LBUTTONUP消息可能只收到一个!比如在一个窗口中按下鼠标按键,然后移动到使用者窗口释放它,就会出现这种情况。类似的情况,当鼠标按键在另一个窗口中被释放时,窗口消息处理程序只能接收到WM_LBUTTONDOWN消息,而没有相应的WM_LBUTTONUP消息。
3.关于双击鼠标:双击的间隔在控制面板里可以设置。但是是否需要相应“双击”则是我们自己写的:在注册窗口时,必须在窗口风格中包含CS_DBLCLKS标识符。这样当你双击时,系统会收到:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
否则,只会收到:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDOWN
WM_LBUTTONUP
看一个例子程序:
- /*--------------------------------------------------
- CONNECT.C -- Connect-the-Dots Mouse Demo Program
- (c) Charles Petzold, 1998
- --------------------------------------------------*/
-
- #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:
- //是否按下了左键且收到的点数不到1000
- if (wParam & MK_LBUTTON && iCount < 1000)
- {
- //记录鼠标位置并在最后iCount+1
- 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) ;
- }
程序比较简单,就不多说了。
那什么是非显示区域的鼠标消息呢?如果鼠标在窗口的显示区域之外但还在窗口内,Windows就给窗口消息处理程序发送一条“非显示区域”鼠标消息。窗口非显示区域包括标题列、菜单和窗口滚动条。
这些消息通常我们是不用管的,将这些消息传给DefWindowProc,从而使Windows执行系统功能。
非显示区域鼠标消息几乎完全与显示区域鼠标消息相对应。消息中含有字母“NC”以表示是非显示区域消息。如果鼠标在窗口的非显示区域中移动,那么窗口消息处理程序会接收到WM_NCMOUSEMOVE消息。鼠标按键产生如表所示的消息:
键 | 按下 | 释放 | 按下(双击) |
左 | WM_NCLBUTTONDOWN | WM_NCLBUTTONUP | WM_NCLBUTTONDBLCLK |
中 | WM_NCMBUTTONDOWN | WM_NCMBUTTONUP | WM_NCMBUTTONDBLCLK |
右 | WM_NCRBUTTONDOWN | WM_NCRBUTTONUP | WM_NCRBUTTONDBLCLK |
但是这些消息参数的意义却不同:
wParam参数指明移动或者按鼠标按键的非显示区域。
lParam参数的低位word为x坐标,高位word为y坐标,但是,它们是屏幕坐标。
最后一个消息是WM_NCHITTEST,它代表“非显示区域命中测试”,Windows应用程序通常把这个消息传送给DefWindowProc,系统会自动的判断你拖动的是标题栏还是边框而做出相应。
MFC解决自己发送WM_NCLBUTTONDOWN消息收不到WM_LBUTTONUP的方法
最近在做一个MFC拖动窗口的程序,在网上搜到了一个方法,在OnLButtonDown里自己发送
PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y));
消息。这样一来确实能够在客户区移动窗口了,但是有一个问题:WM_LBUTTONUP消息被忽略了,写在OnLButtonUp里的内容根本没执行。在网上搜解决方法,要么是太复杂,要么没有很好的解决。最后自己试出了一个方法,经测试可行:
在OnNcLButtonDown函数里完成父类函数后自己发送一个WM_ONLBUTTONUP消息。
如下(CWnd应该换成你自己继承的父类,因为我直接从CWnd类继承过来 的,所以是CWnd):
1.处理左键按下消息函数:
- afx_msg void OnLButtonDown(UINT nFlags, CPoint point)
- {
- RedrawWindow();
- Invalidate();
- //支持拖动
- PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y));
- CWnd::OnLButtonDown(nFlags, point);
- }
这样就发送了非客户区左键按下的消息WM_NCLBUTTONDOWN。
2.处理非客户区左键按下消息函数:
- afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point)
- {
- CWnd::OnNcLButtonDown(nHitTest,point);
- PostMessage(WM_LBUTTONUP, NULL, NULL);
- }
PostMessage(WM_LBUTTONUP, NULL, NULL)这一句就是自己发送左键放开的消息WM_LBUTTONUP。
根据这个结果,本人猜测CWnd::OnNcLButtonDown可能是要一直执行到鼠标放开时才结束,WM_LBUTTONUP被忽略了。这导致我们收不到WM_LBUTTONUP消息,所以在其后自己发送WM_LBUTTONUP后就能收到WM_LBUTTONUP消息了。
注意:
这个方法鼠标左键按下事件是有一秒左右延迟的,当把窗口移动到其他窗口上或控件上,松开再单击时可能会单击到那些窗口或控件,大家若有好的方法解决可以发下言。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。