MFC中的Document-View结构
MFC之所以為Application Framework,最重要的一個(gè)特征就是它能夠?qū)⒐芾頂?shù)據(jù)的程序代碼和負(fù)責(zé)顯示數(shù)據(jù)的程序代碼分離開(kāi)來(lái),而這種能力有Document/View提供。
想要實(shí)現(xiàn)數(shù)據(jù)管理和顯示的分離,需要搞清楚一些幾個(gè)問(wèn)題:
1. ?程序的哪個(gè)部分持有數(shù)據(jù)
2. ?程序的哪個(gè)部分負(fù)責(zé)更新數(shù)據(jù)
3. ?如何以多種方式顯示數(shù)據(jù)
4. ?如何讓數(shù)據(jù)的更改有一致性
5. ?如何實(shí)現(xiàn)數(shù)據(jù)持久化
6. ?如何管理使用者接口。不同的數(shù)據(jù)類(lèi)型可能需要不同的使用者接口,而一個(gè)程序可能管理多種類(lèi)型的數(shù)據(jù)。
其實(shí)Document/View結(jié)構(gòu)與當(dāng)下很流行的MVC架構(gòu)有異曲同工之妙。其間的對(duì)應(yīng)關(guān)系可以這樣認(rèn)為:M-Document, V-View,C-Document Template。
Document
Document其實(shí)就是指數(shù)據(jù)。Document在MFC的CDocument中被實(shí)例化。CDocument本身并無(wú)實(shí)際用途,它只提供一個(gè)框架。當(dāng)我們開(kāi)發(fā)程序時(shí),應(yīng)該從CDocument派生出一個(gè)屬于自己的Document類(lèi),并且在類(lèi)中聲明一些成員變量,用以容納數(shù)據(jù)。然后再(至少)改寫(xiě)專(zhuān)門(mén)負(fù)責(zé)檔案讀寫(xiě)操作的Serialize函數(shù)。當(dāng)然,APPWizard會(huì)為我們搭好框架,我們只要往里面填就行了。例如:
類(lèi)聲明中:virtual void Serialize(CArchive& ar);
類(lèi)定義中:
void CMyDoc::Serialize(CArchive& ar){?? ?if (ar.IsStoring())?? ?{?? ??? ?// TODO: add storing code here?? ?}?? ?else?? ?{?? ??? ?// TODO: add loading code here?? ?}}
View
View負(fù)責(zé)呈現(xiàn)Document中的數(shù)據(jù)。View在MFC的CView中被實(shí)例化。當(dāng)我們開(kāi)發(fā)自己的程序時(shí),應(yīng)該從CView派生出一個(gè)屬于自己的View類(lèi),并且在類(lèi)中(至少)改寫(xiě)專(zhuān)門(mén)負(fù)責(zé)顯示數(shù)據(jù)的OnDraw函數(shù)(針對(duì)屏幕)或Onprint函數(shù)(針對(duì)打印機(jī))。同樣,APPWizard也會(huì)為我們搭好框架,如下:
類(lèi)聲明中:virtual void OnDraw(CDC* pDC); ?// overridden to draw this view
類(lèi)實(shí)現(xiàn)中:
void CMFCView::OnDraw(CDC* pDC){?? ?CMFCDoc* pDoc = GetDocument();?? ?ASSERT_VALID(pDoc);?? ?// TODO: add draw code for native data here}
由于CView派生子CWnd,所以它可以接收一般的Windows消息(如WM_SIZE、WM_PAINT等),又派生自CCmdTarget,所以它可以接收自菜單或工具欄的WM_COMMAND消息。
在傳統(tǒng)的C/SDK程序中,當(dāng)窗口函數(shù)收到WM_PAINT時(shí),我們開(kāi)始獲得DC并繪制畫(huà)面。而在MFC中,一旦WM_PAINT發(fā)生,Framework就會(huì)自動(dòng)調(diào)用OnDraw函數(shù)。
View其實(shí)是一個(gè)沒(méi)有邊框的窗口。真正出現(xiàn)時(shí),其外圍還有一個(gè)有邊框的窗口,我們稱(chēng)之為Frame窗口。
Frame
? ? Frame負(fù)責(zé)窗口顯示時(shí)的UI管理。之所以不然View插手這件事而由Frame全權(quán)處理,涉及到框架設(shè)計(jì)層面的考慮。有時(shí)候功能之間需要一定的關(guān)聯(lián)度,但是這種關(guān)系又不能太強(qiáng)。把UI管理隔離出來(lái),可以降低彼此之間的依賴(lài)程度,也可以是該功能重復(fù)適用于各種場(chǎng)合。
Document Template
MFC把Document/View/Frame視為三位一體,這個(gè)整體就由DocumentTemplate來(lái)掌管。
如果程序中能夠處理多種數(shù)據(jù)類(lèi)型,就必須制造多個(gè)Document Template出來(lái),并使用AddDocTemplate函數(shù)將它們一一加入系統(tǒng)之中。這和程序是不是MDI(Multiple Document Interface,就是所謂的多文檔界面)無(wú)關(guān)。如果程序支持多種數(shù)據(jù)類(lèi)型,但卻是個(gè)SDI(Single Document Interface,就是所謂的單文檔界面),那只不過(guò)表示我們每次只能開(kāi)啟一份文件罷了。
CDocTemplate管理CDocument/CView/CFrameWnd
由Document Template來(lái)管理Document/View/Frame,那么誰(shuí)又是Document Template的管理者呢?其實(shí)是CWinApp,可以看其InitInstance函數(shù)中的操作:
{?? ?CMultiDocTemplate* pDocTemplate;?? ?pDocTemplate = new CMultiDocTemplate(?? ??? ?IDR_MFCTYPE,?? ??? ?RUNTIME_CLASS(CMFCDoc),?? ??? ?RUNTIME_CLASS(CChildFrame), // custom MDI child frame?? ??? ?RUNTIME_CLASS(CMFCView));?? ?AddDocTemplate(pDocTemplate);}
聯(lián)系文件的新建和打開(kāi)操作,如下圖(摘自:《深入淺出MFC》):
當(dāng)使用者單擊【文件/新建】命令項(xiàng)時(shí),根據(jù)APPWizard為我們所做的Message Map( ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ?),此命令由CWinApp::OnFileNew函數(shù)接手處理。后者調(diào)用CDocManager::OnFileNew,后者再調(diào)用CWinApp::OpenDocumentFile,后者再調(diào)用CDocManager:: OpenDocumentFile,后者再調(diào)用CMultiDocTemplate::OpenDocumentFile。最后調(diào)用的函數(shù)主要操作如下(定義于DOCMULTI.CPP):
CDocument*CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
? ? BOOLbMakeVisible)
{
? ? CDocument*pDocument =?
CreateNewDocument();
? ? if(pDocument == NULL)
? ? {
? ? ? ?TRACE0("CDocTemplate::CreateNewDocumentreturned NULL.\n");
? ? ? ?AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
? ? ? ?returnNULL;
? ? }
? ? ASSERT_VALID(pDocument);
?
? ? BOOLbAutoDelete = pDocument->m_bAutoDelete;
? ? pDocument->m_bAutoDelete= FALSE; ? // don't destroy if somethinggoes wrong
? ? CFrameWnd*pFrame = CreateNewFrame(pDocument, NULL);
? ? pDocument->m_bAutoDelete= bAutoDelete;
? ? if(pFrame == NULL)
? ? {
? ? ? ?AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
? ? ? ?deletepDocument; ? ? ? // explicit delete onerror
? ? ? ?returnNULL;
? ? }
? ? ASSERT_VALID(pFrame);
?
? ? if(lpszPathName == NULL)
? ? {
? ? ? ?//create a new document - with default document name
? ? ? ? ? ?……
? ? }
? ? else
? ? {
? ? ? ?//open an existing document
? ? ? ?……
? ? }
?
? ? InitialUpdateFrame(pFrame,pDocument, bMakeVisible);
? ? returnpDocument;
}
其中CreateNewDocument函數(shù)(定義于DOCTEMPL.CPP)的操作如下:
CDocument* CDocTemplate::CreateNewDocument(){?? ?// default implementation constructs one from CRuntimeClass?? ?if (m_pDocClass == NULL)?? ?{?? ??? ?TRACE0("Error: you must override CDocTemplate::CreateNewDocument.\n");?? ??? ?ASSERT(FALSE);?? ??? ?return NULL;?? ?}?? ?CDocument* pDocument = (CDocument*)m_pDocClass-> CreateObject();//動(dòng)態(tài)創(chuàng)建Document對(duì)象?? ?if (pDocument == NULL)?? ?{?? ??? ?TRACE1("Warning: Dynamic create of document type %hs failed.\n",?? ??? ??? ?m_pDocClass->m_lpszClassName);?? ??? ?return NULL;?? ?}?? ?ASSERT_KINDOF(CDocument, pDocument);?? ?AddDocument(pDocument);?? ?return pDocument;}
正如所料,其中動(dòng)態(tài)產(chǎn)生了Document對(duì)象。
CreateNewFrame函數(shù)的主要操作如下:
CFrameWnd*CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{
? ? if(pDoc != NULL)
? ? ? ?ASSERT_VALID(pDoc);
? ? //create a frame wired to the specified document
?
? ? ASSERT(m_nIDResource!= 0); // must have a resource ID to load from
? ? CCreateContextcontext;
? ? context.m_pCurrentFrame= pOther;
? ? context.m_pCurrentDoc= pDoc;
? ? context.m_pNewViewClass=?
m_pViewClass;
? ? context.m_pNewDocTemplate= this;
……
? ? CFrameWnd*pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();//動(dòng)態(tài)創(chuàng)建Document Frame對(duì)象
? ? ……
? ? //create new from resource
? ? if(!pFrame->LoadFrame(m_nIDResource,
? ? ? ? ? ?WS_OVERLAPPEDWINDOW| FWS_ADDTOTITLE, ? // default framestyles
? ? ? ? ? ?NULL, &context))
? ? {
? ? ? ?TRACE0("Warning:CDocTemplate couldn't create a frame.\n");
? ? ? ?//frame will be deleted in PostNcDestroy cleanup
? ? ? ?returnNULL;
? ? }
?
? ? //it worked !
? ? returnpFrame;
}
顯然上面動(dòng)態(tài)創(chuàng)建了所需的Frame,但是View卻是蹤跡全無(wú)。真實(shí)如此嗎?實(shí)則不然。上代碼出現(xiàn)了一個(gè)context變量,該變量有一個(gè)字段指向m_pViewClass。回想CFrameWnd::Create操作的最后一個(gè)參數(shù),它正是context。當(dāng)CFrameWnd收到WM_CREATE時(shí)會(huì)觸發(fā)CFrameWnd::OnCreate(由Message Map指定),其調(diào)用次序如下:
CFrameWnd::OnCreate-> CFrameWnd::OnCreateHelper -> CFrameWnd::OnCreateClient -> CFrameWnd::CreateView
其中最后調(diào)用的函數(shù)(定義于WINFRM.CPP)主要操作如下:
CWnd* CFrameWnd::CreateView(CCreateContext*pContext, UINT nID)
{
? ? ……
? ? CWnd*pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();//動(dòng)態(tài)生成View對(duì)象
? ? ……
? ? //views are always created with a border!
? ? if(!pView->Create(NULL, NULL,AFX_WS_DEFAULT_VIEW,
? ? ? ?CRect(0,0,0,0),this, nID, pContext))
? ? {
? ? ? ?TRACE0("Warning:could not create view for frame.\n");
? ? ? ?returnNULL; ? ? ? ?// can't continue without aview
? ? }
?
? ? if(afxData.bWin4 && (pView->GetExStyle() & WS_EX_CLIENTEDGE))
? ? {
? ? ? ?//remove the 3d style from the frame, since the view is
? ? ? ?// ?providing it.
? ? ? ?//make sure to recalc the non-client area
? ? ? ?ModifyStyleEx(WS_EX_CLIENTEDGE,0, SWP_FRAMECHANGED);
? ? }
? ? returnpView;
}
由上可見(jiàn),不僅View對(duì)象被動(dòng)態(tài)生成,其對(duì)應(yīng)的實(shí)際窗口也以Create函數(shù)產(chǎn)生出來(lái)。
CDocTemplate、CDocument、CView、CFrameWnd關(guān)聯(lián)關(guān)系
1. ?CWinApp擁有一個(gè)對(duì)象指針:CDocManager * m_pDocManager
2. ?CDocManager擁有一個(gè)指針鏈表CPtrList m_templateList,用來(lái)維護(hù)一系列的DocumentTemplate。應(yīng)用程序在CMyWinApp::InitInstance中以AddDocTemplate將這些Document Templates加入到有CDocManager所維護(hù)的鏈表之中。
3. ?CDocTemplate擁有三個(gè)成員變量,分別持有Document、View、Frame的CRuntimeClass指針,另有一個(gè)成員變量m_nIDResource,用來(lái)表示此Document顯示時(shí)應(yīng)該采用的UI對(duì)象。這四份數(shù)據(jù)在CMyWinApp::InitInstance函數(shù)構(gòu)造CDocTemplate時(shí)指定,稱(chēng)為構(gòu)造函數(shù)的參數(shù)。
4. ?CDocument有一個(gè)成員變量CDocTemplate * m_pDocTemplate,回指其DocumentTemplate;另外有一個(gè)成員變量CPtrList m_viewList,表示它可以同時(shí)維護(hù)一組Views。
5. ?CFrameWnd有一個(gè)成員變量CView * m_pViewActive,指向當(dāng)前活動(dòng)的View。
6. ?CView有一個(gè)成員變量CDocument * m_pDocument,指向相關(guān)的Document。
關(guān)系圖示如下(該圖作者水平很高,無(wú)法超越,只有借花獻(xiàn)佛了):
原文:https://blog.csdn.net/wzxq123/article/details/51981507?
?
總結(jié)
以上是生活随笔為你收集整理的MFC中的Document-View结构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: RTP Payload Format f
- 下一篇: 麻雀虽小,五脏俱全:分析CVS活动情况的