现在的位置: 首页 > 综合 > 正文

MFC学习日记七:Mfc动态(静态)设置菜单项

2018年02月03日 ⁄ 综合 ⁄ 共 4065字 ⁄ 字号 评论关闭

Mfc命令更新机制:

        在应用程序中,菜单栏是一直要显示的,所以就要实时去更新它,显示它的即时状态(可用/不可用)。当我们要设置一个菜单不可用时,系统会发出一条消息WM_INITMENUPOPUP(可以理解为初始化菜单项弹出窗口消息) ,在此说明Mfc的命令更新仅适用于即弹出式菜单项(子菜单栏上的子菜单项),不能用于永久显示的菜单栏的菜单项(如上图所示);然后MFC的基类CFrameWnd接管这个消息,并且它创建一个CCmdUI对象与第一个菜单项相关联,调用一个成员函数DoUpdate()。这个函数发出CN_UPDATE_COMMAND_UI消息,这个消息带有一个CCmdUI类型的指针,它会去检测是否有接管这个消息的宏ON_UPDATE_COMMAND_UI,有就去调用对应的响应函数(即消息映射原理)进行处理,执行完成之后这个一开始创建的CCmdUI对象会与第二个菜单项相关联,执行同样的操作...依次下去。

       菜单栏菜单项的ID与工具栏的ID如果相同,则设置这个ID对应的菜单项不可用,对应菜单项和工具栏的工具项都会不可用。即如果想要把工具栏上的图标与菜单栏的菜单项相关联,只需要把他们的ID设置为一样的就可以了

设置菜单栏中的子菜单项剪切从不可用变为可用(从灰的变为黑色的可点击项):

pCmdUI->Enable();             //CCmdUI的成员函数

也可以做更高级的判断以下为设置新建菜单项不可用:

void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI) 
{
// TODO: Add your command update UI handler code here
//	if(ID_FILE_NEW==pCmdUI->m_nID)   //用ID判断是否为新建菜单项,m_nID为CCmdUI的成员变量
//	 pCmdUI->Enable(FALSE);
if(0==pCmdUI->m_nIndex)	 //用索引序号判断是否为新建菜单项,这种判断不能与工具条中的图标相关联
pCmdUI->Enable(FALSE);
}

设计一个右键弹出静态菜单:

1、首先在Resource(资源)下的Menu中新建一个菜单,双击用户界面编辑窗口中的手动增加菜单,再弹出的对话窗中填写右键菜单的父项(这个不会显示在实际应用程序
         中),随便输入一个
caption(标题),在其子项中添加我们想要的右键弹出菜单子项(即会在应用程序中显示);

2、要建立一个右键弹出菜单,就要响应鼠标右键消息,这里需要在CTestView类中增加一个WM_RBUTTONDOWN消息的响应函数OnRButtonDown(),在这个函数中,需要用
   到一个函数来显示我们的菜单,在
msdn中查到BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );参数分别是菜单显示的位置          (当弹出菜单显示的时候,鼠标是在弹出菜单的正中间还是左上角还是右上角等等)、xy是鼠标右键点击位置的xp坐标、这个弹出菜单输入哪个窗口、一个弹出菜单鼠标点击
  时也保持显示的区域;特别注意这里的
x/y坐标默认是整块屏幕的坐标,我们需要把它转移到应用程序窗口中来,这里要用一个msdn中查到的函                  
         数:
ClientToScreenLPPOINT lpPoint )。


void CMenu2View::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, GetParent());                  
//显示右键弹出菜单,GetParent()写到弹出菜单是CMainFrame拥有,当CTestView类中没有弹出菜单子项点击的响应函数的时候会去CMainFrame
//类中查找相应的响应函数;如果这里写的是this,那么不会去检查CMainFrame类中是否有弹出菜单子项的响应函数(可以在两个类中都加上响应函数试一下)
CView::OnRButtonDown(nFlags, point);
}

用代码动态添加左键弹出菜单:

1这里需要在CMainFrame框架类的OnCreate函数中去添加,这里我们在msdn中查到
兩个函数,这个函数可以实现两种方式的添加;

2一种是在上图所示的帮助后面追加一个菜单项:

BOOL AppendMenu( UINT nFlags,  UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );这个函数的参数分别表示:

      1):添加菜单的类型(有MF_POPUP(弹出菜单)、MF_SEPARATOR(分隔符)、   MF_STRING(字符串));

      2):Specifies either the command ID of the new menu item or, if nFlags is set to MF_POPUP,  the menu handle (HMENU) of a pop-up menu. The nIDNewItem parameter is ignored (not 
needed) if nFlags is set to MF_SEPARATOR.(
如果第一个参数设置为MF_POPUP(弹出菜 单)的时候,这个参数就是这个弹出菜单的一个CMenu的句柄,设置为MF_SEPARATOR(分隔符)时,这个参数可以省略,设置为MF_STRING时,这个参数表示菜单项的ID)

      3):是菜单项的名称

3

 CMenu menu;
menu.CreatePopupMenu();	 //创建一个空的弹出菜单与一个CMenu	变量相关联
//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");//添加一个ID为	IDM_HELLO的字符串类型菜单项到WinSun弹出菜单的子项中
menu.AppendMenu(MF_STRING,112,"WangDong");
menu.AppendMenu(MF_STRING,113,"Mybole");
//************Detaches a Windows handle from a CWnd object and returns the 	handle.(意思是解开一个CWnd对象窗口的句柄,返回一个句柄
menu.Detach();//这里就是防止OnCreate函数执行完后menu对象析构而造成的错误
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome");//这里是	在菜单栏的第一个菜单的子菜单中最后追加一个字符型的菜单项Welcome
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,MF_BYCOMMAND | 	MF_STRING, 115,"王东");//通过ID编号进行插入一个字符型的子菜单项
GetMenu()->DeleteMenu(1,MF_BYPOSITION);//删除菜单栏中的第二个菜单项(即	编辑)
GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);//删除菜单栏中第	一个菜单项下的第3个子菜单项(即打开)

现在设计好了弹出菜单,就需要对弹出菜单项进行命令响应:

想法:首先需要获取到我们想要响应的菜单项,这里就需要去VC编辑器坐下变的FILEVIEW中,在Header Files里的Resource.h添加一条声明ID的宏,是

#define IDM_HELLO 111

这样就将ID111Hello菜单项的宏设置为IDM_HELLO,然后由于前面是代码添加的菜单,所以无法在资源编辑器中手动点击ClassWizard来添加消息响应函数,这里就模仿之前点击添加消息响应函数所产生的代码(即消息响应机制):

实现:

1首先在CMainFrame框架类的头文件的声明消息映射DECLARE_MESSAGE_MAP()之前加入消息响应函数原型声明afx_msg void OnHello();

2然后在CMainFrame框架类的源文件的BEGIN_MESSAGE_MAP()END_MESSAGE_MAP()之间加入消息响应函数的一个宏ON_COMMAND(IDM_HELLO,OnHello) //注意
      这里不能加分号。

3然后在CMainFrame框架类的源文件的后面定义消息响应函数:

void CMainFrame::OnHello()
{
MessageBox("Hello!");
}

这样就完成了我们代码添加的菜单项的消息响应。


抱歉!评论已关闭.