windows程序设计(10):矩形与区域

本文详细介绍了Windows编程中矩形结构RECT的使用方法及与之相关的区域操作,包括矩形的创建、移动、判断点是否在矩形内等基本操作,并通过实例展示了如何使用CreateRectRgnIndirect创建矩形区域。此外,文章还探讨了区域的拼接问题,通过编程实现出矩形区域的合并,并指出了一个在Win7下可能出现的bug。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

windows中,矩形结构RECT通过左上角和右下角两个点控制。
RECT rect;
rect.left= xLeft ;
rect.top= xTop ;
rect.right= xRight ;
rect.bottom= xBottom ;
矩形支持操作很多,比如OffsetRect (&rect, x, y) ;(将矩形移动);IntersectRect (&DestRect, &SrcRect1, &SrcRect2) ;获得两个矩形的交集;bInRect = PtInRect (&rect, point) ;判断点是否在矩形里等。这里不一一细讲了。

有一点需要注意,通常我们可以使用GetClientRect来或得整个客户区,将它存在一个RECT结构中,非常方便。

什么又是区域呢?区域可以使矩形、椭圆,多边形,或者是多个它们的组合,如何或得区域呢,以矩形为例,通过CreateRectRgnIndirect就能或得一个矩形区域了。先看一个简单程序:

#include <windows.h>
#include <math.h>


LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,		//当前实例句柄
				   HINSTANCE hPrevInstance, //先前实例句柄
				   LPSTR lpCmdLine,			//命令行
				   int iCmdShow)			//显示状态
{
	static TCHAR szAppName[] = TEXT("画图");
	//窗口句柄
	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))
	{
		return -1;
	}

	//创建窗口
	hwnd = CreateWindow(szAppName,				//窗口类的名称,必须是已经注册的
						TEXT("我的画图"),		//窗口标题
						WS_OVERLAPPEDWINDOW,	//窗口风格
						CW_USEDEFAULT,			//X坐标
						CW_USEDEFAULT,			//Y坐标
						CW_USEDEFAULT,			//宽度
						CW_USEDEFAULT,			//高度
						NULL,					//父窗口句柄
						NULL,					//菜单窗口句柄
						hInstance,				//高级版本的windos忽略
						NULL);					

	//显示窗口
	//ShowWindow(hwnd,SW_SHOWNA);
	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)
{
	HDC				hdc;
	//区域句柄
	HRGN			hRgn;
	//刷子句柄
	HBRUSH			hBrush;
	//矩形区域
	static RECT		rect;
	//客户区的矩形区域
	RECT			clientRect;
	static int		x,y;
  
	switch (message)
	{
	case WM_CREATE:
		x = 100;
		y = 100;
		//矩形区域的默认位置
		rect.left	=  0;
		rect.top	=  0;
		rect.right	= x + 100;
		rect.bottom = y + 100;


		return 0;

	case WM_LBUTTONDOWN:
		
		hdc	= GetDC(hwnd);
		//把整个客户区刷白
		GetClientRect(hwnd,&clientRect);
		hBrush = CreateSolidBrush(RGB(255,255,255));
		FillRect(hdc,&clientRect,hBrush);


		//设置指定颜色的刷子
		hBrush = CreateSolidBrush(RGB(255,0,0));
		//使用指定的刷子填充矩形
		FillRect(hdc,&rect,hBrush);


		//创建一个矩形区域
		//hRgn = CreateRectRgnIndirect(&rect);
		//使用指定的刷子填充区域
		//FillRgn(hdc,hRgn,hBrush);

		//将矩形移动指定的偏移
		OffsetRect(&rect,x,y);

		ReleaseDC(hwnd,hdc);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	}
	return DefWindowProc(hwnd,message,wParam,lParam); 
}


这个程序实现了通过单击鼠标左键是得矩形实现偏移。程序比较简单,唯一值得注意的是,有两种方法实现显示红色的矩形:第一是用红刷子对矩形填充,第二是用矩形创建一个区域,用刷子填充整个区域。

下面这个程序着重讨论区域的拼接问题:

#include <windows.h>		
#include <math.h>		
#define TWO_PI (2.0 * 3.14159)		
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;		
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)		
{		
	static TCHAR szAppName[] = TEXT ("Clover") ;		
	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 ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;		
			return 0 ;	
	}
		
   		
	hwnd = CreateWindow (szAppName, TEXT ("Draw a Clover"),  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 iMsg, WPARAM wParam, LPARAM lParam)
		
{
	//全局区域句柄	
	static HRGN		hRgnClip ;		
	static int		cxClient, cyClient ;		
	double			fAngle, fRadius ;		
	HCURSOR			hCursor ;		
	HDC				hdc ;
	//局部区域句柄数组	
	HRGN			hRgnTemp[6] ;		
	int				i ;		
	PAINTSTRUCT		ps ;

	switch (iMsg)
		
	{
		
	case   WM_SIZE:
		
			cxClient	  = LOWORD (lParam) ;		
			cyClient	  = HIWORD (lParam) ;
			//装载等待光标
			hCursor= SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
			//显示光标
			ShowCursor (TRUE) ;
						
			if (hRgnClip)
				DeleteObject (hRgnClip) ;
		
		
			//创建椭圆区域的参数为外接矩形的左上角x坐标,左上角y坐标;右下角x坐标,右下角y坐标			
			hRgnTemp[0]   = CreateEllipticRgn (0, cyClient / 3, cxClient / 2, 2 * cyClient / 3) ;		
			hRgnTemp[1]   = CreateEllipticRgn (cxClient / 2, cyClient / 3, cxClient, 2 * cyClient / 3) ;		
			hRgnTemp[2]   = CreateEllipticRgn (cxClient / 3, 0,2 * cxClient / 3, cyClient / 2) ;		
			hRgnTemp[3]   = CreateEllipticRgn (cxClient / 3, cyClient / 2,2 * cxClient / 3, cyClient) ;		
			//整个客户区
			hRgnTemp[4]   = CreateRectRgn (0, 0, 1, 1) ;		
			hRgnTemp[5]   = CreateRectRgn (0, 0, 1, 1) ;		
			hRgnClip	= CreateRectRgn (0, 0, 1, 1) ;
		
			//将区域拼接起来
			//参数为:目标区域,原区域,原区域,拼接模式
			//两个区域的并集
			CombineRgn (hRgnTemp[4], hRgnTemp[0], hRgnTemp[1], RGN_OR) ;		
			CombineRgn (hRgnTemp[5], hRgnTemp[2], hRgnTemp[3], RGN_OR) ;	
			//两个区域的并集,但是不包括重叠的部分
			CombineRgn (hRgnClip,	hRgnTemp[4], hRgnTemp[5], RGN_XOR) ;
		
		
			//删除临时的区域
			for (i = 0 ; i < 6 ; i++)		
				   DeleteObject (hRgnTemp[i]) ;
		
		
		
			SetCursor (hCursor) ;
		
			ShowCursor (FALSE) ;
		
			return 0 ;
		
		
		
	case   WM_PAINT:
		
			hdc = BeginPaint (hwnd, &ps) ;		
			//将视口原点设置到客户区中心
			SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;	
			//选择我们已经合并好的区域
			SelectClipRgn (hdc, hRgnClip) ;	
			//通过两条直角边计算斜边
			fRadius = _hypot (cxClient / 2.0, cyClient / 2.0) ;
			//1度1度的变化
			for (fAngle = 0.0 ; fAngle < TWO_PI ; fAngle += TWO_PI / 360)
		
			{
		
				//起点移动到原点
				MoveToEx (hdc, 0, 0, NULL) ;
				//画线
				LineTo (hdc,  (int) ( fRadius * cos (fAngle) + 0.5), (int) (-fRadius * sin (fAngle) + 0.5)) ;
		
			}
		
			EndPaint (hwnd, &ps) ;
		
			return 0 ;
		
	case WM_DESTROY:
			//删除该区域
			DeleteObject (hRgnClip) ;
		
			PostQuitMessage (0) ;
		
			return 0 ;
		
	}
		
	return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
		
}


程序中将4个椭圆拼接起来,形成了新的区域。fRadius是中心到客户区顶点的距离。

将视口的原点设置到了客户区的中心,然后对于每一度,计算一条线与客户区边沿的交点的x,y坐标(加0.5是为了四舍五入),但是由于我们之前定义了区域,所以不论线实际画了多长,屏幕上只会显示出现在区域中的部分。

但是这个程序有一个bug,在win7下,如果将客户区最大化(或者放的非常大)时,画的线会出现一些明显的裂缝,我通过如下代码测试:

	case   WM_PAINT:
		
			hdc = BeginPaint (hwnd, &ps) ;

			//获取整个区域的轮廓
			SelectClipRgn (hdc, hRgnClip) ;	
			hBrush = CreateSolidBrush(RGB(0,0,0));
			FillRgn(hdc,hRgnClip,hBrush);

			/*
			//将视口原点设置到客户区中心
			SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;	
			//选择我们已经合并好的区域
			SelectClipRgn (hdc, hRgnClip) ;	
			//通过两条直角边计算斜边
			fRadius = _hypot (cxClient / 2.0, cyClient / 2.0) ;
			//1度1度的变化
			for (fAngle = 0.0 ; fAngle < TWO_PI ; fAngle += TWO_PI / 360)
		
			{
		
				//起点移动到原点
				MoveToEx (hdc, 0, 0, NULL) ;
				//画线
				LineTo (hdc,  (int) ( fRadius * cos (fAngle) + 0.5), (int) (-fRadius * sin (fAngle) + 0.5)) ;
		
			}
			*/
			EndPaint (hwnd, &ps) ;
			
			return 0 ;



发现最大化窗口时,区域依然四个椭圆拼接而成的的,但是画出的线却出现了问题。

不论是VS2010还是VC++6.0这个问题依然存在。有人说xp下不会出现这种情况,具体为什么我也不知道。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值