Lesson5:菜單
菜單是用戶交互界面中一個重要的角色,本文主要講解了菜單的使用方法,包括解析MFC的Frame,App,Doc,View類對菜單的響應順序,菜單的基本操作和右鍵彈出菜單。
1. MFC的類對菜單命令響應順序
在MFC的資源裏面有一個菜單項,可以在這裏添加菜單。菜單設置裏Pop-up類型的菜單成爲彈出式菜單,這種菜單不能響應命令,一般默認頂層菜單都是彈出式菜單,它們會有子菜單,子菜單可以響應命令。當我們添加了菜單按鈕並給了標識IDM_TEST後,在嚮導裏的命令裏選擇與這個ID對應的命令消息,然後編輯代碼。這裏通過產生消息框進行測試。
void CMenuView::OnTest()
{
//TODO: 在此添加命令處理程序代碼
AfxMessageBox("ViewClicked!");
}
上面的測試是在View類裏的,那麼對於同一個菜單,當在MFC的Frame,App,Doc,View類裏分別添加函數後,事實上響應的先後順序是View,Doc,Frame,App。我們看看MFC的消息分類。
【消息的分類】
①標準消息:除WM_COMMAND之外,所有以WM_開頭的消息(窗口重繪的WM_PAINT消息,窗口創建的WM_CREATE,鍵盤按鍵WM_CHAR消息等)。從CWnd派生的類,都可以接收到這類消息。
②命令消息:來自菜單、加速鍵或工具欄按鈕的消息。這類消息都以WM_COMMAND呈現。在MFC中,通過菜單項的標識(ID)來區分不同的命令消息;在SDK中,通過消息的wParam參數識別。從CCmdTarget派生的類,都可以接收到這類消息。
③通告消息:由控件產生的消息,例如,按鈕的單擊,列表框的選擇等均產生此類消息,爲的是向其父窗口(通常是對話框)通知事件的發生。這類消息也是以WM_COMMAND形式呈現。從CCmdTarget派生的類,都可以接收到這類消息。
從從CWnd派生的類,可以接收標準消息、命令消息、通告消息。從CCmdTarget派生的類,可以接收命令消息、通告消息。
【菜單命令消息的路由過程】 當點擊菜單項時,框架類首先接收到這個消息,並把消息交給它的子窗口,view類,由視類進行處理。view類首先查看自己是否有成員函數處理此消息,如果能處理,就由調用相應的響應函數來處理,如果沒有,由view類交給doc類處理,doc查找自己能否處理,如果能處理,就由調用相應的響應函數來處理,如果沒有,就交還給view類,view類又交還給frame類。這時frame類查看自己能否處理此消息,如果能則處理,不能則交給應用App程序類來處理。
2. 菜單相關操作
對菜單進行修改是在frame框架類窗口的OnCreate函數裏進行的。下面是一系列的菜單操作。
int CMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
if(CFrameWnd::OnCreate(lpCreateStruct) == -1)
return-1;
if(!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP |CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("未能創建工具欄\n");
return-1; // 未能創建
}
if(!m_wndStatusBar.Create(this))
{
TRACE0("未能創建狀態欄\n");
return-1; // 未能創建
}
m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT));
//TODO: 如果不需要可停靠工具欄,則刪除這三行
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
/*************1.標記菜單*****************/
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED); //逐層次訪問子菜單(文件 0)的菜單項(新建 0)
//GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED); //以命令ID訪問
/**************** 2.創建默認菜單*******************/
//對於一個菜單的默認菜單,只能有一個,當設置多個的時候就只有一個生效
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE); //設置默認的菜單項(表現爲加粗)。第一個參數是依賴於第二個參數,如果第二個參數是false,第一個參數用命令的ID,如果第二個參數是true,則第一個參數是位置索引,分割欄也佔一個索引
//GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN,FALSE); //其中,false是默認的,可以不寫
//GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE); //設置默認的菜單項時,如果用索引號,注意分欄也佔一個號
/*******************3.創建圖形標記菜單************/
CStringstr; //以下三行是爲了獲得系統圖標的大小,然後調整圖標大小,使圖標顯示適當
str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str); //標記的大小
//CBitmap m_bitmap 作爲一個成員函數,寫在成員變量裏,不在這
m_bitmap.LoadBitmap(IDB_BITMAP1); //下載一副自己製作的位圖
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION, &m_bitmap, &m_bitmap); //將位圖設置在菜單項前
/**********************4.禁用菜單******************/
GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION|MF_DISABLED|MY_GRAYED); //(不能用時自動變灰)在設置同時,需要在frame的構造函數中m_bAutoMenuEnable = FALSE;
/*以下是移除一個菜單後加載一個菜單*/
//SetMenu(NULL); //當參數爲NULL時,菜單被移除
//CMenumenu; //定義一個菜單,如果用一個局部變量menu,可以用SetMenu設置菜單,但後面用要menu.Detach
//menu.LoadMenuA(IDR_MAINFRAME); //加載一個菜單
//SetMenu(&menu); //設置一個菜單 調用setmenu後一定要緊接着用菜單對象的detach將菜單句柄與菜單對象分開
//menu.Detach(); //將菜單句柄和c++對象斷開,防止窗口重繪時候發生錯誤,menu對象析構時不會銷燬菜單
return0;
}
3. 右鍵彈出菜單
①在資源中增加一個menu,並對菜單的每一個子菜單設置caption 和ID號。
②增加鼠標右鍵點擊的消息響應OnRButtonDown。
③對子菜單編寫命令處理函數,。
void CMenuView::OnRButtonDown(UINT nFlags,CPoint point)
{
//TODO: 在此添加消息處理程序代碼和/或調用默認值
CMenumenu; //定義一個菜單
menu.LoadMenu(IDR_MENU1); //加載一個菜單資源
CMenu*pPopup = menu.GetSubMenu(0); //獲取一個子菜單
ClientToScreen(&point); //將客戶區座標轉換爲屏幕座標
pPopup->TrackPopupMenu(TPM_LEFTALIGN| TPM_RIGHTBUTTON, point.x, point.y, this); //顯示菜單
//TrackPopupMenu第一個參數表示菜單顯示的位置,第二三參數表示鼠標點擊的位置,第四個表示菜單的擁有者,第五個是默認的,指示了一個區域,當點擊區域外面時,菜單無效
//當地四個選項說明擁有者後,菜單的子菜單的擁有者也是它
CView::OnRButtonDown(nFlags,point);
}