下源代碼
一、序言
在絕大多數的Windows應用程序中,其窗體都是使用的正規正矩的矩形窗體,例如我們常用的,“記事本”,“掃雷”,等等。矩形窗體,具有編程實現簡單,風格簡潔的優點,所以在普通文檔應用程序和簡單小游戲中使用足矣。但在某些娛樂游戲程序中使用就略顯呆板些了,這時若用不規則窗體替代原先的矩形窗體,將會使這類程序更添情趣。典型的例子有windows 自代的Media Player,新版本的Media Player有個控制面板的選項,選中這些面板,播放器就以選中的面板形狀出現,這時的播放器比以前版本的Media Player的古老矩形界面要生動有趣的多了。 要實現不規則窗體不是太難,知道了基本原理后,你也可以創建各種有趣的不規則窗體。
二、實現原理
所有的 Windows 窗體都位于一個稱為“region”中,窗體的大小如果超出“region”的范圍,windows 會自動裁剪超出"region"范圍那部分的窗體,使其不可見。所以,要創建不規則窗體有兩個步驟:
第一步就是創建不規則"region".
第二步就是將窗體放到創建的“region”中。
其中第二步很簡單就調用一條語句即可。在SDK中調用API函數SetWindowRgn,該函數原型如下:
其中hWnd為待設置的窗體句柄,hRgn為已經創建的"region"句柄,bRedraw代表是否要重繪窗體。在MFC 中使用窗口類CWnd的成員函數int CWnd::SetWindowRgn(HRGN hRgn, BOOL bRedraw );該函數的參數意義與API中同名函數相同。
相對與第二步,創建不規則窗體的第一步要復雜許多,并且不規則窗體越復雜,創建其"region"的過程也越復雜。接下去我們將由淺入深地介紹各種創建”region”的方法。在MFC中"region"對象,由CRgn類實現。CRgn的幾乎每個成員函數都有同名的SDK API函數對應。
三、簡單“region”的創建
類CRgn創建一個新的"region"的簡單方法有以下幾個成員函數:
?? 1. BOOL CRgn::CreateRectRgn( int x1, int y1, int x2, int y2 ); 創建矩形的“region”。
?? 2. BOOL CRgn::CreateEllipticRgn( int x1, int y1, int x2, int y2 ); 創建圓形或橢圓形“region”。
?? 3. BOOL CRgn::CreateRoundRectRgn( int x1, int y1, int x2, int y2, int x3, int y3 ); 創建圓角矩形“region”。
?? 4. BOOL CRgn::CreatePolygonRgn( LPPOINT lpPoints, int nCount, int nMode ); 創建多邊形“region”。
這里以創建橢圓窗體為例,介紹橢圓窗體創建的方法。在創建橢圓“region”的CreateEllipticRgn函數中,x1,y1指橢圓所在矩形的左上角坐標,x2,y2指該矩形的右下角坐標。
下面的代碼加入到MFC對話框程序的OnInitDialog函數中,可將該對話框變成橢圓窗體:
圖一 橢圓窗體效果圖
四、作圖路徑法創建”region”
使用該方法創建”region”的過程如下:
第一步繪制所要創建的窗體形狀。
該步驟中使用到CDC類中的一些成員函數如下:
調用該函數后當前設備環境(DC)開始追蹤繪圖的過程。
設置繪圖時的背景模式,此應用中nBkMode必須取值為TRANSPARENT 。即設置繪圖時背景不發生變化。
調用該函數后當前設備環境(DC)結束追蹤繪圖的過程。
開始繪圖前,先調用BeginPath,然后調用SetBkMode。接下去就可調用CDC的其他繪圖函數作圖,例如Arc,AngleArc,LineTo,MoveTo,RoundRect,,Textout等等。繪圖完畢調用EndPath().
第二步將繪制的結果轉成”region”.
此步驟中使用SDK API函數
Hdc為作圖DC的句柄, CDC類中的m_hDC成員變量可做此參數傳入。示例,將下面代碼加入某個按鈕單擊事件中,可以將當前窗體變為字符串”hello”的形狀
CClientDC是CDC的派生類,故此該類具有所有CDC類的成員變量和成員函數。
圖二 hello形狀的窗體效果圖
五、根據圖像創建”region”
此法創建不規則窗體比較復雜。首先準備一張含有目標窗體形狀的圖片,設置透明色即將圖片中部不屬于窗體形狀的部分,標記成同一種顏色,例如藍色 RGB(0,0,255).程序運行后先裝入圖片。然后逐個掃描圖片的每個像素,如這個像素不屬于透明色,則在相應位置創建一個只含一個像素的 “region”然后將這些小”region ”合并起來組成一個任意形狀的”region”.這里將使用到CRgn的一個成員函數
其中pRgn1,pRgn2為要合并的兩個“region”,nCombineMode為合并的方式,此應用中取RGN_OR,即兩”region”全部合并去處重復部分。代碼實現如下:
上述代碼創建的不規則窗體中,在OnEraseBkgnd事件中繪制該位圖,就可得到與該位圖形狀一模一樣的窗體。
圖三 根據位圖和位圖中的透明色創建的窗體效果圖
六、小結
三種創建“region”的方法,第一種最簡單,如果所需的窗體形狀是簡單的幾何圖形,這種方法最合適;第二種稍微復雜些,但是創建的窗體形狀更多些;第三種方法可以創建任何在圖片中畫出的窗體形狀,但是實現的復雜度也最高。
注:本文的寫作曾參考了“ 形態各異的不規則窗體 ”一文。
一、序言
在絕大多數的Windows應用程序中,其窗體都是使用的正規正矩的矩形窗體,例如我們常用的,“記事本”,“掃雷”,等等。矩形窗體,具有編程實現簡單,風格簡潔的優點,所以在普通文檔應用程序和簡單小游戲中使用足矣。但在某些娛樂游戲程序中使用就略顯呆板些了,這時若用不規則窗體替代原先的矩形窗體,將會使這類程序更添情趣。典型的例子有windows 自代的Media Player,新版本的Media Player有個控制面板的選項,選中這些面板,播放器就以選中的面板形狀出現,這時的播放器比以前版本的Media Player的古老矩形界面要生動有趣的多了。 要實現不規則窗體不是太難,知道了基本原理后,你也可以創建各種有趣的不規則窗體。
二、實現原理
所有的 Windows 窗體都位于一個稱為“region”中,窗體的大小如果超出“region”的范圍,windows 會自動裁剪超出"region"范圍那部分的窗體,使其不可見。所以,要創建不規則窗體有兩個步驟:
第一步就是創建不規則"region".
第二步就是將窗體放到創建的“region”中。
其中第二步很簡單就調用一條語句即可。在SDK中調用API函數SetWindowRgn,該函數原型如下:
int SetWindowRgn( HWND hWnd, HRGN hRgn, BOOL bRedraw );
其中hWnd為待設置的窗體句柄,hRgn為已經創建的"region"句柄,bRedraw代表是否要重繪窗體。在MFC 中使用窗口類CWnd的成員函數int CWnd::SetWindowRgn(HRGN hRgn, BOOL bRedraw );該函數的參數意義與API中同名函數相同。
相對與第二步,創建不規則窗體的第一步要復雜許多,并且不規則窗體越復雜,創建其"region"的過程也越復雜。接下去我們將由淺入深地介紹各種創建”region”的方法。在MFC中"region"對象,由CRgn類實現。CRgn的幾乎每個成員函數都有同名的SDK API函數對應。
三、簡單“region”的創建
類CRgn創建一個新的"region"的簡單方法有以下幾個成員函數:
?? 1. BOOL CRgn::CreateRectRgn( int x1, int y1, int x2, int y2 ); 創建矩形的“region”。
?? 2. BOOL CRgn::CreateEllipticRgn( int x1, int y1, int x2, int y2 ); 創建圓形或橢圓形“region”。
?? 3. BOOL CRgn::CreateRoundRectRgn( int x1, int y1, int x2, int y2, int x3, int y3 ); 創建圓角矩形“region”。
?? 4. BOOL CRgn::CreatePolygonRgn( LPPOINT lpPoints, int nCount, int nMode ); 創建多邊形“region”。
這里以創建橢圓窗體為例,介紹橢圓窗體創建的方法。在創建橢圓“region”的CreateEllipticRgn函數中,x1,y1指橢圓所在矩形的左上角坐標,x2,y2指該矩形的右下角坐標。
下面的代碼加入到MFC對話框程序的OnInitDialog函數中,可將該對話框變成橢圓窗體:
BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); ... CRgn rgn; rgn. CreateEllipticRgn(0,0,200,100); SetWindowRgn(rgn,TRUE); }

圖一 橢圓窗體效果圖
四、作圖路徑法創建”region”
使用該方法創建”region”的過程如下:
第一步繪制所要創建的窗體形狀。
該步驟中使用到CDC類中的一些成員函數如下:
BOOL CDC::BeginPath( );
調用該函數后當前設備環境(DC)開始追蹤繪圖的過程。
int CDC::SetBkMode( int nBkMode );
設置繪圖時的背景模式,此應用中nBkMode必須取值為TRANSPARENT 。即設置繪圖時背景不發生變化。
BOOL CDC::EndPath( );
調用該函數后當前設備環境(DC)結束追蹤繪圖的過程。
開始繪圖前,先調用BeginPath,然后調用SetBkMode。接下去就可調用CDC的其他繪圖函數作圖,例如Arc,AngleArc,LineTo,MoveTo,RoundRect,,Textout等等。繪圖完畢調用EndPath().
第二步將繪制的結果轉成”region”.
此步驟中使用SDK API函數
HRGN PathToRegion( HDC hdc );
Hdc為作圖DC的句柄, CDC類中的m_hDC成員變量可做此參數傳入。示例,將下面代碼加入某個按鈕單擊事件中,可以將當前窗體變為字符串”hello”的形狀
void CTestDlg::OnTest() { HRGN wndRgn; CClientDC dc(this); CFont mFont; if (dc.m_hDC!=NULL) { VERIFY(mFont.CreateFont( 200, 50, 0, 0, FW_HEAVY, TRUE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "宋體")); //開始記錄窗體輪廓路徑 dc.BeginPath(); //設置背景為透明模式,這句話是必須有的。 dc.SetBkMode(TRANSPARENT); CFont * pOldFont; pOldFont = dc.SelectObject( &mFont ); dc.TextOut(0, 0, "Hello"); //結束記錄窗體輪廓路徑 dc.SelectObject( pOldFont ); dc.EndPath(); //把所記錄的路徑轉化為窗體輪廓句柄 wndRgn = ::PathToRegion(dc.m_hDC); //賦予窗體指定的輪廓形狀 this->SetWindowRgn(wndRgn, TRUE); } }
CClientDC是CDC的派生類,故此該類具有所有CDC類的成員變量和成員函數。

圖二 hello形狀的窗體效果圖
五、根據圖像創建”region”
此法創建不規則窗體比較復雜。首先準備一張含有目標窗體形狀的圖片,設置透明色即將圖片中部不屬于窗體形狀的部分,標記成同一種顏色,例如藍色 RGB(0,0,255).程序運行后先裝入圖片。然后逐個掃描圖片的每個像素,如這個像素不屬于透明色,則在相應位置創建一個只含一個像素的 “region”然后將這些小”region ”合并起來組成一個任意形狀的”region”.這里將使用到CRgn的一個成員函數
int CRgn::CombineRgn( CRgn* pRgn1, CRgn* pRgn2, int nCombineMode );
其中pRgn1,pRgn2為要合并的兩個“region”,nCombineMode為合并的方式,此應用中取RGN_OR,即兩”region”全部合并去處重復部分。代碼實現如下:
void SetupRegion( CDC *pDC, //窗體的DC指針 CBitmap &cBitmap, //含有窗體形狀的位圖對象 COLORREF TransColor //透明色 ) { CDC memDC; //創建與傳入DC兼容的臨時DC memDC.CreateCompatibleDC(pDC); CBitmap *pOldMemBmp=NULL; //將位圖選入臨時DC pOldMemBmp=memDC.SelectObject(&cBitmap); CRgn wndRgn; //創建總的窗體區域,初始region為0 wndRgn.CreateRectRgn(0,0,0,0); BITMAP bit; cBitmap.GetBitmap (&bit);//取得位圖參數,這里要用到位圖的長和寬 int y; for(y=0;y<=bit.bmHeight ;y++) { CRgn rgnTemp; //保存臨時region int iX = 0; do { //跳過透明色找到下一個非透明色的點. while (iX <= bit.bmWidth && memDC.GetPixel(iX, y) == TransColor) iX++; //記住這個起始點 int iLeftX = iX; //尋找下個透明色的點 while (iX <= bit.bmWidth && memDC.GetPixel(iX, y) != TransColor) ++iX; //創建一個包含起點與重點間高為1像素的臨時“region” rgnTemp.CreateRectRgn(iLeftX, y, iX, y+1); //合并到主"region". wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_OR); //刪除臨時"region",否則下次創建時和出錯 rgnTemp.DeleteObject(); }while(iX GetWindow(); pWnd->SetWindowRgn(wndRgn,TRUE); pWnd->SetForegroundWindow(); }
上述代碼創建的不規則窗體中,在OnEraseBkgnd事件中繪制該位圖,就可得到與該位圖形狀一模一樣的窗體。

圖三 根據位圖和位圖中的透明色創建的窗體效果圖
六、小結
三種創建“region”的方法,第一種最簡單,如果所需的窗體形狀是簡單的幾何圖形,這種方法最合適;第二種稍微復雜些,但是創建的窗體形狀更多些;第三種方法可以創建任何在圖片中畫出的窗體形狀,但是實現的復雜度也最高。
注:本文的寫作曾參考了“ 形態各異的不規則窗體 ”一文。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
