MFC多文档框架
1.参考原文文档
https://blog.csdn.net/netanimals/article/details/7490488
2.MFC 架构主要知识点
2.1CWinApp类
它包含并管理着应用程序的文档/视窗的所有信息。它有一个成员变量
CDocManager * m_pDocManager,此变量是文档/视窗的管理器,m_templateList
是CDocManager里的一个列表,此列表里保存了所有文档模板的指针,当用户调用
CWinApp::AddDocTemplate( pDocTemplate ) 后该pDocTemplate存入了
CWinApp::m_pDocManager::m_templateList里。
CWinApp::GetFirstDocTemplatePosition()
CWinApp::GetNextDocTemplate(POSITION& pos)
是遍例所有的文档模板指针。
2.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;
用于遍例该文档类型所有文档实例。
2.3. 文档(CDocument)
CDocument 我们最熟悉不过了。每一个文档实例可有多个视图与之相对应。
CDocument::m_viewList用来保存所有与此文档实例相关的View
CDocument::AddView函数用来维护m_viewList数据但我们一般不用★★★★
void CDocument::AddView(CView* pView)//★★★MFC源码
{
ASSERT_VALID(pView);
ASSERT(pView->m_pDocument == NULL); // must not be already attached
ASSERT(m_viewList.Find(pView, NULL) == NULL); // must not be in list
m_viewList.AddTail(pView);
ASSERT(pView->m_pDocument == NULL); // must be un-attached
pView->m_pDocument = this;
OnChangedViewList(); // must be the last thing done to the document
}
CDocument::GetDocTemplate 可获得CMultiDocTemplate;
2.4. CView
在CMDIChildWnd里的,每一个CMDIChildWnd有一个View
CView::GetDocument可获得与此视相关的CDocument
CView::GetParentFrame() 可获得CMDIChildWnd;
2.5 总结
CWinApp,CMDIChildWnd,CView,CDocument,CMultiDocTemplate之间知道其中一个实例
必可知道其他所有几个实例,CWinApp统领全局,任何时候,只要获得CWinApp实例,则所有的文档模板,
文档实例,视,Frame窗口均可被枚举出来。AfxGetApp() 获得CWinApp实例指针。
2.6 App 整理架构理解
App是应用域,所有的域中的东西都可以通过全局函数访问到它。
MainFrame是主框架,也基本可以用全局函数访问到。
MainFrame下是若干个ChildFrame,ChildFrame中若干个View和Document(可能不成对),ChildFrame管理着View,View和Document进行互操作。
因此整体框架就出来了,一般除了直接应用的关系都可以通过MainFrame-->Active ChildFrame-->Active View-->Document这条线进行访问
2.7 因为对于SDI程序,主框架窗口就是文档框窗(如果这个也不知道,就要查看一下MFC下的单文档的构成原理了).
下面所说的是关于单文档的.
例子: 在CMainFrame框架中如何得到视图类的指针.可以 先得到框架指针,然后调用 GetActiveView 函数指向当前活动视.
C View * pView;
pView=(CView*)((CFrameWnd*)AfxGetApp()->m_pMainWnd)->GetActiveView();
当然这些也许都知道是这么用的,但真正的m_pMainWnd和AfxGetApp()是什么意思也许有的人不明白.
大家也许都知道如何在App中获得MainFrame指针(框架类): CWinApp 中的 m_pMainWnd变量就是CMainFrame的指针.
所以在别的类下也可以先得到m_pMainWnd,就得到了MainFrame的指针. 所以得到视图类的指针,必先 得到CFrameWnd的指针m_pMainWnd,然后在调用FrameWnd下的GetActiveView 就指向当前活动视.
m_pMainWnd的由来:
每一个MFC应用程序都有一个CWinApp派生类的对象。这个对象对应着程序的主线程。而 CWinApp 类中有一个 CWnd * m_pMainWnd 成员变量。这个成员变量记录了应用程序的主窗口。
当你新建一个MFC应用程序的时候,在 InitInstance虚函数里都会出现对 m_pMainWnd 赋值的语句.唯一的例外是单文档界面的MFC应用程序,你无法在 InitInstance 函数里看到这段代码,因为它已经被隐藏在 ProcessShellCommand 这个函数里了。由此你就可以下结论了:只要创建自己的窗口类,就要把这个类的对象赋值给 m_pMainWnd .而这个成员只能在CAPP类中才可以使用,所以怎样使用这个CWinApp类里的CWnd 类型的变量来得到主框架窗口的指针呢??
AfxGetApp函数才可以 , 因为AfxGetApp()得到的是CWinApp类的对象,且AfxGetApp返回值为CWinApp对象指针,就是MFC生成的CApp.cpp中定义的那个对象(对象theApp的指针)。
因为你是在自己创建的项目中得到CWndApp成员函数或者成员变量,所以你必须强制转换为你自己的项目中的类,才能找到成员函数或者变量.
注: 在单文档中,获得视指针的最简单的方法还是
((C**View *)CFrameWnd::GetActiveView())
2: 当然在FrameWnd中也可以得到文档类的指针:
CMyDocument* pDoc;
pDoc=(CMyDocument*)((CFrameWnd*)AfxGetApp()->m_pMainWnd)->GetActiveDocument();
3: 由上面可以知道:在View中怎样获得MainFrame指针
CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
注: 从视图类中获得主帧窗口类指针:用函数:CWnd::GetParentFrame()或AfxGetMainWnd()也
可达到目的。GetParentFrame()的工作原理是在父窗口链中搜索,直到找到CFrameWnd或其派生类为止,并返回其指针。
((CMainFrame *)CWnd::GetParentFrame())
或者
((CMainFrame *)AfxGetMainWnd())
2.8 当然对于MDI程序,由于子窗口才是文档框窗,因此首先要用GetActiveFrame()取得活动子框架窗口,然后通过该子窗口获取活动视图和文档:
CMDIChildWnd* pChild=(CMDIChildWnd*)((CFrameWnd*)AfxGetApp()->m_pMainWnd)-
>GetActiveFrame();
取得活动视图:
CMyView* pView=(CMyView*)pChild->GetActiveView();
取得活动文档:
CMyDocument* pDoc=pChild->GetActiveDocument();
注: 也可以用这种方法来得到多文档中的视指针
//获得活动子框架窗口
CMDIChildWnd* pChild=(CMDIChildWnd)GetActiveFrame();
//或:
CMDIChildWnd pChild=MDIGetActive();
//获得活动子帧窗口的活动视图
CMyView* pView=(CMyView*)pChild->GetActiveView();
2.9 1. 从视图类获得文档类的指针
在视图类中需要引用文档类的地方之前,使用以下语句:
CDoc pDoc=(CDoc)GetDocument();
以后便可使用pDoc指针访问文档类。
2.10 从文档类取得视图类的指针 CDocument类提供了两个函数用于视图类的定位:
GetFirstViewPosition()和GetNextView()
注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变.GetFirstViewPosition()用于
返 回第一个视图位置(返回的并非视图类指针,而是一个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用 调动的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有一个视图类,因此只需将这两个函数调用一次即可得到 CTestView的指针如下(需定义一个POSITION结构变量来辅助操作):
CView pView;
POSITION pos=GetFirstViewPosition();
pView=GetNextView(pos);
这 样,便可到了C*View类的指针pView.执行完成几句后,变量pos=NULL,因为没有下一个视图类,自然也没有下一个视图类的 POSITION.但是之几条语句太简单,不具有太强的通用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们需要遍历所 有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,可用IsKindOf()成员函数时行检查.
如:
pView->IsKindOf(RUNTIME_CLASS(CView));
即可检查pView所指是否是CView类。
有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下:
CView* CDoc::GetVieww(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.");
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为指针。
至于强制类型转换也是为了安全特性考虑的,因为从同一个基类之间的指针类型是互相兼容的。这种强制类型转换也许并不必