MFC中的CMenu---如何動態新增選單/選單項、子選單、右鍵選單
資料來源:
http://www.cnblogs.com/jcss2008/archive/2009/01/02/1366882.html
這篇文章總結的很好也很實用,感謝作者:jcss
如何動態新增選單/選單項、子選單、右鍵選單
有關選單的操作主要用到CMenu類,當然也可用相應API函式,CMenu類只是MFC對API中操作選單的函式的封裝而已。 不過能用類就儘量用類,類的組織方式好唄,程式碼看著也舒服。 若是SDK程式設計,那就用API吧 。
CMenu menuMain,menu1; //首先 定義CMenu物件
一、 建立選單,有兩種方法
1. 用LoadMenu函式從資源載入
menuMain.LoadMenu(IDR_MAINFRAME); //從資源載入,這裡使用SDI的主選單資源
2. 用CreateMenu函式建立
menu1.CreateMenu(); //建立選單,還沒有選單項
二、 新增選單項,可用AppendMenu()在選單的最後加、InsertMenu()在指定的位置加.
// ID_TEST1 在Resource.h 中定義,隨便給個整數值,不要和已有的重複就行了
menu1.AppendMenu(MF_STRING,ID_TEST1,"Test1"); // 第一項選單項
menu1.AppendMenu(MF_STRING,ID_TEST2,"Test2"); // 第二項選單項
menu1.InsertMenu(1,MF_BYPOSITION|MF_STRING,
(UINT)ID_TEST1,"ID_TEST1"); // 在第二項選單項前新增新選單項
三、 新增子選單
同樣用AppendMenu()、InsertMenu()函式。不過要注意引數的設定。
menu1.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING,
(UINT) menuMain.GetSubMenu(0) ->m_hMenu,"子選單");
//第二個引數是選單的控制代碼HMENU
四、 刪除選單
用DeleteMenu()、RemoveMenu()函式來刪除指定位置的選單/選單項。
兩者區別:如果選單項是一個彈出式選單,那麼DeleteMenu和RemoveMenu之間的區別就很重要。DeleteMenu清除彈出式選單,但RemoveMenu不清除它。一個是徹底的刪除,一個只是移除.
MSDN: 1.The DeleteMenu function destroys the handle to the menu or submenu and frees the memory used by the menu or submenu. 它使選單或者子選單的handle無效(destroys)。
2. RemoveMenu does not destroy the menu or its handle, allowing the menu to be reused. 可以再利用,並不從記憶體中將menu刪除。
五、 新增右鍵選單
CMenu menu1;
menu1.CreatePopupMenu(); //動態建立彈出式選單物件
menu1.AppendMenu(MF_STRING,ID_TEST1," 選單項1");
menu1.AppendMenu(MF_STRING,ID_TEST2," 選單項2");
menu1.InsertMenu(2,MF_BYPOSITION|MF_POPUP|MF_STRING,
(UINT) menuMain.m_hMenu,"子選單"); //新增子選單
CPoint pt;
GetCursorPos(&pt);
menu1.TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
menu1.DestroyMenu();
六、 響應選單的事件
1. 若是資源中新增的選單可用Class Wizard新增選單的響應事件。
2. 若是通過程式碼建立的選單,要手工實現選單的訊息對映。本例是在CmainFrame類中,當然也可在View類、Doc類中,基於對話方塊的同樣也可以。
1) 在.h檔案中
// Generated message map functions
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnChangmenuitem(); //這裡新增選單命令處理函式的宣告
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
2) 在.cpp檔案中,
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_COMMAND(IDM_CHANGMENUITEM, OnChangmenuitem) //這裡新增,注意沒有’ ;’
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CMainFrame::OnChangmenuitem()
{
// 這裡寫你要如何處理的程式碼
……
}
其他方法:
若選單ID值是連續的,最好用ON_COMMAND_RANGE來對映訊息處理函式,可以在一個函式中處理一個範圍內的所有訊息。
當用戶按下某個選單項,會發出一個WM_COMMAND訊息,而選單項的ID號,就包含在引數wParam的低位中.
BOOL CYourView::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
UINT m_nItemID=LOWORD(wParam);
if (m_nItemID==ID_YOURITEM) //ID_YOURITEM為你加入選單項時指定的ID號
{
//在這裡放入響應的程式碼
}
return CScrollView::OnCommand(wParam, lParam);
}
對於右鍵選單可以通過TrackPopupMenu的返回值來處理。在引數uFlags中設定TPM_ RETURNCMD,這樣返回值就是你選擇的選單項的ID,然後可以根據ID來處理。
TrackPopupMenu(TPM_ RETURNCMD ,pt.x,pt.y,this);
(關於函式TrackPopupMenu,這裡有個地方需要注意一下,雖然MSDN裡面沒有寫,但根據我的經驗,TPM_ RETURNCMD 值與
TPM_NONOTIFY值一樣當你添加了這個值之後,函式都不會再向訊息響應視窗傳送(send) notification messages when the user clicks on a menu item. 需要你自己處理所有事件,或者你可以呼叫SendMessage 或PostMessage方法,將WM_COMMAND訊息再發送給訊息響應視窗,例如: ::PostMessage(m_nid.hWnd, WM_COMMAND, tSelected, 0); //tSelected是TrackPopupMenu的返回值,即發出訊息的選單項ID。)Edited by YYMSDN:If you specify TPM_RETURNCMD in the uFlags parameter, the return value is the menu-item identifier of the item that the user selected.
(TPM_NONOTIFY If this flag is set, the function does not send notification messages when the user clicks on a menu item.)Edited by YY七、 其他
DrawMenuBar () ; //當您改變選單時,需要重畫選單才能顯示所做的改變
GetSystemMenu () ; //取得視窗控制視窗
GetMenu() //取得當前程式使用的選單
GetSubMenu() //取得子選單
應使用CMenu類的Detach()成員函式從Cmenu物件中分離出選單控制代碼,避免物件失效後程序出錯。
如:
CMenu menu;
menu.CreatePopupMenu(); //動態建立彈出式選單物件
menu.AppendMenu(0,ID_TEST1,"Test1");
menu.AppendMenu(0,ID_TEST2,"Test2");
CMenu* menuMain = GetMenu(); //取得程式主選單 需在CMainFrame類中
menuMain->AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING,(UINT)menu.m_hMenu,"子選單1");
menu.Detach(); //直接用menu.m_hMenu在執行時出錯,menu物件在這個事件結束就銷燬了
DrawMenuBar();
http://y0yblog.spaces.live.com/blog/cns!49A5506A150EC992!339.entry