菜单栏、工具栏和状态栏是组成Windows程序图形界面的三个主要元素。大多数Windows程序都提供了菜单,作为用于与应用程序之间交互的一种途径。本节主要介绍与菜单相关的编程知识。
创建一个单文档的MFC应用程序,并运行,运行结果如下:
此时单击文件->新建,是没有反应的。因为此时它还没有添加它的处理函数。按下图菜单项上点鼠标右键,弹出菜单中选择 添加事件处理程序。
void CMainFrame::OnFileNew()
{
// TODO: 在此添加命令处理程序代码
MessageBox(TEXT("新建"));
}
运行结果如下:
按照上面讲到的方法依次为CMainFrame之外的其他类,视图类、文档类和应用程序类中添加菜单命令响应函数。
因为CMenuApp和CMenuDoc类都不是从CWnd类派生出来的,所有他们没有MessageBox成员函数,我们可以使用全局的MessageBox函数,或者使用应用程序框架的函数AfxMessageBox,这里我们使用的后者。
此时执行程序:发现弹出的是“VIew cliecked”,
这就是说视类最先响应了这个菜单命令。关闭这个提示框在没有其他信息显示,说明其他几个菜单命令响应函数没有起作用。
下面,我们将视图类的OnMyTest响应函数删除,再次运行程序,我们会发现是文档类做出了响应。后面依次删除、运行。
实验发现:响应【新建】菜单项命令的顺序是:视图类、文档类、框架类,最后才是应用程序类。
我们想实现这样的功能:在【文件】子菜单中的【新建】菜单项上添加一个标记。因为程序的主菜单属于框架窗口,所以需要在框架类窗口创建完成之后再去访问菜单对象。可以在框架类的OnCreate函数的最后添加实现这个功能的代码。
/* 在框架窗口中获得指向菜单栏的指针 */
CMenu* pmenu = GetMenu();
/* GetSubMenu: 获取一个菜单的子菜单功能,参数一般置为0
CheckMenuItem:根据位置或ID为菜单项添加一个标记,或者移除菜单项的标记
*/
pmenu->GetSubMenu(0)->CheckMenuItem(0, MF_BYPOSITION | MF_CHECKED);
//pmenu->GetSubMenu(0)->CheckMenuItem(ID_FILE_SAVE, MF_BYCOMMAND | MF_CHECKED);
运行结果如下:
有些应用程序的子菜单下有一个菜单项是以粗体形式显示的,以这种形式显示的就是该子菜单的默认菜单项。 注意:一个子菜单只能有一个默认菜单项。
/* 获取菜单项 */
Menu* pmenu = GetMenu();
/* 利用位置索引的方式来实现,这时,SetDefaultItem函数的第二个参数应该设置为TRUE; */
pmenu->GetSubMenu(0)->SetDefaultItem(0, TRUE);
/* 利用菜单项标识的方式来实现,这时,SetDefaultItem函数的第二个参数应该设置为FALSE。 */
pmenu->GetSubMenu(0)->SetDefaultItem(ID_FILE_NEW, FALSE);
实现功能:屏蔽某一子菜单下某个菜单项的功能。这里我们将要禁用【文件】子菜单下的【打开】菜单项的功能,即将其置灰。
利用CMenu类的成员函数:EnableMenuItem来完成。其声明形式如下所示:
UINT EnableMenuItem( UINT nIDEnabCCleItem, UINT nEnable );
返回以前的状态(MF_DISABLED, MF_ENABLED, 或MF_GRAYED),如果无效,则返回-1。
nIDEnableItem 指定由nEnable决定的将要有效的菜单项。该参数既可以指定弹出菜单项,也可以指定标准菜单项。
nEnable 指定了将要进行的动作。它可以是MF_DISABLED, MF_ENABLED,或MF_GRAYED与 MF_BYCOMMAND或 MF_BYPOSITION的组合。这些值通过位与操作进行组合。这些值有下列含义:
/* 获取菜单项 */
CMenu* pMenu = GetMenu();
/*使用位置索引的方式实现 */
pMenu->GetSubMenu(0)->EnableMenuItem(1, MF_BYPOSITION|MF_DISABLED|MF_GRAYED);
/* 利用菜单项的ID实现 */
pMenu->GetSubMenu(0)->EnableMenuItem(1, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
需要注意的是默认情况下,所有菜单项的更新都是由MFC的命令更新机制完成的。如果我们想自己更改菜单项的状态,那就必须先把m_bAutoMenuEnable变量设置为FALSE,之后我们自己对菜单项的状态更新才能起作用。因此,我们就在程序的CMainFrame类构造函数中把m_bAutoMenuEnable这个变量初始化为FALSE,
运行结果如下:
我们平时在使用程序时,经常会用到单击鼠标右键显示快捷菜单(也称上下文菜单,右键菜单)这一功能。注意:如果要把工具栏上的一个工具按钮与菜单栏中的某个菜单项相关联,只要将它们的ID设置为同一个标识就可以了。如果想要自己实现这个功能,需要通过以下步骤来完成:
1、为程序添加一个新的菜单资源。
可在【资源视图】选项卡上的Menu分支上单击鼠标右键,从弹出的菜单中选择【添加资源】菜单命令,这时,在Menu分支下就多了一个名为IDR_MENU1的菜单资源,并同时在VS开发界面窗口的右边窗口中打开了这个菜单资源。接着就要为这个菜单资源添加菜单项了。因为在显示快捷菜单时顶级菜单是不出现的,所以可以给它设置任意的文本。
2、给视图类添加WM_RBUTTONDOWN消息响应函数。
CMenu menu;
/* 根据ID加载一个菜单 */
menu.LoadMenuW(IDR_MENU1);
/* 获取将要弹出的菜单 */
CMenu* pPopMenu = menu.GetSubMenu(0);
/* 将客户区的坐标转换成屏幕坐标 */
ClientToScreen(&point);
/* 弹出快捷菜单 */
pPopMenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,GetParent());
/*
函数详细信息如下:
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd,LPCRECT lpRect = NULL );
参数:
nFlags 指定屏幕位置标志或鼠标键标志。
屏幕位置标志可以为下列值之一:
TPM_CENTERALIGN 使弹出菜单在水平方向相对于x指定的坐标居中。
TPM_LEFTALIGN 放置弹出菜单,以便弹出菜单在由坐标值x指定的位置左对齐。
TPM_RIGHTALIGN 放置弹出菜单,以便弹出菜单在由坐标值x指定的位置右对齐。
鼠标键标志可以为下列值之一:
TPM_LEFTBUTTON 导致弹出菜单追踪鼠标左键。
TPM_RIGHTBUTTON 导致弹出菜单追踪鼠标右键。
x 指定弹出菜单屏幕坐标的水平位置。根据参数nFlags的值,该菜单可以左对齐、右对齐或在该位置上居中。
y 指定弹出菜单屏幕坐标的垂直位置。
pWnd 标识拥有弹出菜单的窗口。该窗口接收来自菜单的所有WM_COMMAND消息。在Windows 3.1或以后版本中,窗口直到TrackPopupMenu返回才接收WM_COMMAND消息。在Windows 3.0中,窗口在TrackPopupMenu返回之前接收WM_COMMAND消息。
lpRect 指向RECT结构或包含矩形的屏幕坐标CRect对象,用户在该矩形内部单击鼠标也不会消除弹出菜单。若该参数为NULL ,那么用户在该矩形外部单击鼠标,弹出菜单将消失。对于Windows 3.0,该值必须为NULL。
void ClientToScreen( LPPOINT lpPoint ) const;
void ClientToScreen( LPRECT lpRect ) const;
参数:
lpPoint 指向一个POINT结构或CPoint对象,其中包含了要转换的客户区坐标。
lpRect 指向一个RECT结构或CRect对象,其中包含了要转换的客户区坐标。
*/
运行结果如下:
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0,LPCTSTR lpszNewItem = NULL );
/*
该函数的作用是把一个新菜单项目添加到一个指定菜单项目的末尾。
参数:
nFlags
指定了增加到菜单中的新菜单项状态的有关信息。它包括说明中列出的一个或多个值。下面列出的是nFlags可以设置的值:
MF_CHECKED 该值的行为如同使用MF_UNCHECKED来作为一个标记,用于替换项前的检测标记。若应用支持检测标记位图(请参阅SetMenuItemBitmaps成员函数),那么将显示“检测标记打开”位图。
MF_UNCHECKED 该值的行为如同使用MF_CHECKED来作为一个标记,用于删除项前的检测标记。若应用支持检测标记位图(请参阅SetMenuItemBitmaps成员函数),那么将显示“检测标记关闭”位图。
MF_DISABLED 使菜单项无效以便它不能被选择,但菜单项不变灰。
MF_ENABLED 使菜单项有效以便它能够被选择,并从灰色状态中恢复原样。
MF_GRAYED 使菜单项无效以便它不能被选择,同时使菜单项变灰。
MF_MENUBARBREAK 在静态菜单里的新行中或弹出菜单的新列中放置菜单项。新的弹出菜单列与老的菜单列将由垂直分割线分开。
MF_MENUBREAK 在静态菜单里的新行中或弹出菜单的新列中放置菜单项。列与列之间没有分割线。
MF_OWNERDRAW 指定菜单项为一个拥有者描绘的项。当菜单首次显示时,拥有该菜单的窗口将接收WM_MEASUREITEM消息,以获取菜单项的高度与宽度。WM_DRAWITEM消息将使属主窗口必须更新菜单项的可视界面。该选择项对于顶层菜单项无效。
MF_POPUP 指定菜单项有与之相关联的弹出菜单。参数ID指定了与项相关联的弹出菜单的句柄。它用于增加顶层弹出菜单项或用于增加弹出菜单项的下一级弹出菜单。
MF_SEPARATOR 绘制一条水平的分割线。它仅仅能用于弹出菜单项。该线不能变灰、无效或高亮度显示。其它的参数将被忽略。
MF_STRING 指定菜单项为一个字符串。
下面列出的各组标志互相排斥,不能一起使用:
MF_DISABLED, MF_ENABLED,和 MF_GRAYED
MF_STRING, MF_OWNERDRAW, MF_SEPARATOR和位图版本。
MF_MENUBARBREAK和MF_MENUBREAK
MF_CHECKED 和MF_UNCHECKED
nIDNewItem
指定了新菜单项的命令ID号,或如果nFlags被设置为MF_POPUP,该参数指定弹出菜单的菜单句柄(HMENU),否则就是添加新菜单项的命令ID。如果nFlags被设置为MF_SEPARATOR,那么参数NewItem将被忽略。
lpszNewItem
lpszNewItem指定了新菜单项的内容。参数nFlags以下列方式解释lpszNewItem:
nFlags lpszNewItem的解释
MF_OWNERDRAW 包含一个应用支持的32 位值,应用可以用于维护与菜单项关联的附加数据。当应用进行了WM_MEASUREITEM和WM_DRAWITEM,该32位值有效。这些信息存储在提供这些消息的结构的itemData成员中
MF_STRING 包含一个指向以空字符终止的字符串的指针。这是它的缺省说明
MF_SEPARATOR 参数lpszNewItem被忽略
*/
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
在已有菜单项目之间添加新的菜单项。
参数:
nPosition
指定将要插入的新菜单项前的菜单项。参数nFlags被用于以下列方式解释:
MF_BYCOMMAND 指定参数给出已存在菜单项的命令ID号。如果没有设置MF_BYCOMMAND或MF_BYPOSITION,那么此为缺省值
MF_BYPOSITION 指定参数给出已存在菜单项的位置。第一项位于位置0。如果nPosition为-1,那么新菜单项将添加到菜单尾
nFlags
指定nPosition值如何被解释,并指定要增加到菜单中新菜单项的状态。对于能够被设置的标志,请参阅成员函数AppendMenu。如果需要指定多个值,需使用位与操作来组合MF_BYCOMMAND或MF_BYPOSITION标志。
nIDNewItem
指定新菜单项的命令ID号,或者,若nFlags被设置为MF_POPUP,则指定为弹出菜单的菜单句柄(HMENU)。若nFlags被设置为MF_SEPARATOR,那么参数nIDNewItem将被忽略。
lpszNewItem
指定新菜单项的文本。nFlags被用于以下列方式解释lpszNewItem:
MF_OWNERDRAW 该值含有应用用于包含菜单项附加数据应用提供的32位值。该32位值在由WM_MEASUREITEM和WM_DRAWITEM消息提供的itemData结构成员里对于应用有效。这些消息的发送是在菜单项最初显示或更改时发生
MF_STRING 包含指向以空格位终止符的字符串指针。它为缺省解释
MF_SEPARATOR 参数lpszNewltem将被忽略
BOOL DeleteMenu( UINT nPosition, UINT nFlags );
删除指定菜单项
参数:
nPosition
指定由nFlags决定的将要删除的菜单项。
nFlags
以下列方式解释nPosition:
MF_BYCOMMAND 指定参数给出已存在菜单项的命令ID号。如果没有设置MF_BYCOMMAND或MF_BYPOSITION,那么此为缺省值
MF_BYPOSITION 指定参数给出已存在菜单项的位置。第一项位于位置0
利用这个函数可以删除一个菜单项目,包括子菜单,以及子菜单下的菜单项,主要取决于调用这个函数的对象,如果该对象是程序的菜单栏对象,那么删除的就是指定的子菜单;如果该对象是一个子菜单对象,那么删除的就是该子菜单的下一个菜单项。
BOOL SetMenu( CMenu* pMenu );
移除一个菜单,这个函数有一个CMenu类型指针的参数,它指向一个新菜单对象。如果这个参数值为NULL,则当前菜单就被移除了。
LoadMenuW(id):装载一个菜单