《鼠标学习一》描述的是鼠标在客户区情况下,
当鼠标在非客户区的时候呢?
窗口的非客户区包括:标题栏,菜单和窗口滚动条,系统一般不需要用户处理非客户区消息,只要将其发送个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 5
LRESULT 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
}
else
MessageBeep(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);
}
效果图: