首先,在MFC中,具有PopUp属性的菜单是不能用来做命令响应的。
在MFC中:
当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。
所以,其消息传递顺序为:View类--Doc类--CMainFrame类--App类。当然,菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数
windows消息的分类
(1)标准消息:除WM_COMMAND之外,所有以WM_开头的消息。从CWnd类派生的都可以接收到这些消息。
(2)命令消息:来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
(3)通告消息:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。——————从CCmdTarget派生的类,都可以接收到[命令消息]和[通告消息]。
在menu操作过程中,几个函数需要了解:
1 CMenu* GetMenu( ) ;//CWnd::GetMenu得到窗口菜单栏对象指针。 2 CMenu* GetSubMenu( ) ;//CMenu::GetSubMenu获得指向弹出菜单对象指针 3 UINT CheckMenuItem( );//CMenu::CheckMenuItem添加(add)check marks(选择标记)或者是删除(remove)check marks,是针对菜单项的作用 4 BOOL SetDefaultItem();//CMenu::SetDefaultItem 5 BOOL SetMenuItemBitmaps( );//CMenu::SetMenuItemBitmaps 设置位图标题菜单。 6 UINT EnableMenuItem();//CMenu::EnableMenuItem使菜单项有效,无效,或变灰。 7 BOOL SetMenu( CMenu* pMenu );//CWnd::SetMenu在当前窗口上设置新菜单或移除菜单 8 HMENU Detach( );//CMenu::Detach Detaches a Windows menu from a CMenu object and returns the handle.
创建一个menu项的简单过程:
1 CMenu menu;//定义为局部对象 2 menu.LoadMenu(IDR_MAINFRAME); 3 SetMenu(&menu); 4 menu.Detach();//因为局部对象
这里menu对象作为一个局部对象。使用Detach()从menu对象中分离窗口菜单句柄,从而当menu对象析构的时候窗口菜单资源不随之销毁。
命令更新机制:
菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。
在后台操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd接管并创建一个CCmdUI对象和第一个菜单项相关联,调用对象成员函数DoUpdate()(注:这个函数在MSDN中没有找到说明)发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。此后同一个CCmdUI对象又设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。
更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。
利用调用TrackPopupMenu函数,创建ContextMenu(右键菜单):
1 void CMenu2View::OnRButtonDown(UINT nFlags, CPoint point) 2 { 3 CMenu menu; 4 menu.LoadMenu(IDR_MENU1); 5 CMenu *pPopup = menu.GetSubMenu(0); 6 ClientToScreen(&point);//将一个坐标点或一个矩形区域坐标转换成屏幕坐标。 7 pPopup->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,GetParent());//在指定位置以指定的方式显示弹出菜单。 8 CView::OnRButtonDown(nFlags, point); 9 }
1)用资源管理器添加一个菜单资源
2)在鼠标右键消息响应函数中,加载菜单资源,并获得要显示的子菜单指针,并用该指针调用TrackPopupMenu函数便完成任务(但要注意:鼠标响应函数传进来的坐标是客户区坐标,而TrackPopupMenu函数中使用的是屏幕坐标,在调用TrackPopupMenu前要调用ClientToScreen客户区坐标到屏幕坐标的转换)
3)弹出菜单上的消息,在路由的时候,仍然遵循View-DOC-MainFrame-APP的响应顺序。
动态菜单编程:
一些有关的比较重要的函数:
1 CMenu::AppendMenu //Appends a new item to the end of a menu. 2 CMenu::CreatePopupMenu //Creates an empty pop-up menu and attaches it to a CMenu object. 3 CMenu::InsertMenu 4 //Inserts a new menu item at the position specified by nPosition and moves other items down the menu. 5 CMenu::GetSubMenu //Retrieves a pointer to a pop-up menu. 6 CWnd::GetMenu //Retrieves a pointer to the menu for this window. 7 CMenu::DeleteMenu //Deletes an item from the menu.
1 CView* GetActiveView( ) const;//获取当前视窗口指针(单文档框架中)