定義
對於系統選單,創建起來比較簡單,直接使用資源編輯器就能生成選單,再通過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 通過框架來調用,用於決定當創建了擁有者選單時的選單維數 |