本文由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);
……
我們還是來看看修改後的效果嘛:
呵呵,終於講完了,希望鄙人的心得能給大家帶來學習的方便^_^
點擊下載完整代碼和程序