1、第5章 菜单和工具栏设计n5.1创建和编辑菜单n5.2 加速键n5.3 动态更改菜单n5.4 使用上下文菜单n5.5 工具栏设计n5.6 提高实例带有工具栏的画图程序5.1创建和编辑菜单n菜单是Windows应用程序中一个必不可少的用户界面资源,菜单的风格直接影响应用程序是否美观,所以设计菜单非常重要。而从编程的角度讲,菜单是应用程序中可操作命令的集合,体现了程序的功能,当选择某一菜单项时就会执行指定的程序代码,完成相应的功能。Visual C+集成开发环境提供了一个可视化菜单资源编辑器,用于菜单的编辑和添加,使用起来非常方便。5.1.1定义菜单n1.菜单资源编辑器n在创建一个SDI单文档应用
2、程序后,在项目工作区的Resource View页面中选择Menu并展开,然后双击下面的IDR_MAINFRAME项就会弹出菜单资源编辑器,显示应用程序向导所创建的菜单资源,如图51所示。空白主菜单项子菜单项主菜单项空白子菜单项图5-1 菜单资源 从上图可以看出默认生成的主菜单共包含四个主菜单项,分别实现不同的功能,每个主菜单下都分别包含和该菜单相关联的子菜单。用户在实现特定功能的时候需要添加新的菜单命令,可以添加在图中“空白主菜单项”或者“空白子菜单项”的位置,分别表示主菜单功能或者某主菜单下的下一级子菜单功能。2添加菜单项添加菜单项 在打开菜单资源编辑器以后,就可以为程序添加自己的菜单项了
3、,方法是直接用鼠标右键单击图5-1中的“空白菜单项”部分,然后从弹出的快捷菜单中选择“Proterties”菜单命令,就可以弹出如图5-2所示的菜单属性对话框。灰色,不能输入子菜单,可输入ID图5-2 主菜单项属性对话框图5-3 子菜单项属性对话框 在该对话框中,用户可以根据需要设置菜单的属性,下表中给出各个属性的说明:属性标识说明ID菜单资源的身份标识标明菜单项显示的标题分割符设置分割子菜单项的分割线弹出设置菜单项是否弹出下级菜单非活动设置菜单的活动状态,选中后在程序运行时变灰,失效或不可选中断设为“列”时,菜单会以灰色条为界线,横排显示,无分割线的全部横排显示;设为“栏”时,功能与“列”相
4、同,但在横排显示的菜单之间加上一个竖直灰色分隔线已复选菜单项标题前打对勾已变灰菜单项显示为灰色帮助表示该菜单项为Help,并使该菜单项由右端起排列提示当菜单项被选择时,状态区上显示的帮助说明文字3菜单实例演示 例5-1:编写一个单文档应用程序MenuTest,为程序添加一个“测试”主菜单,并在其中添加“画圆”和“输出文本”两个菜单项。编程实现步骤说明:(1)利用MFC App Wizard向导生成单文档(SDI)应用程序MenuTest。在项目工作区的资源页面中选择Menu并展开,再双击IDR_MAINFRAME项弹出菜单资源编辑器,按图5-2添加“测试(&T)”主菜单,该菜单没有相应的ID标
5、识。(2)添加好主菜单后,就可以添加下一级子菜单了。回到菜单编辑器,在刚建好的主菜单“测试(&T)”下方双击带虚框的空白子菜单项,在属性对话框中添加子菜单“画圆(&C)tCtrl+D”,ID为“ID_DRAW_CIRCLE”,提示栏中输入程序运行时状态栏上的提示信息“在客户区中画一个圆”。按同样的步骤为菜单添加菜单项“输出文本(&T)tCtrl+T”,ID为“ID_OUTPUT_TEXT”,提示信息为“在客户区输出一行文字”,如图5-4所示。图5-4 添加子菜单项程序说明:程序说明:在步骤(2)中添加的菜单“画圆(&C)tCtrl+D”,“t”使后面的内容右对齐,Ctrl+D表明该菜单项的快捷
6、键,但此处仅起到提示作用,要真正成为快捷键还需要使用快捷键编辑器,在5-2节将详细介绍此功能。5.1.2编写菜单事件处理程序n1添加消息处理函数添加消息处理函数 在例5-1中,我们仅仅添加了菜单,并没有实现相应的菜单功能,即没有给菜单项添加消息处理函数,因此,添加的菜单项是灰色的,即处于不可用状态。这时可以利用Class Wizard类向导添加菜单命令的WM _COMMAND消息处理函数,在函数中添加代码完成对应的功能。下面给出对应菜单功能实现的具体步骤:(1)单击“查看|建立类向导”命令,或者使用快捷键“Ctrl+W”打开MFC ClassWizard对话框,单击“Message Maps”
7、选项卡,可以看到5个列表框。Project框列出当前工作区中的项目,Class name框列出当前项目的类,这两个框是下拉列表框;Object IDs框列出当前所有能接收消息的对象(ID),包括类、菜单项和控件;Messages框列出Object IDs框中选择的对象可处理的消息和重载的MFC虚函数;Member functions框列出当前已创建的消息处理函数,其中的“V”标记表示该函数是虚函数,“W”标记表示该函数是窗口消息处理函数。(2)在“Project”下拉列表框中选择“MenuTest”,在“Class name”下拉列表框中选择“CMenuTestView”。在“Object I
8、Ds”下拉列表框中选择要添加响应控件的ID,这里选择“ID_DRAW_CIRCLE”。在“Message”项中出现了两种类型的消息COMMAND(用于菜单命令)和UPDATE_COMMAND(用于更新菜单状态),这里我们选择的是COMMAND消息。单击“Add Function”按钮,再单击“OK”按钮就可以创建一个消息的处理函数OnDrawCircle(),创建好的“MFC ClassWizard”对话框如图5-5所示。图5-5 添加消息处理函数类向导对话框(3)按照同样的方法添加“输出文本(&T)tCtrl+T”菜单项的消息处理函数OnOutputText(),和步骤(2)一样,因为要在客
9、户区进行图形的绘制和文本的输出,所以将该消息映射函数映射到View类中。2消息处理函数映射机制消息处理函数映射机制 在完成通过“Class Wizard”添加了消息处理函数以后,就建立起了消息和处理函数的映射关系,用户不需要做太多的工作,实现起来是非常简单。那么对应于用户所做的操作,MFC自动为我们做了那些工作呢?主要有以下3个方面的内容:(1)在需要响应这个菜单命令的类的声明中添加一个响应函数的声明,针对例5-1,会在“CMenuTestView”类的头文件“MenuTestView.h”中添加如下所示的内容(加粗部分):protected:/AFX_MSG(CMenuTestView)af
10、x_msg void OnDrawCircle();afx_msg void OnOutputText();/AFX_MSGDECLARE_MESSAGE_MAP()(2)在该类的实现文件中,为这个类的消息映像表添加这个菜单的映像宏,针对该示例,会在“CMenuTestView”类的源文件“MenuTestView.cpp”中添加如下所示的内容(加粗部分):BEGIN_MESSAGE_MAP(CMenuTestView,CView)/AFX_MSG_MAP(CMenuTestView)ON_COMMAND(ID_DRAW_CIRCLE,OnDrawCircle)ON_COMMAND(ID_OU
11、TPUT_TEXT,OnOutputText)/AFX_MSG_MAP/Standard printing commandsON_COMMAND(ID_FILE_PRINT,CView:OnFilePrint)ON_COMMAND(ID_FILE_PRINT_DIRECT,CView:OnFilePrint)ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView:OnFilePrintPreview)END_MESSAGE_MAP()(3)在该类的实现中,添加消息响应函数的框架代码,用户只需要根据菜单的实际功能添加具体的实现代码就可以了。void CMenuTestVie
12、w:OnDrawCircle()/添加实现用户功能的程序代码void CMenuTestView:OnOutputText()/添加实现用户功能的程序代码程序说明:程序说明:采用消息映射宏处理消息时,要将所有的消息映射进行集中说明,在类的实现源文件中用BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏来定义消息映射。对菜单命令,向导自动添加的消息映射格式如下:ON_COMMAND(MenuItemID,MemberFunction)其中,参数MenuItemID是菜单项的ID标识,参数MemberFunction是处理该消息的成员函数名。通常情况下,默认消息处理函数名
13、是根据MenuItemID自动生成的,在该示例中,ID为“ID_DRAW_CIRCLE”的处理函数是“OnDrawCircle”,可以看到,是有On连接ID中的后两个单词构成的,注意每个单词首字母大写。n3函数功能具体实现函数功能具体实现n为了实现“画圆”和“输出文本”的功能,需要在消息处理函数框架中添加具体的程序代码(加粗部分)。nvoid CMenuTestView:OnDrawCircle()nn/TODO:Add your command handler code herenCClientDC dc(this);ndc.Ellipse(20,20,150,150);nnvoid CMe
14、nuTestView:OnOutputText()nn/TODO:Add your command handler code herenCClientDC dc(this);ndc.TextOut(200,200,这是一个菜单示例这是一个菜单示例);n程序说明:程序说明:该示例实现代码相对简单,首先定义了客户区设备环境类对象dc,然后通过调用该类的成员函数完成画圆和输出文本的功能。函数Ellipse()功能为绘制椭圆,第1,2个参数是椭圆外切矩形的左上角坐标,第3,4个参数是该外切矩形的右下角坐标。函数TextOut()功能是在指定的位置输出字符串,第1,2个参数是指定的坐标位置,第3个参数是
15、要输出的字符串。程序运行结果如图5-6所示。图5-6 程序运行结果5.1.3设置菜单的显示效果 在使用Class Wizard类向导为菜单项添加命令处理函数时(可参看图5-4),可以看到在Messages栏中除了WM_COMMAND消息,MFC应用程序的菜单项还有一个UPDATE_COMMAND_UI消息,它称为更新命令用户接口消息,专门用于处理菜单项和工具栏按钮的更新。每一个菜单命令都对应一个更新命令用户接口消息。可以为更新命令用户接口消息编写消息处理函数来处理用户接口的更新。有时一个菜单项可以有可用和不可用两种状态,即允许或禁止菜单项的使用(用于灰色状态)。例如,在例5-1中,如果想让“测
16、试”菜单下的“画圆”菜单项变为不可以使用的状态,则可以在Class Wizard窗口中(图5-4),在左边的Object IDs列表框中选择ID_DRAW_CIRCLE,在右边的Messages列表框中选择UPDATE_COMMAND_UI消息,添加消息处理函数。如图5-7所示。图5-7 添加UPDATE_COMMAND_UI处理函数图5-7 添加UPDATE_COMMAND_UI处理函数在生成的处理函数框架中添加如下代码(粗体显示):void CMenuTestView:OnUpdateDrawCircle(CCmdUI*pCmdUI)/TODO:Add your command updat
17、e UI handler code herepCmdUI-Enable(FALSE);运行程序后“画圆”菜单项变为灰色显示,处于不可用状态。提示提示:Enable()函数的参数为BOOL类型,当它的值为TRUE时,使菜单项可用;当其值为FALSE时,禁用菜单项。UPDATE_COMMAND_UI消息为程序员根据程序当前的运行情况对菜单项的状态进行动态设置提供了一个简便的方法,但该消息只适合菜单项,不适合顶层的主菜单。5.2 快捷键n快捷键和菜单是紧密联系在一起的,快捷键提供菜单执行的快捷方式,大大提高了用户的操作速度。快捷键通常是键盘上的一个键或几个键的组合,在程序设计中占据着重要的位置。5.
18、2.1 快捷键介绍在图形界面中,用户操作以鼠标为中心,虽然操作简单,但当执行某菜单命令时必须定位菜单位置,如果菜单分为多级,对操作熟练的用户而言,每次进行定位是一个烦琐费时的过程,因此直接使用加速键完成菜单功能给我们提供了极大的方便。加速键又称为键盘快捷键,可以将其理解为通常所说的组合快捷键,它为用户提供了一种快速执行菜单命令或其它某种功能的方法。加速键属于一种资源,可以在资源编辑器中编辑。在示例5-1中,我们添加了菜单项“画圆(&C)tCtrl+D”和“输出文本(&T)tCtrl+T”,这样在程序运行时显示菜单时同时也显示了菜单对应的加速键为“Ctrl+D”和“Ctrl+T”,如果加速键起作
19、用,直接按对应的加速键就可以实现画圆和输出文本的功能,更加的便利。5.2.2 定义加速键 利用MFC AppWizard生成的应用程序本身也带有一些加速键。比如,主框架窗口菜单就实现了New、Open和Save等菜单项的加速键(Ctrl+N、Ctrl+O、Ctrl+S)。用户除了可以对这些加速键进行修改外,还可以对自己添加的菜单项添加或者修改加速键。利用集成开发环境内置的加速键表编辑器可以方便的实现这些功能。打开例5-1编写的工程MenuTest,在项目工作区的资源页面(Resource View)中选择Accelerator并展开,再双击下面的IDR_MAINFRME,就打开了如图5-8所示
20、的加速键表。该表列出了当前应用程序的菜单项或者命令按钮ID以及相应的加速键。选择“插入|新建加速栏”或者在加速表编辑器的最底一列双击,会弹出加速键属性窗口。窗口中的字段说明如下:图5-8 加速键表编辑器 ID:可以选择或者输入菜单项的ID,针对该示例,输入“ID_DRAW_CIRCLE”。键:为加速键标识的键名,只要在这里输入字母或数字即可。如果是不可打印字符,比如Esc键,则必须使用虚拟键盘码。滚动该组合框就可以看到以“VK_”为前缀的键盘码。辅助键:可以选择加速键的修改键,默认的修改键是“ctrl”,用户可以根据需要选择不同的修改键。本示例中使用“ctrl”。类型:可以选用“ASCII”或
21、者“VirKey”。如果加速键中含有不可打印字符或者希望加速键在各种键盘上通用,使用VirKey。如果希望使用一些标点符号,则需要使用ASCII,注意ASCII区分大小写。本示例使用VirKey。5.2.3 创建加速键菜单提示 为了让用户知道菜单项对应的加速键,就需要在菜单显示时给出提示。在例5-1中我们定义的菜单“画圆(&C)tCtrl+D”和“输出文本(&T)tCtrl+T”就给出了明显的加速键提示。建立提示只需插入一个制表符(t)以及加速键名称。注意:某些情况下,加速键并不明显地对应菜单项选择。为了确保用户能够利用这些加速键,应该添加一个帮助数据库条目。5.3 动态更改菜单n一般情况下,
22、对菜单操作时,已经静态地在菜单编辑器中预先把菜单创建出来了。不过,对菜单进行的任何修改也可以在运行时动态地进行显示。MFC提供了一个菜单类CMenu,以支持对菜单的动态操作。5.3.1 CMenu类n1CreateMenu方法 该方法用于创建一个菜单窗口,并将其关联到菜单对象上。BOOL CreateMenu();返回值:执行成功,返回值为非零,否则为零。2CreatePopupMenu方法 该方法用于创建一个弹出式菜单窗口,并将其关联到菜单对象上。BOOL CreatePopupMenu();返回值:执行成功,返回值为非零,否则为零。对于弹出式菜单,如果菜单窗口被释放,菜单对象将自动释放。3
23、LoadMenu方法 该方法用于从应用程序的可执行文件中加载一个菜单资源,将其关联到菜单对象上。BOOL LoadMenu(LPCTSTR lpszResourceName);BOOL LoadMenu(UINT nIDResource);lpszResourceName标识资源名称;nIDResource标识资源ID。返回值:执行成功,返回值为非零,否则为零。4DeleteMenu方法 该方法用于从菜单中删除一个菜单项。BOOL DeleteMenu(UINT nPosition,UINT nFlags);nPosition标识某一个菜单项;nFlags表示如何解释nPosition,可选值
24、如下:(1)MF_BYCOMMAND 根据nPosition标识的菜单ID删除菜单项。(2)MF_BYPOSITION根据nPosition标识的菜单位置删除菜单项。5GetSubMenu方法 该方法用于获取弹出式菜单中的一个菜单项。CMenu*GetSubMenu(int nPos)const;nPos标识菜单项位置,第一个菜单项对应的位置识0,第二个菜单项对应的位置识1,依此类推。6InsertMenu方法 该方法用于向菜单指定位置插入菜单项。BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszN
25、ewItem=NULL);BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem,const CBitmap*pBmp);nPosition标识某一个菜单项;nFlags用法同DeleteMenu方法相同;nIDNewItem标识菜单项的ID;lpszNewItem标识菜单项的内容;pBmp标识关联菜单项的位图对象指针。7ModifyMenu方法方法 该方法用于修改菜单项信息。BOOL ModifyMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewIte
26、m=NULL);BOOL ModifyMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem,const CBitmap*pBmp);各参数的作用同InsertMenu方法相同。8AppendMenu方法方法 该方法实现在菜单项的末尾添加一个新菜单。BOOL AppendMenu(UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewItem=NULL);BOOL AppendMenu(UINT nFlags,UINT nIDNewItem,const CBitmap*pBmp);nFlags标识菜单项的状态信息;nI
27、DNewItem标识菜单项的ID;lpszNewItem标识菜单项的内容;pBmp标识关联菜单项的位图对象指针。9DrawItem方法方法 该方法识一个虚方法,用户可以改写该方法实现菜单的绘制。如果菜单设置成可以自绘类型,则这个函数将在生成菜单、弹出菜单、选中菜单、点击菜单等时由系统框架调用。它可以帮你绘制出各种样式的菜单。Virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);lpDrawItemStruct是一个DRAWITEMSTRUCT结构指针,DRAWITEMSTRUCT结构包含了菜单项的ID、类型、画布、句柄等详细信息。除了
28、上面介绍的函数之外,CMenu类还有很多有用的函数,有兴趣的读者可以查阅相关资料。5.3.2 动态更改菜单项在示例5-1中我们对菜单操作时,已经静态地在菜单编辑器中预先把菜单创建出来,有时为了程序实现功能的需要,也可以在程序运行的过程中,动态的进行修改。1添加菜单项目添加菜单项目要实现在程序运行时动态的添加菜单项目,主要利用了前面讲到的CMenu类的AppendMenu来完成,这个函数的作用是把一个新的菜单项目添加到一个指定菜单项目的末尾。这里详细给出该函数第一个参数nFlags的可取值以及作用:nMF_CHECKED:菜单项显示选中标记nMF_SEPARATOR:菜单项是一个分隔栏项nMF_
29、ENABLED:使菜单项可用nMF_GRAYED:禁用菜单项并加灰显示nMF_OWNERDRAW:菜单项内容由应用程序绘制,可以定制样式更加丰富的菜单项nMF_POPUP:弹出式菜单nMF_STRING:使用一个字符串标识该菜单项目nMF_MENUBREAK:更改菜单项的显示方式nMF_MENUBARBREAK:更改菜单项的显示方式,但在菜单项之间显示分割线下面我们结合一个实例来学习动态添加菜单项的方法。例5-2:在例5-1菜单中添加主菜单项“动态修改菜单”,并添加一个子菜单项“增加菜单”,ID为ID_ ADD_MENU,要求当单击该菜单项时,动态的添加一个菜单项“动态菜单”,要求该菜单项和“
30、增加菜单”之间有一条分割线。编程实现步骤说明:(1)在项目工作区的资源页面中选择Menu并展开,再双击IDR_MAINFRAME项弹出菜单资源编辑器,增加如题目要求的菜单项“动态修改菜单”和“增加菜单”。(2)使用Class Wizard为菜单项ID_ ADD_MENU映射函数。在CMenuTestView类中为其添加“ON_COMMAND”消息响应函数OnAddMenu()和“ON_UPDATE_COMMAND _UI”消息响应函数OnUpdateAddMenu()。(3)添加动态生成的菜单项的ID资源。单击“查看|资源符号”命令弹出资源符号对话框,如图5-9所示,添加要动态生成菜单对应的I
31、D,可以看到,所有的资源ID其实是一个可以唯一标识的整数值。图5-9 资源符号对话框(4)为CMenuTestView类的头文件MenuTestView.h添加一个条件变量,用该变量控制“增加菜单”项的状态,当该菜单项执行后就使其无效。public:BOOL flagAdd;在MenuTestView.cpp文件的构造函数中进行初始化。CMenuTestView:CMenuTestView()/TODO:add construction code hereflagAdd=true;(5)在“增加菜单”的响应函数OnAddMenu()中添加代码:void CMenuTestView:OnAddM
32、enu()/TODO:Add your command handler code hereCWnd*pWnd=AfxGetApp()-GetMainWnd();/获得窗口指针获得窗口指针CMenu*pMenu=pWnd-GetMenu();/获得获得Menu指针指针pMenu=pMenu-GetSubMenu(5);/获得菜单项获得菜单项动态修改菜单动态修改菜单pMenu-AppendMenu(MF_SEPARATOR);/增加分割符增加分割符/增加菜单项增加菜单项动态菜单动态菜单,该菜单项对应该菜单项对应ID_DYNAMIC_MENUpMenu-AppendMenu(MF_STRING,ID
33、_DYNAMIC_MENU,动态菜单动态菜单);flagAdd=false;/改变条件变量改变条件变量在OnUpdateAddMenu()函数中修改“增加菜单”项的状态。void CMenuTestView:OnUpdateAddMenu(CCmdUI*pCmdUI)/TODO:Add your command update UI handler code hereif(!flagAdd)/条件变量为条件变量为false时时pCmdUI-Enable(false);/使使增加菜单增加菜单无效无效运行后单击“增加菜单”后该菜单项变灰显示,同时增加菜单项“动态菜单”,由于该菜单项没有添加消息响应,
34、也为灰色显示,如图5-10。图5-10 动态增加菜单项在5.1.2节中详细讲了添加消息响应时MFC自动为我们完成了哪些工作,在动态菜单被添加后,我们不能按照以往的方法,通过Class Wizard添加响应了,只能够手工进行添加消息映射代码。在头文件MenuTestView.h中声明菜单项“动态菜单”的响应函数OnDynamicMenu():2为动态添加的菜单项添加消息响应为动态添加的菜单项添加消息响应protected:/AFX_MSG(CMenuTestView)afx_msg void OnDrawCircle();afx_msg void OnOutputText();afx_msg v
35、oid OnUpdateDrawCircle(CCmdUI*pCmdUI);afx_msg void OnAddMenu();afx_msg void OnUpdateAddMenu(CCmdUI*pCmdUI);afx_msg void OnDynamicMenu();/AFX_MSG在源文件MenuTestView.cpp中建立消息映射并实现函数功能:nBEGIN_MESSAGE_MAP(CMenuTestView,CView)n/AFX_MSG_MAP(CMenuTestView)nON_COMMAND(ID_DRAW_CIRCLE,OnDrawCircle)nON_COMMAND(ID
36、_OUTPUT_TEXT,OnOutputText)nON_UPDATE_COMMAND_UI(ID_DRAW_CIRCLE,OnUpdateDrawCircle)nON_WM_RBUTTONUP()nON_COMMAND(ID_ADD_MENU,OnAddMenu)nON_UPDATE_COMMAND_UI(ID_ADD_MENU,OnUpdateAddMenu)nON_COMMAND(ID_DYNAMIC_MENU,OnDynamicMenu)n/AFX_MSG_MAPn/Standard printing commandsnON_COMMAND(ID_FILE_PRINT,CView:O
37、nFilePrint)nON_COMMAND(ID_FILE_PRINT_DIRECT,CView:OnFilePrint)nON_COMMAND(ID_FILE_PRINT_PREVIEW,CView:OnFilePrintPreview)nEND_MESSAGE_MAP()nnvoid CMenuTestView:OnDynamicMenu()nnAfxMessageBox(动态菜单测试成功动态菜单测试成功!);/弹出消息框弹出消息框n 运行后单击“动态菜单”项,弹出消息提示对话框,如图5-11。图5-11 运行成功消息提示对话框5.3.3 动态添加菜单图标如果需要动态的为菜单项添加菜单图
38、标,就要用到CMenu类的另一个成员函数SetMenuItemBitmaps,该函数可以方便地为菜单项添加图标,定义如下:BOOL SetMenuItemBitmaps(UINT nPosition,UINT nFlags,const CBitmap*pBmpUnckecked,const CBitmap*pBmpChecked)第一个参数表示需要添加菜单图标的菜单项;第二个参数表示如何理解第一个参数:当参数为MF_BYCOMMAND时,第一个参数指的是菜单项的ID;当参数为MF_BYPOSITION时,第一个参数指的是菜单项在菜单中的位置,索引值从0开始计算。第三个参数和第四个参数分别表示菜
39、单项没选中和选中时的位图,如果希望该位图永远出现在菜单项前,则两个参数保持一致即可。例5-3:为例5-1中的“测试”菜单下的子菜单“画圆”和“输出文本”添加菜单图标。编程实现步骤说明:(1)打开项目工作区的资源页面,单击“插入|资源”菜单命令,弹出插入资源对话框,选中“Bitmap”后,单击“新建”按钮,则在资源页面中出现“Bitmap”项,并新建立一个位图,名称为“IDB_BITMAP1”。图5-12IDB_CIRCLE 图5-13 IDB_TEXT(2)在此位图边框上的调整大小控制点上双击,会弹出该位图的属性对话框,将位图ID改为“IDB_CIRCLE”,并修改宽度和高度均为16,按照同样
40、的方法添加位图“IDB_TEXT”,如图5-12和图5-13。(3)在CMenuTestView类头文件“CMenuTestView.h”中添加两个CBitmap类型的成员变量m_bmpCircle和m_bmpText:private:CBitmap m_bmpCircle;CBitmap m_bmpText;在源文件“CMenuTestView.cpp”中的构造函数中添加初始化代码如下:CMenuTestView:CMenuTestView()/TODO:add construction code hereflagAdd=true;m_bmpCircle.LoadBitmap(IDB_CIR
41、CLE);m_bmpText.LoadBitmap(IDB_TEXT);(4)在CMenuTestView类头文件“CMenuTestView.h”中添加两个CBitmap类型的成员变量m_bmpCircle和m_bmpText:private:CBitmap m_bmpCircle;CBitmap m_bmpText;在源文件“CMenuTestView.cpp”中的构造函数中添加初始化代码如下:CMenuTestView:CMenuTestView()/TODO:add construction code hereflagAdd=true;m_bmpCircle.LoadBitmap(ID
42、B_CIRCLE);m_bmpText.LoadBitmap(IDB_TEXT);运行程序后结果如图5-14。图5-14 添加菜单图标运行结果5.4 使用上下文菜单n菜单分为两类,除了前面讲的依附于框架窗口的一般菜单,另一类菜单是上下文菜单(也称为快捷菜单、浮动菜单)。当用户右击鼠标,上下文菜单就会出现在光标所在位置。在应用程序中添加上下文菜单的过程比较简单,与普通菜单的添加过程相比有一些差别。一般添加上下文菜单的过程如下:(1)使用菜单模板编辑器定制上下文菜单模板,与普通菜单的定制方法相同。(2)添加菜单命令的消息响应函数,与普通菜单的添加方法相同。(3)在适当的位置添加载入并显示菜单:需要
43、手动添加相应的代码,实现菜单资源的载入与显示。5.4.1 建立菜单资源建立菜单资源,即定制菜单模板和添加菜单命令的消息响应函数。上下文菜单的定制方法与普通菜单的定制相同,对于例5-1,我们可以直接利用做好的菜单资源“测试”以及它的两个下一级子菜单,如果希望弹出其他的菜单实现不同的功能,可以参照该示例程序添加资源。为了右键单击后选择相应子菜单后能实现菜单功能,还要对菜单映射菜单命令处理函数,并根据需要编写具体的程序代码。5.4.2 添加上下文菜单资源n上下文菜单用于快捷地访问当前可用的菜单项。当用户右击鼠标后,就会相应地弹出一个浮动的菜单,其菜单项内容由鼠标所指的对象指定。一般而言,弹出式(上下
44、文)菜单是利用已有的菜单项来建立的,但也可以为上下文菜单专门创建一个菜单资源,然后通过调用类CMenu类的成员函数LoadMenu()装入所创建的菜单资源,从而建立一个上下文菜单。(1)如果是利用已有的菜单项来建立的,当用户右击鼠标并释放后,将发送WM_CONTEXTMENU消息。因此,在程序中通过为WN_CONTEXTMENU添加消息处理函数来弹出上下文菜单。在消息处理函数中,先装入菜单资源或添加菜单项,然后调用CMenu类的成员函数TackPopupMenu()显示弹出式菜单。WM_CONTEXTMENU消息是在收到WM_RTUTTONUP消息后由Windows产生的。WM_CONTEXT
45、MENU消息处理函数使用屏幕坐标,所以不用做任何的坐标转换,在什么位置右键单击上下文菜单就出现在什么位置。(2)如果为上下文菜单专门创建一个菜单资源,通过调用CMenu类的成员函数LoadMenu()装入所创建的菜单资源,那么WM_RBUTTONUP的消息处理函数中不调用基类的处理函数,应用程序将不会收到WM_CONTEXTMENU消息。这是直接为WM_RBUTTONUP添加消息处理函数来弹出上下文菜单,要注意的是这时需要调用函数ClientToScreen()将客户区坐标转化为屏幕坐标。方法(1)的程序实现代码如下:void CMenuTestView:OnContextMenu(CWnd*
46、pWnd,CPoint point)/TODO:Add your message handler code hereCMenu menuContext;if(menuContext.CreatePopupMenu()menuContext.AppendMenu(MF_STRING,ID_DRAW_CIRCLE,画圆画圆tCtrl+D);menuContext.AppendMenu(MF_STRING,ID_OUTPUT_TEXT,输出文本输出文本tCtrl+T);menuContext.TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);方法(
47、2)实现之前,要先建立一个新的菜单资源,这里建立一个和方法一种同样的菜单资源,菜单的ID使用默认ID:IDR_MENU1,实现代码如下:void CMenuTestView:OnRButtonUp(UINT nFlags,CPoint point)/TODO:Add your message handler code here and/or call defaultCMenu menuContext;CMenu*pSubMenu;menuContext.LoadMenu(IDR_MENU1);/载入菜单资源载入菜单资源pSubMenu=menuContext.GetSubMenu(0);/获得
48、第一个菜单,这获得第一个菜单,这样效果和方法(样效果和方法(1)相同)相同this-ClientToScreen(&point);/客户区坐标转换成屏幕坐标客户区坐标转换成屏幕坐标pSubMenu-TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);CView:OnRButtonUp(nFlags,point);程序说明:程序说明:函数AppendMenu()实现通过代码来动态的添加菜单项目,第1个参数指定加入菜单项的风格,值MF_STRING表示菜单项是一个字符串;第2个参数指定要加入的菜单项ID,如ID_DRAW_CIRCLE;第3个参数指
49、定菜单项的显示文本。函数TrackPopupMenu()用于在指定位置显示上下文菜单,并响应用户菜单项选择。函数第1个参数是位置标记,TPM_LEFTALIGN表示以x为标准左对齐显示菜单;第2个、第3个参数指定上下文菜单的屏幕坐标;第4个参数指定拥有上下文菜单的窗口。程序运行结果如图5-15所示:图5-15 上下文菜单运行结果5.5 工具栏设计n工具栏是一个完善的Windows应用程序的重要组成部分(但不是必需的部分),它一般位于主框架窗口的上部,上面有一些图形按钮。当用户单击某一按钮时,程序就会执行相应的命令。本节主要介绍工具栏的实现和使用方法。5.5.1 创建和初始化工具栏n工具栏是由一
50、系列的按钮和分隔符组成,并通过CToolBar类实现的,其父类为CControlBar。通常情况下,为了使工具栏可以更快捷、更有效地实现某些操作,应尽量使工具栏的命令ID 与菜单和加速键的命令ID 同步。在MFC 中,工具栏资源和工具栏类CToolBar 是工具栏的两个要素。创建工具栏的基本步骤如下:(1)创建工具栏资源;(2)构建一个CToolBar 对象;(3)调用CToolBar:Create()或CreateEx()函数创建工具栏窗口;(4)调用CToolBar:LoadToolBar 载入工具栏资源。当用户使用MFC AppWizard向导创建一个应用程序时,会自动创建一个默认的工具