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為指針。
至於強制類型轉換也是為了安全特性考慮的,因為從同一個基類之間的指針類型是互相兼容的。這種強制類型轉換也許並不必