《鼠标学习一》描述的是鼠标在客户区情况下,
当鼠标在非客户区的时候呢?
窗口的非客户区包括:标题栏,菜单和窗口滚动条,系统一般不需要用户处理非客户区消息,只要将其发送个DefWindowProc即可,这个系统键盘消息很类似哦。
非客户区鼠标消息几乎和客户区完全类似,标识符“NC_",表示非客户(nonclient)
WM_NCMOUSEMOVE :鼠标在非客户区移动。
重点来了,非客户区鼠标消息参数wParam和lParam与客户区的不同,参数wParam表示非客户区鼠标移动或单击的位置,它的值设定成一个以HT_为首的标识符。
其中HT_表示击中测试(hit-test),这些表示符都定义在WINUSER.h头文件中。参数lParam现在表示的是屏幕坐标,而不再是相对客户区。
不过可以利用下面两个函数实现屏幕坐标和客户区坐标的物理转换。
ScreenToClient(hwnd,&pt);
ClientToScreen(hwnd,&pt);
其中pt是一个POINT结构。
示例程序:
#include <windows.h>#define DIVISIONS 5LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static TCHAR szAppName[] = TEXT ("Division") ;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 ("Division 5"),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 BOOL fState[DIVISIONS][DIVISIONS];static int cxBlock,cyBlock;HDC hdc;int x,y;PAINTSTRUCT ps;RECT rect;POINT point;switch(message){case WM_SIZE:cxBlock=LOWORD(lParam)/DIVISIONS; //获取将客户区划分为25块cyBlock=HIWORD(lParam)/DIVISIONS;return 0;case WM_SETFOCUS:ShowCursor(TRUE);return 0;case WM_KILLFOCUS:ShowCursor(FALSE);return 0;case WM_KEYDOWN: //键盘接口GetCursorPos(&point);<span style="background-color: rgb(255, 0, 0);">ScreenToClient(hwnd,&point);</span> //实现屏幕坐标到客户区坐标的转变x=max(0,min(DIVISIONS-1,point.x/cxBlock));y=max(0,min(DIVISIONS-1,point.y/cyBlock));switch(wParam){case VK_UP:y--;break;case VK_DOWN:y++;break;case VK_LEFT:x--;break;case VK_RIGHT:x++;break;case VK_HOME:x=y=0;break;case VK_END:x=y=DIVISIONS-1; //对于数组来说DIVISIONS-1表示最后一个break;case VK_RETURN: //enter|space键表示最终确定,即鼠标左键效果case VK_SPACE:SendMessage(hwnd,WM_LBUTTONDOWN,MK_LBUTTON,MAKELONG(x*cxBlock,y*cyBlock));break;}x=(x+DIVISIONS)%DIVISIONS;y=(y+DIVISIONS)%DIVISIONS;point.x=x*cxBlock+cxBlock/2; //取当前矩形中心作为焦点point.y=y*cyBlock+cyBlock/2;<span style="background-color: rgb(255, 0, 0);">ClientToScreen(hwnd,&point);</span>SetCursorPos(point.x,point.y);return 0;case WM_LBUTTONDOWN:x=LOWORD(lParam)/cxBlock;y=HIWORD(lParam)/cyBlock;if(x<DIVISIONS && y<DIVISIONS){fState[x][y]^=1;rect.left=x*cxBlock; //确定矩形的范围rect.top=y*cyBlock;rect.right=(x+1)*cxBlock;rect.bottom=(y+1)*cyBlock;InvalidateRect(hwnd,&rect,FALSE); //是这块矩形区域失效,发出WM_PAINT}elseMessageBeep(0);return 0;case WM_PAINT:hdc=BeginPaint(hwnd,&ps);for(x=0;x<DIVISIONS;x++)for(y=0;y<DIVISIONS;y++){Rectangle(hdc,x*cxBlock,y*cyBlock,(x+1)*cxBlock,(y+1)*cyBlock);if(fState[x][y]){/*MoveToEx(hdc,x*cxBlock,y*cyBlock,NULL);LineTo(hdc,(x+1)*cxBlock,(y+1)*cyBlock);MoveToEx(hdc,x*cxBlock,(y+1)*cyBlock,NULL);LineTo(hdc,(x+1)*cxBlock,y*cyBlock);*/rect.left=x*cxBlock; //确定矩形的范围rect.top=y*cyBlock;rect.right=(x+1)*cxBlock;rect.bottom=(y+1)*cyBlock;FillRect(hdc,&rect,CreateSolidBrush(RGB(255,0,0)));}}EndPaint(hwnd,&ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hwnd,message,wParam,lParam);
}
效果图: