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)首先建立一個黑色背景的窗口

 

(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);

 

……

 

我們還是來看看修改後的效果嘛:

 

呵呵,終於講完了,希望鄙人的心得能給大家帶來學習的方便^_^

 

點擊下載完整代碼和程序
發佈了39 篇原創文章 · 獲贊 29 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章