当用visual c++的Application Wizard生成除了以外的应用程序时,将自动产生视图类,文档类,主帧窗口类,应用程序类等等.
一般说来,程序的核心数据及操作在文档类中实现.跟界面有关的数据及操作在视图类中实现.当需要
在某个类中使用不属于该类的数据时,必须要取得概述据所属的类的指针.从视图类获得文档类的指针
是很容易的,用GetDocument()即可,这在一般的MFC文当中有介绍,也是编程中纪委常用的操作,比如视图类
在进行重画等操作时,往往要用到文档类中的数据.然而只是从视图类中获得文档类得指针是远远不够的,
每个类都有获得其他类指针的一套方法.现归纳如下.
为方便说明,现假设已用app wizard生成了一个SDI应用程序test,包含如下几个类:
CTestApp,CTestDoc,CTestView,CMaimFrame
1:从视图类获得文档类指针
如前所述,在视图类中需要引用文档类的地方之前,使用如下语句:
CTestDoc *pDoc=(CTestDoc *)GetDocument();
以后便可使用pDoc指针访问文档类.
此处的强制类型转换在test应用程序中并不需要,因为该程序只有一个视图类,并且在InitInstance()
重用SDI文档模板进行了装配.你可以在Test.cpp种的InitInstance()方法中看到如下语句:
CSingleDocTemplate *pDocTemplate;
pDocTemplate=new CSingleDocTemplate(IDR_MAINRAME,RUNTIME_CLASS(CTestDoc),RUNTIME_CLASS(CMainFrame),RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
以及TestView.h中的定义:
inline CTestDoc *CTestView::GetDocument()
{return (CTestDoc *)m_pDocument;}
简而言之,就是说CTestView的GetDocument()函数自然而然的认为CTestDoc是与它 "相配 "的.当生成了一个具有多个视图类的应用程序时,(如用CSplitterWnd将窗口分为两栏,但这两栏并非从同一种视图类派生就属于这种情况。具体实现在本文讨论范围之外),只有一个视图类能与唯一的文档类用文档模板进行装配,那么在另外一个未经装配的类中要取得文档类的指针,则需进行强制类型转换。
2:从文档类取得视图类指针
CDocument类提供了两个函数用于视图类的定位: GetFirstViewPosition()和 GetNestView(),
virtual POSITION GetFirstViewPosition() const;
virtua CView * GetNextView (POSITION& rPosition )const;
注意:GetFirstView()括号中的参数用的是引用方式,因此执行后值可能改变.
GetFitstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调用和方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变量来辅助操作):
CTestView*pTestView;
POSITISONpos=GetFirstViewPosition();
pTestView=GetNextView(pos);
这样,便得到了CTestView类的指针pTestView.执行完这几句后,变量pos=NULL,因为没有下一个视图类,自然也没有下一个视图类的POSITION.
但是这几条语句太简单,不具有太强的通用性和安全特征,当象前面说的那样,当要在多个视图类中返回某个指定类的指针时,我们需要遍历所有视图类,直到找到指定类为止。判断一个指针指向的是否是某个类的实例时,可用IsKindOf()成员函数是行检查,如:
pView-> IsKindof(RUNTIME-CLASS(CTestView));
即是检查pView所指是否是CTestView类。
有了以上基础,我们可以从文档类取得任何类的指针。为了方便,我们将其作为一个文档类和成员函数,它有一个参数,表示要获得哪个类的指针,它返回的就是这个类的指针。实现如下:
CView * CTestDoc:: GetView(CRuntimeClass*pClass)
{CView*pView;POSITIONpos=GitFirstViewPosition();while(pos!=NULL){pView=GitNextView(pos);
if(pView-> IsKindOf(pClass))break;}
if(!pView-> IsKindof(pClass)){AfxMessageBox( "Connot locate the View. ");
return NULL;}
return pView;}
其中用了两次视图类的成员函数IsKindOf()判断,是因为退出while循环有三种可能:
1.pos为NULL,即已经不存在下一个视图供操作;
2.pView已符合要求;
3.1和2同时满足。这是因为Get-NextView()的功能是将pos改变成下一个视图的位置同时返回当前视图指针,因此pos是pView的下一个视图类的POSITION,完全有可能即是pos==NULL又是pView符合需要。当所需的视图是最后一个视图类时就如此。因此需采用两次判断。
使用该函数应遵循如下格式(以取得CTestView指针为例):
CTestView*pTestView=(CTestView*)GetView(RUNTIME-CLASS(CTestView));
RUNTIME-CLASS()是一个宏,可以简单地理解它的作用:
将类和名字转化为CRuntimeClass类指针。
至于强制类型转换也是为了安全特性考虑的,因这从同一个基类派生的类之间以及它们与基类之间的指针类型是互相兼容的。这种强制类型转换也许并不必要,但能避免一些可能出现的麻烦。
3:从一个视图类取得另一个视图类的指针
综合1和2,很容易得出视图类之间互相获得指针的方法:就是用文档类作中转,先用1的方法得到文档类的指针,再用2的方法,以文档的视图定位函数:
(假设要从CTestAView中取得指向其它视图类的打针)
CView*CTestAView::GetView
(CRuntimeClass*pClass)
{CTestDoc*pDoc*pDoc=
(CTestDoc*)GetDecument();
CView*pView;
POSITION pos=pDoc-> GetFistViewPosition();
while(pos!=NULL){
pView=pDoc-> GetNextView(pos);
if( pView-> IsKindOf(pClass) )
break; }
if(!pView-> IsKindOf(pClass)){
AfxMessageBox( "Connot Iocate the View. ");
return NULL;}
return pView;}
这个函数和2中的GetView()相比,一是多了第一名以取得文档类指针,二是在GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档类成员函数。
有了此函数要从CTestAView中取得CTestBView的打针时,只需如下:
CTestBView
CTestBView*pTestBView=(CTestBView*)
GetView(RUNTIME-CLASS(CTestBView));
4。从主帧窗口类获得视图类指针
对本文所举的Test这种SDI程序来说,这是很简单的,只需用CFrameWnd类的GetAcTIVEvIEW()成员函数即可。格式如下:
CFrameWnd::GetActiveView()
但将此函数应用在MDI应用程序的CMDIFrameWnd类中时,并不象所想的样获得当前活动子窗口的视图类,而是返回NULL。这是一个概念性问题。在MDI程序中,CMDIFrameWnd没有和任何视图类发生关系,也就是说没有视图类直接隶属于它,只有子窗口类CMDIChildWnd才是所有子窗口视图类的父窗口。而子帧窗口的父窗口才是CFrameWnd。因此,在MDI程序中获得活动视图类的正确方法应为:先获得活动子帧窗口,再从子帧窗口中获得活动视图类:(以下这段代码与VC++的InfoViewer中GetActiveView的示范代码类似,并有详细说明, 限于篇幅,此处不一一说明)
//获得活动子帧窗口
CMDIChildWnd*pChild=(CMDIChildWnd*)
GetActiveFrame();
//或:CMDIChildWnd*pChild=MDIGetActive();
//获得活动子帧窗口的活动视图
CMyView*pView=(CMyView*)pChild-> GetAetiveView();
5从视图类中获得主帧窗口类指针
用函数CWnd::GetParentFrame()
可达到目的。它的工作原理是在父窗口链中搜索,直到找到 CFrameWnd或其派生类为止,并返回其指针 。用法在 InfoViewer中有详细介绍。
6在任何类中获得应用程序类
用 MFC全局函数AfxGetApp()可做到。
7从应用程序类中获得生帧窗口类
CWinThread类有一个数据成员叫 m-pMainWnd,由于 CWinApp类由zCWinThread 类派生而来,我们的应用程序类又由CWinApp 派生而来,所以我们的 CTestAPP类也有一个m-pMainWnd 成员, 它所指向的即是CMainFrame 类。(需进行合适的强制类型转换)。
例:在应用程序类中对生帧窗口类的一个整型变量nIntVar 赋值:
((CMainFrame*)m-pMainWnd)-> nIntVar=100;
几点注意:
有了以上几种指针获取方法,已以因以在类中方便地获得其它类的指针,使程序更加灵活高效。但有两点需注意:
1在类 A中获得类 B的指针时,类 A应包含类B 的头文件
例:在 CTestView中欲获得 CMainFrame的指针,应在 TestView.cpp文件头部加入 #include "MainFrm.h "。否则通不过编译。
2在很多时候要进行强制类型转换,并要注意括号的括法
由于派生类和父类指针类型的兼容,使明确区分各个类变得十分重要。在拿不准的时候,最好加上强制类型转换。比如:
CTestBView*pTestBView=(CTestBView*)
GetView(RUNTIME_CLASS(CTestBView));
使获得的类指针强 成 CTestBView*型而非 CView*型。
另外由于指针成员运算符“->”的运算优先级高于强制类型转换,因此括号的定法很重要。见例:
(CMainFrame*)m-pMainWnd-> nIntVar=100;
这样的写法是不正确的, 相当于(CMainFrame*) 类型转换在对nIntVar 进行转换,将在编译时出错,告知无法将整型转换成CMainFrame* 型。正确的写法是:
((CMainFrame*)m-pMainWnd)-> nIntVar=100
这无疑上个微不足道的小问题,然而也正因为它微小且不易觉,如果不注意的话, 可能会浪费掉不少时间.