当前位置:   article > 正文

【转】windows程序设计(14):鼠标消息详解_列举鼠标操作可以产生的消息并对消息进行文字说明

列举鼠标操作可以产生的消息并对消息进行文字说明

关于鼠标的一些细节知识:

通常,我们发消息时,都是对一个特定的窗口,但是对于鼠标消息却不然:只要鼠标跨越窗口或者在某窗口中按下鼠标按键,那么窗口消息处理程序就会收到鼠标消息,而不管该窗口是否活动或者是否拥有输入焦点。鼠标消息一个有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宏来提取这些值:

  1. x = LOWORD (lParam) ;
  2. 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


看一个例子程序:

  1. /*--------------------------------------------------
  2. CONNECT.C -- Connect-the-Dots Mouse Demo Program
  3. (c) Charles Petzold, 1998
  4. --------------------------------------------------*/
  5. #include <windows.h>
  6. #define MAXPOINTS 1000
  7. LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
  8. int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
  9. PSTR szCmdLine, int iCmdShow)
  10. {
  11. static TCHAR szAppName[] = TEXT ("Connect") ;
  12. HWND hwnd ;
  13. MSG msg ;
  14. WNDCLASS wndclass ;
  15. wndclass.style = CS_HREDRAW | CS_VREDRAW ;
  16. wndclass.lpfnWndProc = WndProc ;
  17. wndclass.cbClsExtra = 0 ;
  18. wndclass.cbWndExtra = 0 ;
  19. wndclass.hInstance = hInstance ;
  20. wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
  21. wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
  22. wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
  23. wndclass.lpszMenuName = NULL ;
  24. wndclass.lpszClassName = szAppName ;
  25. if (!RegisterClass (&wndclass))
  26. {
  27. MessageBox (NULL, TEXT ("Program requires Windows NT!"),
  28. szAppName, MB_ICONERROR) ;
  29. return 0 ;
  30. }
  31. hwnd = CreateWindow (szAppName, TEXT ("Connect-the-Points Mouse Demo"),
  32. WS_OVERLAPPEDWINDOW,
  33. CW_USEDEFAULT, CW_USEDEFAULT,
  34. CW_USEDEFAULT, CW_USEDEFAULT,
  35. NULL, NULL, hInstance, NULL) ;
  36. ShowWindow (hwnd, iCmdShow) ;
  37. UpdateWindow (hwnd) ;
  38. while (GetMessage (&msg, NULL, 0, 0))
  39. {
  40. TranslateMessage (&msg) ;
  41. DispatchMessage (&msg) ;
  42. }
  43. return msg.wParam ;
  44. }
  45. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  46. {
  47. static POINT pt[MAXPOINTS] ;
  48. static int iCount ;
  49. HDC hdc ;
  50. int i, j ;
  51. PAINTSTRUCT ps ;
  52. switch (message)
  53. {
  54. case WM_LBUTTONDOWN:
  55. iCount = 0 ;
  56. InvalidateRect (hwnd, NULL, TRUE) ;
  57. return 0 ;
  58. case WM_MOUSEMOVE:
  59. //是否按下了左键且收到的点数不到1000
  60. if (wParam & MK_LBUTTON && iCount < 1000)
  61. {
  62. //记录鼠标位置并在最后iCount+1
  63. pt[iCount ].x = LOWORD (lParam) ;
  64. pt[iCount++].y = HIWORD (lParam) ;
  65. hdc = GetDC (hwnd) ;
  66. //在指定的位置设置指定颜色的点
  67. SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0) ;
  68. ReleaseDC (hwnd, hdc) ;
  69. }
  70. return 0 ;
  71. case WM_LBUTTONUP:
  72. InvalidateRect (hwnd, NULL, FALSE) ;
  73. return 0 ;
  74. case WM_PAINT:
  75. hdc = BeginPaint (hwnd, &ps) ;
  76. //画图时光标为等待状态
  77. SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
  78. ShowCursor (TRUE) ;
  79. for (i = 0 ; i < iCount - 1 ; i++)
  80. for (j = i + 1 ; j < iCount ; j++)
  81. {
  82. MoveToEx (hdc, pt[i].x, pt[i].y, NULL) ;
  83. LineTo (hdc, pt[j].x, pt[j].y) ;
  84. }
  85. ShowCursor (FALSE) ;
  86. SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
  87. EndPaint (hwnd, &ps) ;
  88. return 0 ;
  89. case WM_DESTROY:
  90. PostQuitMessage (0) ;
  91. return 0 ;
  92. }
  93. return DefWindowProc (hwnd, message, wParam, lParam) ;
  94. }


 

程序比较简单,就不多说了。

那什么是非显示区域的鼠标消息呢?如果鼠标在窗口的显示区域之外但还在窗口内,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_ONLBUTTONUP的方法

 

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.处理左键按下消息函数:

 

  1. afx_msg void OnLButtonDown(UINT nFlags, CPoint point)
  2. {
  3. RedrawWindow();
  4. Invalidate();
  5. //支持拖动
  6. PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y));
  7. CWnd::OnLButtonDown(nFlags, point);
  8. }

这样就发送了非客户区左键按下的消息WM_NCLBUTTONDOWN。

2.处理非客户区左键按下消息函数:

  1. afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point)
  2. {
  3. CWnd::OnNcLButtonDown(nHitTest,point);
  4. PostMessage(WM_LBUTTONUP, NULL, NULL);
  5. }

PostMessage(WM_LBUTTONUP, NULL, NULL)这一句就是自己发送左键放开的消息WM_LBUTTONUP。

根据这个结果,本人猜测CWnd::OnNcLButtonDown可能是要一直执行到鼠标放开时才结束,WM_LBUTTONUP被忽略了。这导致我们收不到WM_LBUTTONUP消息,所以在其后自己发送WM_LBUTTONUP后就能收到WM_LBUTTONUP消息了。

注意:

这个方法鼠标左键按下事件是有一秒左右延迟的,当把窗口移动到其他窗口上或控件上,松开再单击时可能会单击到那些窗口或控件,大家若有好的方法解决可以发下言。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/272554
推荐阅读
相关标签
  

闽ICP备14008679号