⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 06.3.7 快捷菜单.txt

📁 网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读
💻 TXT
字号:
6.3.7 快捷菜单
我们平时在使用程序时,经常会用到单击鼠标右键显示快捷菜单(也称为上下文菜单,或右键菜单〉这一功能。现在我们的这个Menu程序还不具备这个功能,读者可以试试在 Menu程序的窗口中单击鼠标右键,会发现程序没有任何反应。
要实现右键快捷菜单功能,读者可能会觉得无从下手。实际上, VC++己经为我们实现了这个功能。我们可以在VC++开发界面中选择【Project】菜单下的【Add To Project>,然后从它的下拉菜单中选择【Components and Controls....】菜单项。如图6.28所示。
图 6.28添加已有组件或控件的菜单命令
这时会出现如图 6.29所示的组件和控件库对话框。这个组件和控件库里的内容是 VC++己经为我们编写好的一些功能,就是一些组件和 ActiveX控件,可以供我们方便地直接调用它们。
在图6.29所示的对话框中,双击Visual C++ Components目录,从而打开这个目录,该目录下的内容如图 6.30所示。其中,可以看到有一个名称为 "Pop-upMenu"的组件,这个组件的作用就是给一个派生于CWnd类的窗口添加一个右键菜单。选中此工页,然后单击【Insert】按钮,系统会提示用户是否确认当前的插入操作,单击【确定】按钮,确认此操作即可。这时就会出现如图6.31所示的添加右键菜单设置对话框。 
图 6.29 Components and Controls Gallery对话框 图 6.30 Visual C++提供的组件
通过如图6.31所示的这个对话框,可以设置新添加的这个弹出菜单将添加到哪个窗口上,即图6.31中的Add pop-up menu to下拉列表框中显示的那个类。这里,不应该选择 CMainFrame类,因为前面己经介绍过了,视类窗口始终是覆盖在框架窗口之上的,框架窗口接收不到鼠标消息,而右键菜单是在鼠标右键单击时产生的,而在框架窗口中又捕获不到鼠标右键单击这一消息,也就无法显示快捷菜单。所以,这里应该选择 CMenuView类。在Menu resource ID编辑框中显示的是快捷菜单的资源ID,我们可以在这里修改这个标识,也可以以后再修改。本例中保持默认标识不变,然后单击【OK】按钮,即可返回到图6.30所示的窗口界面,单击【Close】按钮,结束组件的添加。 
Build并运行Menu程序,然后在程序的窗口中单击鼠标右键,此时会出现一个快捷莱 

‘~... I 189 


第6
单。如图6.32所示。 
这时,在Menu这个工程中可以看到,在插入了这个Pop-upMenu组件后,添加了以下两处内容:
第一处,在 ResourceView选项卡的Menu分支下多了一个标识为CGJDRYOPUP-MENU VIEW的菜单资源,这个菜单只有一个顶层菜单项: _POPUP_,其下有三个菜单项(如图6.33所示),也就是如图6.32所示的快捷菜单中的内容。 
图6.33添加Pop-upMenu组件后增加的快捷菜单资源

第二处,为CMenuView类添加了一个函数: OnContextMenu。在Menu程序运行时,当用鼠标右键单击窗口时,程序就会调用这个函数。如例6-20所示代码是VC++为我们添加的这个函数的源代码。 
f~tl6-20 

void CMenuView : :OnContextMenu(CWnd会, CPoint poínt) 

11  CG :  This b10ck  was  added by  the  Pop-up Menu  component  
{  
if  (point .x  -1  &&  point .y  -1) {  
Ilkeystroke  invocation  
CRect  rect ;  
GetClientRect(rect) ;  
ClientToScreen(rect) ;  
point  =  rect . TopLeft() ;  
point .Offset(5 ,  5) ;  
CMenu nenu ; 
VERIFY(rnenu . LoadMenu(CG_IDR_POPUP_MENU_VIEW)) ; 

CMenu* pPopup = rnenu . GetSubMenu(O) ; 
ASSERT(pPOpup != NULL); 
CWnd* pWndPopupOwner = th工S ; 

while 	(pWndPopupOwner->GetStyle() & WS_CHILD) pWndPopupOwner = pWndPopupOwner->GetParent(); 
pPopup->TrackPopupMenu(TPM_LEFTAL工 GN I TPM_RIGHTBUTTON , point . x , poìnt.y, pWndPopupOwner) ; 
可以看到,这个函数内部调用了 TrackPopupMenu函数来显示一个快捷菜单。后者具有以下形式的声明: 
BOOL TrackPopupMenu(UINT nFlags , int x , int y ,CWnd* pWnd, LPCRECT lpRect = NULL) ; 
该函数的参数含义如下所述: 
. nFlags 
指定菜单在屏幕上显示的位置。 
. x和 y
分别指定快捷菜单显示位置处的 x坐标和 y坐标。 

. pWnd 
指定快捷菜单的拥有者,也就是标识拥有快捷菜单的窗口对象。 
. lpRect 
指定一块矩形区域。如果用户在这个设定区域之内单击鼠标,快捷菜单仍保持显示:否则快捷菜单消失。如果这个参数的值是 NULL,当用户在这个快捷菜单范围之外其他地方单击鼠标时,这个菜单就将消失。 lpRect参数的默认值是 NULL o
既然知道了 TrackPopupMenu这个函数就是用来显示快捷菜单的,就可以在 Menu程序中实现自己的快捷菜单了。这可以通过以下几个步骤来实现 : 
E为 Menu程序增加一个新的菜单资源。可以在 ResourceView选项卡上的 Menu分支上单击鼠标右键,从弹出的菜单中选择【In sert Menu】菜单命令,这时,在 Menu分支下就多了一个名为 IDR MENUl的菜单资源,井同时在 VC++开发界面窗口的右边窗口中打开了这个菜单资源。接着就要为这个菜单资源添加菜单项了。因为在显示快捷菜单时顶级菜单是不出现的,所以可以给它设置任意的文本,例如 abc。接着,依次添加表 6.1中列出的两个菜单项: 
表 6.1需添加的菜单项
菜单项文本 菜单项标识 
显示  IDM SHOW  
退出  IDM EXIT  

回给 CMenuView类添加 WM RBUπONDOWN消息响应函数。如果是在鼠标右键单击窗口时显示快捷菜单,那么就应该捕获这个消息。对于这个消息响应函数处理的内容可以参照刚才系统自动添加的 OnContextMenu函数,最后的实现代码如例 6-21所示。
例 6-21 

void CMenuView :: OnRButtonDown(U工 NT nFlags , CPoint point) 
// TODO: Add your message handler code here and/or call default 
CMenu menu; 
menu.LoadMenu(IDR_MENU1) ; 
CMenu* pPopup = menu.GetSubMenu( O) ; 
pPopup->TrackPopupMenu(TPM_LEFTALIGN TPM_RIGHTBUTTON , point.x, point .y, this) ; 
CView : :OnRButtonDown(nFlags , point); 
在这个鼠标右键消息响应函数中,首先定义了一个 CMenu对象: menu,接着加载 C LoadMenu函数)菜单资源,并获取该菜单的第一个子菜单(GetSubMenu函数)。对快捷菜单来说,实际上只有一个子菜单(位置索引为 0)。最后,调用 TrackPopupMenu函数显示快捷菜单。 
Build井运行 Menu程序,然后在该程序窗口中单击鼠标右键,此时就会出现我们自定义的快捷菜单,如图 6.34所示。
但是,这个快捷菜单显示的位置好像不太对,并不是在鼠标右键单击点处显示的。这是因为 TrackPopupMenu函数中的 x和 y参数都是屏幕坐标,而鼠标单击点处的坐标是窗口客户区坐标,即以程序窗口左上角为坐标原点。图 6.35显示了窗口坐标和屏幕坐标的关系。 
图 6.34自定义的快捷菜单的显示图 6.35窗口坐标和屏幕坐标
这样,就需要把客户区坐标转换为屏幕坐标。我们知道刚才利用OnContextMenu函数显示快捷菜单时一切都是正常的,那么我们看看它的实现代码 (例 6-20所示代码),可以发现它调用了一个名为 ClientToScreen的函数。政函数的作用就是把客户区坐标转换为屏幕坐标,这正是我们所需要做的工作。于是修改例 6-21所示 CMenuView类的 OnRButtonDown函数,在调用TrackPopupMenu函数之前添加下面这行代码完成坐标转换: 
ClientToScreen(&point) ; 
再次Build并运行Menu程序,并在程序窗口中单击鼠标右键,发现此时快捷菜单的显示位置正常了,如图6.36所示。
由为Menu程序添加快捷菜单上各菜单项命令的响应函数。可以在ResourceView选项卡上双击IDR_MENUl菜单资源,使其在资源编辑窗口中打开。然后在【显示】菜单项上用单击鼠标右键,从出现的快捷菜单中选择【ClassWizard..】命令,这时会出现如图6.37所示的对话框。该对话框询问是否为IDR MENUl这个资源创建一个新类或者选择一个己有类。可以不用对此做出处理,单击【Cancel】按钮取消此对话框即可。 
图 6.36正确的快捷菜单显示图 6.37 Adding a Class对话框

然后利用ClassWizard,分别为CMainFrame类和CMenuView类添加一个响应【显示】菜单项(ID为10MSHOW)的函数(直接接受系统提供的默认函数名:OnShow),添加时,应在ClassWizard对话框中的Messages列表中选择COMMAND消息。
接下来为新添加的消息处理函数添加代码,在CMenuView类的 OnShow函数中添加如例6-22所示代码中加灰显示的那行代码。
例6-22 void CMenuV工ew: : OnShow ( ) 
MessageBox ("View show"); 

在CMainFrame类的OnShow函数中添加例6-23所示代码中加灰显示的那行代码。
例6-23 

void CMainFrame: :OnShow() 
MessageBox ("Main show"); , 

, 
Build并运行 Menu程序,并在程序窗口中单击鼠标右键,从出现的快捷菜单中选择【显示】菜单项,这时会弹出一个消息框,发现它显示的信息是: View show,说明是视类响应了这个菜单命令消息。我们将 CMenuView类对【显示】菜单命令消息的响应函数删除,再次运行 Menu程序,并选择快捷菜单中的【显示】菜单项,可是,这时程序没有任何反应。这主要是因为在创建快捷菜单时,即调用 TrackPopupMenu函数时,对这个快捷菜单的拥有者参数传递的是由is值,也就是视类窗口拥有这个快捷菜单。因此,只有视类才能对快捷菜单项命令做出响应。如果想让 CMainFrame类能对这个快捷菜单项进行响应的话,就应该在调用 TrackPopupMenu函数时把快捷菜单的拥有者指定为 CMainFrame类窗口,为此,我们可以修改 CMenuView类 OnRB uttonDown函数中对 TrackPopupMenu函数的调用,结果如例 6-24所示。 
19IJ6-24 

void CMenuView : :OnRButtonDown(UINT nFlags , CPoint point) 
CMenu menu ; 
menu.LoadMenu(IDR_MENU1) ; 
CMenu* pPopup = menu.GetSubMenu(O); 

ClientToScreen(&point) ; 
pPopup->TrackPopupMenu(TPM_LEFTAL工 GN I TPM_R工GHTBUTTON, point.x, point.y, Get Parent()}; 
CView : :OnRButtonDown(nFlags , point); 
Build井运行 Menu程序,并在程序窗口中单击鼠标右键,从出现的快捷菜单中选择【显示】菜单项,这时会弹出一个消息框,发现它显示的信息是: Main show,这说明是框架类窗口响应了这个菜单命令消息。
这时,如果我们在视类中也添加了这个【显示】菜单项的响应函数,那将会是谁做出响应呢?我们可以为 CMenuView类再次添加这个响应函数,并为其添加如例 6-22所示的代码。然后 Build并运行 Menu程序,并选择快捷菜单中的【显示】菜单项,发现这时弹出的消息框中显示的是: View show,说明是视类捕获到了这个菜单命令。读者可以根据本章前面讲述的菜单命令消息路由的过程来解释这个结果。
国际:对于快捷菜单,如果将其拥有者窗口设置为框架类窗口,则框架类
窗口才能有机会获得对该快捷菜单中的菜单项的命令响应,否则,就只能由视
类窗口 f放出响应。 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -