用VC++實(shí)現(xiàn)異形窗口.大連鐵道學(xué)院(116028)李文輝隨著Microsoft憑借Windows在操作系統(tǒng)上取得的巨大成績,Windows用戶界面也日益成為業(yè)界標(biāo)準(zhǔn)。統(tǒng)一的界面給廣大用戶對應(yīng)用軟件的學(xué)習(xí)與使用帶來了很大方便。但每天都面對同一副面孔,日久天長難免會產(chǎn)生一些厭倦,開發(fā)一些“離經(jīng)叛道”,一改Windows應(yīng)用程序千篇一律的“標(biāo)準(zhǔn)”界面,一定會給你帶來一種清新的感覺。標(biāo)準(zhǔn)Windows應(yīng)用程序窗口一般為帶有標(biāo)題欄的淺灰色矩形外觀,因而“異形”對話框/窗口也主要是顏色與外形上動手腳。改變背景顏色
改變對話框(窗口)的背景顏色是最簡單的改變Windows應(yīng)用程序外觀的方法,根據(jù)Windows創(chuàng)建與管理機(jī)理,一般有兩種方法。一種是處理WM_CTLCOLOR消息,首先創(chuàng)建所選背景顏色的刷子,然后調(diào)用SetBkColor()或SetDialogBkColor()以所創(chuàng)建的刷子來繪制窗口或?qū)υ捒虻谋尘啊P枰禺嫶翱诨驅(qū)υ挘ɑ驅(qū)υ挼淖涌丶r(shí),Windows向?qū)υ挵l(fā)送消息WM_CTLCOLOR,應(yīng)用程序處理WM_CTLCOLOR消息并返回一個(gè)用來繪畫對話背景的刷子句柄。另外一種是響應(yīng)Windows的WM_ERASEBKGND消息,Windows向窗口發(fā)送一個(gè)WM_ERASEBKGND消息通知該窗口擦除背景,可以使用VC++的ClassWizard重載該消息的缺省處理程序來擦除背景(實(shí)際是用刷子畫),并返回TRUE以防止Windows擦除窗口。2.改變窗口外形通過使用新的SDK函數(shù)SetWindowRgn(),可以將繪畫和鼠標(biāo)消息限定在窗口的一個(gè)指定的區(qū)域,因此實(shí)際上是使窗口成為指定的不規(guī)則形狀(區(qū)域形狀)。“區(qū)域”是Windows GDI中一種強(qiáng)有力的機(jī)制,區(qū)域是設(shè)備上的一塊空間,可以是任意形狀,復(fù)雜的區(qū)域可以由各個(gè)小區(qū)域組合而成。Windows內(nèi)含的區(qū)域創(chuàng)建函數(shù)有CreateRectRgn()、CreatePolyRgn()、CreatePolygonRgn()、CreateRoundRectRgn()和CreateEllipticRgn(),再通過CombineRgn()來組合區(qū)域,即可得到復(fù)雜形狀的區(qū)域,獲得復(fù)雜形狀的窗口外形。通過上面的方法雖然可以得到“異形”窗口,但感覺顏色單調(diào),外形也不夠“COOL”,能否獲得更酷的“異形”對話框/窗口呢?回答是肯定的。下面就介紹利用位圖和蒙板創(chuàng)建“異形”對話框/窗口的方法。3.利用位圖創(chuàng)建異形對話框窗口利用位圖創(chuàng)建異形對話框原理是根據(jù)象素的顏色來進(jìn)行“扣像”處理,對所有非指定顏色象素區(qū)域進(jìn)行區(qū)域組合。利用這一技術(shù),實(shí)際上就是實(shí)現(xiàn)對話框/窗口的位圖背景,并且對指定的顏色區(qū)域進(jìn)行透明處理。下面就以透明位圖為背景的對話框?yàn)槔齺碚f明:首先用繪圖軟件如PhotoShop繪制編輯一幅擬做對話框背景用的圖片,用BMP格式保存,假設(shè)存為Back.Bmp。需要說明的是,雖然Visual C++集成開發(fā)環(huán)境的資源編輯器只能編輯不超過16色的位圖,但完全我們可以以真彩色方式存儲,不必理會Visual C++的警告。下一步是用Visual C++的AppWizard創(chuàng)建一個(gè)基于對話框的應(yīng)用程序假定命名為Trans。用資源編輯器引入背景圖片Back.Bmp,如果是高彩色,不必理會出現(xiàn)的警告信息,點(diǎn)擊OK確認(rèn)即可。為了明確,修改默認(rèn)的資源ID標(biāo)識IDB_BITMAP1為IDB_BACKBMP。然后修改對話框的Style為Popup,Border為None,如圖1
。圖1向CTransDlg類添加區(qū)域處理功能模塊void CTransDlg::SetupRegion(CDC *pDC /*對話框窗口DC*/, UINT BackBitmapID /*背景位圖資源ID*/, UINT MaskBitmapID /*區(qū)域處理位圖資源ID*/, COLORREF TransColor = 0x00000000 /*透明顏色值,默認(rèn)為黑色*/)。到目前為止,我們暫時(shí)認(rèn)為MaskBitmapID等同于BackBitmapID。其核心工作是根據(jù)MaskBitmapID指示位圖的象素顏色進(jìn)行區(qū)域組合。完整的代碼如下:void CTransDlg::SetupRegion(CDC *pDC /*對話框窗口DC*/, UINT BackBitmapID /*背景位圖資源ID*/,UINT MaskBitmapID /*區(qū)域處理位圖資源ID*/,COLORREF TransColor /*透明顏色值*/){CDC memDC;CBitmap cBitmap;CBitmap* pOldMemBmp = NULL;COLORREF cl;CRect cRect;UINT x, y;CRgn wndRgn, rgnTemp;//取得窗口大小GetWindowRect(&cRect);//背景位圖資源IDm_BackBitmapID = BackBitmapID//裝載位圖cBitmap.LoadBitmap(MaskBitmapID);memDC.CreateCompatibleDC(pDC);pOldMemBmp = memDC.SelectObject(&cBitmap);//首先創(chuàng)建默認(rèn)的完整區(qū)域?yàn)橥暾拇翱趨^(qū)域wndRgn.CreateRectRgn(0, 0, cRect.Width(), cRect.Height());//下面的兩層循環(huán)為檢查背景位圖象素顏色,進(jìn)行透明區(qū)域處理;//當(dāng)象素顏色為指定的透明值時(shí),即將該點(diǎn)從區(qū)域中剪裁掉。//其中用到的幾個(gè)成員變量m_MaskLeftOff、m_MaskTopOff、//m_MaskRightOff、m_MaskBottomOff、m_FrameWidth//和m_CaptionHeight,其作用后面再作說明,此時(shí)可全部當(dāng)作0來處理。for(x= m_FrameWidth+m_MaskLeftOff;x<=cRect.Width() - m_FrameWidth-m_MaskRightOff; x++){for(y = m_CaptionHeight+m_MaskTopOff;
y<=cRect.Height() - m_FrameWidth-m_MaskBottomOff; y++){//取得坐標(biāo)處象素的顏色值cl = memDC.GetPixel(x - m_FrameWidth-m_MaskLeftOff,y - m_CaptionHeight-m_MaskTopOff);if(col == TransColor){//象素顏色為指定的透明色,創(chuàng)建透明“微區(qū)域”rgnTemp.CreateRectRgn(x, y, x+1, y+1);//“扣像”,從完整的區(qū)域中“扣除”透明的“微區(qū)域”wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_XOR);//刪除剛創(chuàng)建的透明“微區(qū)域”,釋放系統(tǒng)資源rgnTemp.DeleteObject(); }}}if (pOldMemBmp) memDC.SelectObject(pOldMemBmp);//用設(shè)定窗口為指定的區(qū)域SetWindowRgn((HRGN)wndRgn, TRUE);}重置系統(tǒng)默認(rèn)的背景擦除操作,即添加WM_ERASEBKGND消息處理過程,這一步可以借助ClassWizard來簡化操作。BOOL CTransDlg::OnEraseBkgnd(CDC* pDC) {// TODO: Add your message handler code here and/or call defaultCRect rect;CDC memDC;CBitmap cBitmap;CBitmap* pOldMemBmp = NULL;GetWindowRect(&rect);//裝載背景位圖cBitmap.LoadBitmap(m_BackBitmapID);memDC.CreateCompatibleDC(pDC);pOldMemBmp = memDC.SelectObject(&cBitmap);//將背景位圖復(fù)制到窗口客戶區(qū)pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRC/DownloadFiles\a\2001-10-12\COPY);if (pOldMemBmp) memDC.SelectObject( pOldMemBmp );//刪除系統(tǒng)卻省的OnEraseBkgnd功能//return CDialog::OnEraseBkgnd(pDC);return TRUE;}接下來是在WM_PAINT的消息處理函數(shù)OnPaint()中添加代碼。由于當(dāng)背景位圖比較大時(shí),進(jìn)行區(qū)域處理比較耗時(shí),所以只在啟動時(shí)進(jìn)行一次處理。一種方法是OnInitDialog()處理,但這樣會在從啟動程序到窗口出現(xiàn)有相當(dāng)?shù)难舆t,易引起程序尚未啟動的誤解。再一種方法就是在OnPaint()處理,但為了避免重復(fù)處理,可以加上一個(gè)判斷標(biāo)志。以下是OnPaint()的代碼,正體為AppWizard生成,粗體為自己添加內(nèi)容。void CTransDlg::OnPaint() {if (IsIconic()){……}else{if(m_nFirstRun){ //首次運(yùn)行標(biāo)志//修改鼠標(biāo)光標(biāo)為等待方式BeginWaitCursor();//設(shè)置背景區(qū)域SetupRegion(GetWindowDC(),
計(jì)算機(jī)教程用VC++實(shí)現(xiàn)異形窗口.來自www.itwen.comIT WEN計(jì)算機(jī)教程網(wǎng)
IDB_BACKBMP,
IDB_BACKBMP,
0x00FFFFFF /*白色*/);//恢復(fù)鼠標(biāo)光標(biāo)為正常模式EndWaitCursor();m_nFirstRun = 0;}CDialog::OnPaint();}}剩下的工作就是根據(jù)背景位圖的大小來設(shè)置對話框窗口的大小和位置,這可以在OnInitDialog()中通過調(diào)用MoveWindow()來實(shí)現(xiàn)。再添加一些變量的聲名和初始化,即可編譯運(yùn)行。圖2為運(yùn)行結(jié)果示例:圖24.進(jìn)一步的討論前面實(shí)現(xiàn)了單一模式的異形對話框,但有些情況下又需要不同的樣式,如有標(biāo)題欄、邊框等,或者只作局部的處理,這就是前面兩個(gè)成員變量m_FrameWidth和m_CaptionHeight作用,通過在OnInitDialog()判斷窗口樣式,使m_FrameWidth和m_CaptionHeight取不同的值。這部分的代碼為:BOOL CTransBmpDlg::OnInitDialog(){……// TODO: Add extra initialization herem_nFirstRun = 1;//數(shù)據(jù)設(shè)置,窗口左上角坐標(biāo):m_Left=0,m_Top=0
//背景位圖寬高:m_Width=535,m_Height=105SetSize(0, 0, 535, 105);//蒙板處理區(qū)域與窗口邊框的距離m_MaskLeftOff=m_MaskTopOff=m_MaskRightOff=m_MaskBottomOff=0;//窗口邊框與標(biāo)題欄象素值m_FrameWidth = m_CaptionHeight = 0;//取得窗口樣式LONG style = ::GetWindowLong(this->m_hWnd, GWL_STYLE);//如保留窗口風(fēng)格樣式,則根據(jù)不同的窗口邊框類型
//選取不同的m_FrameWidth和m_CaptionHeight值,
//也可以根據(jù)處理位置的需要進(jìn)行付值if((style & WS_BORDER) == WS_BORDER)m_FrameWidth = ::GetSystemMetrics(SM_CXBORDER);if((style & WS_THICKFRAME) == WS_THICKFRAME)m_FrameWidth = ::GetSystemMetrics(SM_CXFIXEDFRAME);if((style & DS_MODALFRAME) == DS_MODALFRAME)m_FrameWidth = ::GetSystemMetrics(SM_CXFIXEDFRAME);if((style & WS_CAPTION) == WS_CAPTION){m_FrameWidth = ::GetSystemMetrics(SM_CXFIXEDFRAME);m_CaptionHeight = ::GetSystemMetrics(SM_CYSMCAPTION);}m_CaptionHeight += m_FrameWidth * 2;//重置窗口的位置和大小MoveWindow(m_Left, m_Top,
m_Width + m_FrameWidth * 2,
m_Height + m_CaptionHeight, TRUE);……return TRUE; // return TRUE unless you set the focus to a control}另外,為進(jìn)一步增加靈活性,使窗口樣式不僅僅受背景位圖顏色的控制。通過指定SetupRegion()的MaskBitmapID 為一個(gè)我們稱之為“蒙板”的雙色位圖(多色彩也可以,但一般沒有必要),即可實(shí)現(xiàn)需要的操作。圖4為在同一背景位圖上,通過圖3的蒙板位圖實(shí)現(xiàn)的效果,并且增加了對話框窗體的邊框和標(biāo)題欄屬性。圖3圖4利用這種蒙板技術(shù),可以創(chuàng)建出任意形狀的窗口,而與背景位圖無關(guān)。需要注意的是,對于對話框中的控件如按鈕等,如處在或部分處在通明區(qū)域中,則通明區(qū)域中部分一并被剪裁掉,是否剪裁和剪裁位置與大小,利用蒙板可以很方便地進(jìn)行控制。需要特別指出的是,SetWindowRgn()所指定的區(qū)域是針對整個(gè)窗口的,而Bitblt()/ StretchBlt()的輸出區(qū)域是針對于客戶區(qū),兩者在定位上是不同的,編程中應(yīng)加以注意并靈活應(yīng)用,這也是前面之所以設(shè)置邊框大小等變量的原因。5.結(jié)束語這種異形窗口的創(chuàng)建不僅適應(yīng)于對話框,而且適應(yīng)于所有的基于CWnd類的派生窗口。采用這一方法,你可以創(chuàng)建出任何只要你能夠畫出的窗體,實(shí)現(xiàn)只要可以畫出,就可以做出的目標(biāo)。本文代碼在Visual C++ 5.0、6.0下調(diào)試通過,運(yùn)行正常,操作系統(tǒng)為Windows98SE。
posted on 2008-08-04 11:00
cpsprogramer 閱讀(3924)
評論(0) 編輯 收藏 引用 所屬分類:
VC++