vc學習筆記

資源的添加:右鍵ResourceView->插入,可選擇各類型資源。
VC組件(如右鍵菜單)或其它系統已經安裝的ActiveX控件(如Canldar)的添加:Project->Add to Project->Components and Controls..
在dialog上右鍵,insert ActiveX

1、添加工具條(或菜單等其它資源):
右鍵ResourceView->插入,選擇插入的資源類型,命名其ID等屬性。然後在該資源上右鍵->建立類嚮導(或Ctrl+DBClick),然後選擇一個已經存在的類(也可新建類)如CMainFrame,這時可選擇對應ID,爲其創建事件。 
在MainFrm.h的Protect中加入:CToolBar m_wndToolBar2;
在MainFrm.cpp的OnCreat事件中加入:
    if (!m_wndToolBar2.CreateEx(this) ||
        !m_wndToolBar2.LoadToolBar(IDR_MyID))//你創建的工具欄ID
    {
        TRACE0("Failed to create toolbar/n");
        return -1;      // fail to create
    }
    m_wndToolBar2.SetBarStyle(m_wndToolBar2.GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY);
可以爲新建的工具條設置樣式(加在MainFrm.h上面的語句後面):
   m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    m_wndToolBar2.EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndToolBar);
    DockControlBar(&m_wndToolBar2);
可以控制工具條的顯示:showControlBar(&m_wndToolBar2, FALSE, FALSE);

2、右鍵彈出菜單(各方法有些實質是一樣的):
方法一(最簡單的方法):
Project->Add to Project->Components and Controls..
在visual C++ Components下選擇pop-up menu,然後依次命名其ID,選擇其"Add pop-up menu to"
方法二:
簡單的方法你可以從工具欄上拖個ContextMenu控件下來,並編輯好,然後把你所需要添加ContextMenu的控件的ContextMenu屬性設爲這個右鍵菜單。
方法三:
除了上貼所述的方法,您也可以通過手工添寫代碼來實現彈出式菜單。關鍵的類是ContextMenu類。該類有兩個構造函數,其中ContextMenu()生成一個不含任何菜單項的彈出式菜單;ContextMenu(MenuItem[] menus)生成一個包括參數中所指定的菜單項的彈出式菜單。如要給一個按鈕控件button1添加彈出式菜單,可以參考以下的代碼:
ContextMenu Menu1=new ContextMenu();
Menu1.MenuItems.Add(new MenuItem(“彈出菜單一"));
Menu1.MenuItems.Add(new MenuItem(“彈出菜單二"));
button1.ContextMenu=Menu1;
ContextMenu有幾個關鍵的屬性、方法和事件,可以幫助您定製彈出式菜單,屬性RightToLeft可以使菜單項從右到左對齊,屬性SourceControl返回一個Control值表示當前所顯示彈出菜單對應的控件。Show()方法可以使程序主動顯示彈出菜單。當彈出菜單彈出時將引發一個Popup事件,你可以在該事件的響應方法中進行一些處理使彈出菜單顯示前做一些操作。
參考MSDN中給出的一個示例來定製彈出式菜單:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsContextMenuClassTopic.asp
方法四:
利用調用TrackPopupMenu函數,手工添加彈出菜單:
1)用資源管理器添加一個菜單資源
2)在鼠標右鍵消息響應函數中,加載菜單資源,並獲得要顯示的子菜單指針,並用該指針調用TrackPopupMenu函數便完成任務(但要注意:鼠標響應函數傳進來的座標是客戶區座標,而TrackPopupMenu函數中使用的是屏幕座標,在調用TrackPopupMenu前要調用ClientToScreen客戶區座標到屏幕座標的轉換)
事例代碼:
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,this);
說明:
CWnd::ClientToScreen(..);//將一個座標點或一個矩形區域座標轉換成屏幕座標。
CMenu::TrackPopupMenu(..);//在指定位置以指定的方式顯示彈出菜單。
CWnd::ScreenToClient(..);
//Converts the screen coordinates of a given point or rectangle on the display to client coordinates.
說明:至少有兩類方法,一種是右鍵鼠標消息來處理,另一種是處理WM_CONTEXTMENU窗口消息(推薦使用)。範例代碼如下:IDR_GISPROPERTY爲指定的菜單資源。
a.右鍵鼠標消息
void CRightView::OnRButtonDown(UINT nFlags, CPoint point)
{

    // TODO: Add your message handler code here and/or call default
    CMenu menuMain;
    if(!menuMain.LoadMenu(IDR_GISPROPERTY))
        return;
    CMenu* pRecordMenu = menuMain.GetSubMenu(2);    
    ASSERT(pRecordMenu);
    ClientToScreen(&point);
    pRecordMenu->TrackPopupMenu(TPM_LEFTALIGN |TPM_LEFTBUTTON, point.x,
      point.y, this);

    CScrollView::OnRButtonDown(nFlags, point);
}
b.處理WM_CONTEXTMENU窗口消息
void CRightView::OnContextMenu(CWnd* pWnd, CPoint point)
{
    // TODO: Add your message handler code here
    CMenu menuMain;
    if(!menuMain.LoadMenu(IDR_GISPROPERTY))
        return;
    CMenu* pRecordMenu = menuMain.GetSubMenu(2);    
    ASSERT(pRecordMenu);
    pRecordMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON| TPM_RIGHTBUTTON, point.x,
      point.y, this);

}
 
3、菜單的Check
  poMenu->CheckMenuItem(ID_VIEW_FORMATTOOLBAR,MF_CHECKED);

4、Dialog窗體的菜單:在Dialog上右鍵,屬性->即可選擇菜單。

5、顯示對話框:新建一個對話框,在其上右鍵->建立類嚮導,新建一個MyDlg的類,
然後在主窗體的cpp文件(如MainFrm.cpp)中加入#include "MyDlg.h",就可以在程序中引用了,如:
模式顯示:
  MyDlg dlg;
  dlg.DoModal();
無模式顯示:


以下爲摘錄部分:
1、SDK-菜單詳解
接着上一天的內容,添加一個菜單,並加入各項,用記事本打開resource.rc,可以找到如下內容
IDR_MENU1 MENU DISCARDABLE   //這裏定義菜單名
BEGIN
    MENUITEM "Exit",                        IDM_EXIT  //添加菜單項EXIT
    POPUP "Edit"                //添加彈出菜單
    BEGIN
        MENUITEM "Copy(&C)/tCtrl+C", IDM_COPY
        MENUITEM "Paste",  IDM_PASTE
        MENUITEM SEPARATOR
        MENUITEM "SeleceAll",  IDM_SELECTALL
    END
    MENUITEM "About", IDM_ABOUT
END
同時在resource.h中可以找到IDM_EXIT等的定義
#define IDM_EXIT                        40001
#define IDM_COPY                        40002
#define IDM_PASTE                       40003
#define IDM_SELECTALL                   40004
#define IDM_ABOUT                       40005
#define IDM_ALIGNLEFT                                40006
#define IDM_ALIGNRIGHT                             40007
這樣就把菜單資源和我們的主程序關聯起來了。
接着就要把菜單在窗口中顯示出來,我們可以在初始化窗口時指定菜單
wndclass.lpszMenuName=(LPCSTR)IDR_MENU1;
這樣編譯,運行就可以看到菜單出現在主窗口上部了
菜單項的消息響應:
菜單消息歸類於WM_COMMAND,而對於特定的菜單項,有相應的消息值跟他對應,所以只要對WM_COMMAND消息進行進一步的分流,就可以添加各個菜單的消息響應。具體代碼如下:
case WM_COMMAND:
              wparam=LOWORD(wParam);
              switch(wparam)
              {
              case IDM_EXIT:
                     PostQuitMessage(0);
                     return 0;
              case IDM_COPY:
                     MessageBox(hwnd,"Copy Content","Menu",MB_OK);
                     return 0;
              case IDM_PASTE:
                     MessageBox(hwnd,"Paste Content","Menu",MB_OK);
                     return 0;
              case IDM_SELECTALL:
                     MessageBox(hwnd,"Select All Content","Menu",MB_OK);
                     return 0;
              case IDM_ABOUT:
                     MessageBox(hwnd,"About: Designed by csenior","Menu",MB_OK);
                     return 0;
              }
              Break;
另外還有一種創建菜單的方式:利用CreateMenu,CreatePopupMenu,GetMenu,InsertMenu,AppendMenu函數,我們試着在WM_CREATE消息中添加如下代碼:
case WM_CREATE:
              HMENU hMainMenu;
              HMENU hAddMenu;
              hMainMenu=GetMenu(hwnd);
              hAddMenu=CreatePopupMenu();
              InsertMenu(hMainMenu,2,MF_BYPOSITION | MF_STRING | MF_POPUP,(UINT)hAddMenu,"格式");
              AppendMenu(hAddMenu,MF_STRING,IDM_ALIGNLEFT,"Left Align");
              AppendMenu(hAddMenu,MF_STRING,IDM_ALIGNRIGHT,"Right Align");
編譯運行,可以看到多了個菜單條。
這裏GetMenu(hwnd)獲得hwnd窗口的菜單。然後通過InsertMenu把之前創建的hAddMenu連接到hMainMenu上去。最後添加各子菜單項
之前在CreateWindow創建窗口時,倒數第三個參數就是HMENU結構,試着在這裏給他賦一個值:
HMENU hMenu,hCMenu1,hCMenu2,hCMenu3;
       hMenu=CreateMenu();
       hCMenu1=CreatePopupMenu();
       hCMenu2=CreatePopupMenu();
       hCMenu3=CreatePopupMenu();
       InsertMenu(hMenu,0,MF_BYPOSITION | MF_STRING | MF_POPUP,(UINT)hCMenu1,"File");
       InsertMenu(hMenu,1,MF_BYPOSITION | MF_STRING | MF_POPUP,(UINT)hCMenu2,"View");
       InsertMenu(hMenu,2,MF_BYPOSITION | MF_STRING | MF_POPUP,(UINT)hCMenu3,"Edit");
       AppendMenu(hMenu,MF_STRING,(UINT)41001,"About");
       AppendMenu(hCMenu1,MF_STRING,(UINT)41002,"Open");
       AppendMenu(hCMenu1,MF_STRING,(UINT)41003,"Close");
       AppendMenu(hCMenu2,MF_STRING,(UINT)41004,"Big");
       AppendMenu(hCMenu2,MF_STRING,(UINT)41005,"Small");
       AppendMenu(hCMenu3,MF_STRING,(UINT)41006,"Copy");
       AppendMenu(hCMenu3,MF_STRING,(UINT)41007,"Paste");
       hWnd=CreateWindow("HelloWin",wintitle,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
              CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,hInstance,NULL);
運行,我們可以看到菜單變了,因此在此處也可以添加修改程序的菜單,但是這裏不能用GetMenu,因爲hWnd窗口句柄還沒被實例化
關於菜單命令的消息參數說明下:
基本參數說明:
    wNotifyCode=HIWORD(wParam)1
    WID=LOWORD(wParam);
    hWndCtl=(HWND)1Param”;
    當WM_CoMMAND命令是由選擇菜單命令產生時WNotifycode參數爲0,wID參數
爲菜單項的標識導,hwndctl參數爲NULL。
    當WM_COMMAND命今是來自於快捷鍵時WNotifycode參數爲1 WID是快捷鍵標
識號,hWndCtl參數爲NULL。
    如果是因爲控件向父窗口發出通知消息面產生WM—COMMAND消息時。
WNotifycode參數爲通知代碼,wID爲控件標識號,hwndctl參數爲控件句柄。
通過EnabaleMenuItem,EnableMenuRadioItem,可以設置菜單項的狀態爲有效,無效,灰色三種,同時可以調用CheckMenuItem,CheckMenuRadioItem來改變菜單項的選種狀態具體如下代碼:
              EnableMenuItem(hMainMenu,IDM_EXIT,MF_BYCOMMAND | MF_GRAYED);
              EnableMenuItem(hAddMenu,1,MF_BYPOSITION | MF_GRAYED);
              CheckMenuItem(hMainMenu,IDM_COPY,MF_BYCOMMAND |MF_CHECKED);          CheckMenuRadioItem(hAddMenu,IDM_ALIGNLEFT,IDM_ALIGNRIGHT,IDM_ALIGNLEFT,MF_BYCOMMAND);

2、孫鑫VC++講座筆記-(6)菜單編程
1,彈出菜單(Pop-up)是不能用來作命令響應的。

2,MFC中菜單項消息如果利用ClassWizard來對菜單項消息分別在上述四個類中進行響應,則菜單消息傳遞順序:View類--Doc類--CMainFrame類--App類。菜單消息一旦在其中一個類中響應則不再在其它類中查找響應函數。
具體:
當點擊一個菜單項的時候,最先接受到菜單項消息的是CMainFrame框架類,CMainFrame框架類將會把菜單項消息交給它的子窗口View類,由View類首先進行處理;如果View類檢測到沒對該菜單項消息做響應,則View類把菜單項消息交由文檔類Doc類進行處理;如果Doc類檢測到Doc類中也沒對該菜單項消息做響應,則Doc類又把該菜單項消息交還給View類,由View類再交還給CMainFrame類處理。如果CMainFrame類查看到CMainFrame類中也沒對該消息做響應,則最終交給App類進行處理。

3,消息的分類:標準消息,命令消息,通告消息。
[標準消息]:除WM_COMMAND之外,所有以WM_開頭的消息。
[命令消息]:來自菜單、加速鍵或工具欄按鈕的消息。這類消息都以WM_COMMAND呈現。
在MFC中,通過菜單項的標識(ID)來區分不同的命令消息;在SDK中,通過消息的wParam參數識別。
[通告消息]:由控件產生的消息,例如,按鈕的單擊,列表框的選擇等均產生此類消息,爲的是向其父窗口(通常是對話框)通知事件的發生。這類消息也是以WM_COMMAND形式呈現。
說明:
1)從CWnd派生的類,都可以接收到[標準消息]。
2)從CCmdTarget派生的類,都可以接收到[命令消息]和[通告消息]。

4,一個菜單攔可以有若干個子菜單,一個子菜單又可以有若干個菜單項等。對菜單欄的子菜單由左至右建立從0開始的索引。對特定子菜單的菜單項由上至下建立了從0開始的索引。訪問子菜單和菜單項均可以通過其索引或標識(如果有標識的話)進行。
相關重要函數:
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.
說明:
1)在計算子菜單菜單項的索引的時候,分隔欄符也算索引的。
2)int GetSystemMetrics()獲取系統信息度量。可以用它來獲取菜單標題的尺寸從而設置位圖標題菜單中位圖的大小。
3)在MFC中MFC爲我們提供了一套命令更新機制,所有菜單項的更新都是由這套機制來完成的。所以要想利用CMenu::EnableMenuItem來自己控制菜單使用或不使用變灰等,必須要在CMainFrame的構造函數中將變量m_bAutoMenuEnable設置爲FALSE。
4)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. The menu itself is automatically destroyed when the window is destroyed.
5)You can use the LoadMenuIndirect member function to create a menu from a template in memory, but a menu created from a resource by a call to LoadMenu is more easily maintained, and the menu resource itself can be created and modified by the menu editor.
6)EXAMPLE:
CMenu menu;//定義爲局部對象
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();// 這裏menu對象作爲一個局部對象。使用Detach()從menu對象中分離窗口菜單句柄,從而當menu對象析構的時候窗口菜單資源不隨之銷燬。

5,命令更新機制:
菜單項狀態的維護是依賴於CN_UPDATE_COMMAND_UI消息,誰捕獲CN_UPDATE_COMMAND_UI消息,MFC就在其中創建一個CCmdUI對象。
在後臺操作系統發出WM_INITMENUPOPUP消息,然後由MFC的基類如CFrameWnd接管並創建一個CCmdUI對象和第一個菜單項相關聯,調用對象成員函數DoUpdate()(注:這個函數在MSDN中沒有找到說明)發出CN_UPDATE_COMMAND_UI消息,這條消息帶有指向CCmdUI對象的指針。此後同一個CCmdUI對象又設置爲與第二個菜單項相關聯,這樣順序進行,直到完成所有菜單項。
更新命令UI處理程序僅應用於彈出式菜單項上的項目,不能應用於永久顯示的頂級菜單項目。
說明:
1)可以手工或用ClassWizard來給菜單項添加UPDATE_COMMAND_UI消息響應,利用響應函數中傳進來的CCmdUI對象指針可完成設置菜單項可使用,不可使用,變灰,設置標記菜單等操作。

6,如果要想讓工具欄上的某個圖標與菜單項的某個菜單相關聯,只需要將圖標的ID設置爲該菜單項的ID。
工具欄圖標的索引記數順序是:從做至右從0開始,分隔符也算索引號。

7,利用向項目中添加VC的POPMENU控件:Project->Add to Project->Components and Controls..
系統增加的內容:A,一個菜單資源;B,在派生View類中增加了OnContextMenu()函數
說明:
1)CWnd::OnContextMenu Called by the framework when the user has clicked the right mouse button (right clicked) in the window. You can process this message by displaying a context menu using the TrackPopupMenu.
2)BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );
//CMenu::TrackPopupMenu Displays a floating pop-up menu at the specified location and tracks the selection of items on the pop-up menu. A floating pop-up menu can appear anywhere on the screen.

8,利用調用TrackPopupMenu函數,手工添加彈出菜單:
1)用資源管理器添加一個菜單資源
2)在鼠標右鍵消息響應函數中,加載菜單資源,並獲得要顯示的子菜單指針,並用該指針調用TrackPopupMenu函數便完成任務(但要注意:鼠標響應函數傳進來的座標是客戶區座標,而TrackPopupMenu函數中使用的是屏幕座標,在調用TrackPopupMenu前要調用ClientToScreen客戶區座標到屏幕座標的轉換)
事例代碼:
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,this);
說明:
CWnd::ClientToScreen(..);//將一個座標點或一個矩形區域座標轉換成屏幕座標。
CMenu::TrackPopupMenu(..);//在指定位置以指定的方式顯示彈出菜單。
CWnd::ScreenToClient(..);
//Converts the screen coordinates of a given point or rectangle on the display to client coordinates.

9,當彈出菜單屬於框架窗口的時候(可在TrackPopupMenu函數參數中設置),彈出菜單上的消息,在路由的時候,仍然遵循View-DOC-MainFrame-APP的響應順序。

10,動態菜單編程:
所有的資源對象都有一個數據成員保存了資源的句柄。
CMenu::AppendMenu //Appends a new item to the end of a menu.
CMenu::CreatePopupMenu //Creates an empty pop-up menu and attaches it to a CMenu object.
CMenu::InsertMenu
//Inserts a new menu item at the position specified by nPosition and moves other items down the menu.
CMenu::GetSubMenu //Retrieves a pointer to a pop-up menu.
CWnd::GetMenu //Retrieves a pointer to the menu for this window.
CMenu::DeleteMenu //Deletes an item from the menu.

11,手動給動態菜單項添加響應函數:
在Resource.h中可以添加資源的ID
在頭文件中寫消息函數原型
在代碼文件中添加消息映射和添加消息響應函數
說明:
可以先創建一個臨時的菜單項,設置它的ID和動態菜單項的一致,然後對它用嚮導進行消息響應,然後刪除臨時菜單。
再在代碼文件中把消息映射放到宏外(注意一定要放到宏外面,因爲CLASSWIZARD發現菜單刪除了,同時要把其宏對裏的消息映射也刪除掉的)

12,CWnd::DrawMenuBar
//Redraws the menu bar. If a menu bar is changed after Windows has created the window, call this function to draw the changed menu bar

CWnd::GetParent //get a pointer to a child window’s parent window (if any).
CWnd::Invalidate //注意其參數的作用

13,集合類:
CStringArray,CStringArray,CDWordArray,CPtrArray,CStringArray,CUIntArray,CWordArray
其中成員函數:
CArray::GetAt
CArray::Add

14,命令消息是到OnCommand函數的時候完成路由的。
由於CWnd::OnCommand 是個虛函數,可以在框架類中重寫OnCommand函數,從而可以截獲菜單消息使它不再往下(VIEW類)路由。
例:
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
 int MenuCmdId=LOWORD(wParam);//取命令ID
 CMenu2View *pView=(CMenu2View*)GetActiveView();//獲取當前VIEW類指針
 if(MenuCmdId>=IDM_PHONE1 && MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())//消息範圍判斷
 {
  CClientDC dc(pView);
  dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));
  return TRUE;
   //函數返回,避免調用CFrameWnd::OnCommand函數,在CFrameWnd::OnCommand中截獲的消息會交由VIEW類處理
 }
 return CFrameWnd::OnCommand(wParam, lParam);
  //調用基類OnCommand函數,在CFrameWnd::OnCommand中截獲的消息會交由VIEW類處理
}
 
MSDN說明:
virtual BOOL OnCommand( WPARAM wParam, LPARAM lParam );
//The framework calls this member function when the user selects an item from a menu, when a child control sends a notification message, or when an accelerator keystroke is translated.
OnCommand processes the message map for control notification and ON_COMMAND entries, and calls the appropriate member function.
Override this member function in your derived class to handle the WM_COMMAND message. An override will not process the message map unless the base class OnCommand is called.

15,LOWORD與HIWORD宏
WORD LOWORD(
  DWORD dwValue  // value from which low-order word is retrieved
);
WORD HIWORD(
  DWORD dwValue  // value from which high-order word is retrieved
);

//The LOWORD macro retrieves the low-order word from the given 32-bit value.
//The HIWORD macro retrieves the high-order word from the given 32-bit value.


16,CFrameWnd::GetActiveView
CView* GetActiveView( ) const;//獲取當前視窗口指針(單文檔框架中)

17,源文件是單獨參與編譯的。



Lesson6 菜單 ---孫鑫VC++教程

Lesson6 菜單
state:finished

1.AfxMessageBox應用程序框架的MessageBox函數

2.接收菜單命令消息的順序,View類->Doc類->MainFrame類->App類

3.消息的分類:
 標準消息
    除WM_COMMAND之外,所有以WM_開頭的消息。
    從CWnd派生的類,都可以接收到這類消息。
 命令消息
    來自菜單、加速鍵或工具欄按鈕的消息。這類消息都以WM_COMMAND呈現。
    在MFC中,通過菜單項的標識(ID)來區分不同的命令消息;在SDK中,
    通過消息的wParam參數識別。
    從CCmdTarget派生的類,都可以接收到這類消息。(CWnd是CCmdTarget類的子類)
 通告消息
    由控件產生的消息,例如,按鈕的單擊,列表框的選擇等均產生此類消息,
    爲的是向其父窗口(通常是對話框)通知事件的發生。這類消息也是以
    WM_COMMAND形式呈現。
    從CCmdTarget派生的類,都可以接收到這類消息。
4.源代碼的變化
  MenuView.h
  ------------------------------------------------------------------------------------
  protected:
 //{{AFX_MSG(CMenuView)
 afx_msg void OnTest();                  //命令消息處理函數原型的聲明
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
  -------------------------------------------------------------------------------------
  MenuView.CPP
  -------------------------------------------------------------------------------------
  BEGIN_MESSAGE_MAP(CMenuView, CView)
 //{{AFX_MSG_MAP(CMenuView)
 ON_COMMAND(IDM_TEST, OnTest)            //使用ON_COMMAND宏將我們的菜單ID和消息
 //}}AFX_MSG_MAP                         //響應函數聯繫起來
 // Standard printing commands
 ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
 ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
 ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
  END_MESSAGE_MAP()
  ---------------------------------------------------------------------------------------
5.創建標記菜單
 CWnd::GetMenu
 MSDN
 -----------------------------------------------------------------------------------------
 CWnd::GetMenu
 Retrieves a pointer to the menu for this window.
   
    CMenu* GetMenu( ) const;  //獲取框架窗口菜單欄的指針
 ---------------------------------------------------------------------------------------
    CMenu派生自CObject,封裝了和菜單有關的操作,封裝了一個菜單句柄
 CMenu::GetSubMenu
 MSDN
 -----------------------------------------------------------------------------------------
 CMenu::GetSubMenu                 //返回子菜單的操作
    Retrieves the CMenu object of a pop-up menu.

 CMenu* GetSubMenu(
    int nPos                              //彈出菜單的位置
 ) const;
 ----------------------------------------------------------------------------------------
 CMenu::CheckMenuItem
 MSDN
 --------------------------------------------------------------------------------------
 CMenu::CheckMenuItem
 Adds check marks to or removes check marks from menu items in the pop-up menu.

 UINT CheckMenuItem(
    UINT nIDCheckItem,
    UINT nCheck
 );
 nIDCheckItem
    Specifies the menu item to be checked, as determined by nCheck.
    nCheck
       Specifies how to check the menu item and how to determine the item's position in the
    menu. The nCheck parameter can be a combination of MF_CHECKED or MF_UNCHECKED with
    MF_BYPOSITION or MF_BYCOMMAND flags. These flags can be combined by using the bitwise
    OR operator. They have the following meanings:
    MF_BYCOMMAND   Specifies that the parameter gives the command ID of the existing menu
                item. This is the default.
    MF_BYPOSITION  Specifies that the parameter gives the position of the existing menu
                 item. The first item is at position 0.
    MF_CHECKED     Acts as a toggle with MF_UNCHECKED to place the default check mark next
                to the item.
    MF_UNCHECKED   Acts as a toggle with MF_CHECKED to remove a check mark next to the item.
 ---------------------------------------------------------------------------------------

 加上選擇標記:
 MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
  //GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
  GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYPOSITION | MF_CHECKED);
 }
 --------------------------------------------------------------------------------------

6.創建缺省的菜單項
   CMenu::SetDefaultItem
   MSDN
   -----------------------------------------------------------------------------------------
   CMenu::SetDefaultItem
 Sets the default menu item for the specified menu.

 BOOL SetDefaultItem(
    UINT uItem,
    BOOL fByPos = FALSE
 );
   ----------------------------------------------------------------------------------------
   MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
  GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
  //GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
 }
 --------------------------------------------------------------------------------------
 一個子菜單項中不能有兩個缺省的菜單

7.圖形標記菜單的創建
CMenu::SetMenuItemBitmaps
MSDN
  ----------------------------------------------------------------------------------------
   CMenu::SetMenuItemBitmaps
 Associates the specified bitmaps with a menu item.

 BOOL SetMenuItemBitmaps(
    UINT nPosition,
    UINT nFlags,                        //MF_BYCOMMAND or MF_BYPOSITION
    const CBitmap* pBmpUnchecked,       //標記取消時候顯示的位圖
    const CBitmap* pBmpChecked          //點中的時候顯示的位圖
 );
  ------------------------------------------------------------------------------------------
  獲取圖形標記菜單標記的大小
  GetSystemMetrics
  -----------------------------------------------------------------------------------------
  MSDN
  GetSystemMetrics
 The GetSystemMetrics function retrieves various system metrics (widths and heights of
 display elements) and system configuration settings. All dimensions retrieved by
 GetSystemMetrics are in pixels.

 int GetSystemMetrics(
   int nIndex                  //SM_CXMENUCHECK, SM_CYMENUCHECK Dimensions of the default
 );                            //menu check-mark bitmap, in pixels.
 
  ------------------------------------------------------------------------------------------

8.使菜單項失效
  CMenu::EnableMenuItem
  MSDN
  --------------------------------------------------------------------------------------------
  CMenu::EnableMenuItem
 Enables, disables, or dims a menu item.

 UINT EnableMenuItem(
    UINT nIDEnableItem,    
    UINT nEnable  //MF_BYCOMMAND or MF_BYPOSITIONMF_DISABLED, MF_ENABLED, or MF_GRAYED
 );

 // NOTE: m_bAutoMenuEnable is set to FALSE in the constructor of
 // CMainFrame so no ON_UPDATE_COMMAND_UI or ON_COMMAND handlers are
 // needed, and CMenu::EnableMenuItem() will work as expected.
    // 需要在CMainFrame的構造函數中寫入 m_bAutoMenuEnable=FALSE 才能更新菜單,使上面的命令生效
  ---------------------------------------------------------------------------------------------
  MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
  GetMenu()->GetSubMenu(0)->EnableMenuItem(ID_FILE_OPEN,MF_BYCOMMAND | MF_DISABLED
                                        | MF_GRAYED);
 }
 --------------------------------------------------------------------------------------

9.取消系統菜單
  CWnd::SetMenu
  MSDN
  ---------------------------------------------------------------------------------------------
  CWnd::SetMenu
 Sets the current menu to the specified menu.

 BOOL SetMenu(
    CMenu* pMenu
 );
  -------------------------------------------------------------------------------------------
  MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
   SetMenu(NULL);

   CMenu menu;
   menu.LoadMenu(IDR_MAINFRAME);
   SetMenu(&menu);
   menu.Detach();      //將句柄與C++局部變量斷開

 }
 --------------------------------------------------------------------------------------

10. MFC對菜單項採用的命令更新機制
 菜單項狀態的維護是依賴於CN_UPDATE_COMMAND_UI消息,誰捕獲CN_UPDATE_COMMAND_UI消息,
 MFC就在其中創建一個CCmdUI對象。我們可以通過手工或利用ClassWizard在消息映射中添加
 ON_UPDATE_COMMAND_UI宏來捕獲CN_UPDATE_COMMAND_UI消息。

    在後臺所做的工作是:操作系統發出WM_INITMENUPOPUP消息,然後由MFC的基類如CFrameWnd
 接管。它創建一個CCmdUI對象,並與第一個菜單項相關聯,調用對象的一個成員函數
 DoUpdate()。這個函數發出CN_UPDATE_COMMAND_UI消息,這條消息帶有指向CCmdUI對象的指針。
 同一個CCmdUI對象就設置爲與第二個菜單項相關聯,這樣順序進行,直到完成所有菜單項。

    更新命令UI處理程序僅應用於彈出式菜單項上的項目,不能應用於永久顯示的頂級菜單項目。
 CCmdUI::Enable
 MSDN
 ----------------------------------------------------------------------------------------
 CCmdUI::Enable
  Call this member function to enable or disable the user-interface item for this
  command.

  virtual void Enable(
     BOOL bOn = TRUE
  );
  Parameters
   bOn
   TRUE to enable the item, FALSE to disable it.
 -----------------------------------------------------------------------------------------

  MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
 {
  // TODO: Add your command update UI handler code here
  pCmdUI->Enable();
 
 }

 void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI)
 {
  // TODO: Add your command update UI handler code here
  /*if (ID_FILE_NEW == pCmdUI->m_nID)
  {
   pCmdUI->Enable(FALSE);
  }*/

  if (0 == pCmdUI->m_nIndex)   //工具欄的圖標和菜單項是使用ID相關聯的
  {                            //最好使用ID,而不使用索引
   pCmdUI->Enable(FALSE);
  }
 
 }
 --------------------------------------------------------------------------------------

11.增加右鍵彈出菜單

   step1: Project->Add To Project->Components and Controls->
          Visual C++ Components->Pop-up Menu
 
   變化:1. 在資源項中加入了彈出菜單
        2. 在CMenuView類中加入OnContextMenu(CWnd*, CPoint point)函數
 CWnd::OnContextMenu
 MSDN
 --------------------------------------------------------------------------------------
 CWnd::OnContextMenu
  Called by the framework when the user has clicked the right mouse button
        (right clicked) in the window.

  afx_msg void OnContextMenu(
     CWnd* pWnd,
     CPoint pos
  );

 Remarks
  You can process this message by displaying a context menu using the TrackPopupMenu.
 ---------------------------------------------------------------------------------------
 CMenu::TrackPopupMenu
 MSDN
 ---------------------------------------------------------------------------------------
 CMenu::TrackPopupMenu
  Displays a floating pop-up menu at the specified location and tracks the selection
  of items on the pop-up menu.

  BOOL TrackPopupMenu(
     UINT nFlags,             //彈出菜單時菜單顯示的位置(相對鼠標的位置)
     int x,                   //鼠標點擊的位置座標
     int y,
     CWnd* pWnd,              //彈出菜單的擁有者
     LPCRECT lpRect = 0       //指示一個矩形的區域,在這個矩形區域內點擊,彈出菜單不消失
  );                          //在矩形區域之外點擊彈出菜單消失
    -----------------------------------------------------------------------------------------
 CWnd::ClientToScreen
 MSDN
 ------------------------------------------------------------------------------------------
 CWnd::ClientToScreen
 Converts the client coordinates of a given point or rectangle on the display to
 screen coordinates.

  void ClientToScreen(
     LPPOINT lpPoint
  ) const;
  void ClientToScreen(
     LPRECT lpRect
  ) const;
    -------------------------------------------------------------------------------------------
 MenuView.cpp
 -------------------------------------------------------------------------------------------
 void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
 {
  // TODO: Add your message handler code here and/or call default

 
  CMenu menu;
  menu.LoadMenu(IDR_MENU1);

  CMenu* pPopup=menu.GetSubMenu(0);
  ClientToScreen(&point); //客戶區座標轉換爲屏幕座標
  //pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
        //this);//讓View窗口擁有彈出菜單
  pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
        GetParent());  //讓框架窗口擁有彈出菜單
  CView::OnLButtonDown(nFlags, point);
 }

 //DEL void CMenuView::OnShow()
 //DEL {
 //DEL  // TODO: Add your command handler code here
 //DEL  MessageBox("View Show!");
 //DEL }

 void CMenuView::OnShow()
 {
  // TODO: Add your command handler code here
  MessageBox("View Show!");
 }
 -------------------------------------------------------------------------------------------------
    小結:消息總是先由頂層窗口響應,如果頂層窗口類沒有消息響應函數,則立即交由底層窗口類響應

12.添加修改刪除動態菜單
   (1)動態添加菜單
   CMenu::AppendMenu
   MSDN
   -------------------------------------------------------------------------------------------------
 CMenu::AppendMenu //可以將菜單或菜單項添加到現有菜單的末尾
  Appends a new item to the end of a menu.

  BOOL AppendMenu(
     UINT nFlags,       //MF_POPUP 表示添加的是彈出菜單,MF_SEPARATOR分隔欄,MF_STRING 字符串
     UINT_PTR nIDNewItem = 0, //新菜單項的命令ID
     LPCTSTR lpszNewItem = NULL //指示菜單的名稱
  );
  BOOL AppendMenu(
     UINT nFlags,
     UINT_PTR nIDNewItem,
     const CBitmap* pBmp
  );
   ----------------------------------------------------------------------------------------------------
 (2)創建彈出菜單
 CMenu::CreatePopupMenu
 MSDN
 ---------------------------------------------------------------------------------------------------
 CMenu::CreatePopupMenu
  Creates a pop-up menu and attaches it to the CMenu object.

  BOOL CreatePopupMenu( );
    ---------------------------------------------------------------------------------------------------
 MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
   CMenu menu;
   menu.CreatePopupMenu();
   GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"VC");
   menu.Detach();      //將句柄與C++局部變量斷開
 }
 --------------------------------------------------------------------------------------

 (3)插入菜單
 CMenu::InsertMenu
 MSDN
 -----------------------------------------------------------------------------------------
 CMenu::InsertMenu
  Inserts a new menu item at the position specified by nPosition and moves other items
  down the menu.

  BOOL InsertMenu(
     UINT nPosition,
     UINT nFlags,            //MF_BYCOMMAND or MF_BYPOSITION
     UINT_PTR nIDNewItem = 0,
     LPCTSTR lpszNewItem = NULL
  );
  BOOL InsertMenu(
     UINT nPosition,
     UINT nFlags,
     UINT_PTR nIDNewItem,
     const CBitmap* pBmp
  );
   -------------------------------------------------------------------------------------------
 (4)在彈出菜單增加菜單項
 -------------------------------------------------------------------------------------------
 MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
   CMenu menu;
   menu.CreatePopupMenu();

   //GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"VC");
   GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"VC");
   menu.AppendMenu(MF_STRING,111,"Hello");
   menu.AppendMenu(MF_STRING,112,"VC");
   menu.AppendMenu(MF_STRING,113,"MFC");//添加3個菜單項

   GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Close");//在文件菜單添加菜單項
   GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,MF_BYCOMMAND |
    MF_STRING,115,"Windows");//在打開與新建之間加入菜單項
   menu.Detach();      //將句柄與C++局部變量斷開
 }
 --------------------------------------------------------------------------------------

 (5)刪除子菜單
 CMenu::DeleteMenu
 MSDN
 -----------------------------------------------------------------------------------------
 CMenu::DeleteMenu
  Deletes an item from the menu.

  BOOL DeleteMenu(
     UINT nPosition,
     UINT nFlags         //MF_BYCOMMAND or MF_BYPOSITION
  );
 -----------------------------------------------------------------------------------------
 MainFrm.cpp
 
 --------------------------------------------------------------------------------------
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
  CMenu menu;
  menu.CreatePopupMenu();

  GetMenu()->DeleteMenu(1,MF_BYPOSITION); //刪除編輯菜單
  GetMenu()->GetSubMenu(0)->DeleteMenu(ID_FILE_OPEN,MF_BYCOMMAND);//刪除文件菜單的打開項
  menu.Detach();      //將句柄與C++局部變量斷開
 }
 --------------------------------------------------------------------------------------

 (6)對動態添加的菜單項進行命令響應
 step1:在Resource.h中添加資源ID
 Resource.h
 ----------------------------------------------------------------
 #define IDM_HELLO     111
 ----------------------------------------------------------------
 step2:在頭文件中添加命令響應函數原型
 MainFrm.h
 ---------------------------------------------------------------------------
 protected:
  //{{AFX_MSG(CMainFrame)
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   // NOTE - the ClassWizard will add and remove member functions here.
   //    DO NOT EDIT what you see in these blocks of generated code!
  //}}AFX_MSG
  afx_msg void OnHello();
  DECLARE_MESSAGE_MAP()
 ----------------------------------------------------------------------------
 step3:在源文件中用ON_COMMAND宏關聯消息響應函數
 MainFrm.cpp
 -------------------------------------------------------------------------------
 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
  //{{AFX_MSG_MAP(CMainFrame)
   // NOTE - the ClassWizard will add and remove mapping macros here.
   //    DO NOT EDIT what you see in these blocks of generated code !
  ON_WM_CREATE()
  //}}AFX_MSG_MAP
  ON_COMMAND(IDM_HELLO,OnHello)
 END_MESSAGE_MAP()
 ------------------------------------------------------------------------------
 step4: 編寫消息響應函數
 MainFrm.cpp
 -------------------------------------------------------------------------------
 void CMainFrame::OnHello()
 {
  MessageBox("Hello!");
 }
 --------------------------------------------------------------------------------

13.製作電話本程序
 菜單欄重繪
 CWnd::DarwMenuBar
 MSDN
 ---------------------------------------------------------------------------------------
 CWnd::DrawMenuBar
  Redraws the menu bar.

  void DrawMenuBar( );
 Remarks
  If a menu bar is changed after Windows has created the window, call this function
  to draw the changed menu bar.
 -------------------------------------------------------------------------------------
 窗口重繪
 CWnd::Invalidate
 MSDN
 -----------------------------------------------------------------------------------
 CWnd::Invalidate
  Invalidates the entire client area of CWnd.

  void Invalidate(
     BOOL bErase = TRUE  //表示背景會被擦除
  );
 -------------------------------------------------------------------------------------
 CString::Find
 MSDN
 ------------------------------------------------------------------------------
 CString::Find
  int Find( TCHAR ch )  const;
  int Find( LPCTSTR lpszSub )  const;
  int Find( TCHAR ch, int nStart) const;
  int Find( LPCTSTR pstr, int nStart)  const;
 --------------------------------------------------------------------------------
 使用CStringArray類保存CString對象
 ------------------------------------------------------------------------------------------------
 CWnd::OnCommand
  This method is called by the framework when the user selects an item from a menu, when
  a child control sends a notification message, or when an accelerator keystroke is translated.

  virtual BOOL OnCommand(
  WPARAM wParam,
  LPARAM lParam );
 wParam
  The low-order word of wParam identifies the command ID of the menu item, control, or
  accelerator. The high-order word of wParam specifies the notification message if the
  message is from a control. If the message is from an accelerator, the high-order word
  is 1. If the message is from a menu, the high-order word is 0.
 lParam
  Identifies the control that sends the message if the message is from a control. Otherwise,
  lParam is 0.
 -------------------------------------------------------------------------------------------------
 獲取當前視類的指針
 CFrameWnd::GetActiveView
 MSDN
 ------------------------------------------------------------------------------------------------
 CFrameWnd::GetActiveView
  This method obtains a pointer to the active view, if any, attached to a frame window, CFrameWnd.

  CView* GetActiveView( )
  const;
 ---------------------------------------------------------------------------------------------------

Menu2 Project Source Code:
Menu2View.cpp
---------------------------------------------------------------------------------
void CMenu2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
 // TODO: Add your message handler code here and/or call default
 CClientDC dc(this);
 if (0x0d == nChar)
 {
  if (0==++m_nIndex)
  {
   m_menu.CreatePopupMenu();  
   //添加彈出菜單
   GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"PhoneBook");
   //重畫菜單欄,NOTE:菜單是與框架窗口相關,所以需要使用GetParent();
   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);
 
 }
 CView::OnChar(nChar, nRepCnt, nFlags);
}

void CMenu2View::OnPhone1()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 dc.TextOut(0,0,m_strArray.GetAt(0));

}

void CMenu2View::OnPhone2()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 dc.TextOut(0,0,m_strArray.GetAt(1));

}

void CMenu2View::OnPhone3()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 dc.TextOut(0,0,m_strArray.GetAt(2));

}

void CMenu2View::OnPhone4()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 dc.TextOut(0,0,m_strArray.GetAt(3));

}
-------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
//在框架窗口中截獲由視類響應的命令消息,通過覆蓋基類的OnCommand函數
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
 int MenuCmdID=LOWORD(wParam);//獲得命令消息ID
 CMenu2View* pMenu2View=(CMenu2View*)GetActiveView();//獲得視類的指針
 if (MenuCmdID>=IDM_PHONE1 && MenuCmdID <IDM_PHONE1+pMenu2View->m_strArray.GetSize())
 {
  CClientDC dc(pMenu2View);
  dc.TextOut(0,0,pMenu2View->m_strArray.GetAt(MenuCmdID-IDM_PHONE1));
  //MessageBox("Test");
  return TRUE;
 }

 return CFrameWnd::OnCommand(wParam, lParam);

發佈了21 篇原創文章 · 獲贊 20 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章