1) 標(biāo)準(zhǔn)消息:
除了WM_COMMAND之外,所有以WM_的消息。
從CWnd派生的類,都可以接收這樣的消息
2) 命令消息:
來自菜單,加速鍵或工具欄按鈕的消息,這類消息都以WM_COMMAND呈現(xiàn),在MFC中以菜單欄的ID來區(qū)分不同的命令消息,在SDK中,以參數(shù)wParam參數(shù)來標(biāo)識。
從CCmdTarget派生的類,都可以接收這樣的消息
3) 通告消息:
從控件產(chǎn)生的消息,如按鈕的單擊,列表框的選擇均產(chǎn)生這樣的消息,為的是向其父窗口(通常是對話框)通知事件的發(fā)生,這類消息也是以WM_COMMAND的形式呈現(xiàn)。
從CCmdTarget派生的類,都可以接收這樣的消息
4) CWnd類派生于CCmdTarget類,即直接派生于CWnd的類可以接收以上三種消息
2. 應(yīng)用程序菜單欄命令的消息路由順序是:
先有MainFrame類接收到消息,然后將消息拋給子窗口,即CView類,如果CView類中沒有處理函數(shù),則CView類會將該消息拋給CDoc類,如果CDoc類沒有處理函數(shù),則CDoc會將消息拋回給CView類,而后由CView將消息拋回給MainFrame,如果MainFrame類中有處理函數(shù),則響應(yīng),如果MainFrame類中沒有處理函數(shù),則最終將消息拋回給App類處理
3. 分清楚子菜單和菜單項的區(qū)別:
子菜單是在菜單欄上頂層直接顯示的菜單,如文件,編輯,查看等都是子菜單;對于某一個子菜單的下拉列表上所對應(yīng)的菜單稱為菜單項。
4. 菜單項操作:
以下所有代碼放在CMainFrame類的OnCreate函數(shù)中:
CMenu* pFatherMenu=GetMenu(); //得到最菜單欄上的頂層菜單的總的一個類
CMenu* pSubMenu=pFatherMenu->GetSubMenu(0); //得到子菜單,如File等,因為沒有ID號,只有索引號,當(dāng)然也可以將子菜單的Popup屬性去除設(shè)置ID號
//設(shè)置菜單項為缺省,即用粗體顯示。注意,一個子菜單中只有一個菜單項能缺省,多則無效,以最終一條設(shè)置為準(zhǔn),要不怎么叫缺省菜單呢。
pSubMenu->SetDefaultItem(0,TRUE);
//在菜單項前設(shè)置選擇標(biāo)記
pSubMenu->CheckMenuItem(1,MF_CHECKED|MF_BYPOSITION);
//在菜單項前設(shè)置位圖
CString str;
//GetSystemMetrics函數(shù)中得到系統(tǒng)設(shè)置能支持的最大菜單位圖值
str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXVSCROLL),GetSystemMetrics(SM_CYVSCROLL));
MessageBox(str); //看一下最大支持的像素是多少,即資源中的位圖最大只能設(shè)置成前面得到的最大值
//注意bmp對象必須聲明為類的變量,否則函數(shù)結(jié)束,CBitmap對象會析構(gòu)
bmp.LoadBitmap(IDB_BITMAP1);
// SetMenuItemBitmaps的第四個參數(shù)是選中時顯示的位圖,第三個參數(shù)是將標(biāo)記取消的時候顯示的位圖
pSubMenu->SetMenuItemBitmaps(2,MF_BYPOSITION,&bmp,&bmp);
//將菜單項禁用
//注意要在構(gòu)造函數(shù)中將m_bAutoMenuEnable變量設(shè)置為False,否則以下語句執(zhí)行沒有效果。在MSDN中該變量的相關(guān)解釋是這樣的:
When this data member is enabled (which is the default), menu items that do not have ON_UPDATE_COMMAND_UI or ON_COMMAND handlers will be automatically disabled when the user pulls down a menu.
Menu items that have an ON_COMMAND handler but no ON_UPDATE_COMMAND_UI handler will be automatically enabled.
//下面的函數(shù)中MF_DISABLED僅僅是禁用,但菜單不會變灰,為使更復(fù)合常用習(xí)慣,使禁用的菜單同時變灰MF_GRAYED
pSubMenu->EnableMenuItem(3,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
//移除整個菜單
SetMenu(NULL);
//動態(tài)添加菜單欄
CMenu menu; //注意,此處CMenu的對象是局部的,在函數(shù)結(jié)束的時候會銷毀,所以會導(dǎo)致程序出現(xiàn)一些問題,建議將此聲明為類的成員變量。
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach(); //如果還是想將CMenu的對象設(shè)置成函數(shù)的局部變量,可以調(diào)用此方法,從而消除程序原先會出現(xiàn)的bug
關(guān)于Detch函數(shù),在MSDN中關(guān)于CMenu類中有這么一段話:
Create a CMenu object on the stack frame as a local(局部變量), then call CMenu’s member functions to manipulate the new menu as needed. Next, call CWnd::SetMenu to set the menu to a window, followed immediately by a call to the CMenu object’s Detach member function. The CWnd::SetMenu member function sets the window’s menu to the new menu, causes the window to be redrawn to reflect the menu change, and also passes ownership of the menu to the window. The call to Detach detaches the HMENU from the CMenu object, so that when the local CMenu variable passes out of scope, the CMenu object destructor does not attempt to destroy a menu it no longer owns
5. 菜單項的命令更新機(jī)制:
菜單項狀態(tài)的維護(hù)是依賴于CN_UPDATE_COMMAND_UI消息,誰捕獲CN_UPDATE_COMMAND_UI消息,MFC就在其中創(chuàng)建一個CCmdUI對象。我們可以通過手工或利用ClassWizard在消息映射中添加ON_UPDATE_COMMAND_UI宏來捕獲CN_UPDATE_COMMAND_UI消息。在后臺所做的工作是:操作系統(tǒng)發(fā)出WM_INITMENUPOPUP消息,然后由MFC的基類如CFrameWnd接管。它創(chuàng)建一個CCmdUI對象,并與第一個菜單項相關(guān)聯(lián),調(diào)用對象的一個成員函數(shù)DoUpdate()。這個函數(shù)發(fā)出CN_UPDATE_COMMAND_UI消息,這條消息帶有指向CCmdUI對象的指針。同一個CCmdUI對象就設(shè)置為與第二個菜單項相關(guān)聯(lián),這樣順序進(jìn)行,直到完成所有菜單項。
注意:更新命令UI處理程序僅應(yīng)用于彈出式菜單項上的項目,不能應(yīng)用于永久顯示的頂級菜單項目。
6. 右鍵彈出式菜單:
a. 利用MFC現(xiàn)有創(chuàng)建好的右鍵菜單:
Project->Add to Project->Components and Controls添加pop menu即可。
向?qū)砑恿艘粋€右鍵菜單資源,并且在CView類中添加了一個函數(shù)OnContextMenu,并在該函數(shù)中調(diào)用了函數(shù)TrackPopupMenu,該函數(shù)是彈出右鍵菜單的核心函數(shù)。
b. 自己創(chuàng)建右鍵彈出菜單:
1. 創(chuàng)建菜單資源。
2. 在CView類中捕獲的WM_RBUTTONDOWN消息中添加如下代碼:
//代碼模仿OnContextMenu中MFC自動添加的內(nèi)容操作
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu* pPopup = menu.GetSubMenu(0); //彈出式菜單只有一個子菜單
ClientToScreen(&point); //如果沒有此函數(shù),則菜單彈出時出現(xiàn)的位置是按照屏幕計算的
//彈出右鍵菜單核心函數(shù)
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); //最后一個參數(shù)代表擁有這個菜單的父窗口的指針,如果是this,則CView先進(jìn)行響應(yīng),如果是GetParent(),則CMainFrame進(jìn)行響應(yīng),但如果CView類中有可響應(yīng)函數(shù),則即使有GetParent,優(yōu)先得到響應(yīng)的也是CView中的函數(shù)。
7. 動態(tài)添加子菜單和菜單項:(以上添加菜單子項或菜單項是直接操作菜單資源的,故為靜態(tài)操作)
在CMainFrame類的OnCreate中添加如下代碼:
1) 在現(xiàn)有的菜單結(jié)尾處添加子菜單或在子菜單的結(jié)尾處添加菜單項
CMenu menu;
menu.CreatePopupMenu(); //創(chuàng)建空的彈出菜單,即子菜單
//AppendMenu函數(shù)表示在現(xiàn)有的菜單結(jié)尾處添加子菜單或在子菜單的結(jié)尾處添加菜單項
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"test");
注意:如果將資源menu聲明為局部對象,點擊該新建的菜單,會出現(xiàn)運行錯誤,改正方法是聲明為類的成員變量或者調(diào)用detech函數(shù)。由以往的使用經(jīng)驗可以得出,凡是涉及到的資源類對象,如菜單,位圖等如果在函數(shù)的內(nèi)部聲明的話,則該資源會在函數(shù)退出后析構(gòu),則會導(dǎo)致程序的運行錯誤。在VC的資源視圖中的有資源類別:Dialog,Icon,Menu,Cussor,Bitmap,StringTable,Toolbar,其中Dialog資源也是會有這樣的問題,但它有另一種解決辦法,即將局部對象聲明為指針。StringTable好像不存在上述的問題,不知道這個想法是否正確。
2) 在現(xiàn)有的菜單中間添加菜單項或子菜單:
GetMenu()->InsertMenu(3,MF_POPUP|MF_BYPOSITION,(UINT)menu.m_hMenu,"test");
3) 給新創(chuàng)建的子菜單中創(chuàng)建菜單項:
CMenu menu;
menu.CreatePopupMenu(); //創(chuàng)建空的彈出菜單,即子菜單
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"test");
menu.AppendMenu(MF_STRING,110,"item1");
menu.AppendMenu(MF_STRING,111,"item2");
menu.Detach();
4) 刪除指定菜單項:DeleteMenu;
8. 給動態(tài)創(chuàng)建的菜單創(chuàng)建消息響應(yīng)函數(shù):
9. CObArray,CStringArray:兩個非常有用的數(shù)組類