前言
本主要介绍菜单的基本使用,在MFC中用于指定菜单项位置有两种方式,它们是MF_BYCOMMAND和MF_BYPOSITION,在菜单操作中经常用到,具体含义如下:
MF_BYCOMMAND 是指将相关参数解释为菜单项ID(缺省值)
MF_BYPOSITION 是指将相关参数解释为零基偏移的菜单项,需要特别注意,菜单中的分隔符也占据一个位置。
如图1是一个MFC窗口,其中文件、编辑、视图所在的位置是菜单栏,其中“文件(F)”、“编辑(E)”都是子菜单,“新建(N)”,“保存(S)”等是菜单项。菜单项可以通过菜单项的标识ID进行访问,而子菜单只能通过索引号进行访问。
图1 MFC窗口
在CWnd类中,MFC提供了5个关于菜单的接口,同时MFC还为我们提供了CMenu类,该类是Windows HMENU的封装类。它提供了成员函数以用于创建、追踪、更新及销毁菜单,在文章末尾给出它们成员函数,这里主要介绍CWnd类的5个接口和CMenu类中基本菜单操作接口。
CWnd类菜单
DrawMenu函数用于重画菜单条。如果在Windows创建窗口以后菜单条发生了改变,则应调用这个函数以画出改变了的菜单条,声明如下:
void DrawMenuBar( );
GetMenu函数用于获得主窗口菜单栏指针,其函数声明如下:
CMenu* GetMenu( ) const;
GetSystemMenu用于应用程序访问控制菜单,用于拷贝和修改,其函数声明如下:
CMenu* GetSystemMenu( BOOL bRevert ) const;
HiliteMenuItem用于加亮显示一个顶层(菜单条)菜单项或清除其加亮显示状态,其函数声明如下:
BOOL HiliteMenuItem( CMenu* pMenu, UINT nIDHiliteItem, UINT nHilite );
SetMenu将当前菜单设为指定的菜单。它使窗口被重画以反映菜单的变化,其函数声明如下:
BOOL SetMenu( CMenu* pMenu );
CMenu类菜单操作
我们可以通过GetMenu获得菜单栏的指针,而子菜单的指针是通过CMenu类中的GetSubMenu来完成的,其函数声明如下:
CMenu* GetSubMenu( int nPos ) const;
由于程序的主菜单属于框架窗口,所以需要在框架窗口创建完成之后,再去访问菜单对象,我们可以在框架类的OnCreate函数return之前添加相应的代码,如果在CMainFrame类中没有OnCreate响应函数,需要我们手动添加该函数。
标记菜单
在视图子菜单下面有“工具栏(T)”和"状态栏(S)"两个菜单项,在它们前面都有一个对号(√),我们称这种类型的菜单为“标记菜单”,如图2所示。
图2 标记菜单
为了实现这样的标记菜单,我们可以调用CMenu中的CheckItem函数来完成这样的效果,其函数声明如下:
UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );
现在我们将“文件(F)”下的“打开(O)”菜单项设置为标记菜单,实现代码如下:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... //标记菜单 GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_OPEN, MF_BYCOMMAND | MF_CHECKED); return 0; }
运行效果:
默认菜单
在一些应用程序中的子菜单下有一个菜单项,它是以粗体形式出现的,以这种形式显示的就是该子菜单的默认菜单,我们可以利用SetDefaultItem函数来完成这个设置,其函数声明如下:
BOOL SetDefaultItem(UINT uItem,BOOL fByPos = FALSE);
实现代码如下:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... //默认菜单 GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_NEW, FALSE); return 0; }运行效果:
图形标记菜单
为了实现图形标记菜单项的效果,可以利用SetMenuItemBitmaps函数来显示,这个函数的作用是将制定的位图和菜单项关联起来,其函数声明如下:
BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked );在图形标记菜单中,显示的位图尺寸有固定的大小,其高度和宽度都是13*13。
代码实现如下:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... //图形标记菜单 m_BmpItem.LoadBitmap(IDB_BITMAP1); GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(2, MF_BYPOSITION, &m_BmpItem, &m_BmpItem); return 0; }运行效果:
禁用菜单
为了达到屏蔽某些菜单功能,我们可以使用EnableMenuItem函数来实现,该函数的功能是设置菜单状态:能够使用、禁用或变灰显示,其函数如下:
UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );为了将EnalbeMenuItem函数正常生效,需要在MainFrame构造函数中将m_bAutoEnableMenus设置为False,此时MFC菜单项的显示状态不会自动更新,需要我们自己维护菜单显示状态。
我们将m_bAutoEnableMenu设置为False后,那些没有设置功能的菜单项也都会显示为非灰色,造成误解,为了将某些功能禁用需要调用EnableMenuItem函数,具体代码:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... //禁用编辑下的所有菜单项功能 CMenu *SubEditMenu = GetMenu()->GetSubMenu(1); SubEditMenu->EnableMenuItem(0, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); SubEditMenu->EnableMenuItem(2, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); SubEditMenu->EnableMenuItem(3, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); SubEditMenu->EnableMenuItem(4, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); return 0; }运行效果:
移除和装载菜单
在程序中,如果希望移除一个菜单的话或同时加载一个新的菜单可以利用Cwnd类提供的setMenu函数来实现。移除和装载一个菜单代码实现如下:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... //默认的菜单栏从窗口中移除 SetMenu(NULL); //设置新的菜单栏 CMenu menu; menu.LoadMenu(IDR_MENU_TEST);//自定义的菜单栏 //将自定义菜单栏绑定到窗口 SetMenu(&menu); //将menu对象中的m_hMenu和菜单栏资源分离, //避免menu对象析构时,菜单栏资源也被析构 menu.Detach(); /* HMENU CMenu::Detach() { HMENU h = m_hMenu; m_hMenu = NULL; return h; } */ return 0; }运行效果:
快捷菜单
如果我们想要提供一个右键快捷菜单功能,我们可以添加ON_WM_CONTEXTMENU或者ON_WM_RBUTTONDOWN消息响应函数,在消息响应函数中调用TrackPopupMenu函数来完成,其函数声明如下:
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd,LPCRECT lpRect = NULL );
具体代码如下:
方式一:
void CCMenuView::OnContextMenu(CWnd* /*pWnd*/, CPoint point) { // TODO: 在此处添加消息处理程序代码 CMenu menu; VERIFY(menu.LoadMenu(IDR_MENU_RBUTTON)); CMenu* pPopup = menu.GetSubMenu(0); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); }
方式二:
void CCMenuView::OnRButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //该消息响应函数需要将转换鼠标点击坐标 ClientToScreen(&point); CMenu menu; VERIFY(menu.LoadMenu(IDR_MENU_RBUTTON)); CMenu* pPopup = menu.GetSubMenu(0); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); CView::OnRButtonDown(nFlags, point); }运行效果:
CMenu类的成员函数
构造函数
CMenu | 构造一个CMenu对象 |
初始化
Attach | 附加一个Windows菜单句柄给CMenu对象 |
Detach | 从CMenu对象中分离Windows菜单的句柄,并返回该句柄 |
FromHandle | 返回一个指向给定Windows菜单句柄的CMenu对象的指针 |
GetSafeHmenu | 返回由CMenu对象包含的m_hMenu值 |
DeleteTempMap | 删除由FromHandle成员函数创建的所有临时CMenu对象 |
CreateMenu | 创建一个空菜单,并将其附加给CMenu对象 |
CreatePopupMenu | 创建一个空的弹出菜单,并将其附加给CMenu对象 |
LoadMenu | 从可执行文件中装载菜单资源,并将其附加给CMenu对象 |
LoadMenuIndirect | 从内存的菜单模板中装载菜单,并将其附加给CMenu对象 |
DestroyMenu | 销毁附加给CMenu对象的菜单,并释放菜单占用的内存 |
菜单操作
DeleteMenu | 从菜单中删除指定的项。如果菜单项与弹出菜单相关联,那么将销毁弹出菜单的句柄,并释放它占用的内存 |
TrackPopupMenu | 在指定的位置显示浮动菜单,并跟踪弹出菜单的选择项 |
菜单项操作
AppendMenu | 在该菜单末尾添加新的菜单项 |
CheckMenuItem | 在弹出菜单的菜单项中放置或删除检测标记 |
CheckMenuRadioItem | 将单选钮放置在菜单项之前,或从组中所有的其它菜单项中删除单选钮 |
SetDefaultItem | 为指定的菜单设置缺省的菜单项 |
GetDefaultItem | 获取指定的菜单缺省的菜单项 |
EnableMenuItem | 使菜单项有效、无效或变灰 |
GetMenuItemCount | 决定弹出菜单或顶层菜单的项数 |
GetMenuItemID | 获取位于指定位置菜单项的菜单项标识 |
GetMenuState | 返回指定菜单项的状态或弹出菜单的项数 |
GetMenuString | 获取指定菜单项的标签 |
GetMenuItemInfo | 获取有关菜单项的信息 |
GetSubMenu | 获取指向弹出菜单的指针 |
InsertMenu | 在指定位置插入新菜单项,并顺次下移其它菜单项 |
ModifyMenu | 改变指定位置的已存在的菜单项 |
RemoveMenu | 从指定的菜单中删除与弹出菜单相关联 的菜单项 |
SetMenuItemBitmaps | 将指定检测标记的位图与菜单项关联 |
GetMenuCountextHelpID | 获取与菜单关联的帮助文本的ID号 |
SetMenuCountextHelpID | 设置与菜单关联的帮助文本的ID号 |