文档、视图、框架窗口、文档模板之间的相互关系【转】

要了解文档、视图、框架窗口、文档模板之间的相互关系,关键要理解他们的结构  
1、首先应该对CWinApp类有充分的了解  
它包含并管理着应用程序的文档/视窗的所有信息。
它有一个成员变量CDocManager   *m_pDocManager,此变量是文档/视窗的管理器。
m_templateList是CDocManager里的一个列表,此列表里保存了所有文档模板的指针。
当用户调用
CWinApp::AddDocTemplate(pDocTemplate)后,该pDocTemplate存入了CWinApp::m_pDocManager::m_templateList里。  
CWinApp::GetFirstDocTemplatePosition()  
CWinApp::GetNextDocTemplate(POSITION&   pos)  
是遍例所有的文档模板指针。  
2、上面我们提到了文档模板(CMultiDocTemplate(我们主要针对文档)),  
这是一个极重要的类。
CMultiDocTemplate::m_docList保存的所有该种文档的文档实例的指针列表。
下面两个函数用于维护CMultiDocTemplate::m_docList数据  
CMultiDocTemplate::AddDocument(CDocument*   pDoc);  
CMultiDocTemplate::RemoveDocument(CDocument*   pDoc);  
而  
CMultiDocTemplate::GetFirstDocPosition()   const;  
CMultiDocTemplate::CDocument*   GetNextDoc(POSITION&   rPos)   const;  
用于遍例该文档类型所有文档实例。  
3、上面提到文档(CDocument)  
CDocument我们最熟悉不过了。每一个文档实例可有多个视与之相对应。  
CDocument::m_viewList用来保存所有与此文档实例相关的View  
CDocument::GetDocTemplate可获得CMultiDocTemplate;  
4、CView
他是放在CMDIChildWnd里的,每一个CMDIChildWnd有一个View  
CView::GetDocument可获得与此视相关的CDocument  
CView::GetParentFrame()可获得CMDIChildWnd;

总结
通过以上分析可见CWinApp,CMDIChildWnd,CView,CDocument,CMultiDocTemplate之间知道其中一个实例必可知道其他所有几个实例,CWinApp统领全局,任何时候,只要获得CWinApp实例,则所有的文档模板,文档实例,视,Frame窗口均可被枚举出来。AfxGetApp()获得CWinApp实例指针。

***************************************************************************************************************

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(已建立)指针
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\n http://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项中可以得到。 
————————————————————————————————————————————

--------------------------------------------------------------------------------

VC多文档程序结构[转]
2008-04-03 15:03
1、模板、文档、视图、框架的关系

连载1~5我们各个击破地讲解了文档、文档模板、视图和框架类,连载1已经强调这些类有着亲密的内部联系,总结 1~5我们可以概括其联系为:

(1)文档保留该文档的视图列表和指向创建该文档的文档模板的指针;文档至少有一个相关联的视图,而视图只能与一个文档相关联。

(2)视图保留指向其文档的指针,并被包含在其父框架窗口中;

(3)文档框架窗口(即包含视图的mdi子窗口)保留指向其当前活动视图的指针;

(4)文档模板保留其已打开文档的列表,维护框架窗口、文档及视图的映射;

(5)应用程序保留其文档模板的列表。

我们可以通过一组函数让这些类之间相互可访问,表6-1给出这些函数。

表6-1 文档、文档模板、视图和框架类的互相访问

--------------------------------------------------------------------------------
调用全局函数afxgetapp可以得到cwinapp应用类指针
应用afxgetapp()->m_pmainwnd为框架窗口指针;
用cwinapp::getfirstdoctemplatepostion、cwinapp::getnextdoctemplate来遍历所有文档模板文档
调用cdocument::getfirstviewposition,cdocument::getnextview来遍历所有和文档关联的视图;
调用cdocument:: getdoctemplate 获取文档模板指针文档模板
调用cdoctemplate::getfirstdocposition、cdoctemplate::getnextdoc来遍历所有对应文档视图
调用cview::getdocument 得到对应的文档指针;
调用cview::getparentframe 获取框架窗口文档框架窗口
调用cframewnd::getactiveview 获取当前得到当前活动视图指针;
调用cframewnd::getactivedocument 获取附加到当前视图的文档指针
mdi框架窗口调用cmdiframewnd::mdigetactive 获取当前活动的mdi子窗口(cmdichildwnd)

我们列举一个例子,综合应用上表中的函数,写一段代码,它完成遍历文档模板、文档和视图的功能:

cmyapp *pmyapp = (cmyapp*)afxgetapp();  //得到应用程序指针
position p = pmyapp->getfirstdoctemplateposition(); //得到第1个文档模板
while (p != null)  //遍历文档模板
{
cdoctemplate *pdoctemplate = pmyapp->getnextdoctemplate(p);
position p1 = pdoctemplate->getfirstdocposition(); //得到文档模板对应的第1个文档
while (p1 != null)  //遍历文档模板对应的文档
{
cdocument *pdocument = pdoctemplate->getnextdoc(p1);
position p2 = pdocument->getfirstviewposition();  //得到文档对应的第1个视图
while (p2 != null)  //遍历文档对应的视图
{
cview *pview = pdocument->getnextview(p2);
}
}
}
由此可见,下面的管理关系和实现途径都是完全类似的:

(1)应用程序之于文档模板;

(2)文档模板之于文档;

(3)文档之于视图。

×××××××××××××××××××××××××××××××××××××××××

1、应用程序对象有一个文档模板管理器CDocManager* m_pDocManager(第一次调用AddDocTemplate时new出来)

2、文档模板管理器有一个文档模板对象列表CPtrList m_templateList(AddDocTemplate 函数负责添加该列表)

3、文档模板对象拥有文档、视图、框架的静态CRuntimeClass成员指针用于动态创建,还有一个 m_nIDResource用来表示应采用的UI对象

4、每个文档模板对象拥有 m_pOnlyDoc 或 m_docList (文档指针或文档指针列表),OnFileNew 和 OnFileOpen都调用文档模板对象的OpenDocumentFile,OpenDocumentFile 调用文档模板的 CreateNewDocument,CreateNewDocument再调用文档模板的 AddDocument 填充该文档列表或文档指针

5、文档对象有一个文档模板指针 m_pDocTemplate (回指文档对象所属模板对象).同上,也是文档模板的 AddDocument 成员函数把 this 指针(文档模板自身).塞给刚刚创建的文档对象

6、文档对象有一个 m_viewList(视图列表),OnFileNew 和 OnFileOpen 都调用文档模板对象的OpenDocumentFile,该函数调用 CreateNewDocument 创建文档,然后调用 CreateNewFrame 创建框架对象.

CreateNewFrame 构造CCreateContext对象
CCreateContext两个重要字段:(1)刚创建的文档指针(2)视图的CRuntimeClass指针
CreateNewFrame 创建框架对象后由该对象调用 LoadFrame
LoadFrame 的最后一个参数即为 CCreateContext 指针

LoadFrame 调用 Create,Create 再调用 CreateEx 最后一个参数均为此CCreateContext指针
Create的调用由消息映射表引发CFrameWnd::OnCreate被调用
OnCreate的LPCREATESTRUCT的一个字段lpCreateParams 仍然是这个CCreateContext指针

则在CFrame::OnCreate中,由这个CCreateContext的CRuntimeClass(视图的)来调用 CreateObject
产生视图对象后,由该对象调用Create(最后一个参数仍然是这个CCreateContext指针)

视图对象的Create由消息映射表引发视图对象的OnCreate被调用
视图的OnCreate的参数 LPCREATESTRUCT 的 lpCreateParams 还是这个CCreateContext指针)
于是利用 CCreateContext 的成员 m_pCurrentDoc (当前文档)
来调用 CDocument::AddView 把视图加入文档的视图列表

7、视图有一个文档指针m_pDocument (指向所属文档)
同上,也是CDocument::AddView函数初始化的,如下所示:
pView->m_pDocument = this;

8、框架有一个m_pViewActive(活动视图)
由框架的SetActiveView进行设置

发表评论