文档库 最新最全的文档下载
当前位置:文档库 › MFC_菜单

MFC_菜单

MFC_菜单
MFC_菜单

********************************************************************* ****************************************

程序中会用到多种资源,在为资源确定其ID号时,为了明确区分资源类型,一般都遵循这样一个原则:①菜单资源ID号以“IDM_”开始;光标资源ID号以“IDC_”开始;图标资源ID号以“IDI_”开始。

********************************************************************* ****************************************

(CFrameWnd类、CDialog类和CView类)派生于CWnd类的有MessageBox成员函数,而CXXApp类和CXXDoc类都不是从CWnd类派生的,所以它们都没有MessageBox成员函数,只能使用应用程序框架的函数:AfxMessageBox。最保险的就是只用AfxMessageBox函数。

********************************************************************* ****************************************

程序类对菜单命令的响应顺序

自定义添加一个菜单项【test】:

打开ClassWizard对话框,在Class Name下拉列表框中选择CMainFrame之外的其他类,分别在视图类、文档类和应用程序类中添加菜单命令响应函数。

运行将会看到弹出的对话框上提示信息:“View Clicked!”,这就是说,视图类最先响应这个菜单命令。

下面,我们将视图类的OnTest响应函数删除,再次运行程序,我们会发现是文档类做出了响应。后面依次删除、运行。

实验发现:响应【test】菜单项命令的顺序是:视图类、文档类、框架类,最后才是应用程序类。

菜单命令消息路由的具体过程:当点击某个菜单项时,最先接收到这个菜单命令消息的是框架类。框架类将把接收到的这个消息交给它的子窗口,即视图类,由视类首先进行处理。视类首先根据命令消息映射机制查找自身是否对此消息进行了响应,如果响应了,就调用相应响应函数对这个消息进行处理,消息路由过程结束;如果视类没有对此命令消息做出响应,就交由文档类,文档类同样查找自身是否对这个菜单命令进行了响应,如果响应了,就由文档类的命令消息响应函数进行处理,路由过程结束。如果文档类也未做出响应,就把这个命令消息交还给视类,后者又把该消息交还给框架类。框架类查看自己是否对这个命令消息进行了响应,如果它也没有做出响应,就把这个菜单命令消息交给应用程序类,由后者来进行处理。

********************************************************************* ****************************************

标记菜单:

我们想实现这样的功能:在【文件】子菜单中的【新建】菜单项上添加一个标记(√)。

因为程序的主菜单属于框架窗口,所以需要在框架类窗口创建完成之后再去访问菜单对象。可以在框架类的OnCreate函数的最后(但一定要在return语句之前)添加实现这个功能的代码。

首先要获得程序的菜单栏,也就是要在框架窗口中获得指向菜单栏的指针,这可以通过CWnd的成员函数:GetMenu来实现:

该函数返回一个指向CMenu类对象的指针。CMenu类是一个MFC类,提供了一些与菜单操作有关的成员函数,其中就有获取一个菜单的子菜单功能的成员函数GetSubMenu函数。

为了设置一个标记菜单,需要使用CMenu类的CheckMenuItem这个函数,该函数的功能就是为菜单项添加一个标记,或者移除菜单项的标记。该函数声明如下:

具体实现代码:

********************************************************************* ****************************************

默认菜单项:

有些应用程序的子菜单下有一个菜单项是以粗体形式显示的,以这种形式显示的就是该子菜单的默认菜单项。

要实现的功能:将【文件】子菜单下的【打开】菜单项设置为该子菜单的默认菜单项。为了实现这种菜单项,可以利用CMenu类的SetDefaultItem成员函数来完成。这个函数的声明形式如下所示:

其代码实现方式有两种:①利用位置索引的方式来实现,这时,SetDefaultItem 函数的第二个参数应该设置为TRUE;②利用菜单项标识的方式来实现,这时,SetDefaultItem函数的第二个参数应该设置为FALSE。具体如下:

注意:一个子菜单只能有一个默认菜单项。

********************************************************************* ****************************************

分隔栏:

要实现的功能:在子菜单中添加分隔栏。

在新添加的子菜单的菜单项的属性页中进行修改,具体如下:

注意:分隔栏在子菜单中是占据索引位置的。

********************************************************************* ****************************************

图形标记菜单:

实现功能:将【查看】子菜单下的【工具栏】菜单项改成图形标记菜单项的效果,并指定当选中/取消该菜单项时,显示出不同的图形标记来表示它选中/取消的状态。

为了实现这种图形标记菜单的效果,可以利用CMenu类的SetMenuItemBitmaps 函数,这个函数的作用是将指定的位图与菜单项关联起来。该函数的声明如下所示:

为了显示图形标记菜单,首先就需要准备图形,可以新建一个位图资源。其次,可以在CMainFrame类的OnCreate函数中完成图形标记菜单的实现。

此时需注意:不能把位图作为局部变量来使用,否则,当OnCreate函数调用结束时,这个位图对象的生命周期也就结束了,在程序中就看不到位图了。所以要把位图对象设置为CMainFrame的成员变量。因此,为CMainFrame类添加两个CBitmap类型的成员变量:m_bitmap1和m_bitmap2,分别用来作选中/取消该菜单项状态表示的位图。

实现代码:

运行后,打开【查看】子菜单,将发现【工具栏】菜单项的前面并没有显示位图。失败的原因:图形标记菜单上显示的位图的尺寸有固定的标准,这里我们创建的位图太大了,只显示了当前位图左上角的一小部分内容,而正好我们创建的这个位图资源左上角的这块范围没有内容,因此,看到的结果就是位图没有显示。

通过GetSystemMetrics函数可以得到图形标记菜单上显示的位图的尺寸。这个函数的声明如下所示:

int GetSystemMetrics(

int nIndex // system metric or configuration setting to retrieve );

我们可以分别以“SM_CXMENUCHECK”和“SM_CYMENUCHECK”这两个参数值调用GetSystemMetrics函数两次,从而得到标记菜单项上位图的尺寸。于是,我们对上述程序中CMainFrame类的OnCreate函数进行改进,如下所示:

我们会发现:我们就需要把程序中位图资源的尺寸调整到13×13。当在位图资源编辑器中,用鼠标拖动位图外围的蓝色方框以改变位图资源大小时,在VC++开发环境窗口底部的状态条中有一格专门用来显示位图当前的尺寸信息,该信息会随着鼠标的拖动而变化。

********************************************************************* ****************************************

禁用菜单项:

实现功能:屏蔽某一子菜单下某个菜单项的功能。这里我们将要禁用【文件】子菜单下的【打开】菜单项的功能。

利用CMenu类的成员函数:EnableMenuItem来完成。该函数的作用是设置菜单

项的状态:能够使用、禁用或变灰显示。其声明形式如下所示:

实现代码:

问题:运行后,选择【文件】子菜单下的【打开】菜单项,但是这时仍会出现“打开文件”对话框,这说明【打开】菜单项未被禁用。

默认情况下,所有菜单项的更新都是由MFC的命令更新机制完成的。如果我们想自己更改菜单项的状态,那就必须先把m_bAutoMenuEnable变量设置为FALSE,之后我们自己对菜单项的状态更新才能起作用。

因此,我们就在程序的CMainFrame类构造函数中把m_bAutoMenuEnable这个变量初始化为FALSE,代码如下所示:

********************************************************************* ****************************************

移除和装载菜单:

(1)实现功能:移除一个菜单。

我们利用Cwnd类提供的SetMenu成员函数来实现,该函数的声明形式如下所示:

这个函数有一个CMenu类型指针的参数,它指向一个新菜单对象。如果这个参数值为NULL,则当前菜单就被移除了。

实现代码如下:

(2)实现功能:装载一个菜单资源并显示。我们可将上面程序中移除的菜单在这里再把它显示出来。

我们利用CMenu类提供的LoadMenu成员函数来实现,该函数的声明如下所示:

实现代码:

问题:这里定义的CMenu对象:menu是一个局部对象。虽然运行时未出现错误,但在窗口的操作过程中还是会出现一些错误,这都是因为CMenu对象menu是一个局部对象造成的。

解决问题:(1)将CMenu对象定义为CMainFrame类的一个成员变量;(2)仍把这个菜单对象定义为局部对象,但在调用SetMenu函数把此对象设置为窗口的菜单之后,立即调用CMenu类的另一个成员函数Detach,以便把菜单句柄与这个菜单对象分离。

CMenu类中的Detach函数声明如下所示:

实现代码:

********************************************************************* ****************************************

设置某个菜单项的状态:

如果要在程序中设置某个菜单项的状态,首先通过ClassWizard为这个菜单项添加UPDATE_COMMAND_UI消息响应函数,然后在这个函数中进行状态的设置即可。

当程序框架捕获到了CN_UPDATE_COMMAND_UI消息后,最终还是交由该消息的响应函数来处理,我们会发现在这个函数有一个CCmdUI指针类型的参数,利用这个CCmdUI类,可以决定一个菜单项是否可以使用(Enable函数)、是否有标记(SetCheck函数),还可以改变菜单项的文本(SetText函数)。

实现功能:改变【编辑】子菜单下的【剪切】菜单项的状态。

实现代码:

我们可从工具栏上的剪切按钮观察代码修改后的结果。

注意:如果要把工具栏上的一个工具按钮与菜单栏中的某个菜单项相关联,只

要将它们的ID设置为同一个标识就可以了。

*********************************************************************

****************************************

快捷菜单:

我们在VC++开发界面中选择【Project】菜单下的【Add To Project】,然后从它的下拉菜单中选择【Components and Controls….】菜单项。这时会出现如下图所示的组件和控件库对话框,双击Visual C++ Components目录,从而打开这个目录。该目录下的内容如下图所示,其中可以看到有一个名称为“Pop-up Menu”的组件,这个组件的作用就是给一个派生于CWnd类的窗口添加一个右键菜单。选中此项,然后单击【Insert】按钮,系统会提示用户是否确认当前的插入操作,单击【确定】按钮,确认此操作即可。

随后又会出现“Pop-up Menu”对话框,用来设置新添加的这个弹出菜单将添加到哪个窗口上。注意:这里不应该选择CMainFrame类,因为视类窗口始终是覆盖在框架类窗口之上的,框架窗口接收不到鼠标消息,而右键菜单是在鼠标右

键单击时产生的,而在框架窗口中又捕获不到鼠标右键单击这一消息,也就无

法显示快键菜单。所以,这里应该选择CXXView类。在Menu resource ID编辑框中显示的是快键菜单的资源ID,我们可以在这里修改这个标识,也可以以后

再修改。

这时,在本工程中可以看到,在插入了这个Pop-up Menu组件后,添加了以下两处内容:

第一处:在ResourceView选项卡的Menu分支下多了一个标识为

CG_IDR_POPUP_TEST1_VIEW的菜单资源,这个菜单只有一个顶层菜单项:

_Pop-up_。

第二处:为CXXView类添加了一个函数:OnContextMenu。在本工程运行时,当用鼠标右键单击窗口时,程序就会调用这个函数。其源代码如下:

可以看到,这个函数内部调用了TrackPopupMenu函数来显示一个快捷菜单。该函数声明形式如下:

既然我们知道了TrackPopupMenu这个函数就是用来显示快捷菜单的,就可以在程序中实现自己的快捷菜单了。这可以通过以下几个步骤来实现:

(1)为程序增加一个新的菜单资源。可在ResourceView选项卡上的Menu分支上单击鼠标右键,从弹出的菜单中选择【Insert Menu】菜单命令,这时,在Menu 分支下就多了一个名为IDR_MENU1的菜单资源,并同时在VC++开发界面窗口的右边窗口中打开了这个菜单资源。接着就要为这个菜单资源添加菜单项了。因为在显示快捷菜单时顶级菜单是不出现的,所以可以给它设置任意的文本。

(2)给CXXView类添加WM_RBUTTONDOWN消息响应函数。实现代码如下所示:

问题:运行后发现这个快捷菜单显示的位置好像不太对,并不是在鼠标右键单击点处显示的。这是因为TrackPopupMenu函数中的x和y参数都是屏幕坐标,而鼠标单击点处的坐标是窗口客户区坐标,即以程序窗口左上角为坐标原点。

解决:需要把客户区坐标转换为屏幕坐标。我们须在调用TrackPopupMenu函数之前调用ClientToScreen函数。该函数声明形式如下:

在调用TrackPopupMenu函数之前添加下面这行代码完成坐标转换:

(3)为程序添加快捷菜单上各菜单项命令的响应函数。这里无需为IDR_MENU1这个资源创建一个新类或者选择一个已有类,对“Adding Class”对话框不用做出处理,单击【Cancel】按钮取消此对话框即可。然后利用ClassWizard,分别为CMainFrame类和CXXView类添加一个响应【显示】菜单项的函数,运行后你会发现是CXXView类中添加的响应在处理这个消息。我们将CXXView类对【显示】菜单命令消息的响应函数删除,再次运行,可是发现CMainFrame类对【显示】菜单命令消息的响应函数还是没有反应。这主要是因为在创建快捷菜单时,即调用TrackPopupMenu函数时,对这个快捷菜单的拥有者参数传递的是this值,也就是视类窗口拥有这个快捷菜单。因此,只有视类才能对快捷菜单项命令做出响应。如果想让CMainFrame类能对这个快捷菜单项进行响应的话,就应该在调用TrackPopupMenu函数时把快捷菜单的拥有者指定为CMainFrame类窗口,为此我们可以修改CXXView类OnRButtonDown函数中队TrackPopupMenu函数的调用,如下:

注意:即便如此,框架类窗口能有机会获得对该快捷菜单中的菜单项的命令响应,但最先响应还应是CXXView类窗口,除非CXXView类窗口没有设置快捷菜单的函数,才能由框架类窗口做出响应。这可由菜单命令消息路由的过程来解释。

********************************************************************* ****************************************

动态添加菜单项目:

实现功能:通过代码来动态地在已有菜单项目后面添加新的菜单项目。

我们可以利用CMenu类提供的一个成员函数:AppendMenu来完成。该函数的作用是把一个新菜单项目添加到一个指定菜单项目的末尾。该函数具体声明形式如下:

实现代码:

********************************************************************* ****************************************

插入菜单项目:

实现功能:通过代码来动态地在已有菜单项目之间添加新的菜单项目。

我们可以利用CMenu类的InsertMenu成员函数来实现,该函数的声明形式如下:

实现代码:

同样可以使用AppendMenu函数来实现,我们可以在上述例子新的【Test】子菜单下添加三个新的菜单项。

实现代码:

如果要在程序的【文件】子菜单下最后添加一个菜单项,实现代码如下:

GetMenu()->GetSubMenu(0)->AppendMenu( MF_STRING,114,"Welcome" );

如果要在程序的【文件】子菜单的【新建】和【打开】菜单项之间插入一个新菜单项,实现代码如下:

********************************************************************* ****************************************

删除菜单:

CMenu提供了一个DeleteMenu成员函数,该函数具有以下形式的声明:

如果想要删除【编辑】子菜单,实现代码如下:

如果想要删除【文件】子菜单下的【打开】菜单项,实现代码如下:

********************************************************************* ****************************************

小结: CMenu类

一个菜单是声明成CMenu类,它直接继承至CObject类。我们可以利用它的成员函数来更新菜单的内容。动态变更一个菜单的顺序,不外乎菜单声明、菜单载入、菜单增减与修改、显示与消除菜单。

CMenu类常用的成员变量与函数摘录如下:

相关文档