先看第一种的程序:
/*-------------------------------------------------
CHECKER1.C -- Mouse Hit-Test Demo Program No. 1
(c) Charles Petzold, 1998
-------------------------------------------------*/
#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 ("Checker1") ;
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 ("Checker1 Mouse Hit-Test 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 BOOL fState[DIVISIONS][DIVISIONS] ;
//每个格子的大小
static int cxBlock, cyBlock ;
HDC hdc ;
int x, y ;
PAINTSTRUCT ps ;
//每个格子耳朵位置
RECT rect ;
switch (message)
{
case WM_SIZE :
//获取格子的大小
cxBlock = LOWORD (lParam) / DIVISIONS ;
cyBlock = HIWORD (lParam) / DIVISIONS ;
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) ;
}
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) ;
}
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
原理是这样的,每次WM_PAINT消息下,都对把客户区分成5*5个矩形方框,为每个方框建立一个bool类数组,当点击一下方框时该方框的数组改为true,再点击一下,变为false。并保存点击的矩形框的位置。每次点击都会是得方框变为无效,引起WM_PAINT消息,而在该消息下,不管三七二十一,先画出25个矩形框,然后通过数组来判断每个矩形框是否需要打叉。
再看另一种程序:
/*-------------------------------------------------
CHECKER3.C -- Mouse Hit-Test Demo Program No. 3
(c) Charles Petzold, 1998
-------------------------------------------------*/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK ChildWndProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szChildClass[] = TEXT ("Checker3_Child") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Checker3") ;
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 ("Checker3 Mouse Hit-Test 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),
//应用实例句柄
(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_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 :
//用SetWindowWord在窗口结构保留的额外区域中储存一个0值
SetWindowLong (hwnd, 0, 0) ; // on/off flag
return 0 ;
case WM_LBUTTONDOWN :
//1 与GetWindowLong (hwnd, 0)的结果求异或
//如果原来是1,异或结果为0
SetWindowLong (hwnd, 0, 1 ^ GetWindowLong (hwnd, 0)) ;
InvalidateRect (hwnd, NULL, FALSE) ;
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
//或得子窗口的客户区大小
GetClientRect (hwnd, &rect) ;
Rectangle (hdc, 0, 0, rect.right, rect.bottom) ;
//如果为1,画图
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) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
这个程序就完全是另外一种思路了:在WinMain函数中,注册了两个窗口,第一个窗口就是我们正常使用的。第二个窗口有附加的bite,这个比特是用来保留点击信息的,这样与第一个程序相比,就少使用了标记。
在主窗口的WM_CREATE消息下,创建了25个子窗口;在WM_SIZE消息下,将这些窗口摆放到了特定的位置。
程序的主要工作是在子窗口的响应函数下完成的(注意,这时当你点击客户区时,由于客户区被子窗口覆盖满了,所以消息响应全是子窗口的):
WM_CREATE消息下:SetWindowLong (hwnd, 0, 0) ;函数的作用实际上是给标志位设为0,表示没有打叉;
WM_LBUTTONDOWN消息下:SetWindowLong (hwnd, 0, 1 ^ GetWindowLong (hwnd, 0))是将/1 与GetWindowLong (hwnd, 0)的结果求异或,而GetWindowLong返回的,就是你设置的那个值(因为设置和返回的偏移量都为0),。所以如果之前里面设为1(有叉),那么与1异或结果为0(无叉)。
WM_PAINT下就用过GetWindowLong返回结果来判断是否需要画插了。
总体上讲,其实我们并没有判断鼠标到底停留在哪个窗口的上空,而是操作系统替我们完成这个工作。