在
《自繪按鈕的實現(xiàn)》一文中所示范的CXPButton按鈕從形狀上看可以說是一個不規(guī)則按鈕。我們把MFC中提供的控件成為標準控件,而其中的按鈕控件則可稱為規(guī)則按鈕,因為它是的形狀是一個規(guī)則的矩形。但是隨著流線型設(shè)計在界面設(shè)計中被廣泛采用,越來越多的程序設(shè)計者都樂意在自己的作品中加入一些非規(guī)則形狀的控件。以按鈕為例,最簡單的就到有圓形按鈕,三角按鈕等。下圖是一組定義好的圓形按鈕:
但是這些在我們眼中似乎呈現(xiàn)出不規(guī)則形狀的按鈕,有時候的表現(xiàn)卻不近乎人意。以之前設(shè)計的CXPButton為例,下面我們就來找找它的缺陷。我們把對話框的背景色改為其它顏色,看看會有什么效果。修改背景色的方法如下:為對話框類添加WM_ERASEBKGND消息,如果你在類向?qū)е姓也坏皆撓ⅲ垍⒖?a target=_blank>
《自繪按鈕的實現(xiàn)》中介紹的方法,注意在添加對話框中要把Filter for messages available to設(shè)為Child Window才能在列表中看到WM_ERASEBKGND消息。我們在函數(shù)中添加繪制背景色的代碼:
BOOL CXPButtonDemoDlg::OnEraseBkgnd(CDC* pDC)
{
BOOL retValue= CDialog::OnEraseBkgnd(pDC);
CRect rc;
GetClientRect(&rc);
pDC->FillSolidRect(&rc,RGB(0,0,255));
return retValue;
}
編譯后運行程序,可以看到下面的效果,在按鈕的四個角上出現(xiàn)了難看的邊角,這就是我所說的缺陷了:
既然發(fā)現(xiàn)了問題,下面當然就是動手來解決問題的時間了。在《自繪按鈕的實現(xiàn)》一文中曾經(jīng)講過,按鈕的繪制主要在DrawItem()函數(shù)中完成。這里要補充一點就是DrawItem()是MFC所提供的系統(tǒng)函數(shù),所以可以通過下圖的方法添加。選擇Add Virtual Function…之后在彈出對話框左邊的列表中找到DrawItem,選擇Add and Edit即可。
好了,我們接著上面的話題。要解決按鈕顯示的“殘角”問題,在這里需要使用一個叫做CRgn的類,這個類在創(chuàng)建不規(guī)則控件的時候經(jīng)常要用到,我們可以通過CRgn類來設(shè)置控件的有效區(qū)域。那么什么是有效區(qū)域呢?以圓形按鈕為例,MFC默認的按鈕形狀是矩形的,為了實現(xiàn)圓形按鈕的效果,我們希望能夠把原來矩形的四角裁剪掉,只保留中間的圓形區(qū)域。這種關(guān)系可以用下圖來表示:

圖中的A是原來的矩形區(qū)域,B是需要裁剪的區(qū)域,而C是有效區(qū)域。我們希望程序不要把B看作按鈕的一部分,當鼠標在B上面點擊的時候不要產(chǎn)生任何效果。設(shè)置按鈕的有效區(qū)域一般是在PreSubclassWindow()函數(shù)里面實現(xiàn)的:
void CXPButton::PreSubclassWindow()
{
CButton::PreSubclassWindow();
ModifyStyle(0, BS_OWNERDRAW);
//設(shè)置按鈕的有效區(qū)域
CRgn rgn;
CRect rc;
GetClientRect(&rc);
//有效區(qū)域為一個角半徑為5的圓角矩形
rgn.CreateRoundRectRgn(rc.left,rc.top,rc.right,rc.bottom,5,5);
SetWindowRgn(rgn,TRUE);
rgn.DeleteObject();
}
編譯后運行程序,我們發(fā)現(xiàn)盡管已經(jīng)設(shè)置了按鈕的有效區(qū)域,但是問題還是沒有解決,這是為什么呢?前面我們曾經(jīng)通過對話框的WM_ERASEBKGND消息函數(shù)來改變對話框的底色,其實按鈕也有它的WM_ERASEBKGND消息函數(shù),它會使用系統(tǒng)顏色根據(jù)控件的默認形狀來繪制控件的底色。所以我們要重載按鈕的WM_ERASEBKGND消息函數(shù),讓它什么都不做:
BOOL CXPButton::OnEraseBkgnd(CDC* pDC)
{
//禁止繪制底色
return TRUE;
}
再編譯一次,運行后發(fā)現(xiàn),難看的"殘角"不見了:
在
《自繪按鈕的實現(xiàn)》一文中還講過在DrawItem()函數(shù)中應(yīng)該先畫底色,其實這是有前提的,前提就是你已經(jīng)知道了按鈕所在對話框所使用的背景色,你可以使用這個顏色作為按鈕的底色來進行填充。這樣即使程序會在WM_ERASEBKGND消息函數(shù)中使用系統(tǒng)顏色來繪制控件的底色,但是等到它執(zhí)行DrawItem()的時候,馬上又會把之前的矩形底色覆蓋,從而不留痕跡地把“殘角”掩蓋掉。
在本篇開頭列舉的圓形按鈕中也有類似的問題,大家不防把它作為練習,看看是否能夠解決圓形按鈕中的“殘角”問題。我在本篇提供的練習程序中需要修改的地方會表明“提示”的字眼,大家可以先把源程序中有“提示”字眼的地方找出來,再根據(jù)提示的內(nèi)容進行修改。
最后還要補充的是各位對CRgn類的關(guān)注。在VC中,要創(chuàng)建出各種復(fù)雜形狀的控件、窗口經(jīng)常要依靠CRgn類的強大功能來實現(xiàn)。如果你想在界面設(shè)計這個環(huán)節(jié)更進一步的話,建議你抽點時間仔細研究一下Msdn中關(guān)于CRgn類的使用說明。如果你有什么好的使用經(jīng)驗和心得,不防把相關(guān)資料發(fā)到我的郵箱。以后有機會的話我會專門寫一篇文章探討一下CRgn類的使用技巧的。