轉自:http://webbery.tianyablog.com
閱讀本文前,我們假設您已經:
1,知道如何創建一個單文檔的App Wizard
2,知道C++ 類、函數重載等簡單知識
3,知道如何給View類或者Doc文檔添加成員變量
4,會用MFC的IDE調試工具最好,那麼本文的程序您可以copy去調試
5,知道如何爲某個框架類添加虛函數或消息處理函數
一、 消息的分類:
消息的分類:標準消息,命令消息,通告消息。
[標準消息]:除WM_COMMAND之外,所有以WM_開頭的消息。
[命令消息]:來自菜單、加速鍵或工具欄按鈕的消息。這類消息都以WM_COMMAND呈現。
在MFC中,通過菜單項的標識(ID)來區分不同的命令消息;在SDK中,通過消息的wParam參數識別。
[通告消息]:由控件產生的消息,例如,按鈕的單擊,列表框的選擇等均產生此類消息,爲的是向其父窗口(通常是對話框)通知事件的發生。這類消息也是以WM_COMMAND形式呈現。
注意:
1)從CWnd派生的類,都可以接收到[標準消息]、[命令消息]和[通告消息]。
2)從CCmdTarget派生的類,都可以接收到[命令消息]和[通告消息]。
3)CCmdTarget是CWnd的父類
二、 菜單消息傳遞過程
MFC中菜單項消息如果利用ClassWizard來對菜單項消息分別在上述四個類中進行響應,則菜單消息傳遞順序:View類èDoc類èCMainFrame類èApp類。菜單消息一旦在其中一個類中響應則不再在其它類中查找響應函數。
當點擊一個菜單項的時候,最先接受到菜單項消息的是CMainFrame框架類,CMainFrame框架類將會把菜單項消息交給它的子窗口View
類,由View類首先進行處理;如果View類檢測到沒對該菜單項消息做響應,則View類把菜單項消息交由文檔類Doc類進行處理;如果Doc類檢測到
Doc類中也沒對該菜單項消息做響應,則Doc類又把該菜單項消息交還給View類,由View類再交還給CMainFrame類處理。如果
CMainFrame類查看到CMainFrame類中也沒對該消息做響應,則最終交給App類進行處理。
三、 菜單指針的獲取,及相關設置
在CMainFrame::OnCreate下可以直接實驗以下操作
幾個相關和重要的函數
CMenu* GetMenu( ) ;//CWnd::GetMenu得到窗口菜單欄對象指針。
CMenu* GetSubMenu( ) ;//CMenu::GetSubMenu獲得指向彈出菜單對象指針
UINT CheckMenuItem( );//CMenu::CheckMenuItem Adds check marks to or removes check marks from menu items in the pop-up menu.
BOOL SetDefaultItem();//CMenu::SetDefaultItem Sets the default menu item for the specified menu.
BOOL SetMenuItemBitmaps( );//CMenu::SetMenuItemBitmaps 設置位圖標題菜單。
UINT EnableMenuItem();//CMenu::EnableMenuItem使菜單項有效,無效,或變灰。
BOOL SetMenu( CMenu* pMenu );//CWnd::SetMenu在當前窗口上設置新菜單或移除菜單。
HMENU Detach( );//CMenu::Detach Detaches a Windows menu from a CMenu object and returns the handle.
獲取菜單的寬和高:
GetSystemMetrics(SM_CXMENUCHECK),
GetSystemMetrics(SM_CYMENUCHECK)
例子:
1, 給菜單項打上標記
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED);
2, 設置缺省菜單項
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
3, 圖形標記菜單
先創建圖形,注意底色不要是白色
m_bitmap.LoadBitmap(IDB_BITMAP1);
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
4, 屏蔽菜單,使之不能用
(需要在CMainFrame::CMainFrame()中設置m_bAutoMenuEnable=FALSE;)
GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
5, 取消和加載菜單
用此功能,可以動態的修改菜單
SetMenu(NULL);//取消菜單項
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();//菜單句柄和對象斷開,使對象析構時不銷燬菜單
四、 命令更新機制
菜單項狀態的維護是依賴於CN_UPDATE_COMMAND_UI消息,誰捕獲CN_UPDATE_COMMAND_UI消息,MFC就在其中創建一個CCmdUI對象。
在後臺操作系統發出WM_INITMENUPOPUP消息,然後由MFC的基類如CFrameWnd接管並創建一個CCmdUI對象和第一個菜單項相關
聯,調用對象成員函數DoUpdate()(注:這個函數在MSDN中沒有找到說明)發出CN_UPDATE_COMMAND_UI消息,這條消息帶有指
向CCmdUI對象的指針。此後同一個CCmdUI對象又設置爲與第二個菜單項相關聯,這樣順序進行,直到完成所有菜單項。
更新命令UI處理程序僅應用於彈出式菜單項上的項目,不能應用於永久顯示的頂級菜單項目。
注意:以下兩語句的效果不同。對菜單項一樣,對工具欄索引對應不一樣
if(0==pCmdUI->m_nIndex)pCmdUI->Enable(FALSE);
if(ID_FILE_NEW==pCmdUI->m_nID)pCmdUI->Enable(FALSE);
五、 右鍵彈出菜單
1, Project->Add to Project->Components and Controls添加pop menu即可。
2, 靜態添加菜單方法。
1) 在資源裏編輯一個菜單
2) View中添加WM_RBUTTONDOWN消息對應函數。
3) 在OnRButtonDown中添加如下
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,GetParent());//不想在框架類顯示就把GetParent()改爲this。
4) 給自己編輯的菜單加對應的處理函數(利用Classwizard)。
如果加在CMainFrame
5)
3, 動態添加菜單(子菜單數目變化)
在MainFrame::OnCreate中試驗如下
CMenu menu;
menu.CreatePopupMenu();
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");
menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello");
menu.AppendMenu(MF_STRING,112,"Weixin");
menu.AppendMenu(MF_STRING,113,"Mybole");
menu.Detach();
響應函數添加方法:
1) 在resource.h中添加資源ID定義
#define IDM_HELLO 111
2) 在MainFrm.h中聲明消息響應。DECLARE_MESSAGE_MAP()之前添加
afx_msg void OnHello();
3) 在MainFrm.cpp中END_MESSAGE_MAP()之前,添加
ON_COMMAND(IDM_HELLO,OnHello)
4) 在MainFrm.cpp中添加CMainFrame::OnHello()函數定義
4, 給系統菜單添加/刪除菜單項
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome");
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,
MF_BYCOMMAND | MF_STRING,115,"維新");
GetMenu()->DeleteMenu(1,MF_BYPOSITION);
GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);*/
GetMenu()->DeleteMenu( GetMenu()->GetSubMenu(1)->GetMenuItemID(0),MF_BYCOMMAND);
六、 動態添加系統菜單項
電話本程序:鍵盤輸入人名,空格之後電話號碼。回車之後,把人名添加到菜單項。當點擊菜單時,顯示電話本信息。
1, 給View添加private 的
CString m_strLine;//存儲輸入的字符串
CMenu m_menu;//菜單
int m_nIndex;//用於計數,是不是第一次
2, 添加View的public成員
CStringArray m_strArray;
3, 添加View的WM_CHAR消息響應函數
4, 在View::OnChar中添加如下代碼
CClientDC dc(this);
if(0x0d==nChar)
{
if(0==++m_nIndex)//如果是第一次,就創建菜單
{
m_menu.CreatePopupMenu();
GetParent()->GetMenu()->AppendMenu(MF_POPUP,
(UINT)m_menu.m_hMenu,"PhoneBook");
GetParent()->DrawMenuBar();//立即顯示菜單
}
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,m_strLine.Left(m_strLine.Find(' ')));
m_strArray.Add(m_strLine);
m_strLine.Empty();
Invalidate();//擦除先前的背景,防止重疊顯示
}
else
{
m_strLine+=nChar;
dc.TextOut(0,0,m_strLine);
}
5, 添加菜單的響應函數
方法一:
1) 在Toolbar上添加一個菜單,其彈出項爲ID號爲IDM_PHONE1, IDM_PHONE2, IDM_PHONE3。
2) 在Rsource.h中添加
#define IDM_PHONE1 201
#define IDM_PHONE2 202
#define IDM_PHONE3 203
3) 利用ClassWizard給菜單項添加響應函數,並編輯函數
4) 刪除菜單項(其響應函數會依然存在,並且4步中利用了ID號,可以對應過去)
方法二:
視頻沒有提供,但我感覺應該有其他方法。方法一太死板
6, 另:由框架類捕獲菜單響應,而不是由View處理,可以節約代碼
1) 給MainFrame添加WM_COMMAND消息響應函數
2) 在其中添加如下代碼
int MenuCmdId=LOWORD(wParam);
CMenu2View *pView=(CMenu2View*)GetActiveView();
if(MenuCmdId>=IDM_PHONE1 &&MenuCmdIdm_strArray.GetSize())
{
CClientDC dc(pView);
dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));
return TRUE;
}
第6點提供的方法,不能解決第5點提出方法的限制,響應菜單還是和事先編好的ID相對應,不能動態增加
其他注意的地方:
1, 彈出菜單(Pop-up)是不能用來作命令響應的。此項設置在菜單屬性欄內。
2, 將Toolbar的ID設爲菜單的ID,二者即發生了關聯。
3, 在計算子菜單菜單項的索引的時候,分隔欄符也算索引。
4, 消息影射函數
Cpp文件中
BEGIN_MESSAGE_MAP(CMenu2View, CView)
//{{AFX_MSG_MAP(CMenu2View)
//位置1
//}}AFX_MSG_MAP
位置2
END_MESSAGE_MAP()
頭文件中
//{{AFX_MSG(CMenu2View)
位置3
//}}AFX_MSG
位置4
DECLARE_MESSAGE_MAP()
消息映射在位置1和位置2不一樣,可以調用Classwizard看到修改結果
位置3和位置4的函數聲明,隨便放1或2都可以,沒有影響