一、序言
在絕大多數(shù)的Windows應(yīng)用程序中,其窗體都是使用的正規(guī)正矩的矩形窗體,例如我們常用的,“記事本”,“掃雷”,等等。矩形窗體,具有編程實(shí)現(xiàn)簡(jiǎn)單,風(fēng)ge簡(jiǎn)潔的優(yōu)點(diǎn),所以在普通文檔應(yīng)用程序和簡(jiǎn)單小游戲中使用足矣。但在某些娛樂游戲程序中使用就略顯呆板些了,這時(shí)若用不規(guī)則窗體替代原先的矩形窗體,將會(huì)使這類程序更添情趣。典型的例子有windows 自代的Media Player,新版本的Media Player有個(gè)控制面板的選項(xiàng),選中這些面板,播放器就以選中的面板形狀出現(xiàn),這時(shí)的播放器比以前版本的Media Player的古老矩形界面要生動(dòng)有趣的多了。 要實(shí)現(xiàn)不規(guī)則窗體不是太難,知道了基本原理后,你也可以創(chuàng)建各種有趣的不規(guī)則窗體。
二、實(shí)現(xiàn)原理
所有的 Windows 窗體都位于一個(gè)稱為“region”中,窗體的大小如果超出“region”的范圍,windows 會(huì)自動(dòng)裁剪超出"region"范圍那部分的窗體,使其不可見。所以,要?jiǎng)?chuàng)建不規(guī)則窗體有兩個(gè)步驟:第一步就是創(chuàng)建不規(guī)則"region".第二步就是將窗體放到創(chuàng)建的“region”中。
其中第二步很簡(jiǎn)單就調(diào)用一條語句即可。在SDK中調(diào)用API函數(shù)SetWindowRgn,該函數(shù)原型如下:
int SetWindowRgn( HWND hWnd, HRGN hRgn, BOOL bRedraw );
其中hWnd為待設(shè)置的窗體句柄,hRgn為已經(jīng)創(chuàng)建的"region"句柄,bRedraw代表是否要重繪窗體。在MFC 中使用窗口類CWnd的成員函數(shù)int CWnd::SetWindowRgn(HRGN hRgn, BOOL bRedraw );該函數(shù)的參數(shù)意義與API中同名函數(shù)相同。
相對(duì)與第二步,創(chuàng)建不規(guī)則窗體的第一步要復(fù)雜許多,并且不規(guī)則窗體越復(fù)雜,創(chuàng)建其"region"的過程也越復(fù)雜。接下去我們將由淺入深地介紹各種創(chuàng)建”region”的方法。
在MFC中"region"對(duì)象,由CRgn類實(shí)現(xiàn)。CRgn的幾乎每個(gè)成員函數(shù)都有同名的SDK API函數(shù)對(duì)應(yīng)。
三、簡(jiǎn)單“region”的創(chuàng)建
類CRgn創(chuàng)建一個(gè)新的"region"的簡(jiǎn)單方法有以下幾個(gè)成員函數(shù): BOOL CRgn::CreateRectRgn( int x1, int y1, int x2, int y2 ); 創(chuàng)建矩形的“region”。
BOOL CRgn::CreateEllipticRgn( int x1, int y1, int x2, int y2 ); 創(chuàng)建圓形或橢圓形“region”。 BOOL CRgn::CreateRoundRectRgn( int x1, int y1, int x2, int y2, int x3, int y3 ); 創(chuàng)建圓角矩形“region”。 BOOL CRgn::CreatePolygonRgn( LPPOINT lpPoints, int nCount, int nMode ); 創(chuàng)建多邊形“region”。 |
BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); ... CRgn rgn; rgn. CreateEllipticRgn(0,0,200,100); SetWindowRgn(rgn,TRUE); } |
![]() 圖一 橢圓窗體效果圖 |
int CDC::SetBkMode( int nBkMode ); |
BOOL CDC::EndPath( ); |
HRGN PathToRegion( HDC hdc ); |
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(); //設(shè)置背景為透明模式,這句話是必須有的。 dc.SetBkMode(TRANSPARENT); CFont * pOldFont; pOldFont = dc.SelectObject( &mFont ); dc.TextOut(0, 0, "Hello"); //結(jié)束記錄窗體輪廓路徑 dc.SelectObject( pOldFont ); dc.EndPath(); //把所記錄的路徑轉(zhuǎn)化為窗體輪廓句柄 wndRgn = ::PathToRegion(dc.m_hDC); //賦予窗體指定的輪廓形狀 this->SetWindowRgn(wndRgn, TRUE); } } |
![]() 圖二 hello形狀的窗體效果圖 |
void SetupRegion( CDC *pDC, //窗體的DC指針 CBitmap &cBitmap, //含有窗體形狀的位圖對(duì)象 COLORREF TransColor //透明色 ) { CDC memDC; //創(chuàng)建與傳入DC兼容的臨時(shí)DC memDC.CreateCompatibleDC(pDC); CBitmap *pOldMemBmp=NULL; //將位圖選入臨時(shí)DC pOldMemBmp=memDC.SelectObject(&cBitmap); CRgn wndRgn; //創(chuàng)建總的窗體區(qū)域,初始region為0 wndRgn.CreateRectRgn(0,0,0,0); BITMAP bit; cBitmap.GetBitmap (&bit);//取得位圖參數(shù),這里要用到位圖的長和寬 int y; for(y=0;y<=bit.bmHeight ;y++) { CRgn rgnTemp; //保存臨時(shí)region int iX = 0; do { //跳過透明色找到下一個(gè)非透明色的點(diǎn). while (iX <= bit.bmWidth && memDC.GetPixel(iX, y) == TransColor) iX++; //記住這個(gè)起始點(diǎn) int iLeftX = iX; //尋找下個(gè)透明色的點(diǎn) while (iX <= bit.bmWidth && memDC.GetPixel(iX, y) != TransColor) ++iX; //創(chuàng)建一個(gè)包含起點(diǎn)與重點(diǎn)間高為1像素的臨時(shí)“region” rgnTemp.CreateRectRgn(iLeftX, y, iX, y+1); //合并到主"region". wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_OR); //刪除臨時(shí)"region",否則下次創(chuàng)建時(shí)和出錯(cuò) rgnTemp.DeleteObject(); }while(iX GetWindow(); pWnd->SetWindowRgn(wndRgn,TRUE); pWnd->SetForegroundWindow(); } |
圖三 根據(jù)位圖和位圖中的透明色創(chuàng)建的窗體效果圖
六、小結(jié)
三種創(chuàng)建“region”的方法,第一種最簡(jiǎn)單,如果所需的窗體形狀是簡(jiǎn)單的幾何圖形,這種方法最合適;第二種稍微復(fù)雜些,但是創(chuàng)建的窗體形狀更多些;第三種方法可以創(chuàng)建任何在圖片中畫出的窗體形狀,但是實(shí)現(xiàn)的復(fù)雜度也最高。
新聞熱點(diǎn)
疑難解答
圖片精選