windows程序设计(15):对比两种鼠标命中测试

先看第一种的程序:

/*-------------------------------------------------
   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返回结果来判断是否需要画插了。

总体上讲,其实我们并没有判断鼠标到底停留在哪个窗口的上空,而是操作系统替我们完成这个工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值