孫鑫VC++視頻學習筆記之5: VC菜單相關編程

轉自: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都可以,沒有影響

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章