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

📄 06.5.4 框架类窗口及本章小结.txt

📁 网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读
💻 TXT
📖 第 1 页 / 共 2 页
字号:
6.5.4 框架类窗口
下面我们想让程序中动态添加的菜单项命令由框架类来捕获。根据前面"菜单命令的路由"一节中的内容,我们知道,当框架类窗口接收到一个消息时,它首先会把消息交给其子窗口,即视类窗口去处理。那么这里如何让框架类窗口首先捕获菜单命令井响应呢?我们再回想一下"菜单命令的路由"一节中讲述的命令消息的路由过程 (如图 6.8所示),我们知道菜单命令是交由 OnCommand函数来处理的,在这个函数中将完成命令消息的路由。那么我们就可以这样设想,如果 OnCommand函数是一个虚函数的话,我们就可以在框架类中重写它,然后截获本该交由视类去响应的命令消息,让框架类来完成这些消息的处理。
如果在 MSDN中查看 OnCommand这个函数的信息,可以找到该函数具有以下形式的声明: 
virtual BOOL OnCornmand( WPARAM wParam, LPARAM lparam ); 
可以发现它确实是一个虚函数。这样,我们就可以在 
Menu2程序的框架类中重写这个函数,截获那些动态添加的菜
单项的命令消息,让它们不再继续向下路由。遵照这样的思路,
我们先给 CMainFrame类添加一个 OnCornmand虚函数,添加方法可以是在 ClassView标签上,用鼠标右键单击 CMainFrame类的名称,然后在弹出的快捷菜单中选择【 
Add Virtual Function.. .】命令 (如图 6.47所示 ),就会出现图 6.48所示的添加虚拟函数对话框。在这个对话框左边的 New Virtual Functions列表中选择 OnCornmand函数,然后单击【 
Add Handler】按钮,为 CMainFrame类增加一个 OnCornmand虚函数的重写。这时,在这个对话框右边的 Existing virtual function 
图 6 .47添加虚函数的命令

overrides列表中就会多了一个重写的 OnCornmand函数,并处于选中状态,如图 6 .49所示。 
图 6.48添加虚拟函数对话框
图 6 .4 9添加了一个 OnCommand虚拟函数的重写
然后,我们就可以单击对话框上的【Edit Existing】按钮,这时 VC++将会定位到程序中 OnCommand这个重写函数的定义处,该函数的定义代码如例 6-43所示。例 6-43 
BOOL CMa工 nFrame : : OnCommand(WPARAM wParam , LPARAM lparam) 11 TODO: Add your specialized code here and/ or call the base class return CFrameWnd : :OnCommand(wParam, lparam); 
这时,在程序运行时,当菜单命令由程序框架类 ( CFrameWnd类)的 OnWndMsg函数交由 OnCommand函数后,因为其子类: CMainFrame类重写了这个 OnCommand函数,菜单命令消息就会先到子类的这个函数(即上述例 6-43所示的 OnCommand函数)中报到。后者最后将调用基类(即程序框架类: CFrameWnd )的 OnCommand函数进行消息的路由。 
OnCommand函数是对所有的命令消息进行路由处理的,包括菜单、工具按钮,以及加速键的命令消息。而这里我们只对动态添加的菜单项的命令感兴趣。因此,在上述例 6-43所示的 OnCommand函数中,需要对到达的消息加以判断,检查该消息是否是我们需要的。根据上述例 6-43所示的 OnCommand函数代码,可以看到, OnCommand这个函数带有两个参数,其中第一个参数的类型是 WPARAM,这是一个 4字节的无符号整型。在参数 wParam低端的两个字节中放置的是发送当前消息的菜单项、工具按钮,或加速键的命令 E。因此,就可以利用 LOWORD这个宏取得当前消息的命令 ID,然后判断其是否是程序中动态添加的菜单项,即当前消息的命令 E是否在这些菜单项 E范围之内,如果是,就处理:否则,就把消息交由基类继续路由。
LOWORD宏从给定的 32位值中取得低端字。回WORD宏从给定的 32

位值中取得高端字。
本例中要处理的菜单项命令范围的下限就是我们刚才定义的 IDM PHONEl,但是我们并不知道在程序中会动态增加多少菜单项。为了让程序具有较好的可扩充性,在程序中
4‘~.... I 213 


就不应该用具体的数字来指定这个范围,例如,把这个范围的上限指定为 IDM_PHONEl +3,或者 IDM PHONEl+5,这是不合适的。因为我们已经把每次输入的字符串都保存到 m s位Array这个集合类变量中了。集合类的成员函数GetSize可以获得集合变量中元素的个数,也就是输入的字符串个数。但是m_strArray是视类CMenu2View的成员变量,在框架类 CMainFrame中如何去调用视类的成员变量呢?当然,我们首先需要获得视类对象,然后才能访问该对象的公有成员。我们可以利用CMainFrame类提供的GetActiveView成员函数,获取与框架相关联的当前视类的指针。这个函数的声明如下所示: 
CView* GetActiveView( ) const; 
我们可以看到这个函数返回一个 CView类型的指针,而程序需要的是 CMenu2View类型的指针,因此需要进行类型转换。有了这个视类指针,就可以调用其public类型的成员变量了,这就是先前把m_strArray定义为public类型的原因了。
下面,我们就在CMainFrame类的OnCommand函数中添加需处理的菜单命令范围的实现代码,代码如例6-44所示。
锣IJ 6-44 

BOOL CMainFrarne 	:: OnCommand(WPARAM wParam , LPARAM lpararn) 
{ // TODO : Add your specialized code here and/or call the base class int MenuCmdID = LOWORD(wParam); 
I 	CMenu2view *pView = (CMenu2View *)GetActiveView(); if ( MenuCmd工D>=工DM_PHONEl && MenuCmd工D<工DM_PHONEl + pView->m_strArray. 
GetSize() ) 
{ 
} 

return CFrameWnd : :OnCommand(wPararn, lPararn) ; 
对于例6-44所示这段代码,还有一个问题需要提醒读者注意,就是菜单项范围的上限的确定问题,到底应该是小于,还是小于等于这个上限值呢?读者可以简单地计算一下,例如,如果m_strArray中存储了 2个字符串,对应的菜单项标识就应该是IDM_PHONEl和IDMPHONE2。并且 GetSize函数返回值是 2,而EM-PHONE1+2相当于 IDM_PHONE3,因此到底应该是小于,还是小于等于,经过这种计算就很清楚了。 
E 	小技玛:在编写程序时,经常会遇到一些这种逻辑上的问题。我们可以像
这里遇到的问题一样,简单地把问题变成一些具体的数值,然后去计算,就可以得到答案了。
编译这时的Menu2程序,将会出现如下错误信息:
--一----------------Configuration: Menu2 -win32 Debug-一一-----------------Cornpiling . . . 
Ma工nFrm.cpp 
214 	I ~~~ 

0 :\VC++深入编程 \Chapter6\Menu2\MainFrm.cpp(132} error C2065: 'CMenu2View' : undeclared identifier 

0:\VC++深入编程\Chapter6\Menu2\MainFrm.cpp(132} : error C2065: 'pView' undeclared identifier 


0:\VC++深入编程\Chapter6\Menu2\MainFrm. cpp (132) : error C2059: syntax error : 

0 :\VC++深入编程\Chapter6\Menu2\MainFrm.cpp(133} : error C2 227: left o f '->m_strArray' must point to class/ struc t / union 

0 :\VC++深入编程\Chapter6\Menu2\MainFrm.cpp(133} : error C2228: left of 


'.GetSize' 	must have class/ struct/ union type Error executing cl.exe. Creating browse info file... 
Menu2.exe -5 error(s} , 0 warning(s} 
因为在框架类(CMainFrame)中用到了视类(CMenu2View )类型,所以应该在框架类的源文件中包含视类的头文件。即把下面灰色显示的那行代码添加到CMainFrame类的源文件的前部,结果如例6-45所示。 
iJtl6-45 

// MainFrm.cpp : implementation of the CMainFrame class 

// 
#include "stdafx.h" #include "Menu2 .h" 
#include "MainFrm.h" #include "Menu2View.h" 
国际z阳C程序中,除了CMainFrame类以外,其他程序类去掉类名前面
的第一个字母:C,就是该类头文件和源文件的名称(指不带文件%后缀的名称)。另外一种获知类的头文件名称的方法是,在ClassView选项卡上双击类名,就可以打开该类的头文件,从而在VC++开发环境的窗口标题栏上就可以看到该类的头文件名称。
再次编译Menu2程序,仍有错误发生,编译器提示发生以下错误: 
-------------------Configuration: Menu2 -Win32 Debug-----------------一-Compiling . . . 
Ma工nFrm.cpp 
d:\vc++深入编程\chapter6\menu2\menu2view.h(21} : error C2143 : syntax error : missing ';' before ,*, 

d : \vc++深入编程\chapter6\menu2\menu2view.h(21} : error C2501: 'CMenu2Doc' : missing storage-class or type spec工fiers 

d:\vc++深入编程 \chapter6\menu2\menu2view.h(21} error C2501: 'GetDocument' : missing storage-class or type specifiers 



" ‘ I 215 
鹅"
Error executing cl .exe. Creating browse info file... 
Menu2.exe -3 error(s) , 0 warning(s) 
上述错误信息提示:在"*"号之前少了一个l' . "。读者可以在第一条错误提示上双击
鼠标左键, VC++将在代码编辑窗口打开CMenu2View类头文件,井定位到如图6.50中光标所示位置。这个错误在程序开发中会经常遇到,实际
上,并不是错误信息所说的那样要在*号前加一class CMe nu2Uiew : public CUiew 
p~otected: 11 c~eatp from serialization on19
个分号,而是程序不认识CMenu2Doc这个类。< CMe nu2Uiew( >; 
在第二章中我们曾经介绍过,C++程序在编译时,DECLARE_DYHCREATE(CMe nu2Uiew> 11 Attributes
只有源文件参与编译,我们刚才在 CMainFramepubl1c: 
类的源文件前部加入了包含Menu2view.h文件的_1 CMe nu2Doc* C.tOocu回nt() ; 

11 Operations 
. 代码,因此,在编译CMainFrame类的源文件时,public: 
当遇到这行语句,就会展开Menu2View.h文件的图6.50编译器提示的出错位置内容,但该文件中引用了尚未定义的CMenu2Doc 类,即图6.50中所示代码处引用了尚未定义的CMenu2Doc类。因此编译器就会报告上述错误信息。那为什么视类的源文件包含它的头文件,在编译时没有出错呢?可以看看 CMenu2View类的源文件,如例6-46所示是它开始的几行语句。 
f9IJ 6-46 
11 Menu2View.cpp ; implementation of the CMenu2View class 

11 
#include "stdafx.h" 
#include "Menu2 .h" 

#include "Menu2Doc.h" 
#include "Menu2View .h" 

可以看到, Menu2View.cpp文件在包含Menu2view.h文件之前包含了 Menu2Doc.h文件,这样,当编译器在编译这个源文件时,将先展开Menu2Doc.h类文件的内容,该文件中是 CMenu2Doc类的定义。然后才展开 Menu2View.h文件,这时编译器己经知道了 CMenu2Doc类的定义。其实,为了解决上述错误,归根到底就是要让编译器在引用 CMenu2View类定义之前就已经知道了 CMenu2Doc类的定义。因此,为了解决这里出现的问题,可以把视类源文件中包含文档类的定义语句移到视类的头文件中,并放置在视类定义之前。即剪切Menu2View.cpp文件中的"州nclude "Menu2Doc.h""这行语旬,并将其粘贴到Menu2View.h文件的前部。如例6-47所示是修改之后的Menu2View.h文件的前部代码。
例6-47 

11 Menu2View.h : interface of the CMenu2View class 

216 I ~~~ 

// 

⌨️ 快捷键说明

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