定義
對於系統選單,創建起來比較簡單,直接使用資源編輯器就能生成選單,再通過ClassWizard創建選單命令函式。在我的資源中上傳了一個工程,實現了一個右鍵彈出貼圖選單。結合這個工程,介紹動態創建選單、創建彈出式選單和重繪選單。
首先介紹基礎知識:
一、CMenu類的成員函式:
1. CreateMenu()和CreatePopupMenu(),這兩個函式用來創建一個選單實例,CreateMenu()創建的是普通的選單實例,如果想創建彈出式選單,就要用CreatePopupMenu()函式。
2. AppendMenu()向選單中添加一個子項,這個函式有兩個主要的參數。第一個UINT nFlags,這個參數表明了該子項的屬性特徵,可以這樣說,這個參數規定了選單的樣式和功能。後面會詳細講這個參數所能使用的值。第二個參數UINT_PTR nIDNewItem,根據nFlags使用不同的設定,該參數將標明選單的資源ID或在這個選單中的索引號。第三個參數可以省略,如果不省略,可以傳入一個字元串,這個字元串將顯示在選單中(因為我準備用突破表示選單項,所以我的工程中省略了這個參數)。
3. DrawItem(),這是一個虛函式,如果選單設定成可以自繪類型,則這個函式將在生成選單、彈出選單、選中選單、點擊選單等時由系統框架調用。因此,這個函式是一個很有用的函式,它可以幫你繪製出各種樣式的選單。
4. MeasureItem()也是一個虛函式,當選單被創建的時候由系統框架調用。這個函式用來設定選單的大小。
二、nFlags說明:
只有當nFlags設定成MF_OWNERDRAW的時候,系統框架才會重繪選單。
MF_CHECKED:命令旁顯示默認複選標誌
MF_UNCHECKED:清除命令旁的複選標誌
MF_DISABLED:禁止此選單命令,但是不變灰顯示
MF_ENABLED:允許此選單命令,恢復到正常狀態
MF_GRAYED:禁止此選單命令,變灰顯示
MF_MENUBARBREAK:對於靜態選單,放到新行;對於彈出選單,放到新欄 中,欄間有分隔線
MF_MENUBREAK:對於靜態選單,放到新行;對於彈出選單,放到新欄,欄間無分隔線
MF_OWNERDRAW:指定該命令是自畫式選單命令
MF_POPUP:指定該選單命令有一個關聯的彈出式選單
MF_SEPARATOR:畫一條水平分隔線,只用於彈出式選單
MF_STRING:指定此選單命令是一個字元串
CMenu類從CObject類派生而來。為什麼要使用CMenu類呢?AppWzard不是把菜 單做好了嗎?在資源編輯器上修改選單不是很方便嗎?
感覺學vc++稍微深入一點好,至少要能搞清楚AppWizard在背後都幹了些什麼東西。
事實上mfc就是用CMenu類來生成選單的。讓我們就從CMenu開始吧。
CMenu生成的選單有兩種:Popup類型和非Popup類型。這兩種方法裡又可以分
成使用資源編輯器生成的選單資源和不使用這個資源。對於非Popup類型的選單,
必須在創建出來後把它張貼到某個視窗上,它才會顯示出來,從而才有用處。Po
pup的選單卻不能張貼到視窗上。
說明之前,先定義幾個常量:
#define IDM_MENU0 0
#define IDM_MENU1 1
#define IDM_MENU2 2
#define IDM_MENU3 3
#define IDM_ITEM0 10
#define IDM_ITEM1 11
#define IDM_ITEM2 12
#define IDM_ITEM3 13
#define IDM_ITEM4 14
#define IDM_ITEM5 15
#define IDM_ITEM6 16
一。創建非Popup類型選單,不使用資源。
(一)創建非下拉選單。
1。在視窗類的OnCreate函式里創建CMenu對象。如果是創建運用程式主框架視窗
的話,也可以在InitInstance()函數裡。
2。聲明一個CMenu對象:CMenu MyMenu;
3。調用MyMenu.CreateMenu()或MyMenu.LoadMenu()
4。調用若干次MyMenu.AppendMenu()或MyMenu.InsertMenu(),每調用一次創建一
個選單項。
5。調用MyMneu.SetMenu()將選單Attach到視窗上。
6。調用MyMenu.Detach()。
例子:
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
{
CMenu MyMenu;
MyMenu.CreateMenu();
MyMenu.AppendMenu(MF_STRING,IDM_MENU0,"檔案");
MyMenu.AppendMenu(MF_STRING,IDM_MENU1,"編輯");
MyMenu.AppendMenu(MF_STRING,IDM_MENU2,"查看");
MyMenu.AppendMenu(MF_STRING,IDM_MENU3,"幫助");
MyMenu.InsertMenu(IDM_MENU2,MF_BYCOMMAND,IDM_ITEM0,"有關");
this->SetMenu(&MyMenu);
MyMenu.Detach();
return 0;
}//各個函式的細節就不講解了,看在線上幫助是最好的。
這個方法是先把選單創建好後再貼到視窗上去,然後用Detach()使選單和My
Menu對象脫離關係,因為MyMenu對象馬上就要超出作用域了,這一步是必須的。
(二)創建下拉選單,不使用資源。
這種選單當滑鼠移動到選單條目上麵點擊時不是去執行某段程式,而是彈出
一個下拉選單。這需要用前面的方法創建兩個選單。第一個是滑鼠未點擊時看到
的那個選單,另一個就是扮演下拉選單的選單。例子:
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
CMenu MyMenu0,MyMenu1;
//下面這幾條創建下拉選單
MyMenu1.CreateMenu();
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM0,"拷貝");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM1,"剪下");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM2,"貼上");
MyMenu1.AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM4,"全選");
MyMenu1.AppendMenu(MF_SEPARATOR,IDM_ITEM5,"");
MyMenu1.AppendMenu(MF_STRING,IDM_ITEM6,"刪除");
//下面這兩條創建滑鼠未點擊時看到的那個選單
//其中第二句將下拉選單張貼到第一個選單上。
MyMenu0.CreateMenu();
MyMenu0.AppendMenu(MF_POPUP,(UINT)MyMenu1.m_hMenu,"編輯");
this->SetMenu(&MyMenu0);//將選單張貼到視窗上
MyMenu0.Detach();//必須有
MyMenu1.Detach();//必須有
return 0;
}
二。創建Popup類型的選單,也不用資源。
很多程式里,只要用滑鼠右鍵點一下視窗客戶區,就會在滑鼠的位置彈出一
個選單,這叫右鍵選單。我們可以用CMenu類來製作。
製作這種選單比製作第一類選單稍微複雜點。首先要在視窗類里加個成員變
量:CMenu *MyMenu2;
然後在視窗類的構造函數裡(或OnCreate()函數裡)加上創建選單的語句,再
在析構函數裡加上銷毀選單的語句,最後在OnRButtonDown()函數裡加上顯示選單
的語句。
創建選單時,CMenu類對象應該用new來分配。
例子:
CMyWnd::CMyWnd()
{
//CMyWnd是從CWnd派生來的。
//先把選單創建起來。
MyMenu2=new CMenu;
MyMenu2->CreatePopupMenu();
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM0,"拷貝");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM1,"剪下");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM2,"貼上");
MyMenu2->AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM4,"全選");
MyMenu2->AppendMenu(MF_SEPARATOR,IDM_ITEM3,"");
MyMenu2->AppendMenu(MF_STRING,IDM_ITEM5,"刪除");
}
CMyWnd::~CMyWnd()
{
MyMenu2->DestroyMenu();//銷毀選單所占用的系統資源
delete MyMenu2;//銷毀選單類對象
}
void CMyWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
RECT rect;
GetWindowRect(&rect);
//顯示選單
MyMenu2->TrackPopupMenu(TPM_RIGHTALIGN,point.x+rect.left,point.y+
rect.top,this,NULL);
}三。如果使用資源的話,創建選單確實非常簡單了,只須在視窗類的OnCreate()
函數裡加幾句話就行了:
int CMyWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
CMenu MyMenu3;
MyMenu3.LoadMenu(IDR_MENU1);//IDR_MENU1是你的選單的資源ID。
this->SetMenu(&MyMenu3);
MyMenu3.Detach();
return 0;
}
CMenu類還有很多成員函式,使你可以在運行中對選單進行裁剪,比如加上幾
項或減去幾項等等,使用非常方便。大家可以去看msdn。
如果要實驗以上的選單創建方法的話,可以用一個非常簡單的mfc程式來搞:
//這是一個非常簡單的mfc程式,必要的函式自己去加吧。
#include
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
};
class CMyWnd : public CWnd
{
public:
DECLARE_MESSAGE_MAP()
};
CMyApp MyApp;
BEGIN_MESSAGE_MAP(CMyWnd,CWnd)
END_MESSAGE_MAP()
BOOL CMyApp::InitInstance()
{
RECT rect={30,30,400,300};
CMyWnd* pCWindow=new CMyWnd;
pCWindow->CreateEx
(
NULL,
AfxRegisterWndClass(NULL,0,(HBRUSH)::GetStockObject(WHITE_BRU
SH),0),
"實驗程式",
WS_OVERLAPPEDWINDOW,
rect,NULL,NULL,NULL
);
m_pMainWnd = pCWindow;
pCWindow->ShowWindow(m_nCmdShow);
pCWindow->UpdateWindow();
return TRUE;
CMenu類為Windows HMENU的封裝類。它提供了成員函式以用於創建、追蹤、更新及銷毀選單。
在本地的堆疊框架中創建一個CMenu對象,然後調用CMenu的成員函式來操縱所需的新選單。接著,調用CWnd::SetMenu函式為視窗設定選單。然後立即調用CMenu對象的Detach成員函式。CWnd::SetMenu成員函式將視窗的選單設定為新選單,這將導致在視窗刷新後將影響選單的改變,同時也將選單的擁有者傳遞給視窗。調用Detach函式將把HMENU從CMenu對象中分離出來,以便當本地的CMenu變數超出範圍後,CMenu對象的構造函式將不會銷毀不再擁有的選單。當視窗銷毀後,選單自動銷毀。
可以調用LoadMenuIndirect成員函式在記憶體中創建來自模板的選單,不過通過調用LoadMenu創建的選單更容易維護。並且這種選單資源本身也可以由選單編輯器創建或修改。
#include <afxwin.h>
CMenu類的成員
數據成員
| m_hMenu 指定附加給CMenu對象的視窗選單的句柄 |
構造函式
| CMenu 構造一個CMenu對象 |
初始化
| Attach 附加一個Windows選單句柄給CMenu對象 Detach 從CMenu對象中分離Windows選單的句柄,並返回該句柄 FromHandle 返回一個指向給定Windows選單句柄的CMenu對象的指針 GetSafeHmenu 返回由CMenu對象包含的m_hMenu值 DeleteTempMap 刪除由FromHandle成員函式創建的所有臨時CMenu對象 CreateMenu 創建一個空選單,並將其附加給CMenu對象 CreatePopupMenu 創建一個空的彈出選單,並將其附加給CMenu對象 LoadMenu 從執行檔中裝載選單資源,並將其附加給CMenu對象LoadMenuIndirect從記憶體的選單模板中裝載選單,並將其附加給CMenu對象 DestroyMenu 銷毀附加給CMenu對象的選單,並釋放選單占用的記憶體 |
選單操作
| DeleteMenu 從選單中刪除指定的項。如果選單項與彈出選單相關聯,那么將銷毀彈出選單的句柄,並釋放它占用的記憶體 TrackPopupMenu 在指定的位置顯示浮動選單,並跟蹤彈出選單的選擇項 |
選單項操作
| AppendMenu 在該選單末尾添加新的選單項 CheckMenuItem 在彈出選單的選單項中放置或刪除檢測標記 CheckMenuRadioItem 將單選鈕放置在選單項之前,或從組中所有的其它選單項中刪除單選鈕 SetDefaultItem 為指定的選單設定預設的選單項GetDefaultItem獲取指定的選單預設的選單項 EnableMenuItem 使選單項有效、無效或變灰GetMenuItemCount決定彈出選單或頂層選單的項數 GetMenuItemID 獲取位於指定位置選單項的選單項標識 GetMenuState 返回指定選單項的狀態或彈出選單的項數 GetMenuString 獲取指定選單項的標籤GetMenuItemInfo獲取有關選單項的信息 GetSubMenu 獲取指向彈出選單的指針 InsertMenu 在指定位置插入新選單項,並順次下移其它選單項 ModifyMenu 改變指定位置的已存在的選單項 RemoveMenu 從指定的選單中刪除與彈出選單相關聯的選單項 SetMenuItemBitmaps 將指定檢測標記的點陣圖與選單項關聯 GetMenuCountextHelpID 獲取與選單關聯的幫助文本的ID號 SetMenuCountextHelpID 設定與選單關聯的幫助文本的ID號 |
可覆蓋的函式
| DrawItem 通過框架來調用,其發生於擁有者選單的可視部分有所改變 MeasureItem 通過框架來調用,用於決定當創建了擁有者選單時的選單維數 |
