本文由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)首先建立一个黑色背景的窗口
(2)点击一次鼠标后(你会发现,椭圆窗口颜色不是我们想要的绿色,而是原先的黑色)
(3)再点击一次鼠标(你会发现五角形窗口的颜色也有问题,虽然有黄色的部分,但左边一部分区域确是黑色)
(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);
……
我们还是来看看修改后的效果嘛:
呵呵,终于讲完了,希望鄙人的心得能给大家带来学习的方便^_^
点击下载完整代码和程序