應用程序中的菜單,在界面中占據了重要位置,它的效果如何,直接影響了整個程序的界面效果,正因為這個原因,當今流行的應用程序的菜單都支持附帶圖標、反映當前狀態的功能,也就是說,菜單項上不再僅僅有文字,還有附帶一個小小的圖標,同時在用戶操作菜單時,菜單能夠以不同的狀態反映用戶的操作,這些功能的實現,可以大大,美化程序界面,增強程序的吸引力。
Visual C++為開發人員提供了應用程序自動生成方法,使開發人員可以避開繁瑣的界面開發,專著於各種功能的具體實現。雖然這種自動生成應用程序框架的手段方便了初級程序設計人員,但是它也有著先天的不足,那就是通過這種手段生成的應用程序界面比較簡單,不適宜應用程序的推廣。對於Visual C++自動生成的框架程序,要實現菜單的上述功能,唯一的辦法就是通過菜單自繪的手段來實現,本例將通過實現CMenu類的子類CmenuEx類,詳細介紹了如何編程實現菜單的自繪,使應用程序中的菜單也可以擁有圖標,並且能夠反映用戶的不同操作,下圖為程序編譯運行後的效果:
一、實現方法:
要實現漂亮的界面菜單,必須要啟動菜單項的自繪功能,所謂菜單的自繪,就是讓菜單自己管理自己的顯示效果,為此,首先要作的就是設置菜單項的風格為MF_OWNERDRAW(自繪制),設置菜單的自繪功能即可以通過CMenu類的AppendMenu()函數在菜單的初始階段實現,也可以通過ModifyMenu()函數對已存在的菜單項進行類型修改。
具體的菜單的自繪是通過重載CMenu類的DrawItem()函數來實現的,這個函數根據各種菜單狀態,處理當前菜單項中菜單圖標、文字顯示的功能。DrawItem()函數的原形為:virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ),它參數為一個指向DRAWITEMSTRUCT結構的指針,DRAWITEMSTRUCT結構如下:
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType; //控件類型;
UINT CtlID; //組合框、列表框等控件的ID標識號;
UINT itemID; //菜單項的ID標識號或列表框、組合框中某一項的索引值;
UINT iteMaction; //控件行為;
UINT itemState; //控件狀態;
HWND hwndItem; //父窗口句柄或菜單句柄
HDC hDC; //控件對應的繪圖設備句柄
RECT rcItem; //控件所占據的矩形區域
DWord itemData; //列表框或組合框中某一項的值
}
可以看出,上面的DRAWITEMSTRUCT結構包含了控件自繪時的各種信息。
其中,結構成員CtlType指定了控件的類型,其取值ODT_BUTTON表示按鈕控件;ODT_COMBOBOX表示組合框控件;ODT_LISTBOX表示列表框控件;ODT_LISTVIEW表示列表視圖控件;ODT_MENU菜單項;ODT_STATIC表示靜態文本控件;ODT_TAB表示Tab控件。CtlID指定了自繪控件的ID值,而對於菜單項則不需要使用該成員。itemID表示菜單項ID,也可以表示列表框或者組合框中某項的索引值,對於一個空的列表框或組合框,該成員的值為-1。
iteMaction指定繪制行為,其取值可以為下表中所示值的一個或者多個的聯合:ODA_DRAWENTIRE表示整個控件都需要被繪制;ODA_FOCUS表示控件需要在獲得或失去焦點時被繪制;ODA_SELECT表示控件需要在選中狀態改變時被繪制。
itemState指定了當前繪制操作時所繪項的狀態,例如,如果菜單項應該被灰色顯示,則可以指定ODS_GRAYED狀態標志。其取值可以為下表中所示值的一個或者多個的聯合:ODS_CHECKED表示菜單項將被選中,該值只對菜單項有用;
ODS_COMBOBOXEDIT在自繪組合框控件中只繪制選擇區域;
ODS_DEFAULT表示當前控件處於默認狀態;
ODS_DISABLED表示控件將被禁止;
ODS_FOCUS表示控件需要輸入焦點;
ODS_GRAYED表示控件需要被灰色顯示,該值只在繪制菜單時使用;
ODS_HOTLIGHT表示鼠標指針位於控件之上時控件會顯示高亮顏色(支持Windows 98/Me, Windows 2000/XP);
ODS_SELECTED表示選中控件;hwndItem 指定了組合框、列表框和按鈕等自繪控件的窗口句柄;如果自繪的對象時菜單項,則表示包含該菜單項的菜單句柄。hDC指定了繪制操作所使用的設備環境。 rcItem指定了將被繪制的矩形區域。這個矩形區域就是上面hDC的作用范圍。系統會自動裁剪組合框、列表框或按鈕等控件的自繪制區域以外的部分。也就是說rcItem中的坐標點(0,0)指的就是控件的左上角。但是系統不裁剪菜單項,所以在繪制菜單項的時候,必須先通過一定的換算得到該菜單項的位置,以保證繪制操作在我們希望的區域中進行。
itemData這個成員變量最為關鍵,菜單自繪時所需要的圖標、文本等信息都是通過它獲取的,至於它的具體值,是通過CMenu類的CMenu::AppendMenu()、CMenu::InSertMenu()、CMenu::ModifMenu()等函數的調用來傳遞的。
菜單自繪僅僅重載CMenu::DrawItem()函數是不夠的,還需要重載CMenu:: MeasureItem()函數,在這個函數裡面填充MEASUREITEMSTRUCT結構,通知Windows自繪控件的尺寸。該函數的原形為:
virtual void MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct );
該函數的參數為一個指向MEASUREITEMSTRUCT結構的指針對象,該對象結構為:
typedef struct tagMEASUREITEMSTRUCT {
UINT CtlType; //控件類型;
UINT CtlID; //控件的ID識別號,它不包括菜單控件;
UINT itemID; //菜單項的ID識別號;
UINT itemWidth; //菜單項的寬度;
UINT itemHeight; //菜單項的高度;
DWord itemData //自繪控件所需要的數據,
} MEASUREITEMSTRUCT;
上面這個結構中,成員變量CtlType等於ODT_COMBOBOX 時,表示當前控件為自繪型的組合框,等於ODT_LISTBOX 時表示當前控件為自繪列表控制件,等於ODT_MENU 時表示當前控件為自繪菜單。對於組合框和列表框控件,成員變量itemData是通過相應的AddString()、InsertString()獲取的, 對於菜單控件,成員變量itemData與DRAWITEMSTRUCT結構中的itemData是一致的。
菜單自繪時所需要的圖標資源,可以預先定義,使用時直接裝載,但是這種方法比較呆板,另外一種方法是通過搜索狀態條上的按鈕信息,如果當前按鈕的ID識別號與某一菜單項的ID識別號一致,那末通過就將該按鈕上的圖標提取出來,作為菜單圖標,如果菜單項與工具條上的所有按鈕的ID都不相同,那麼對該菜單項不畫圖標。至於菜單的文本信息,直接采用用戶自定義菜單的文本就可以了。
根據上面介紹的知識,本例定義一個CMemu類的子類CMemuEx類來實現菜單的自繪功能,該類不僅支持在菜單中顯示圖標、即時反映當前菜單項狀態的功能,還支持在菜單中添加縱向位圖。在具體實現過程中,CMemuEx類除了上述介紹的需要重載的DrawItem()、MeasureItem()等函數外,另外的一些主要成員函數如下:
1、void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar);
說明:這是CMenuEx類最主要的一個接口。該函數根據狀態欄信息來初始化CMenuEx類;
2、void SetImageLeft(UINT idBmpLeft);
說明:這也是CMenuEx類中的一個重要的接口。調用CMenuEx類對象的SetImageLeft()可以實現菜單中的縱向位圖(像Windows系統中的"開始"菜單),調用該函數時參數是位圖的ID值。需要注意的是,目前CMenuEx類實現的是對主框架菜單設置縱向位圖,對上下文菜單不適用,讀者朋友可以稍加修改,自由的決定對何種菜單設置縱向位圖。
3、void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar)
說明:這個函數是為了處理上下文菜單的自繪而編寫的,CMenuEx類的任一實例都只能調用InitMenu()、InitPopupMenu()這兩個成員函數中的一個,不能一同使用。
4、int GetImageFromToolBar(UINT uToolBar, CToolBar *pToolBar,COLORREF crMask)
說明
[1] [2] [3] [4] [5] 下一頁