中山百度網(wǎng)站排名網(wǎng)頁百度網(wǎng)盤
6.1? ?菜 單 設(shè)計(jì)
菜單是可視化編程的重要組成部分,是一種方便地給命令分組并訪問這些命令的方法。菜單通常用來顯示程序的各項(xiàng)功能,以方便用戶選擇執(zhí)行,通過對菜單命令進(jìn)行編程可以調(diào)用程序中相應(yīng)的功能。
?
6.1.1? ?菜單類CMenu
在MFC中,CMenu類封裝了Windows的菜單功能,它提供了多個(gè)方法用于創(chuàng)建、修改、合并菜單。CMenu類的主要方法如下。
(1)Attach方法:該方法用于將句柄關(guān)聯(lián)到菜單對象上。語法如下:
BOOL Attach( HMENU hMenu );
參數(shù)說明
?
返回值:為非零,表示執(zhí)行成功,否則執(zhí)行失敗。
(2)Detach方法:該方法從菜單對象上分離菜單句柄。語法如下:
HMENU Detach( );
返回值:分離的菜單句柄。
(3)FromHandle方法:該方法根據(jù)菜單句柄返回一個(gè)菜單對象指針,如果句柄沒有關(guān)聯(lián)的菜單對象,則一個(gè)臨時(shí)的菜單對象指針將要被創(chuàng)建。語法如下:
static CMenu* PASCAL FromHandle( HMENU hMenu );
參數(shù)說明
?
返回值:菜單對象指針。
(4)CreateMenu方法:該方法用于創(chuàng)建一個(gè)菜單窗口,并將其關(guān)聯(lián)到菜單對象上。語法如下:
?
返回值:執(zhí)行成功,返回值為非零,否則為零。
(5)CreatePopupMenu方法:該方法用于創(chuàng)建一個(gè)彈出式菜單窗口,并將其關(guān)聯(lián)到菜單對象上。語法如下:
BOOL CreatePopupMenu( );
返回值:執(zhí)行成功,返回值為非零,否則為零。對于彈出式菜單,如果菜單窗口被釋放,菜單對象將要被自動釋放。
(6)LoadMenu方法:該方法從應(yīng)用程序的可執(zhí)行文件中加載一個(gè)菜單資源,將其關(guān)聯(lián)到菜單對象上。語法如下:
BOOL LoadMenu( LPCTSTR lpszResourceName );
BOOL LoadMenu( UINT nIDResource );
參數(shù)說明
?
?
返回值:執(zhí)行成功,返回值為非零,否則為零。
(7)DestroyMenu方法:該方法用于釋放菜單窗口,當(dāng)菜單窗口被釋放前,它將從菜單對象上分離出來。語法如下:
BOOL DestroyMenu( );
?
(8)DeleteMenu方法:該方法用于從菜單中刪除一個(gè)菜單項(xiàng)。語法如下:
BOOL DeleteMenu( UINT nPosition, UINT nFlags );
參數(shù)說明
?
?
?
?
(9)TrackPopupMenu方法:該方法用于顯示一個(gè)彈出式菜單。語法如下:
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );
參數(shù)說明
?
?
?
?
?
?
?
?
?
?
(10)AppendMenu方法:該方法用于在菜單項(xiàng)的末尾添加一個(gè)新菜單。語法如下:
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
參數(shù)說明
?
?
?
?
(11)CheckMenuItem方法:該方法用于在彈出的菜單項(xiàng)中放置或刪除標(biāo)記。語法如下:
UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );
參數(shù)說明
?
?
?
?
?
?
(12)CheckMenuRadioItem方法:該方法用于將單選按鈕放置在菜單項(xiàng)之前,或從組中所有的其他菜單項(xiàng)中刪除單選按鈕。語法如下:
BOOL CheckMenuRadioItem( UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags );
參數(shù)說明
?
?
?
?
?
?
(13)EnableMenuItem方法:該方法用于設(shè)置菜單項(xiàng)有效、無效或變灰。語法如下:
UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );
參數(shù)說明
?
?
?
?
?
?
?
(14)GetMenuItemCount方法:該方法用于返回彈出式菜單或頂層菜單的菜單數(shù)。語法如下:
UINT GetMenuItemCount( ) const;
返回值:如果菜單項(xiàng)沒有子菜單,函數(shù)返回值為-1,否則返回子菜單數(shù)。
(15)GetMenuItemID方法:該方法根據(jù)菜單項(xiàng)的位置返回菜單ID,如果菜單項(xiàng)是一個(gè)彈出式菜單,返回值為-1;如果菜單項(xiàng)是一個(gè)分隔條,返回值為0。語法如下:
UINT GetMenuItemID( int nPos ) const;
參數(shù)說明
?
(16)GetMenuState方法:該方法用于返回指定菜單項(xiàng)的狀態(tài)或彈出菜單的項(xiàng)數(shù)。語法如下:
UINT GetMenuState( UINT nID, UINT nFlags ) const;
參數(shù)說明
?
?
(17)GetMenuString方法:該方法用于設(shè)置菜單項(xiàng)的文本。語法如下:
int GetMenuString( UINT nIDItem, LPTSTR lpString, int nMaxCount, UINT nFlags ) const;
int GetMenuString( UINT nIDItem, CString& rString, UINT nFlags ) const;
參數(shù)說明
?
?
?
?
?
返回值:實(shí)際復(fù)制到緩沖區(qū)中的字符數(shù)。
(18)GetSubMenu方法:該方法用于獲取彈出式菜單中的一個(gè)菜單項(xiàng)。語法如下:
CMenu* GetSubMenu( int nPos ) const;
參數(shù)說明
?
(19)InsertMenu方法:該方法用于向菜單中指定位置插入菜單項(xiàng)。語法如下:
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem
= NULL );
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
參數(shù)說明
?
?
?
?
?
?
?
(20)ModifyMenu方法:該方法用于修改菜單項(xiàng)信息。語法如下:
BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem
= NULL );
BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
參數(shù)說明
?
?
?
?
?
?
?
(21)RemoveMenu方法:該方法用于移除一個(gè)菜單項(xiàng)。語法如下:
BOOL RemoveMenu( UINT nPosition, UINT nFlags );
參數(shù)說明
?
?
?
?
(22)DrawItem方法:該方法是一個(gè)虛方法,用戶可以改寫該方法實(shí)現(xiàn)菜單的繪制。語法如下:
virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
參數(shù)說明
?
?
6.1.2? ?菜單資源設(shè)計(jì)
下面來介紹如何設(shè)計(jì)菜單資源,步驟如下。
(1)在工作區(qū)窗口中選擇資源視圖(ResourceView),鼠標(biāo)右鍵單擊一個(gè)節(jié)點(diǎn),在彈出的快捷菜單中選擇“Insert”菜單項(xiàng),將打開“Insert Resource”對話框,如圖6.1所示。
?
圖6.1 “Insert Resource”對話框
(2)在資源類型列表中選擇“Menu”節(jié)點(diǎn),單擊“New”按鈕,將創(chuàng)建一個(gè)菜單,如圖6.2所示。
?
圖6.2?
(3)在菜單設(shè)計(jì)窗口中按<Enter>鍵打開“Menu Item Properties”窗口,在“Caption”編輯框中設(shè)計(jì)菜單標(biāo)題,如圖6.3所示。
?
圖6.3 “Menu Item Properties”窗口
(4)關(guān)閉“Menu Item Properties”窗口,返回到菜單設(shè)計(jì)窗口中,如圖6.4所示。
?
圖6.4?
(5)在新建菜單下的虛線框上按<Enter>鍵打開“Menu Item Properties”窗口可以添加子菜單,在“Menu Item Properties”窗口中設(shè)置子菜單ID和菜單的標(biāo)題,如圖6.5所示。
?
圖6.5?
如果用戶不設(shè)置菜單ID,系統(tǒng)將會自動生成一個(gè)ID。
(6)關(guān)閉菜單屬性窗口,返回到菜單設(shè)計(jì)窗口中,如圖6.6所示。
?
(7)如果想要設(shè)計(jì)一個(gè)級聯(lián)菜單,可以在菜單項(xiàng)的屬性窗口中選中Pop-up復(fù)選框,這樣,在菜單項(xiàng)的右側(cè)將顯示一個(gè)箭頭,效果如圖6.7所示。
?
?
?
(8)打開級聯(lián)子菜單的屬性窗口,設(shè)置菜單ID和菜單標(biāo)題。重復(fù)以上的步驟就可以設(shè)置需要的菜單。
?
?
6.1.3? ?菜單的顯示與命令處理
在編輯了菜單資源之后運(yùn)行程序并不能顯示菜單,要顯示菜單,還需要將菜單和對話框資源進(jìn)行關(guān)聯(lián),打開“Dialog Properties”窗口,在“General”選項(xiàng)卡的“Menu”列表框中選擇要進(jìn)行關(guān)聯(lián)的菜單ID,如圖6.8所示。
?
圖6.8?
現(xiàn)在運(yùn)行程序就包含菜單了。但是想要用菜單進(jìn)行對程序的操作還需要設(shè)置菜單的命令處理。打開類向?qū)?#xff0c;選擇“Message Maps”選項(xiàng)卡,在“Class name”列表框選擇關(guān)聯(lián)菜單的對話框類,在“Object Ids”列表中選擇菜單項(xiàng)ID,在“Messages”列表中選擇“COMMAND”項(xiàng),如圖6.9所示。
?
圖6.9?
單擊“Add Function…”按鈕,彈出“Add Member Function”對話框,并給出默認(rèn)時(shí)的命令處理函數(shù)名,如圖6.10所示。
?
圖6.10 “Add Member Function”對話框
單擊“OK”按鈕,就添加了菜單項(xiàng)的命令處理函數(shù)。再單擊“Edit Code”按鈕可以打開添加的OnMenumessage命令處理函數(shù),在這里可以編輯菜單項(xiàng)的功能,代碼如下:
void CMenuDlg::OnMenumessage()
{
?
?
}
?
6.1.4? ?動態(tài)創(chuàng)建菜單
在前面已經(jīng)介紹了如何編輯菜單資源以及通過類向?qū)Ь帉懖藛蔚拿钐幚砗瘮?shù)。本節(jié)將通過CMenu類實(shí)現(xiàn)動態(tài)創(chuàng)建菜單。
(1)創(chuàng)建一個(gè)基于對話框的應(yīng)用程序。
(2)向?qū)υ捒蛑刑砑右粋€(gè)按鈕控件。
(3)在主窗口的頭文件中聲明一個(gè)CMenu類對象,代碼如下:
CMenu m_Menu;
(4)在資源頭文件(Resource.h)中定義命令I(lǐng)D,如圖6.11所示。
?
圖6.11?
(5)在主窗口的頭文件中添加消息處理函數(shù),如圖6.12所示。
?
圖6.12?
(6)在主窗口的源文件中添加消息映射宏,將命令I(lǐng)D關(guān)聯(lián)到消息處理函數(shù)中,如圖6.13所示。
(7)在主窗口的源文件中添加消息處理函數(shù)的實(shí)現(xiàn)代碼,代碼如下:
?
圖6.13?
void CDynamicMenuDlg::OnMenured()
{
?
}
void CDynamicMenuDlg::OnMenublue()
{
?
}
void CDynamicMenuDlg::OnMenugreen()
{
?
}
MessageBox函數(shù)用來彈出一個(gè)消息框,該函數(shù)有3個(gè)參數(shù),第一個(gè)參數(shù)表示消息框顯示的文本,第二個(gè)參數(shù)表示消息框的標(biāo)題,默認(rèn)為可執(zhí)行文件名,第3個(gè)參數(shù)表示消息框顯示的按鈕風(fēng)格和圖標(biāo)風(fēng)格的組合。
(8)處理“創(chuàng)建”按鈕的消息響應(yīng)事件,用代碼創(chuàng)建菜單,代碼如下:
void CDynamicMenuDlg::OnButton1()
{
?
?
?
?
?
?
?
?
?
}
SetMenu函數(shù)用于分配一個(gè)菜單到指定窗口。
程序運(yùn)行結(jié)果如圖6.14所示。
?
圖6.14?
6.1.5? ?菜單項(xiàng)的更新機(jī)制
在使用類向?qū)椴藛翁砑用钐幚砗瘮?shù)時(shí),發(fā)現(xiàn)菜單除了COMMAND消息外,還有一個(gè)UPDATE_COMMAND_UI消息,該消息是“更新命令用戶接口消息”。菜單項(xiàng)的狀態(tài)維護(hù)就依賴于UPDATE_COMMAND_UI消息。下面就來看看如何使用這個(gè)消息。
打開一個(gè)基于單文檔的應(yīng)用程序,運(yùn)行程序后發(fā)現(xiàn)“編輯”菜單下的菜單項(xiàng)都不可用,如圖6.15所示。
如果想要使“編輯”菜單下的菜單項(xiàng)都可用,就要為相應(yīng)的菜單項(xiàng)處理UPDATE_ COMMAND_UI消息。以“撤銷”菜單項(xiàng)為例,打開類向?qū)?#xff0c;選擇“Message Maps”選項(xiàng)卡,在“Class name”列表框中選擇“CmainFrame”類,在“Object Ids”列表中選擇“撤銷”菜單項(xiàng)“ID_EDIT_UNDO”,在“Messages”列表中選擇“UPDATE_COMMAND_UI”項(xiàng),單擊“Add Function…”按鈕,創(chuàng)建該消息的處理函數(shù),如圖6.16所示。
?
圖6.16?
單擊“Edit Code”按鈕,定位到新建的消息處理函數(shù),在函數(shù)中添加代碼使“撤銷”菜單項(xiàng)可用,代碼如下:
void CMainFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
{
?
}
?
程序運(yùn)行結(jié)果如圖6.17所示。
?
圖6.17?
通過上面的運(yùn)行結(jié)果發(fā)現(xiàn)“撤銷”菜單項(xiàng)已經(jīng)可用,這是因?yàn)镸FC菜單項(xiàng)的命令更新機(jī)制,當(dāng)要顯示菜單時(shí),系統(tǒng)發(fā)出WM_INITMENUPOPUP消息,該消息被程序窗口的基類接管后會創(chuàng)建一個(gè)CCmdUI對象,CCmdUI對象會與程序的第一個(gè)菜單項(xiàng)相關(guān)聯(lián),并調(diào)用CCmdUI對象的DoUpdate方法發(fā)送UPDATE_COMMAND_UI消息,系統(tǒng)會查找是否有捕獲這個(gè)消息的宏,如果找到就調(diào)用相應(yīng)的函數(shù)進(jìn)行處理。當(dāng)這個(gè)菜單項(xiàng)更新后,CCmdUI對象會繼續(xù)對下一個(gè)菜單項(xiàng)進(jìn)行關(guān)聯(lián),直到完成所有菜單項(xiàng)的處理。
6.1.6? ?自繪彈出式菜單
在Windows操作系統(tǒng)中單擊鼠標(biāo)右鍵彈出的菜單稱為彈出式菜單。彈出式菜單可以方便用戶的操作,下面來介紹一下如何創(chuàng)建彈出式菜單。
(1)創(chuàng)建一個(gè)基于對話框的程序。
(2)向工程中導(dǎo)入兩個(gè)位圖,將資源ID改為IDB_LEFTBITMAP和IDB_ITEMBITMAP。
(3)以CMenu類為基類派生一個(gè)CCustomMenu類,定義一個(gè)記錄菜單項(xiàng)信息的數(shù)據(jù)結(jié)構(gòu)CMenuItemInfo,代碼如下:
struct CMenuItemInfo
{
?
?
?
};
(4)在CCustomMenu類的頭文件中聲明如下變量:
CMenuItemInfo m_ItemLists[10]; //菜單項(xiàng)信息
int?
CFont?
(5)在CCustomMenu類中添加DrawMenuTitle方法,用于繪制菜單左側(cè)標(biāo)題,代碼如下:
void CCustomMenu::DrawMenuTitle(CDC *pDC, CRect rect, CString title)
{
?
?
?
?
?
?
?
?
?
?
?
?
?
}
通過LoadBitmap方法裝載位圖IDB_LEFTBITMAP,再使用StretchBlt方法將位圖繪制成標(biāo)題大小。
(6)在CCustomMenu類中添加DrawItemText方法,用于繪制菜單項(xiàng)文本,代碼如下:
void CCustomMenu::DrawItemText(CDC *pDC, LPSTR str, CRect rect)
{
?
?
}
(7)在CCustomMenu類中添加DrawSeparater方法,用于繪制分隔條,代碼如下:
void CCustomMenu::DrawSeparater(CDC *pDC, CRect rect)
{
?
?
?
?
?
}
(8)在CCustomMenu類中添加ChangeMenuItem方法,用于修改菜單項(xiàng)信息,代碼如下:
BOOL CCustomMenu::ChangeMenuItem(CMenu *menu)
{
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
}
(9)改寫CMenu類的MeasureItem方法,設(shè)置菜單項(xiàng)大小,代碼如下:
void CCustomMenu::MeasureItem(LPMEASUREITEMSTRUCT lpStruct)
{
?
?
?
?
?
?
?
?
?
?
?
}
(10)改寫CMenu類的DrawItem方法,重繪菜單項(xiàng),代碼如下:
void CCustomMenu::DrawItem(LPDRAWITEMSTRUCT lpStruct)
{
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
}
(11)在主窗口的頭文件中聲明一個(gè)CCustomMenu類對象m_Menu,在主窗口的OnInit Dialog函數(shù)中添加如下代碼,裝載菜單資源。
m_Menu.LoadMenu(IDR_MENU1); //裝載菜單資源
m_Menu.ChangeMenuItem(&m_Menu);
(12)處理主窗口的WM_CONTEXTMENU消息,彈出菜單,代碼如下:
void CPopMenuDlg::OnContextMenu(CWnd* pWnd, CPoint point)
{
?
?
}
當(dāng)鼠標(biāo)右鍵單擊程序窗口的客戶區(qū)時(shí),程序會收到一條WM_CONTEXTMENU消息,在該消息的處理函數(shù)中使用TrackPopupMenu方法彈出菜單。
(13)在主窗口中添加消息WM_DRAWITEM和WM_MEASUREITEM,并分別在這兩個(gè)消息的處理函數(shù)中調(diào)用m_Menu對象的DrawItem方法和MeasureItem方法。
菜單欄
在對話框窗口里顯示菜單欄
像工具欄一樣,菜單欄在按件面板里沒有對應(yīng)的選項(xiàng),但有一個(gè)菜單控件類CMenu,所以如果想要在對話框里顯示菜單欄,就得像工具欄那樣,到ResourceView選項(xiàng)卡里新建一個(gè)菜單欄資源,步驟跟新建工具欄資源一樣,只是資源類型是:Menu,菜單資源設(shè)計(jì)如下圖:
如果想改菜單項(xiàng)文本內(nèi)容的話,方法是右擊要更改的菜單項(xiàng),選擇屬性,接著會彈出這樣一個(gè)對話框:
上面那個(gè)ID項(xiàng)就是該菜單項(xiàng)對應(yīng)的ID號了,添加菜單項(xiàng)單擊消息處理函數(shù)時(shí)會用到,而標(biāo)明項(xiàng)里的內(nèi)容就是菜單項(xiàng)要顯示的文本了。
這里還得注意一下“彈出”這個(gè)選項(xiàng),勾上這個(gè)選項(xiàng)表明對應(yīng)的菜單項(xiàng)還有下級菜單,如:
上面“轉(zhuǎn)到”這個(gè)菜單項(xiàng)具有彈出屬性,有下級菜單
設(shè)計(jì)好了菜單資源,接著我們就來在對話框顯示菜單欄吧,方法是進(jìn)入對話框編輯區(qū),右擊對話框界面,選擇屬性,然后在菜單項(xiàng)里選擇菜單資源ID號,回車,編譯,運(yùn)行,效果如下圖:
當(dāng)然還有第二種在對話框顯示菜單的方法:調(diào)用SetMenu函數(shù)把菜單跟對話框關(guān)聯(lián)起來,函數(shù)第一個(gè)參數(shù)是窗口句柄,第二個(gè)參數(shù)是菜單句柄。在On
?CMenu menu;//定義一個(gè)菜單類變量
?menu.LoadMenu(IDR_MENU1);//裝載IDR_MENU1菜單資源
?SetMenu(&menu);//和當(dāng)前窗口關(guān)聯(lián)起來
?menu.Detach();//分離
如果要處理菜單項(xiàng)單擊消息的話,方法跟處理工具欄項(xiàng)單擊消息一樣,進(jìn)入類向?qū)?#xff0c;找到對應(yīng)的菜單項(xiàng)ID,為它添加COMMAND消息處理函數(shù)。
?設(shè)置菜單左邊顯示位圖和背景位圖
CMenu類里要了解的函數(shù)
SetMenuItemBitmaps//設(shè)置菜單項(xiàng)左邊的位圖
函數(shù)定義:BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked );
nPostion指明具體要設(shè)置的菜單項(xiàng),可以是菜單項(xiàng)索引,菜單項(xiàng)ID,具體由nFlags參數(shù)指明,為MF_BYPOSITION,則以菜單項(xiàng)索引指明,
為MF_BYCOMMAND則第一個(gè)參數(shù)nPosition是菜單項(xiàng)ID號。pBmpUnchecked未被檢測時(shí)顯示的位圖(正常時(shí)),pBmpChecked檢測時(shí)顯示的圖片(就是菜單項(xiàng)被打上勾時(shí)所顯示的圖片,跟CheckMenuItem函數(shù)有關(guān)聯(lián))
一個(gè)API函數(shù)SetMenuInfo,這個(gè)函數(shù)可以設(shè)置菜單重繪時(shí)選擇的填充畫刷類型,該函數(shù)有兩個(gè)參數(shù),第一個(gè)是要設(shè)置的菜單句柄,第二個(gè)是一個(gè)MENUINFO結(jié)構(gòu)指針,我們只要了解這結(jié)構(gòu)里有一個(gè)成員hbrBack就行了,hbrBack是一個(gè)畫刷句柄,而菜單背景位圖就通過創(chuàng)建位圖畫刷來實(shí)現(xiàn)的。
好了,以上面的工程為例,引入三張位圖,ID號默認(rèn)不變,然后再引入一張位圖(菜單背景位圖,ID:IDB_MENUBACK),接著在對話框類的On
CMenu *pMenu=GetMenu();//獲取對話框關(guān)聯(lián)菜單
?CMenu *pSubMenu=pMenu->GetSubMenu(0);//獲得子菜單(如果有)0表示索引,對應(yīng)“文件”菜單
?for(int i=0;i<3;i++)
?{
???? CBitmap bmp;
???? bmp.LoadBitmap(IDB_BITMAP1+i);
? pSubMenu->SetMenuItemBitmaps(i,MF_BYPOSITION,&bmp,&bmp);
??bmp.Detach();
?}
?CBitmap bmp;
?CBrush m_BKBrush;
?bmp.LoadBitmap(IDB_MENUBACK);
?m_BKBrush.CreatePatternBrush(&bmp);//創(chuàng)建位圖畫刷
?MENUINFO mnInfo;
?memset(&mnInfo,0,sizeof(MENUINFO));
?mnInfo.cbSize=sizeof(MENUINFO);
?mnInfo.fMask=MIM_BACKGROUND;
?mnInfo.hbrBack=m_BKBrush;
?::SetMenuInfo(pSubMenu->m_hMenu,&mnInfo);
??? m_BKBrush.Detach();
運(yùn)行效果如下圖:
(PS:不知道大家有沒有碰到過這個(gè)問題,MENUINFO結(jié)構(gòu)未定義,解決的方法是進(jìn)入文件選項(xiàng)卡(FileView),在Source? File文件下的StdAfx.cpp文件里的最前面部分添加這個(gè)語句:#define? WINVER 0x0501)
設(shè)計(jì)彈出式菜單
?CMenu類里要了解的函數(shù):
TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd,LPCRECT lpRect = NULL );
該函數(shù)用于在界面顯示菜單,nFlags參數(shù)指明菜單顯示標(biāo)志,x,y用于確定菜單顯示基于的坐標(biāo)點(diǎn),pWnd是菜單相關(guān)聯(lián)的窗口。
在“MFC類庫詳解”中有關(guān)參數(shù)nFlags的解釋如下:
? | 指定屏幕位置標(biāo)志或鼠標(biāo)鍵標(biāo)志。 屏幕位置標(biāo)志可以為下列值之一:
|
鼠標(biāo)鍵標(biāo)志可以為下列值之一:
· | TPM_LEFTBUTTON | 導(dǎo)致彈出菜單追蹤鼠標(biāo)左鍵。 |
· | TPM_RIGHTBUTTON | 導(dǎo)致彈出菜單追蹤鼠標(biāo)右鍵。 |
以上面工程為例,給對話框添加鼠標(biāo)右鍵抬起(WM_RBUTTONUP)消息處理函數(shù),在函數(shù)里添加如下代碼:
void CSeventhDlg::On
{
?// TODO: Add your message handler co
?CMenu *Menu=GetMenu();
?ClientToScreen(&point);//將窗口坐標(biāo)轉(zhuǎn)換成屏幕坐標(biāo)
?Menu->GetSubMenu(0)->TrackPopupMenu(
??TPM_LEFTBUTTON|TPM_VERTICAL,point.x,point.y,this);
?Menu->Detach();
?CDialog::On
}
要注意的是,要在界面顯示的菜單,必須是一個(gè)彈出菜單,雖然Menu->TrackPopupMenu也可以顯示,但顯然不是想要的結(jié)果。
運(yùn)行效果:
動態(tài)(純代碼)創(chuàng)建一個(gè)菜單
上面的例子,都是使用了菜單資源創(chuàng)建的菜單,這一次我們用代碼來創(chuàng)建菜單,其實(shí)本質(zhì)跟前面的用控件類的Create函數(shù)創(chuàng)建一個(gè)控件一樣。只不過這里的“Create”函數(shù)是CreateMenu和CreatePopupMenu函數(shù)。
CMenu類里要了解的函數(shù):
CreateMenu //創(chuàng)建一個(gè)主菜單,函數(shù)沒有參數(shù)
CreatePopupMenu//創(chuàng)建一個(gè)具有彈出屬性的菜單,函數(shù)沒有參數(shù)
AppendMenu//往一個(gè)菜單里添加菜單項(xiàng),或彈出式菜單
AppendMenu函數(shù)定義如下:
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
nFlags參數(shù)常用取值如下:
?MF_STRING 表明添加的是一個(gè)不具有彈出屬性的菜單項(xiàng)。
MF_POPUP 添加的一個(gè)彈出菜單項(xiàng)
MF_SEPARATOR 添加的是一個(gè)菜單分隔條
MF_OWNERDRAW? 表明對應(yīng)菜單具有自繪屬性
nIDNewItem參數(shù),如果添加的是一個(gè)不具有彈出屬性的菜單項(xiàng),那么該值就是菜單項(xiàng)ID號,否則是彈出式菜單句柄,lpszNewItem是菜單項(xiàng)名稱(菜單文本內(nèi)容)
好了,接著我們來動態(tài)創(chuàng)建一個(gè)菜單,首先往對話框里添加一個(gè)按鈕控件,當(dāng)單擊這個(gè)按鈕時(shí),就創(chuàng)建菜單,響應(yīng)這個(gè)按鈕控件的單擊消息,消息處理函數(shù)里添加如下代碼:
CMenu Menu;
? Menu.CreateMenu();//創(chuàng)建一個(gè)主菜單
? CMenu popMenu;
? popMenu.CreatePopupMenu();//創(chuàng)建一個(gè)彈出式菜單
? popMenu.AppendMenu(MF_STRING,1001,"新建");//添加菜單項(xiàng)
? popMenu.AppendMenu(MF_STRING,1002,"打開");
? popMenu.AppendMenu(MF_STRING,1003,"保存");
? Menu.AppendMenu(MF_POPUP,(UINT)popMenu.m_hMenu,"文件");//添加彈出菜單項(xiàng)
? SetMenu(&Menu);//關(guān)聯(lián)到窗口中
? Menu.Detach();
? popMenu.Detach();
更改菜單項(xiàng)大小(寬高),設(shè)置菜單文本字體大小
由于CMenu類里并沒有提供設(shè)置菜單項(xiàng)大小以及字體大小的函數(shù),所以我們?nèi)绻獙?shí)現(xiàn)上述功能的話,只能采取自繪的方法。
如果對CMenu類的某些函數(shù)不了解的話,可以參考"MFC 類大全",這里就不講述了
首先從CMenu派生出一個(gè)子類CNewMenu(類的類型為Generic Class),然后往這個(gè)類添加三個(gè)成員函數(shù),MeasureItem(設(shè)置菜單寬高),
DrawItem(自繪菜單),ChangeMenuItem(修改菜單項(xiàng)類型)
三個(gè)函數(shù)分別定義如下:
void CNewMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
void CNewMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
void CNewMenu::ChangeMenuItem(CMenu *pMenu)
其中MeasureItem和DrawItem是CMenu類的虛函數(shù)。
各函數(shù)的代碼如下:
void CNewMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
?lpMeasureItemStruct->itemHeight=25;//項(xiàng)高
?lpMeasureItemStruct->itemWidth=120;//項(xiàng)寬
}
void CNewMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
?CRect rect=lpDrawItemStruct->rcItem;
?CDC dc;
?dc.Attach(lpDrawItemStruct->hDC);
?dc.FillSolidRect(rect,RGB(0,166,170));
?CFont Font;
?Font.CreatePointFont(125,"宋體");//創(chuàng)建字體
?dc.SelectObject(&Font);
?CString *pText=(CString *)lpDrawItemStruct->itemData;
?if(lpDrawItemStruct->itemState&ODS_SELECTED)
?? dc.FillSolidRect(rect,RGB(80,89,202));//菜單被選中
?dc.SetTextColor(RGB(10,0,181));//設(shè)置文本顏色
?dc.DrawText(*pText,rect,DT_VCENTER|DT_LEFT|DT_SINGLELINE);
?dc.Detach();
}
void CNewMenu::ChangeMenuItem(CMenu *pMenu)
{
int itemCount=pMenu->GetMenuItemCount();
for(int i=0;i<itemCount;i++)
{
??? CString *pText=new CString;
?UINT itemID=pMenu->GetMenuItemID(i);//獲取菜單項(xiàng)ID號
?pMenu->GetMenuString(i,*pText,MF_BYPOSITION);//獲取菜單文本
//ModifyMenu函數(shù)最后一個(gè)參數(shù)對應(yīng)DRAWITEMSTRUCT結(jié)構(gòu)里的itemData變量
?pMenu->ModifyMenu(i,MF_OWNERDRAW|MF_BYPOSITION|MF_STRING,itemID,(LPSTR)pText);
? if(itemID==-1)//如果是一個(gè)彈出式菜單
?{
??ChangeMenuItem(pMenu->GetSubMenu(i));
?}
}??
}
必須讓每個(gè)菜單項(xiàng)具有MF_OWNERDRAW屬性,不然接不到WM_MEASUREITEM和WM_DRAWITEM消息,而且菜單項(xiàng)不具有MF_OWNERDRAW屬性, 即使接到消息,也無法自繪,所以上面的ChangeMenuItem函數(shù)就是用于修改菜單項(xiàng)屬性
WM_MEASUREITEM和WM_DRAWITEM消息不是直接發(fā)給菜單窗口的,會被父窗口給收到,所以得處理父窗口的WM_MEASUREITEM和WM_DRAWITEM消息,給話框類添加這兩個(gè)消息處理函數(shù),兩個(gè)函數(shù)里的代碼分別如下:
void CFirstDlg::On
{
?// TODO: Add your message handler co
???? if(lpMeasureItemStruct->CtlType==ODT_MENU)//如果類型是菜單
?????? newMenu.MeasureItem(lpMeasureItemStruct);//調(diào)用CNewMenu類的MeasureItem成員函數(shù)
?????? else
??? CDialog::On
??
}
void CFirstDlg::On
{
?// TODO: Add your message handler co
?if(lpDrawItemStruct->CtlType==ODT_MENU)
? newMenu.DrawItem(lpDrawItemStruct);
?else
?CDialog::On
}
接著我們在對話類添加一個(gè)成員變量:
CNewMenu newMenu; (記得要包含頭文件:"NewMenu.h"),然后在對話框類的On
?newMenu.LoadMenu(IDR_MENU1);
?SetMenu(&newMenu);
?//只更改下主菜單下的第一項(xiàng),更改全部:newMenu.ChangeMenuItem(&newMenu);
?newMenu.ChangeMenuItem(newMenu.GetSubMenu(0));
IDR_MENU1是菜單資源的ID號,可自行創(chuàng)建。好了,到了這里大功已經(jīng)告成了,點(diǎn)編譯運(yùn)行,效果如下:
跟自繪按鈕控件一樣,上面依然沒處理菜單的所有狀態(tài),如獲得焦點(diǎn),被核記,有無關(guān)聯(lián)圖片。也不處理菜單分隔條。。
如果想處理這些狀態(tài)的話。建議查看DRAWITEMSTRUCT結(jié)構(gòu)的itemState變量,這個(gè)成員指明菜單項(xiàng)的狀態(tài),
關(guān)聯(lián)圖片,就查看CMenu類的函數(shù)。。。
(菜單項(xiàng)數(shù)據(jù)(itemData)對應(yīng)的分配內(nèi)存,就自己釋放吧)