继《鼠标学习二》中的矩形块绘制,当时采用的是坐标转换的方式。
这次用一种新的绘图方式即子窗口,对,就是用25个子窗口来表示矩形。这样实现了将客户区划分成几个更小的逻辑区域。每个子窗口有属于自己的句柄,窗口过程和客户区。每个子窗口过程只接受与自身窗口有关的鼠标消息。
tips:鼠标消息参数lParam中包含的坐标是相当于子窗口客户区左上角的,而不是父窗口。
那么在两个窗口之间,窗口的焦点如何确定和切换呢?
示例:
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK ChildWndProc(HWND,UINT,WPARAM,LPARAM);
int idFocus=0;
TCHAR szChildClass[]=TEXT("Check3_Child");
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Check3") ;
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 ;
}
wndclass.lpfnWndProc=ChildWndProc; //窗口对应一个窗口类,和唯一的窗口过程
wndclass.cbWndExtra=sizeof(long); //预留的额外空间
wndclass.hIcon=NULL;
wndclass.lpszClassName=szChildClass;
RegisterClass(&wndclass);
hwnd = CreateWindow (szAppName, TEXT ("check3 mouse ht 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 HWND hwndChild[DIVISIONS][DIVISIONS];
int cxBlock,cyBlock,x,y;
switch(message)
{
case WM_CREATE:
for(x=0;x<DIVISIONS;x++)
for(y=0;y<DIVISIONS;y++)
hwndChild[x][y]=CreateWindow(szChildClass,NULL,
WS_CHILDWINDOW |WS_VISIBLE,
0,0,0,0,
hwnd,(HMENU)(y<<8 |x), //子id,这里简单的设定为x和y的组合
(HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE), //获取应用程序句柄
NULL);
return 0;
case WM_SIZE:
cxBlock=LOWORD(lParam)/DIVISIONS;
cyBlock=HIWORD(lParam)/DIVISIONS;
for(x=0;x<DIVISIONS;x++)
for(y=0;y<DIVISIONS;y++)
MoveWindow(hwndChild[x][y], //相对于父窗口左上角的坐标,长宽
x*cxBlock,y*cyBlock,
cxBlock,cyBlock,TRUE);
return 0;
case WM_LBUTTONDOWN:
MessageBeep(0);
return 0;
case WM_SETFOCUS:
SetFocus(GetDlgItem(hwnd,idFocus)); //父窗口句柄和子控件id,即可获得子控件句柄
return 0;
case WM_KEYDOWN:
x=idFocus &0xFF; //只取低八位 0xff=00000000 00000000 00000000 11111111,就是这么巧妙,刚刚好
y=idFocus >>8; //右移八位,将上一个焦点id转换为实际坐标
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; break;
default: return 0;
}
x=(x+DIVISIONS)%DIVISIONS;
y=(y+DIVISIONS)%DIVISIONS; //处理负数的情况
idFocus=y<<8 |x; //相或运算
SetFocus(GetDlgItem(hwnd,idFocus)); //当初设定的时候,根据实际坐标在移位而成的
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
LRESULT CALLBACK ChildWndProc(HWND hwnd,UINT message,
WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch(message)
{
case WM_CREATE:
SetWindowLong(hwnd,0,0);
return 0;
<span style="color:#FF0000;">case WM_KEYDOWN:
//虽然父子窗口都处理键盘消息,但对于enter和space键才将击键消息给子窗口处理,反之,给父窗口
if(wParam!=VK_RETURN && wParam!=VK_SPACE)
{
SendMessage(GetParent(hwnd),message,wParam,lParam); //也有切换焦点的意思
return 0;
}</span>
case WM_LBUTTONDOWN: //WM_KEYDOWN不通就直接跳到下一个
SetWindowLong(hwnd,0,1^GetWindowLong(hwnd,0)); //取得的是额外窗口内存中的值
InvalidateRect(hwnd,NULL,FALSE);
return 0;
case WM_SETFOCUS:
idFocus=GetWindowLong(hwnd,GWL_ID);
case WM_KILLFOCUS:
InvalidateRect(hwnd,NULL,TRUE);
return 0;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
GetClientRect(hwnd,&rect);
Rectangle(hdc,0,0,rect.right,rect.bottom); //当前矩形就是一个子窗口,所以从起点(0,0)
if(GetWindowLong(hwnd,0)) //点击的窗口是否已绘制过。
{
MoveToEx(hdc,0,0,NULL);
LineTo(hdc,rect.right,rect.bottom);
MoveToEx(hdc,0,rect.bottom,NULL);
LineTo(hdc,rect.right,0);
}
<span style="color:#FF6600;"><span style="color:#000000;"><span style="color:#000099;">if(hwnd==GetFocus()) </span> /<span style="background-color: rgb(255, 0, 0);">/判断焦点在子窗口还是父窗口</span>
{
rect.left+=rect.left/10;
rect.right-=rect.left;
rect.top+=rect.bottom/10;
rect.bottom-=rect.top;
SelectObject(hdc,GetStockObject(NULL_BRUSH));
SelectObject(hdc,CreatePen(PS_DASH,0,0));
Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
DeleteObject(SelectObject(hdc,GetStockObject(BLACK_PEN)));
}</span>
<span style="color:#000000;">EndPaint(hwnd,&ps);</span>
<span style="color:#000000;">return 0;</span></span>
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
由此可见,窗口焦点的切换是通过发送消息到另一个窗口来实现的,因为同一应用程序公用一个消息队列,