l 在resource view下,Mfc提供所见所得的资源编辑器,新建一个test菜单项,发现它的id项是灰色的,原因是它是弹出式菜单,如果把【弹出式】的钩取消了,发现id项变为可编辑的。Vc默认顶层菜单为弹出式菜单,这种菜单不能响应命令。
l 我们把test菜单项的【弹出式】取消掉,ID为IDM_TEST,用class wizard添加命令相应函数,在OnTest中添加MessageBox(“ MainFrame Clicked”)
l 测试菜单的命令路由:我们分别为CMainFrame,CMenuApp,CMenuDoc,CMenuView都添加IDM_TEST的响应函数,因为CMenuApp和CMenuDoc都不是从CWnd派生的,所以他们都没有messagebox函数,我们可以使用afxmessagebox函数。挨个测试发现最先响应的是view,doc,mainframe,最后是app。根据这个实验,我们发现菜单项的命令响应顺序依次是:视类,文档类,框架类,最后是应用程序。
l Windows中三类消息:标准消息(WM_COMMAND之外,所有WM开头,CWnd的派生都可以接收),命令消息(来自菜单,加速键,工具栏,CCmdTarget派生可以接收,WM_COMMAND),通告消息(控件产生,click等,WM_COMMAND,CCmdTarget派生可以接收)。凡是从CWnd派生的类,他们既可以接收标准消息,也可以接收命令消息和通告消息。而对于那些从CCmdTarget派生的类,则只能接收命令消息和通告消息,不能接受标准消息。Doc和App都是派生于CCmdTarget,所以他们可以接受菜单命令消息。
l
菜单结构:菜单很像房子(额。。。几层几号房那个意思)。
标记菜单:【查看】子菜单下的两个菜单项前面都带“勾”
我们实现这样的功能,在【文件】的子菜单中的【新建】菜单项添加一个标记,程序的主菜单属于框架窗口,所以需要在框架类窗口创建完成之后再去访问菜单对象,可以在框架类的OnCreate函数里添加这个功能。为了获得【文件】子菜单下的【新建】菜单项,首先要获得程序的菜单栏,也就是要在框架窗口中获得指向菜单栏的指针,CWnd的成员函数CMenu* CWnd::GetMenu()可以实现,为了获得子菜单,可以通过
CMenu* CMenu::GetSubMenu(int nPos)实现(找到楼层)。
为了设置标记菜单使用UINT CMenu::CheckMenuItem(UINT nIDCheckItem,UINT nCheck)
第一个参数指定需要处理的菜单项,第二个参数是以下的几个
MF_CHECKED设置复选标记
MF_UNCHECED移走复选标记
MF_BYPOSITION根据菜单项的位置来访问菜单项,即第一个参数指定的是索引号
MF_BUCOMMAND根据菜单项的命令来访问菜单项,即第一个参数指定的是命令id
在OnCreate中添加GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
这样就可以发现【文件,新建】下面多了一个标记符号
默认菜单选项:粗体形式显示的菜单项
BOOL CMEnu::SetDefaultItem( UINT uItem,BOOL fByPos=FALSE);
false时是第一个参数用id
true时用索引
在OnCreate中添加
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);或者
GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN,FALSE);
分割栏是占据索引位置的
一个子菜单只能有一个默认菜单
l
禁用菜单项:我们想实现这样的功能:禁用【文件,打开】菜单项。
UINT CMenu::EnableMenuItem(UINT nIDEnableItem,UINT nEnable);
第二个参数为:
MF_BYCOMMAND指定第一个参数是菜单项的id
MF_BYPOSITION指定第一个参数是菜单项的索引
MF_DISABLE
MF_ENABLE
MF_GRAYED
通常把MF_GRAYED和MF_DISABLE放一起用,但不是必须的
这个函数有点特殊,需要做些设置:一旦在CMainFrame的构造函数中把m_bAutoMenuEnable设置为false后,就不需要对ON_UPDATE_COMMAND_UI或ON_COMMAND消息进行响应处理,CMenu类的EnableMenuItem就能正常工作
在CMainFrame()中m_bAutoMenuEnable=FALSE;
在OnCreate中GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
m_bAutoMenuEnable被设置为FALSE后,MFC不再利用它的菜单更新机制去判断菜单的可用状态,所有设置由程序员做
l
移除和装载菜单
使用BOOL CWnd::SetMenu(CMene* pMenu);这个函数有个CMenu类型的指针参数,它指向一个新菜单对象。如果这个参数为null,则当前菜单被移除
在OnCreate中添加SetMenu(NULL);即可移除菜单。
现在我们再让菜单显示出来,在CMainFrame中添加成员变量menu。
注意:我们菜单是属于MainFrame的,
this->menu.LoadMenu(IDR_MAINFRAME);
this->SetMenu(&menu);
这样菜单又被再次装载进来
l
MFC菜单更新机制,菜单项状态的维护依赖于ON_UPDATE_COMMAND_UI消息。使用class wizard可以设置UPDATE_COMMAND_UI消息响应函数,CCmdUI中有
virtual void Enable(BOOL bOn=TRUE);true时菜单项可用,false则否
我们把【编辑,剪切】设置为可用
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable();
}
经过这个设置后,我们发现连工具栏的【剪切】也可用了,实际上工具栏的【剪切】的id和【编辑,剪切】的id是一样的
如果要把工具栏上的一个工具按钮和菜单栏中的某个菜单项相关联,只要将他们的id设置为同一个标识就可以了。
l 注意一下菜单项和工具按钮的索引位置计算方式不一样
l
右键快捷菜单:【projects】【add to projcts】【component and control】,add popup menu to哪个类时,不应该选择CMainFrame类,因为视类窗口始终覆盖在框架窗口之上,框架窗口中又捕获不到鼠标右键这一消息。所以我们选择CMenuView类。
我们为自己定制一个快捷菜单
首先,插入资源IDR_MENU1,添加显示和退出两项
其次,添加响应右键消息函数,加入: CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu* pPopup=menu.GetSubMenu(0);
pPopup->TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x,point.y,this );
CMenu::TrackPopupMenu
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );
nFlags指定菜单在屏幕上的显示位置
x和y分别指定快捷菜单显示位置处的x坐标和y坐标
pWnd指定快捷菜单的拥有者,也就是标识拥有快捷菜单的窗口对象
lpRect指定一块矩形,用户在这个区域内单击鼠标,快捷菜单保持原型。如果null则用户在这个快捷菜单范围之外单击时,菜单就消失
最后,为快捷菜单添加响应函数