|
本文專題討論VC中的界面美化,適用于具有中等VC水平的讀者。讀者最好具有以下VC基礎(chǔ): 1. 大致了解MFC框架的基本運(yùn)作原理; 2. 熟悉Windows消息機(jī)制,熟悉MFC的消息映射和反射機(jī)制; 3. 熟悉OOP理論和技術(shù); 本文根據(jù)筆者多年的開發(fā)經(jīng)驗(yàn),并結(jié)合簡(jiǎn)單的例子一一展開,希望對(duì)讀者有所幫助。
1. 美化界面之開題篇 相信使用過《金山毒霸》、《瑞星殺毒》軟件的讀者應(yīng)該還記得它們的精美界面:
圖1 瑞星殺毒軟件的精美界面 程序的功能如何如何強(qiáng)大是一回事,它的用戶界面則是另一回事。千萬不要忽視程序的用戶界面,因?yàn)樗墙o用戶最初最直接的印象,丑陋的界面、不友好的風(fēng)格肯定會(huì)影響用戶對(duì)軟件程序的使用。 “受之以魚,不若授之以漁”,本教程并不會(huì)向你推薦《瑞星殺毒軟件》精美界面的具體實(shí)現(xiàn),而只是向你推薦一些常用的美化方法。
2. 美化界面之基礎(chǔ)篇 美化界面需要先熟悉Windows下的繪圖操作,并明白Windows的幕后繪圖操作,才能有的放矢,知道哪些可以使用,知道哪些可以避免……
2.1 Windows下的繪圖操作
熟悉DOS的讀者可能就知道:DOS下面的圖形操作很方便,進(jìn)入圖形模式,整個(gè)屏幕就是你的了,你希望在哪畫個(gè)點(diǎn),那個(gè)地方就會(huì)出現(xiàn)一個(gè)點(diǎn),紅的、或者黃的,隨你的便。你也可以花點(diǎn)時(shí)間畫個(gè)按鈕,畫個(gè)你自己的菜單,等等…… Windows本身就是圖形界面,所以Windows下面的繪圖操作功能更豐富、簡(jiǎn)單。要了解Windows下的繪圖操作,要實(shí)現(xiàn)Windows界面的美化,就必須了解MFC封裝的設(shè)備環(huán)境類和圖形對(duì)象類。
2.1.1 設(shè)備環(huán)境類
Windows下的繪圖操作說到底就是DC操作。DC(Device Context設(shè)備環(huán)境)對(duì)象是一個(gè)抽象的作圖環(huán)境,可能是對(duì)應(yīng)屏幕,也可能是對(duì)應(yīng)打印機(jī)或其它。這個(gè)環(huán)境是設(shè)備無關(guān)的,所以你在對(duì)不同的設(shè)備輸出時(shí)只需要使用不同的設(shè)備環(huán)境就行了,而作圖方式可以完全不變。這也就是Windows的設(shè)備無關(guān)性。 MFC的CDC類封裝了Windows API 中大部分的畫圖函數(shù)。CDC的常見操作函數(shù)包括: Drawing-Attribute Functions:繪圖屬性操作,如:設(shè)置透明模式 Mapping Functions:映射操作 Coordinate Functions:坐標(biāo)操作 Clipping Functions:剪切操作 Line-Output Functions:畫線操作 Simple Drawing Functions:簡(jiǎn)單繪圖操作,如:繪制矩形框 Ellipse and Polygon Functions:橢圓/多邊形操作 Text Functions:文字輸出操作 Printer Escape Functions:打印操作 Scrolling Functions:滾動(dòng)操作 *Bitmap Functions:位圖操作 *Region Functions:區(qū)域操作 *Font Functions:字體操作 *Color and Color Palette Functions:顏色/調(diào)色板操作 其中,標(biāo)注*項(xiàng)會(huì)用到相應(yīng)的圖形對(duì)象類,參見2.1.2內(nèi)容。
2.1.2 圖形對(duì)象類
設(shè)備環(huán)境不足以包含繪圖功能所需的所有繪圖特征,除了設(shè)備環(huán)境外, Windows還有其他一些圖形對(duì)象用來儲(chǔ)存繪圖特征。這些附加的功能包括從畫線的寬度和顏色到畫文本時(shí)所用的字體。圖形對(duì)象類封裝了所有六個(gè)圖形對(duì)象。 下面的表格列出了MFC的圖形對(duì)象類: MFC類 圖形對(duì)象句柄 圖形對(duì)象目的 CBitmap HBITMAP 內(nèi)存中的位圖 CBrush HBRUSH 畫刷特性—填充某個(gè)圖形時(shí)所使用的顏色和模式 CFont HFONT 字體特性—寫文本時(shí)所使用的字體 CPalette HPALETTE 調(diào)色板顏色 CPen HPEN 畫筆特性—畫輪廓時(shí)所使用的線的粗細(xì) CRgn HRGN 區(qū)域特性—包括定義它的點(diǎn) 表1 圖形對(duì)象類和它們封裝的句柄 使用CDC和圖形對(duì)象類,在Windows里繪圖還算是很簡(jiǎn)單的。觀察以下的畫面:
圖2 使用CDC繪制出的按鈕 該畫面通過以下代碼自行繪制的假按鈕: 呵呵,不好意思,這并不是真的Windows按鈕,它只是一個(gè)假的空框子,當(dāng)用戶在按鈕上點(diǎn)擊鼠標(biāo)時(shí),放心,什么事情都不會(huì)發(fā)生。 2.2 Windows的幕后繪圖操作 在Window中,如果所有的界面操作都由用戶代碼來實(shí)現(xiàn),那將是一個(gè)很浩大的工程。筆者曾經(jīng)在DOS設(shè)計(jì)過窗口圖形界面,代碼上千行,但實(shí)現(xiàn)的界面還是很古板、難看,除了我那個(gè)對(duì)編程一竅不通的女友,沒有一個(gè)人欣賞它L;而且,更要命的是,操作系統(tǒng),包括別的應(yīng)用程序并不認(rèn)識(shí)你的界面元素,這才是真正悲哀的。認(rèn)識(shí)這些界面的只有你的程序,圖2中的按鈕永遠(yuǎn)只是一個(gè)無用的框子。 有了Windows,一切都好辦了,Windows將諸如按鈕、菜單、工具欄等等這些通用界面的繪制及動(dòng)作都交給了系統(tǒng),程序員就不用花心思再畫那些按鈕了,可以將更多的精力放在程序的功能實(shí)現(xiàn)方面。 所有的標(biāo)準(zhǔn)界面元素都被Windows封裝好了。Windows知道怎么畫你的菜單以及你的標(biāo)注著“Hello, Cfan!”的按鈕。當(dāng)CFan某個(gè)快樂的小編(譬如:小飛)點(diǎn)擊這個(gè)按鈕的時(shí)候,Windows也明白按鈕按下去的時(shí)候該有的模樣,甚至,當(dāng)這個(gè)友好的按鈕獲取焦點(diǎn)時(shí),Windows也會(huì)不失時(shí)機(jī)地為它準(zhǔn)備一個(gè)虛框…… 有利必有弊。你的不滿這時(shí)候產(chǎn)生了:你既想使用Windows的True Button,可也嫌它的界面不夠好看,譬如,你喜歡用藍(lán)色的粗體表達(dá)你對(duì)CFan的無限情懷(正如圖2那樣)——人心不足,有辦法嗎?有的。
3. 美化界面之實(shí)現(xiàn)篇 Windows還是給程序員留下了很多后門,通過一些途徑還是可以美化界面的。本章節(jié)我們系統(tǒng)學(xué)習(xí)一下Windows界面美化的實(shí)現(xiàn)。
3.1 美化界面的途徑
如何以合法的手段來達(dá)到美化界面的效果?一般美化界面的方法包括: 1. 使用MFC類的既有函數(shù),設(shè)定界面屬性; 2. 利用Windows的消息機(jī)制,截獲有用的Windows的消息。通過MFC的消息映射(Message Mapping)和反射(Message Reflecting)機(jī)制,在Windows準(zhǔn)備或者正在繪制該元素時(shí),偷偷修改它的狀態(tài)和行為,譬如:讓按鈕的邊框?yàn)榧t色; 3. 利用MFC類的虛函數(shù)機(jī)制,重載有用的虛函數(shù)。在MFC框架調(diào)用該函數(shù)的時(shí)候,重新定義它的狀態(tài)和行為; 一般來說,應(yīng)用程序可以通過以下兩種途徑來實(shí)現(xiàn)以上的方法: 1. 在父窗口里,截獲自身的或者由子元素(包括控件和菜單等元素)傳遞的關(guān)于界面繪制的消息; 2. 子類化子元素,或者為子元素準(zhǔn)備一個(gè)新的類(一般來說該類必須繼承于MFC封裝的某個(gè)標(biāo)準(zhǔn)類,如:CButton)。在該子元素里,截獲自身的或者從父窗口反射過來的關(guān)于界面繪制的消息。譬如:用戶可以創(chuàng)建一個(gè)CXPButton類來實(shí)現(xiàn)具有XP風(fēng)格的按鈕,CXPButton繼承于CButton。 對(duì)于應(yīng)用程序,使用CXPButton類的途徑相對(duì)于對(duì)話框窗口和普通窗口分成兩種: ① 對(duì)話框窗口中,直接將原先綁定按鈕的CButton類替換成CXPButton類,或者在綁定變量時(shí)直接指定Control類型為CXPButton,如圖3所示:
圖3 為按鈕指定CXPButton類型 ②在普通窗口中,直接創(chuàng)建一個(gè)CXPButton類對(duì)象,然后在OnCreate()中調(diào)用CXPButton的Create方法; 以下的章節(jié)將綜合地使用以上的方法,請(qǐng)讀者朋友留心觀察。
3.2 使用MFC類的既有函數(shù)
在界面美化的專題中,MFC也并非一無是處。MFC類對(duì)于界面美化也做了部分的努力,以下是一些可以使用的,參數(shù)說明略去。 CWinApp::SetDialogBkColor void SetDialogBkColor( COLORREF clrCtlBk = RGB(192, 192, 192), COLORREF clrCtlText = RGB(0, 0, 0) ); 指定對(duì)話框的背景色和文本顏色。 CListCtrl::SetBkColor CReBarCtrl::SetBkColor CStatusBarCtrl::SetBkColor CTreeCtrl::SetBkColor COLORREF SetBkColor( COLORREF clr ); 設(shè)定背景色。 CListCtrl::SetTextColor CReBarCtrl::SetTextColor CTreeCtrl::SetTextColor COLORREF SetTextColor( COLORREF clr ); 設(shè)定文本顏色。 CListCtrl::SetBkImage BOOL SetBkImage( LVBKIMAGE* plvbkImage ); BOOL SetBkImage( HBITMAP hbm, BOOL fTile = TRUE, int xOffsetPercent = 0, int yOffsetPercent = 0); BOOL SetBkImage( LPTSTR pszUrl, BOOL fTile = TRUE, int xOffsetPercent = 0, int yOffsetPercent = 0 ); 設(shè)定列表控件的背景圖片。 CComboBoxEx::SetExtendedStyle CListCtrl::SetExtendedStyle CTabCtrl::SetExtendedStyle CToolBarCtrl::SetExtendedStyle DWORD SetExtendedStyle( DWORD dwExMask, DWORD dwExStyles ); 設(shè)置控件的擴(kuò)展屬性,例如:設(shè)置列表控件屬性帶有表格線。 圖4是個(gè)簡(jiǎn)單應(yīng)用MFC類的既有函數(shù)來改善Windows界面的例子:
圖4 使用MFC類的既有函數(shù)美化界面 相關(guān)實(shí)現(xiàn)代碼如下: 嗯,這樣的界面還算不錯(cuò)吧? 3.3 使用Windows的消息機(jī)制
使用MFC類的既有函數(shù)來美化界面,其功能是有限的。既然Windows是通過消息機(jī)制進(jìn)行通訊的,那么我們就可以通過截獲一些有用的消息來美化我們的界面,以下是一些有用的Windows消息: WM_PAINT WM_ERASEBKGND WM_CTLCOLOR* WM_DRAWITEM* WM_MEASUREITEM* NM_CUSTOMDRAW* 注意,標(biāo)注*的消息是子元素發(fā)送給父窗口的通知消息,其它的為窗口或者子元素自身的消息。
3.3.1 WM_PAINT
WM_PAINT消息相信大家都很熟悉,一個(gè)窗口要重繪了,就會(huì)有一個(gè)WM_PAINT消息發(fā)送給窗口。 可以響應(yīng)窗口的WM_PAINT,以更改它們的模樣。WM_PAINT的映射函數(shù)原型如下: afx_msg void OnPaint(); 控件也是窗口,所以控件也有WM_PAINT消息,通過消息映射我們完全可以定義控件的界面。如圖5所示:
圖5 利用WM_ PAINT消息美化界面 實(shí)現(xiàn)代碼也很簡(jiǎn)單:
哈哈,簡(jiǎn)單吧?不過WM_PAINT確實(shí)絕了點(diǎn),它要求應(yīng)用程序完成元素界面的所有繪制過程,想象一下如何畫出一個(gè)完整的列表控件?太煩了吧。一般來說,很少有人喜歡使用WM_PAINT,還有其它更細(xì)致的消息。
3.3.2 WM_ERASEBKGND
Windows在向窗口發(fā)送WM_PAINT消息之前,總會(huì)發(fā)送一個(gè)WM_ERASEBKGND消息通知該窗口擦除背景,默認(rèn)情況下,Windows將以窗口的背景色清除該窗口。 可以響應(yīng)窗口(包括子元素)的WM_ERASEBKGND,以更改它們的背景。WM_ERASEBKGND的映射函數(shù)原型如下: afx_msg BOOL OnEraseBkgnd( CDC* pDC ); 返回值: 指定背景是否已清除,如果為FALSE,系統(tǒng)將自動(dòng)清除 參數(shù): pDC指定了繪制操作所使用的設(shè)備環(huán)境。 圖6是個(gè)簡(jiǎn)單的例子,通過OnEraseBkgnd為對(duì)話框加載了一副位圖背景:
圖6 利用WM_ ERASEBKGND消息美化界面 實(shí)現(xiàn)代碼也很簡(jiǎn)單:
同時(shí)別忘了響應(yīng)OnCtlColor,否則窗口里面的控件就不透明了。OnCtlColor的內(nèi)容,詳見3.3.3章節(jié)。
3.3.3 WM_CTLCOLOR
在控件顯示之前,每一個(gè)控件都會(huì)向父對(duì)話框發(fā)送一個(gè)WM_CTLCOLOR消息要求獲取繪制所需要的顏色。WM_CTLCOLOR消息缺省處理函數(shù)CWnd::OnCtlColor返回一個(gè)HBRUSH類型的句柄,這樣,就可以設(shè)置前景和背景文本顏色,并為控件或者對(duì)話框的非文本區(qū)域選定一個(gè)刷子。 WM_CTLCOLOR的映射函數(shù)原型如下: afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor ); 返回值: 用以指定背景的刷子 參數(shù): pDC指定了繪制操作所使用的設(shè)備環(huán)境。 pWnd 控件指針 nCtlColor 指定控件類型,其取值如表2所示: 類型值 含義 CTLCOLOR_BTN 按鈕控件 CTLCOLOR_DLG 對(duì)話框 CTLCOLOR_EDIT 編輯控件 CTLCOLOR_LISTBOX 列表框 CTLCOLOR_MSGBOX 消息框 CTLCOLOR_SCROLLBAR 滾動(dòng)條 CTLCOLOR_STATIC 靜態(tài)控件 表2 nCtlColor的類型值與含義 作為一個(gè)簡(jiǎn)單的例子,觀察以下的代碼:
生成的界面如下:
圖7 利用WM_CTLCOLOR消息美化界面 3.3.4 WM_DRAWITEM
OnCtlColor只能修改元素的顏色,但不能修改元素的界面框架,WM_DRAWITEM則可以。 當(dāng)一個(gè)具有Owner draw風(fēng)格的元素(包括按鈕、組合框、列表框和菜單等)需要顯示外觀時(shí),該元素會(huì)發(fā)送一條WM_DRAWITEM消息至它的隸屬窗口(Owner)。 WM_DRAWITEM的映射函數(shù)原型如下: afx_msg void OnDrawItem( int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct ); 參數(shù): nIDCtl 該控件的ID,如果該元素為菜單,則nIDCtl為0 lpDrawItemStruct 指向DRAWITEMSTRUCT結(jié)構(gòu)對(duì)象的指針,DRAWITEMSTRUCT的結(jié)構(gòu)定義如下:
CtlType指定了控件的類型,其取值如表3所示: 類型值 含義 ODT_BUTTON 按鈕控件 ODT_COMBOBOX 組合框控件 ODT_LISTBOX 列表框控件 ODT_LISTVIEW 列表視圖 ODT_MENU 菜單項(xiàng) ODT_STATIC 靜態(tài)文本控件 ODT_TAB Tab控件 表3 CtlType的類型值與含義 CtlID 指定自繪控件的ID值,該成員不適用于菜單項(xiàng) itemID表示菜單項(xiàng)ID,也可以表示列表框或者組合框中某項(xiàng)的索引值。對(duì)于一個(gè)空的列表框或組合框,該成員的值為?C1。這時(shí)應(yīng)用程序只繪制焦點(diǎn)矩形(該矩形的坐標(biāo)由rcItem 成員給出)雖然此時(shí)控件中沒有需要顯示的項(xiàng),但是繪制焦點(diǎn)矩形還是很有必要的,因?yàn)檫@樣做能夠提示用戶該控件是否具有輸入焦點(diǎn)。當(dāng)然也可以設(shè)置itemAction 成員為合適值,使得無需繪制焦點(diǎn)。 itemAction 指定繪制行為,其取值為表4中所示值的一個(gè)或者多個(gè)的聯(lián)合: 類型值 含義 ODA_DRAWENTIRE 當(dāng)整個(gè)控件都需要被繪制時(shí),設(shè)置該值。 ODA_FOCUS 如果控件需要在獲得或失去焦點(diǎn)時(shí)被繪制,則設(shè)置該值。此時(shí)應(yīng)該檢查itemState成員,以確定控件是否具有輸入焦點(diǎn)。 ODA_SELECT 如果控件需要在選中狀態(tài)改變時(shí)被繪制,則設(shè)置該值。此時(shí)應(yīng)該檢查itemState 成員,以確定控件是否處于選中狀態(tài)。 表4 itemAction的類型值與含義 itemState 指定了當(dāng)前繪制項(xiàng)的狀態(tài)。例如,如果菜單項(xiàng)應(yīng)該被灰色顯示,則可以指定ODS_GRAYED狀態(tài)標(biāo)志。其取值為表5中所示值的一個(gè)或者多個(gè)的聯(lián)合: 類型值 含義 ODS_CHECKED 標(biāo)記狀態(tài),僅適用于菜單項(xiàng)。 ODS_DEFAULT 默認(rèn)狀態(tài)。 ODS_DISABLED 禁止?fàn)顟B(tài)。 ODS_FOCUS 焦點(diǎn)狀態(tài)。 ODS_GRAYED 灰化狀態(tài),僅適用于菜單項(xiàng)。 ODS_SELECTED 選中狀態(tài)。 ODS_HOTLIGHT 僅適用于Windows 98/Me/Windows 2000/XP,熱點(diǎn)狀態(tài):如果鼠標(biāo)指針位于控件之上,則設(shè)置該值,這時(shí)控件會(huì)顯示高亮顏色。 ODS_INACTIVE 僅適用于Windows 98/Me/Windows 2000/XP,非激活狀態(tài)。 ODS_NOACCEL 僅適用于Windows 2000/XP,控件是否有快速鍵。 ODS_COMBOBOXEDIT 在自繪組合框控件中只繪制選擇區(qū)域。 ODS_NOFOCUSRECT 僅適用于Windows 2000/XP,不繪制捕獲焦點(diǎn)的效果。 表5 itemState的類型值與含義 hwndItem 指定了組合框、列表框和按鈕等自繪控件的窗口句柄;如果自繪的對(duì)象為菜單項(xiàng),則表示包含該菜單項(xiàng)的菜單句柄。 hDC 指定了繪制操作所使用的設(shè)備環(huán)境。 rcItem 指定了將被繪制的矩形區(qū)域。這個(gè)矩形區(qū)域就是上面hDC的作用范圍。系統(tǒng)會(huì)自動(dòng)裁剪組合框、列表框或按鈕等控件的自繪制區(qū)域以外的部分。也就是說rcItem中的坐標(biāo)點(diǎn)(0,0)指的就是控件的左上角。但是系統(tǒng)不裁剪菜單項(xiàng),所以在繪制菜單項(xiàng)的時(shí)候,必須先通過一定的換算得到該菜單項(xiàng)的位置,以保證繪制操作在我們希望的區(qū)域中進(jìn)行。 itemData 對(duì)于菜單項(xiàng),該成員的取值為由CMenu::AppendMenu、CMenu::InsertMenu、CMenu::ModifyMenu等函數(shù)傳遞給菜單的值。 對(duì)于列表框或這組合框,該成員的取值為由ComboBox::AddString、CComboBox::InsertString、CListBox::AddString或者CListBox::InsertString等函數(shù)傳遞給控件的值。 如果ctlType 的取值是ODT_BUTTON或者ODT_STATIC,itemData的取值為0。 圖5是個(gè)相應(yīng)的例子,它修改了按鈕的界面:
圖8 利用WM_DRAWITEM消息美化界面 實(shí)現(xiàn)代碼如下:
別忘了標(biāo)記Owner draw屬性:
圖9 指定按鈕的Owner draw屬性 值得一提的是,CWnd內(nèi)部截獲了WM_DRAWITEM、WM_MEASUREITEM等消息,并映射成子元素的相應(yīng)虛函數(shù)的調(diào)用,如CButton::DrawItem()。所以,以上例子也可以通過派生出一個(gè)CButton的派生類,并重載該類的DrawItem()函數(shù)來實(shí)現(xiàn)。使用虛函數(shù)機(jī)制實(shí)現(xiàn)界面美化參見3.4章節(jié)。
3.3.5 WM_MEASUREITEM
僅僅WM_DRAWITEM還是不夠的,對(duì)于一些特殊的控件,如ListBox,系統(tǒng)在發(fā)送WM_DRAWITEM消息前,還發(fā)送WM_MEASUREITEM消息,需要你設(shè)置ListBox中每個(gè)項(xiàng)目的高度。 WM_DRAWITEM的映射函數(shù)原型如下: afx_msg void OnMeasureItem( int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct ); nIDCtl 該控件的ID,如果該元素為菜單,則nIDCtl為0 lpMeasureItemStruct指向MEASUREITEMSTRUCT結(jié)構(gòu)對(duì)象的指針,MEASUREITEMSTRUCT的結(jié)構(gòu)定義如下:
CtlType指定了控件的類型,其取值如表6所示: 類型值 含義 ODT_COMBOBOX 組合框控件 ODT_LISTBOX 列表框控件 ODT_MENU 菜單項(xiàng) 表6 CtlType的類型值與含義 CtlID 指定自繪控件的ID值,該成員不適用于菜單項(xiàng) itemID表示菜單項(xiàng)ID,也可以表示可變高度的列表框或組合框中某項(xiàng)的索引值。該成員不適用于固定高度的列表框或組合框。 itemWidth 指定菜單項(xiàng)的寬度 itemHeight指定菜單項(xiàng)或者列表框中某項(xiàng)的的高度,最大值為255 itemData 對(duì)于菜單項(xiàng),該成員的取值為由CMenu::AppendMenu、CMenu::InsertMenu、CMenu::ModifyMenu等函數(shù)傳遞給菜單的值。 對(duì)于列表框或這組合框,該成員的取值為由ComboBox::AddString、CComboBox::InsertString、CListBox::AddString或者CListBox::InsertString等函數(shù)傳遞給控件的值。 圖示出了OnMeasureItem的效果:
圖10 利用WM_MEASUREITEM消息美化界面 相應(yīng)的OnMeasureItem()實(shí)現(xiàn)如下:
同樣別忘了指定列表框的Owner draw屬性:
圖11 指定下拉框的Owner draw屬性
3.3.6 NM_CUSTOMDRAW
大家也許熟悉WM_NOTIFY,控件通過WM_NOTIFY向父窗口發(fā)送消息。在WM_NOTIFY消息體中,部分控件會(huì)發(fā)送NM_CUSTOMDRAW告訴父窗口自己需要繪圖。 可以反射NM_CUSTOMDRAW消息,如: ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) afx_msg void OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult); 參數(shù): pNMHDR 說到底只是一個(gè)指針,大多數(shù)情況下它指向一個(gè)NMHDR結(jié)構(gòu)對(duì)象,NMHDR結(jié)構(gòu)如下:
其中: hwndFrom 發(fā)送方控件的窗口句柄 idFrom 發(fā)送方控件的ID code 通知代碼 對(duì)于某些控件來說,pNMHDR則會(huì)解釋成其它內(nèi)容更豐富的結(jié)構(gòu)對(duì)象的指針,如:對(duì)于列表控件來說,pNMHDR常常指向一個(gè)NMCUSTOMDRAW對(duì)象,NMCUSTOMDRAW結(jié)構(gòu)如下:
hdr NMHDR對(duì)象 dwDrawStage 當(dāng)前繪制狀態(tài),其取值如表7所示: 類型值 含義 CDDS_POSTERASE 擦除循環(huán)結(jié)束 CDDS_POSTPAINT 繪制循環(huán)結(jié)束 CDDS_PREERASE 準(zhǔn)備開始擦除循環(huán) CDDS_PREPAINT 準(zhǔn)備開始繪制循環(huán) CDDS_ITEM 指定dwItemSpec, uItemState, lItemlParam參數(shù)有效 CDDS_ITEMPOSTERASE 列表項(xiàng)擦除結(jié)束 CDDS_ITEMPOSTPAINT 列表項(xiàng)繪制結(jié)束 CDDS_ITEMPREERASE 準(zhǔn)備開始列表項(xiàng)擦除 CDDS_ITEMPREPAINT 準(zhǔn)備開始列表項(xiàng)繪制 CDDS_SUBITEM 指定列表子項(xiàng) 表7 dwDrawStage的類型值與含義 hdc指定了繪制操作所使用的設(shè)備環(huán)境。 rc指定了將被繪制的矩形區(qū)域。 dwItemSpec 列表項(xiàng)的索引 uItemState 當(dāng)前列表項(xiàng)的狀態(tài),其取值如表8所示: 類型值 含義 CDIS_CHECKED 標(biāo)記狀態(tài)。 CDIS_DEFAULT 默認(rèn)狀態(tài)。 CDIS_DISABLED 禁止?fàn)顟B(tài)。 CDIS_FOCUS 焦點(diǎn)狀態(tài)。 CDIS_GRAYED 灰化狀態(tài)。 CDIS_SELECTED 選中狀態(tài)。 CDIS_HOTLIGHT 熱點(diǎn)狀態(tài)。 CDIS_INDETERMINATE 不定狀態(tài)。 CDIS_MARKED 標(biāo)注狀態(tài)。 表8 uItemState的類型值與含義 lItemlParam 當(dāng)前列表項(xiàng)的綁定數(shù)據(jù) pResult 指向狀態(tài)值的指針,指定系統(tǒng)后續(xù)操作,依賴于dwDrawStage: 當(dāng)dwDrawStage為CDDS_PREPAINT,pResult含義如表9所示: 類型值 含義 CDRF_DODEFAULT 默認(rèn)操作,即系統(tǒng)在列表項(xiàng)繪制循環(huán)過程不再發(fā)送NM_CUSTOMDRAW。 CDRF_NOTIFYITEMDRAW 指定列表項(xiàng)繪制前后發(fā)送消息。 CDRF_NOTIFYPOSTERASE 列表項(xiàng)擦除結(jié)束時(shí)發(fā)送消息。 CDRF_NOTIFYPOSTPAINT 列表項(xiàng)繪制結(jié)束時(shí)發(fā)送消息。 表9 pResult的類型值與含義(一) 當(dāng)dwDrawStage為CDDS_ITEMPREPAINT,pResult含義如表10所示: 類型值 含義 CDRF_NEWFONT 指定后續(xù)操作采用應(yīng)用中指定的新字體。 CDRF_NOTIFYSUBITEMDRAW 列表子項(xiàng)繪制時(shí)發(fā)送消息。 CDRF_SKIPDEFAULT 系統(tǒng)不必再繪制該子項(xiàng)。 表10 pResult的類型值與含義(二) 以下是一個(gè)利用NM_CUSTOMDRAW消息繪制出的多色列表框的例子:
圖12 利用NM_CUSTOMDRAW消息美化界面 對(duì)應(yīng)代碼如下:
注意到上例采取了3.1所推薦的第2種實(shí)現(xiàn)方法,派生了一個(gè)新類CCoolList。
3.4 使用MFC類的虛函數(shù)機(jī)制
修改Windows界面,除了從Windows消息機(jī)制下功夫,也可以從MFC類下功夫,這應(yīng)該得益于類的虛函數(shù)機(jī)制。為了防止諸如“面向?qū)ο蠹夹g(shù)”等術(shù)語在此泛濫,以下僅舉一段代碼作為例子:
這是MFC中viewcore.cpp中的源代碼,很多讀者總不明白OnDraw()和OnPaint()之間的關(guān)系,從以上的代碼中很容易看出,CView的WM_PAINT消息響應(yīng)函數(shù)OnPaint()會(huì)自動(dòng)調(diào)用CView::OnDraw()。而作為開發(fā)者的用戶,可以通過簡(jiǎn)單的OnDraw()的重載實(shí)現(xiàn)對(duì)WM_PAINT的處理。所以說,對(duì)MFC類的虛函數(shù)的重載是對(duì)消息機(jī)制的擴(kuò)展。 以下列出了與界面美化相關(guān)的虛函數(shù),參數(shù)說明略去: CButton::DrawItem CCheckListBox::DrawItem CComboBox::DrawItem CHeaderCtrl::DrawItem CListBox::DrawItem CMenu::DrawItem CStatusBar::DrawItem CStatusBarCtrl::DrawItem CTabCtrl::DrawItem virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ); Owner draw元素自繪函數(shù) 很顯然,位圖菜單都是通過這個(gè)DrawItem畫出來的。限于篇幅,在此不再附以例程。
本文為白喬原創(chuàng),曾經(jīng)在《電腦愛好者》合訂本上發(fā)表。 |
|
|