1、Lesson 61MFC 中的顶层菜单默认为弹出菜单(Pop-up) ,它是不能用来作命令响应的,当取消Pop-up 选项后可接受命令响应。2MFC 中菜单项消息如果利用 ClassWizard 来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:View 类-Doc 类-CMainFrame 类-App 类。菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。菜单消息与前面介绍的标准消息的实现机制是相类似的,都是在消息响应函数原型(头文件) ,消息映射宏(源文件)和消息函数实现(源文件)中添加代码。注意:文档类与应用程序类都是由 CCmndTarget 类派生,所以可以接收
2、菜单命令消息,但不能接收标准消息(只能由 CWnd 类派生才可以) 。具体消息路由过程:当点击一个菜单项的时候,最先接受到菜单项消息的是 CMainFrame 框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口 View 类,由 View 类首先进行处理;如果 View类检测到没对该菜单项消息做响应,则 View 类把菜单项消息交由文档类 Doc 类进行处理;如果 Doc 类检测到 Doc 类中也没对该菜单项消息做响应,则 Doc 类又把该菜单项消息交还给 View 类,由 View 类再交还给 CMainFrame 类处理。如果 CMainFrame 类查看到CMainFram
3、e 类中也没对该消息做响应,则最终交给 App 类进行处理。3消息的分类:标准消息,命令消息,通告消息。标准消息 :除 WM_COMMAND 之外,所有以 WM_开头的消息。从 CWnd 类派生的类都可以接收到这一消息;命令消息 :来自菜单、加速键或工具栏按钮的消息。这类消息都以 WM_COMMAND 呈现。在 MFC 中,通过菜单项的标识(ID)来区分不同的命令消息;在 SDK 中,通过消息的 wParam 参数识别。从 CCmdTarget(CWnd 的父类)派生的类都可以接收到这一类消息;通告消息 :由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常
4、是对话框)通知事件的发生。这类消息也是以 WM_COMMAND形式呈现。从 CCmdTarget(CWnd 的父类)派生的类都可以接收到这一类消息。总结:凡是从 CWnd 派生的类,既可以接收标准消息,也要以接收命令消息和通告消息;而对于那些从 CCmdTarget 派生的类,则只能接收命令消息和通告消息,不能接收标准消息。4一个菜单拦可以有若干个子菜单,一个子菜单又可以有若干个菜单项等。对菜单栏的子菜单由左至右建立从 0 开始的索引。对特定子菜单的菜单项由上至下建立了从 0 开始的索引。访问子菜单和菜单项均可以通过其索引或标识(如果有标识的话)进行。相关重要函数:CMenu* GetMenu
5、( ) ;/CWnd:GetMenu 得到窗口菜单栏对象指针。CMenu* GetSubMenu( ) ;/CMenu:GetSubMenu 获得指向弹出菜单对象指针。UINT CheckMenuItem( );/CMenu:CheckMenuItem 添加选中标识。BOOL SetDefaultItem();/CMenu:SetDefaultItem 为指定菜单设置缺省菜单项。BOOL SetMenuItemBitmaps( );/CMenu:SetMenuItemBitmaps 设置位图标题菜单。UINT EnableMenuItem();/CMenu:EnableMenuItem 使菜单
6、项有效,无效,或变灰。BOOL SetMenu( CMenu* pMenu );/CWnd:SetMenu 在当前窗口上设置新菜单或移除菜单。HMENU Detach( );/CMenu:Detach 断开一个菜单资源与相关的类对象句柄关联,可以定义局部对象,在使用完后调用 Detach 函数,则不会因为局部对象影响使用。注意:1)在计算子菜单菜单项的索引的时候,分隔栏符也算索引的。2)int GetSystemMetrics()获取系统信息度量。可以用它来获取菜单标题的尺寸从而设置位图标题菜单中位图的大小。3)在 MFC 中 MFC 为我们提供了一套命令更新机制,所有菜单项的更新都是由这套机
7、制来完成的。所以要想利用 CMenu:EnableMenuItem 来自己控制菜单使用或不使用变灰等,必须要在 CMainFrame 的构造函数中将变量 m_bAutoMenuEnable 设置为 FALSE。4)EXAMPLE:CMenu menu; /定义为局部对象menu.LoadMenu(IDR_MAINFRAME);SetMenu(menu.Detach(); /这里 menu 对象作为一个局部对象。使用 Detach()从 menu 对象中分离窗口菜单句柄,从而当 menu 对象析构的时候窗口菜单资源不随之销毁。5命令更新机制:菜单项状态的维护是依赖于 CN_UPDATE_COMM
8、AND_UI 消息,谁捕获CN_UPDATE_COMMAND_UI 消息,MFC 就在其中创建一个 CCmdUI 对象。在后台操作系统发出 WM_INITMENUPOPUP 消息,然后由 MFC 的基类如 CFrameWnd 接管并创建一个 CCmdUI 对象和第一个菜单项相关联,调用对象成员函数 DoUpdate()(注:这个函数在 MSDN 中没有找到说明)发出 CN_UPDATE_COMMAND_UI 消息,这条消息带有指向 CCmdUI 对象的指针。此后同一个 CCmdUI 对象又设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。更新命令 UI 处理程序仅应用于弹出式菜单项
9、上的项目,不能应用于永久显示的顶级菜单项目。说明:可以手工或用 ClassWizard 来给菜单项添加 UPDATE_COMMAND_UI 消息响应,利用响应函数中传进来的 CCmdUI 对象指针可完成设置菜单项可使用,不可使用,变灰,设置标记菜单等操作。6如果要想让工具栏上的某个图标与菜单项的某个菜单相关联,只需要将图标的 ID 设置为该菜单项的 ID。工具栏图标的索引记数顺序是:从做至右从 0 开始,分隔符也算索引号。7利用向项目中添加 VC 的 POPMENU 控件:Project-Add to Project-Components and Controls。系统增加的内容:A,一个菜单
10、资源; B,在派生 View 类中增加了 OnContextMenu()函数。8利用调用 TrackPopupMenu 函数,手工添加弹出菜单:1)用资源管理器添加一个菜单资源2)在鼠标右键消息响应函数中,加载菜单资源,并获得要显示的子菜单指针,并用该指针调用 TrackPopupMenu 函数便完成任务(但要注意:鼠标响应函数传进来的坐标是客户区坐标,而 TrackPopupMenu 函数中使用的是屏幕坐标,在调用 TrackPopupMenu 前要调用ClientToScreen 客户区坐标到屏幕坐标的转换)事例代码:CMenu menu;menu.LoadMenu(IDR_MENU1);
11、CMenu *pPopup=menu.GetSubMenu(0);ClientToScreen(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 rectangl
12、e 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 CMe
13、nu 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手动给动态
14、菜单项添加响应函数:在 Resource.h 中可以添加资源的 ID在头文件中写消息函数原型在代码文件中添加消息映射和添加消息响应函数说明:可以先创建一个临时的菜单项,设置它的 ID 和动态菜单项的一致,然后对它用向导进行消息响应,然后删除临时菜单。再在代码文件中把消息映射放到宏外(注意一定要放到宏外面,因为 CLASSWIZARD 发现菜单删除了,同时要把其宏对里的消息映射也删除掉的)12CWnd:DrawMenuBar/Redraws the menu bar. If a menu bar is changed after Windows has created the window, c
15、all this function to draw the changed menu barCWnd:GetParent /get a pointer to a child windows parent window (if any). CWnd:Invalidate /注意其参数的作用13集合类:CStringArray,CStringArray ,CDWordArray ,CPtrArray ,CStringArray,CUIntArray,CWordArray其中成员函数:CArray:GetAt CArray:Add14命令消息是到 OnCommand 函数的时候完成路由的。由于 CW
16、nd:OnCommand 是个虚函数,可以在框架类中重写 OnCommand 函数,从而可以截获菜单消息使它不再往下(VIEW 类)路由。例:BOOL CMainFrame:OnCommand(WPARAM wParam, LPARAM lParam) / TODO: Add your specialized code here and/or call the base classint MenuCmdId=LOWORD(wParam);/取命令 IDCMenu2View *pView=(CMenu2View*)GetActiveView();/获取当前 VIEW 类指针if(MenuCmdI
17、d=IDM_PHONE1 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类处理15LOWORD 与 HIWORD 宏WORD LOWORD(DWORD dwV
18、alue / 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.16CFrameWnd:GetActiveView CView* GetActiveView( ) const;/获取当前视窗口指针(单文档框架中)17源文件是单独参与编译的。