基于MFC的windows程序各个对象之间互相访问的方法

 当用VC++的Application Wizard生成除了CDialog Basiced以外的应用程序时,将自动产生视图类、文档类、主帧窗口类、应用程序类等等。一般来说,程序的核心数据及操作在文档类中实现。跟界面有关的数据及操作在视图类中实现。当需要在某个类中使用不属于该类的数据时,必须要取得该数据所属类的指针。从视图类获得文档类的指针是很容易的,用GetDocument即可,这在一般的MFC文档中有介绍,也是编程中极为常用的的操作,比如视图类在进行重画等操作时,往往要用到文档类中的数据。然而只能从视图类获得文档类的指针是远远不够的,每个类都有获得其它各个类指针的一套方法,现归纳如下:

  为方便说明,现假设已用Application Wizard生成一个SDI应用程序Test,包含如一几个类:CTestApp,CTestDoc,CTestView,CMainFrm.

1.从视图类获得文档类的指针

  如前所述,在视图类中需要引用文档类的地方之前,使用以下语句:
 CTextDoc *pDoc=(CTestDoc*)GetDocument();
以后便可使用pDoc指针访问文档类。
此处的强制类型转换在Test应用程序中并不必需,因为该程序中只有一个视图类,并且在Initstance()中用SDI文档模板进行了装配,你可以在Test.cpp中的Initstance()方法中看到以下语句:
  CSingleDocTemplate *pDocTemplate;
  pDocTemplate=new CSingleDocTemplate(IDR_MAINFRAME,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()和GetNextView(),具体语法如下:
  virtual POSITION GetFirstViewPosition() const;
  virtual CView* GetNextView(POSITION& rPosition) const;
  注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。
  GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调动的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变量来辅助操作):
  CTestView* pTestView;
  POSITION pos=GetFirstViewPosition();
  pTestView=GetNextView(pos);
这样,便可到了CTestView类的指针pTestView.执行完成几句后,变量pos=NULL,因为没有下一个视图类,自然也没有下一个视图类的POSITION.

但是之几条语句太简单,不具有太强的通用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们需要遍历所有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,可用IsKindOf()成员函数时行检查,如:
  pView->IsKindOf(RUNTIME_CLASS(CTestView));
  即可检查pView所指是否是CTestView类。
有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下:
  CView* CTestDoc::GetVieww(CRuntimeClass* pClass)
  { CView* pView;
  POSITION pos=GetFirstViewPosition();
  while(pos!=NULL)
  {
  pView=GetNextView(pos);
  if(pView->IsKindOf(pClass))
  break;
  }
  if(!pView->IsKindOf(pClass))
  return NULL;
  return pView;}
  其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能:
  1.pos为NULL,即已经不存在下一个视图类供操作;
  2.pView已符合要求。
  3.1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图的位置同时返回当前视图指针,因此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=(CTestDoc*)GetDocument();
  CView* pView;
  POSITION pos=pDoc->GetFirstViewPosition();
  while(pos!=NULL)
  {
  pView=pDoc->GetNextView(pos);
  if(pView->IsKindOf(pClass))
  break;
  }
  if(!pView->IsKindOf(pClass))
  return NULL;
  return pView;}
  这个函数和2中的GetView()相比,一是多了第一句以取得文档类指针,二是在GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档类成员函数。
  有了此函数;当要从CTestAView中取得CTestBView的指针时,只需如下:
  CTestBView* pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView));

4. 从主帧窗口类获得视图类指针

  对本文所举的Test这各SDI程序来说,这是简单的,只需用CFrameWnd类的GetActiveView()成员函数即可。格式如下:
  CFrameWnd::GetActiveView()
  但将此函数应用在MDI应用的CMDIFrameWnd为中时,并不象所想的那样获得当前活动子窗口的视图类,而是返回NULL,这是一个要领性问题。在MDI程序中,CMDIFrameWnd没有和任何视图类发生关系,也就是说没有视图类直接属于它,只有子帧窗口类CMDIChildWnd才是所有子窗口视图类的父窗口。而子帧窗口的父窗口才是CFrameWnd。因此,在MDI程序中获得活动视图类的正确方法应为:1)先获得活动子帧窗口,2)再从活动子帧窗口中获得活动视图类
  //获得活动子帧窗口
  CMDIChildWnd* pChild=(CMDIChildWnd*)GetActiveFrame();
  //或:CMDIChildWnd* pChild=MDIGetActive();
  //获得活动子帧窗口的活动视图
  CMyView* pView=(CMyView*)pChild->GetActiveView();

5.从视图类中获得主帧窗口类指针:

用函数:CWnd::GetParentFrame()或AfxGetMainWnd();
可达到目的。GetParentFrame()的工作原理是在父窗口链中搜索,直到找到CFrameWnd或其派生类为止,并返回其指针。用法在InfoViewer中有详细介绍。

6.在任何类中获得应用程序类
  用MFC全局函数AfxGetApp()可做到。

7.从应用程序类中获得主帧窗口类
  CWinThread类有一个数据成员叫m_pMainWnd,由于CWinApp类由CWinThread派生而来,我们的应用程序为又由CWinApp派生而来,所以我们的CTestApp类也有一个m_pMainWnd成员,它所指南的即是CMainFrame类。(需进行合适的强制类型转换)。

总结起来有几点注意:
  A.在类A中获得类B的指针时,类A应包含类B的头文件。
  B.在很多时候要进行强制类型转换,并要注意括号的括法。
  由于派生类和父类指针类型的兼容,使明确区分各个类变得十分重要。在拿不准的时候,最好加上强制类型转换。

 

 

 

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

 

1) 在View中获得Doc指针

2) 在App中获得MainFrame指针

3) 在View中获得MainFrame指针

4) 获得View(已建立)指针

5) 获得当前文档指针

6) 获得状态栏与工具栏指针

7) 获得状态栏与工具栏变量

8) 在Mainframe获得菜单指针

9) 在任何类中获得应用程序类

10) 从文档类取得视图类的指针(1)

11) 在App中获得文档模板指针

12) 从文档模板获得文档类指针

13) 在文档类中获得文档模板指针

14) 从文档类取得视图类的指针(2)

15) 从一个视图类取得另一视图类的指针

 

VC中编程对于刚刚开始学习的同学,最大的障碍和问题就是消息机制和指针获取与操作。其实这些内容基本上是每本VC学习工具书上必讲的内容,而且通过MSDN很多问题都能解决。下面文字主要是个人在编程中指针使用的一些体会,说的不当的地方请指正。一般我们使用的框架是VC提供的Wizard生成的MFC App Wizard(exe)框架,无论是多文档还是单文档,都存在指针获取和操作问题。下面这节内容主要是一般的框架,然后再讲多线程中的指针使用。使用到的类需要包含响应的头文件。首先一般获得本类(视,文档,对话框都支持)实例指针this,用this的目的,主要可以通过类中的函数向其他类或者函数中发指针,以便于在非本类中操作和使用本类中的功能。

1) 在View中获得Doc指针 CYouSDIDoc *pDoc=GetDocument();一个视只能有一个文档。

2) 在App中获得MainFrame指针CWinApp 中的 m_pMainWnd变量就是MainFrame的指针,也可以: CMainFrame *pMain =(CMainFrame *)AfxGetMainWnd();

3) 在View中获得MainFrame指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd;

4) 获得View(已建立)指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd;CyouView *pView=(CyouView *)pMain->GetActiveView();

5) 获得当前文档指针 CDocument * pCurrentDoc =(CFrameWnd *)m_pMainWnd->GetActiveDocument();

6) 获得状态栏与工具栏指针 CStatusBar * pStatusBar=(CStatusBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR);CToolBar * pToolBar=(CtoolBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);

7) 如果框架中加入工具栏和状态栏变量还可以这样 (CMainFrame *)GetParent()->m_wndToolBar;(CMainFrame *)GetParent()->m_wndStatusBar;

8) 在Mainframe获得菜单指针 CMenu *pMenu=m_pMainWnd->GetMenu();

9) 在任何类中获得应用程序类用MFC全局函数AfxGetApp()获得。

10) 从文档类取得视图类的指针我是从http://download.cqcnc.com/soft/program/article/vc/vc405.html学到的,从文档获得视图类指针目的一般为了控制同一文档的多个视图的定位问题,我的体会特别是文字处理CEditView当产生多个视图类时,这个功能是非常需要的。 CDocument类提供了两个函数用于视图类的定位:GetFirstViewPosition()和GetNextView() virtual POSITION GetFirstViewPosition() const;virtual CView* GetNextView(POSITION& rPosition) const;注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调用的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变量来辅助操作): CTestView* pTestView;POSITION pos=GetFirstViewPosition();pTestView=GetNextView(pos);这样,便可到了CTestView类的指针pTestView.执行完几句后,变量pos=NULL,因为没有下一个视图类,自然也没有下一个视图类的POSITION.但是这几条语句太简单,不具有太强的通用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们需要遍历所有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,可用IsKindOf()成员函数时行检查,如: pView->IsKindOf(RUNTIME_CLASS(CTestView));即可检查pView所指是否是CTestView类。

有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下: CView* CTestDoc::GetView(CRuntimeClass* pClass){ CView* pView; POSITION pos=GetFirstViewPosition(); while(pos!=NULL){  pView=GetNextView(pos);  if(!pView->IsKindOf(pClass))  break; } if(!pView->IsKindOf(pClass)){  AfxMessageBox("Connt Locate the View.\r\nhttp://www.VCKBASE.com");  return NULL; } return pView;}

其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能:

1.pos为NULL,即已经不存在下一个视图类供操作;2.pView已符合要求。

1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图的位置同时返回当前视图指针,因此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=(CTestDoc*)GetDocument(); CView* pView; POSITION pos=pDoc->GetFirstViewPosition(); while(pos!=NULL){  pView=pDoc->GetNextView(pos);  if(!pView->IsKindOf(pClass))  break; } if(!pView->IsKindOf(pClass)){  AfxMessageBox("Connt Locate the View.");  return NULL; } return pView;}这个函数和2中的GetView()相比,一是多了第一句以取得文档类指针,二是在GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档类成员函数。有了此函数;当要从CTestAView中取得CTestBView的指针时,只需如下:CTestBView* pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView)); 11)对于单文档中也可以加入多个文档模板,但是一般的开发就使用MDI方式开发多文档模板,其方法与上述视图的获取方法很接近,这里稍做解释,如果不清楚,请查阅MSDN,(以下四个内容(11、12、13、14)来源:http://sanjianxia.myrice.com/vc/vc45.htm

可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位置;利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。 POSITION GetFirstDocTemplate( ) const; CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是模板列表中的最后一个,则pos参数被置为NULL。

12)一个文档模板可以有多个文档,每个文档模板都保留并维护了一个所有对应文档的指针列表。 用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一个文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与模板相关的文档列表。函数原形为: viaual POSITION GetFirstDocPosition( ) const = 0; visual CDocument *GetNextDoc(POSITION & rPos) const = 0;  如果列表为空,则rPos被置为NULL.

13)在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。函数原形如下: CDocTemplate * GetDocTemplate ( ) const; 如果该文档不属于文档模板管理,则返回值为NULL。

14)一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。CDocument::AddView将一个视连接到文档上,将该视加入到文档相联系的视的列表中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或Window/Split的命令而将一个新创建的视的对象连接到文档上时, MFC会自动调用该函数,框架通过文档/视的结构将文档和视联系起来。当然,程序员也可以根据自己的需要调用该函数。 Virtual POSITION GetFirstViewPosition( ) const; Virtual CView * GetNextView( POSITION &rPosition) cosnt; 应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的列表中的第一个视的位置,并调用CDocument::GetNextView返回指定位置的视,并将rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个视,则将rPosition置为NULL.

15)从一个视图类取得另一视图类的指针这个应用在多视的应用程序中很多见,一般如果自己在主程序或者主框架中做好变量记号,也可以获得,还有比较通用的就是用文档类作中转,以文档类的视图遍历定位,取得另一个视图类。这个功能从本文第10项中可以得到。

这些资料大部分都是从网上和MSDN中获得的,写这个文档就是为了让大家不用再寻找,列出标题,可操作性更强


 

你可能感兴趣的:(C++,MFC)