鼠标操作是Windows的重要内容,为了下一章用鼠标作图,本章先作一些基础知识的铺垫,以免下一章新内容太多。前面章节已经说过,窗口函数总共处理消息结构体中的三个内容,即message、wParam、lParam,其中message是主消息,wParam、lParam许多场合都不用,也就是值为0。有的消息只体用wParam、lParam的一个,不过键盘鼠标消息传递的信息比较多,wParam、lParam这两个副消息全用上了。
WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE是鼠标的三个基本消息处理,分别表示鼠标左键按下、鼠标左键松开、鼠标移动,这三个主消息的副消息的内容完全相同,下面小雅用WM_MOUSEMOVE的二个副消息为例,来剖析其内容究竟是什么。
lParam副消息存放的是鼠标的座标位置,字节的低4位为x座标,高4位为y座标。用位操作符&很容易就取到鼠标的x和y座标,VC也提供了宏HIWORD()和LOWORD()。wParam的高4位不用,低4位表示组合键的使用状态。第1位为“1”表示鼠标左键按下,第2位为“1”表示鼠标右键按下,第3位为“1”表示Shift键按下,第4位为“1”表示Ctrl键按下,这4种状态可以组合使用。例如,鼠标左右键同时按下,wParam为3;鼠标左右键和Shift、Ctrl键全部按下则wParam为F。
1(Ctrl)1(Shift)1(右键)1(左键)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {switch
(message) {case
WM_MOUSEMOVE: HDC hdc; RECT rc;char
strx[64];char
strAction[64];int
xPos, yPos; xPos = LOWORD(lParam); yPos = HIWORD(lParam); wsprintf((LPSTR)strx, "鼠标位置: (%3d, %3d) wParam=%X ",xPos, yPos, wParam); hdc = GetDC(hWnd); GetClientRect(hWnd, &rc); TextOut(hdc,10,10,strx, (int
)strlen(strx));if
(wParam & 0x1) { wsprintf((LPSTR)strAction, "鼠标左键: ON "); }else
{ wsprintf((LPSTR)strAction, "鼠标左键: OFF"); } TextOut(hdc, 10, 25, strAction, (int
)strlen(strAction));if
(wParam & 0x2) { wsprintf((LPSTR)strAction, "鼠标右键: ON "); }else
{ wsprintf((LPSTR)strAction, "鼠标右键: OFF"); } TextOut(hdc, 10, 40, strAction, (int
)strlen(strAction));if
(wParam & 0x4) { wsprintf((LPSTR)strAction, "Shift键: ON "); }else
{ wsprintf((LPSTR)strAction, "Shift键: OFF"); } TextOut(hdc, 10, 55, strAction, (int
)strlen(strAction));if
(wParam & 0x8) { wsprintf((LPSTR)strAction, "Ctrl键: ON "); }else
{ wsprintf((LPSTR)strAction, "Ctrl键: OFF"); } TextOut(hdc, 10, 70, strAction, (int
)strlen(strAction)); ReleaseDC(hWnd, hdc);break
;case
WM_DESTROY: PostQuitMessage(0);break
;default
:return
DefWindowProc(hWnd, message, wParam, lParam); }return
0; }
作图时,习惯上按下鼠标左键时光标变为“十字形”,松开后恢复为缺省的“箭头”,因此在WM_LBUTTONDOWN消息处理和WM_LBUTTONUP消息处理中要增加对鼠标光标的控制。
光标都是由窗口类最初设定的,一般为箭头,但有人会改变成自己喜好的光标。窗口要控制光标首先要取当前光标的类型并保存,然后取消窗口对光标的控制,并设置光标为“十字形”,这些都是在WM_LBUTTONDOWN的消息处理程序中实现的。在WM_LBUTTONUP的消息处理程序中,还必须恢复窗口对光标的控制,并立即让光标成为窗口默认的光标。
设置光标是用SetCursor()函数,因其简单,不作解释应该没有问题。取当前系统光标的类型是用GetClassLong()函数,设置系统光标的类型是用SetClassLong()函数,这2个函数很重要,并不仅仅针对光标,根据参数的不同,可以取得或设置窗口类型、背景、图标、光标、菜单等等,这些全是注册窗口类时设置的内容。
DWORD GetClassLong( DWORD SetClassLong( HRESULT SetCursor( HWND hWnd, HWND hWnd, //HDC LONG lPartIDint
nIndexint
nIndex, //类型 ); ); LONG dwNewLong //设置值 );
HCURSOR clsCur; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {switch
(message) {case
WM_LBUTTONDOWN: clsCur = (HCURSOR)GetClassLong(hWnd,GCL_HCURSOR); //取当前的光标值 SetClassLong(hWnd,GCL_HCURSOR,NULL); //关闭窗口类对光标的控制 SetCursor(LoadCursor(NULL, IDC_CROSS)); //设置光标为十字形break
;case
WM_MOUSEMOVE: //(与上例相同,省略)break
;case
WM_LBUTTONUP: SetClassLong(hWnd,GCL_HCURSOR,(LONG)clsCur); //恢复窗口类对光标的控制 SetCursor(LoadCursor(NULL, IDC_ARROW)); //恢复箭头光标break
;case
WM_DESTROY: PostQuitMessage(0);break
;default
:return
DefWindowProc(hWnd, message, wParam, lParam); }return
0; }
有人问,在WM_LBUTTONUP中既然恢复了窗口类对光标的控制,为什么还要下面一句设置光标?其实SetCursor()函数省略也可以,但松开左键后如果不移动,光标不会改变,因为没有消息传过来。不过,上面程序是有以下BUG!
上例的BUG中,设置光标为“十字形”应该放在WM_MOUSEMOVE处理中便解决了,光标不能恢复正常是因为未受到WM_LBUTTONUP消息。注意:这时一定要区分是鼠标左键是否按下。因为按下和未按下光标不一样。另外,“SetCursor(LoadCursor(NULL, IDC_ARROW));”这一句是错误的,因为窗口光标不一定是“箭头”。正确写法是SetCursor((HCURSOR)clsCur); 本章故意将窗口光标设置成“问号”。
下面我们在更正BUG的同时,将鼠标左刍按下的点到松开点画一条直线。画直线是用MoveToEx()函数先移动到起点,再用LineTo()函数画到终点。这样,便产生了一个你所预想之外的效果。
我们已经知道定义一个矩形块变量用结构体RECT,基中有4个座标值分别表示左上角的x和y、右下角的x和y。同样点是用结构体POINT来表示的,其中有2个座标值表示点的x和y。要实现下面的功能,必须事先保存好起始点。
LONG clsCur;bool
bDrawing; POINTS point; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {switch
(message) {case
WM_LBUTTONDOWN:if
(!clsCur) { clsCur = GetClassLong(hWnd,GCL_HCURSOR); } SetClassLong(hWnd,GCL_HCURSOR,NULL); bDrawing =true
; point.x = LOWORD(lParam); point.y = HIWORD(lParam);break
;case
WM_MOUSEMOVE: HDC hdc; RECT rc;char
strx[64];char
strAction[64];int
xPos, yPos; xPos = LOWORD(lParam); yPos = HIWORD(lParam); wsprintf((LPSTR)strx, "鼠标位置: (%3d, %3d) wParam=%X ",xPos, yPos, wParam); hdc = GetDC(hWnd); GetClientRect(hWnd, &rc); TextOut(hdc,10,10,strx, (int
)strlen(strx));if
(wParam & 0x1) { wsprintf((LPSTR)strAction, "鼠标左键: ON "); }else
{ wsprintf((LPSTR)strAction, "鼠标左键: OFF"); } TextOut(hdc, 10, 25, strAction, (int
)strlen(strAction));if
(wParam & 0x2) { wsprintf((LPSTR)strAction, "鼠标右键: ON "); }else
{ wsprintf((LPSTR)strAction, "鼠标右键: OFF"); } TextOut(hdc, 10, 40, strAction, (int
)strlen(strAction));if
(wParam & 0x4) { wsprintf((LPSTR)strAction, "Shift键: ON "); }else
{ wsprintf((LPSTR)strAction, "Shift键: OFF"); } TextOut(hdc, 10, 55, strAction, (int
)strlen(strAction));if
(wParam & 0x8) { wsprintf((LPSTR)strAction, "Ctrl键: ON "); }else
{ wsprintf((LPSTR)strAction, "Ctrl键: OFF"); } TextOut(hdc, 10, 70, strAction, (int
)strlen(strAction));if
(bDrawing) { SetCursor(LoadCursor(NULL, IDC_CROSS)); //设置光标为十字形 MoveToEx(hdc, point.x, point.y,NULL); LineTo(hdc, LOWORD(lParam), HIWORD(lParam)); } ReleaseDC(hWnd, hdc);break
;case
WM_LBUTTONUP: bDrawing =false
; SetClassLong(hWnd,GCL_HCURSOR, clsCur); SetCursor((HCURSOR)clsCur);break
;case
WM_DESTROY: PostQuitMessage(0);break
;default
:return
DefWindowProc(hWnd, message, wParam, lParam); }return
0; }