MFC之是以能成為application framework,很大的原因就在于其Document/View結構對于快速開發的支援。Document/View很好地劃分了程式代碼的前台背景,讓程式員可以專心于設計資料結構和UI。
Document即為“資料”,按我了解就是飯店的廚師;而View就是飯店的服務員。View負責點菜和上菜(對使用者請求做出直接響應),而Document負責烹饪,即處理使用者的要求。
除了Document和View,還有一個Frame,因為View要放在Frame内部,Frame就是承載View的架構。而三者之間的關系是由Document Template來管理的,一份Document Template管理一個document/frame/view三件組,而一個程式可以有多個document template,多個document template由一個CDocManager對象管理。
document template
一個MDI(多文檔接口)應用程式使用主架構視窗(main frame window)作為工作區,在工作區裡使用者可以打開多個文檔架構視窗,每一個文檔架構視窗用以顯示一份文檔。
Document template是用來定義以下三種類之間關系的模闆:
Document(文檔)類,從CDocument派生而來,用于處理資料,即所謂資料之體。
View(視圖)類,用于将來自Document類的資料顯示出來,可以從CView、CScrollView、CFormView和CEditView類派生,也可以直接使用CEditView類。
架構視窗(frame window)類,用以包含View。對于MDI程式,可以從CMDIChildWnd派生,也可以直接使用該類。
MDI應用程式可以支援不止一種文檔,而且不同種類的文檔可以同時打開(比如一個text和一個bitmap)。對于每一種所支援的文檔,應用程式都應該有一份對應的document template進行管理。也就是說你的應用程式支援幾種文檔,就應該有幾個Document template。
當使用者建立新文檔的時候,應用程式就會使用document template。如果程式支援的文檔種類在一種以上,那麼程式架構就會從document templates處取得所有的文檔類型名字,顯示在File New對話框裡。一旦使用者選擇了文檔類型,應用程式就會建立一個document對象,一個frame window對象和一個view對象,并且将它們聯系在一起(通過document template)。
通常程式員不需要使用CMultiDocTemplate的任何成員函數(除了構造函數外)。架構會在内部自動處理CMultiDocTemplate對象。
為了管理通過相關view對象和frame window對象來建構document的複雜過程,framework使用兩種document template類:
CSingleDocTemplate類用于SDI程式;CMultiDocTemplate類用于MDI程式。一個CSingleDocTemplate在同一時刻隻能建立并儲存單一種類的一個文檔;一個CMultiDocTemplate在同一時刻可以管理單一種類的多個文檔。
有些應用程式支援不止一種文檔類型,比如同時支援文本和圖形。這種應用程式為每個支援的類型使用單獨的document template對象,見下圖:
這個應用程式支援兩種文檔類型,是以具備兩個document template對象。對于每一種文檔類型可以打開多個文檔,每打開一個文檔應用程式就為之建立三個對象:CMyDocument對象用于處理資料,CMyView對象用于顯示,CMyFrameWnd用于裝載view,但是不管打開多少個同類型文檔,負責管理該類型的document template對象隻有一個,它負責管理的是上述三個類之間的關系,負責在這三個類的對象建立之時指定它們之間的關系。
上面說到每打開一個document,會随之一起建立一個view和一個frame window,而這三者的建立工作就是由document template完成的,當使用者點選“File/New”或者“File/Open”後,消息發出,被theApp的OnFileNew()接到,但它經過一系列的調用(比較繞)最終調用的是CMultiDocTemplate::OpenDocumentFile(),該函數完成此三對象的建立,其中view的建立又是非常的繞,最終經過一系列的調用由CFrameWnd::CreateView()完成,另外還會調用CView從CWnd繼承來的函數Create()用于産生與該view對應的真實視窗。而建立什麼種類的document、window、view是在建立document template時由document template的構造函數的參數指定的。下面顯示了建立一個CMultiDocTemplate(用以管理MDI的document template)的過程:
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_CMyDocTypeTYPE,
?? RUNTIME_CLASS(CMyDoc),
?? RUNTIME_CLASS(CChildFrame), // custom MDI child frame
?? RUNTIME_CLASS(CMyView));
if (!pDocTemplate)
?? return FALSE;
AddDocTemplate(pDocTemplate);
傳給構造函數的第一個參數是一個資源ID,該資源用于提供該文檔類型的菜單、快捷鍵、按鈕等。剩餘三個參數用RUN_CLASS()宏提供CMultiDocTemplate建立document/window/view時所需要的類型資訊(即對應的RuntimeClass對象,當使用者打開一個檔案時,document template就可以據此動态建立出document/window/view,這就很好展現了MFC動态建立的用途,關于動态建立是由DECLARE_DYNCREATE()/IMPLEMENT_DYNCREATE()宏實作的),最後用AddDocTemplate()加載此document template,AddDocTemplate()實際上是将document template加到由theApp的一個指針CDocManager* m_pDocManager所維護的document template連結清單中CDocTemplate有三個成員變量分别持有document/window/view的RuntimeClass對象的指針,另外還有一個資源ID成員。
Document template對象是被theApp建立的。在theApp的InitInstance()中的一個關鍵任務就是建立一個或多個适當種類的document template。theApp會在template list中儲存指向每一個document template的指針并提供一個接口用于增加document template(AddDocTemplate())。如果你想要支援兩個或以上的文檔類型,你必須為每個文檔類型顯式地調用AddDocTemplate()。
多個Document template是由一個CDocManager對象管理的,很多原本由CWinApp做的關于document template的工作如:AddDocTemplate()、OpenDocumentFile()、NewDocumentFile(),在MFC4.0後都由CDocManager來做了。
CDocTemplate/CDocment/CFrameWnd/CView之間的指針互指關系
列出:
CDocTemplate有指向其餘三者RuntimeClass對象的指針:
?CRuntimeClass* m_pDocClass;
?CRuntimeClass* m_pFrameClass;
?CRuntimeClass* m_pViewClass;
還有指向Document清單的指針:CPtrList m_pDocList;表示一個CDocTemplate可以維護多個同類型文檔。
CDocument有CDocTemplate* m_pDocTemplate回指CDocTemplate;另有CPtrList m_pViewList指向一個view的連結清單,表示一個Document可以對應多個View。
CFrameWnd有CView* m_pViewActive指向目前活動在其中的view。
CView有CDocument* m_pDocument指向對應的Document。
CDocument/CFrameWnd/CView之間互相操作的函數
CDocument::UpdateAllViews()—————>CView:OnUpdate()
CView::GetDocument();
CView::GetParentFrame();
CFrameWnd::GetActiveView();
CFrameWnd::GetActiveDocument();
View和Document的通信
程式員通過改寫CMyView的如下函數達到View和Document通信的目的:
CView::OnInitialUpdate():負責view的初始化。
CView::OnUpdate():Frameword在Document發生變化時調用此函數,此為預留給程式員的“用Document的變化指導View”的接口。
CView::OnDraw():該函數作為WM_PAINT的間接響應,負責View的更新。
CDocument::UpdateAllViews()/CView::OnUpdate()這一對函數是指令與執行的關系,調用UpdateAllViews()就會通知所有的View,通知方法就是調用其OnUpdate()。