SetWindowRgn函数的应用——绘制个性化形状的窗口

本文由BlueCoder编写   转载请说明出处:

http://blog.csdn.net/crocodile__/article/details/9873037

我的邮箱:[email protected]    欢迎大家和我交流编程心得

我的微博:BlueCoder_黎小华    欢迎光临^_^




SetWindowRgn这个函数比较好玩,它可以通过设定的区域(RGN)来制定该形状的窗口

先来看看函数原型:

int SetWindowRgn(
  HWND hWnd,     // handle to window
  HRGN hRgn,     // handle to region
  BOOL bRedraw   // window redraw option
);

由此可以看出我们需要先建立一个区域RGN,以此来设定窗口形状

这个不难,可以调用CreateEllipticRgn、CreatePolygonRgn等相关创建RGN的函数

 

关键是第三个参数bRedraw,它有点儿学问,我们先暂时放一下,待会儿细究……

 

今天写的程序,实现的功能为:

通过鼠标点击的次数来变换窗口的形状(为了简便起见,我只用了两种:椭圆形和五角形),同时变换窗口背景的颜色(待会儿你会看到奇怪的现象)

 

具体实现细节如下:

(1)首先建立两个自定义消息

//自定义消息
#define WM_ELLIPSEWND	(WM_USER + 100)//椭圆窗口消息
#define WM_PENTAGONWND	(WM_USER + 101)//五角形窗口消息


(2)在窗口过程设定以下静态变量:

static int		iClick;//标记鼠标点击的次数
static HRGN		hRgnWnd;//窗口区域
static POINT	pt[5];//五角形的五个点
static HBRUSH	hBr;//背景刷


(3)在WM_SIZE消息中初始化五角形的五个点

case WM_SIZE:
	{
		//获取客户区大小
		int cxClient, cyClient;
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		//初始化五角形的五个点
		pt[0].x = cxClient / 2;
		pt[0].y = 0;
		pt[1].x = 50;
		pt[1].y = cyClient / 2;
		pt[2].x = cxClient / 3;
		pt[2].y = cyClient;

		pt[3].x = cxClient * 2 / 3;
		pt[3].y = cxClient;

		pt[4].x = cxClient - 50;
		pt[4].y = cyClient / 2;
	}
	return 0;


(4)通过鼠标消息来控制窗口的形状和颜色

case WM_LBUTTONDOWN:
	{
		UINT		msg;
		COLORREF	brColor;
		iClick++;

		//根据鼠标点击的次数来确定发送消息种类以及背景刷颜色
		msg		= iClick % 2 ? WM_ELLIPSEWND : WM_PENTAGONWND;
		brColor	= iClick % 2 ? RGB(36, 204, 40): RGB(254, 243, 39);//前者是绿色,后者是黄色

		//创建背景刷
		hBr = CreateSolidBrush(brColor);

		//发送消息
		SendMessage(hwnd, msg, wParam, lParam);
	}
	return 0;

不难推理出,当窗口形状为椭圆的时候应该为绿色,当窗口形状为五角形时就为黄色

 

(5)在自定义消息中绘制相应窗口形状

//绘制椭圆窗口
case WM_ELLIPSEWND:
	hRgnWnd = CreateEllipticRgn(100, 100, 400, 400);
	SetWindowRgn(hwnd, hRgnWnd, TRUE);

 

//绘制五角形窗口
case WM_PENTAGONWND:
	hRgnWnd = CreatePolygonRgn(pt, 5, WINDING);
	SetWindowRgn(hwnd, hRgnWnd, TRUE);


 

//重绘窗口背景
case WM_PAINT:
	hdc = BeginPaint(hwnd, &ps);
		{
		RECT rect;

		GetClientRect(hwnd, &rect);
		FillRect(hdc, &rect, hBr);
		}

	EndPaint(hwnd, &ps);


接下来,我们就来看看奇怪现象的产生(为了效果的显著,我一开始设定背景颜色为黑色):

(1)首先建立一个黑色背景的窗口

<Win32_9>SetWindowRgn函数的应用——绘制个性化形状的窗口_第1张图片

 

(2)点击一次鼠标后(你会发现,椭圆窗口颜色不是我们想要的绿色,而是原先的黑色)

<Win32_9>SetWindowRgn函数的应用——绘制个性化形状的窗口_第2张图片

 

(3)再点击一次鼠标(你会发现五角形窗口的颜色也有问题,虽然有黄色的部分,但左边一部分区域确是黑色)

<Win32_9>SetWindowRgn函数的应用——绘制个性化形状的窗口_第3张图片

 

(4)最后再来点击一次鼠标(这个和五角形的现象类似,你仔细瞧瞧,椭圆黑色的区域和五角形的黑色区域是同一个区域)

<Win32_9>SetWindowRgn函数的应用——绘制个性化形状的窗口_第4张图片

 

 

看到这儿,你一定会疑问了:是哪儿的代码出问题了吗?

其实不是代码的问题,主要是SetWindowRgn函数的特点决定的

还记得一开始说的这个函数最后一个参数吗——BOOL bRedraw

当为TRUE时,重绘窗口;否则,不重绘

 

而我们这里设定的是TRUE:

SetWindowRgn(hwnd, hRgnWnd, TRUE);


你可能还是会有疑问:对啊,重绘了的啊,怎么还是会出现这个问题呢?

经过本人仔细的思考和测试,我得出以下结论(虽然不能保证100%的正确,但至少能解释他的原因):

这里设定了第三个参数为TRUE,当调用该函数的时候,windows的确会重绘当前区域,但是:

它会依然保存当前区域和上一次区域的交集,仅仅重绘当前区域中除开这个交集的部分

 

我只解释第一个,之后的三个是相同的,大家通过我讲的第一个就能明白了:

首先建立窗口时,这个区域是该窗口的大小

当点击第一次鼠标时,当前区域是这个椭圆,由于这个椭圆在原始窗口内部,因此这个椭圆区域与初始窗口的交集就是这个椭圆区域,那么,按照我总结的结论,这个椭圆区域不会被重绘,依然是黑色

 

……

 

到此,你是不是应该明白了

当然,明白之后,你还会想知道解决方法,这个其实很简单,因为windows在调用SetWindowRgn函数时,并不是重绘完整的当前区域,那么我们寻找一种方式来迫使windows完全重绘该区域,一种很简单的方式就是设定无效区域为整个原始窗口:

//这里必须调用InvalidateRect和InvalidateRgn效果是一样的,只不过第二个参数要为NULL,因为我们希望整个窗口都会重绘
InvalidateRgn(hwnd, NULL, TRUE);
InvalidateRect(hwnd, NULL, TRUE);

 

……

 

我们还是来看看修改后的效果嘛:

<Win32_9>SetWindowRgn函数的应用——绘制个性化形状的窗口_第5张图片

 

呵呵,终于讲完了,希望鄙人的心得能给大家带来学习的方便^_^

 

点击下载完整代码和程序

你可能感兴趣的:(Win32,个性化窗口形状,SetWindowRgn)