深入知道MFC中的文擋/視結(jié)構(gòu)
發(fā)表時(shí)間:2024-06-19 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]作者: 李澤宇 金剛 熊聯(lián)歡 姜軍 Visual C++ 6.0 以其功能強(qiáng)大、用戶界面友好而倍受程序員們的青睞。但是,在當(dāng)前的Microsoft 基本類(lèi)庫(kù)4.2 版本中,大約有將近200 個(gè)類(lèi),數(shù)千個(gè)函數(shù),加之Microsoft 公司隱藏了一些技術(shù)細(xì)節(jié),使得人們深入學(xué)習(xí)MFC變得十分困難。 ...
作者: 李澤宇 金剛 熊聯(lián)歡 姜軍
Visual C++ 6.0 以其功能強(qiáng)大、用戶界面友好而倍受程序員們的青睞。但是,在當(dāng)前的Microsoft 基本類(lèi)庫(kù)4.2 版本中,大約有將近200 個(gè)類(lèi),數(shù)千個(gè)函數(shù),加之Microsoft 公司隱藏了一些技術(shù)細(xì)節(jié),使得人們深入學(xué)習(xí)MFC變得十分困難。
MFC的AppWizard可以生成三種類(lèi)型的應(yīng)用程序:基于對(duì)話框的應(yīng)用、單文檔應(yīng)用(SDI)和多文檔應(yīng)用(MDI)。前兩者的結(jié)構(gòu)較簡(jiǎn)單,本文不再贅敘。筆者擬從MFC中的文檔/視結(jié)構(gòu)入手,分析一些函數(shù)的流程,并解決編制MDI 應(yīng)用程序過(guò)程中的一些常見(jiàn)問(wèn)題。
(一)、了解文檔/視結(jié)構(gòu)
MFC應(yīng)用程序模型歷經(jīng)多年以有了相當(dāng)大的發(fā)展。有一個(gè)時(shí)期,它只是個(gè)使用應(yīng)用程序?qū)ο蠛椭鞔翱趯?duì)象的簡(jiǎn)單模型。在這個(gè)模型中,應(yīng)用程序的數(shù)據(jù)作為成員變量保持在框架窗口類(lèi)中,在框架窗口的客戶區(qū)中,該數(shù)據(jù)被提交顯示器。隨著MFC2。0的問(wèn)世,一種應(yīng)用程序結(jié)構(gòu)的新方式----MFC文檔/視結(jié)構(gòu)出現(xiàn)了。在這種結(jié)構(gòu)中,CFrameWnd繁重的任務(wù)被委派給幾個(gè)不同類(lèi),實(shí)現(xiàn)了數(shù)據(jù)存儲(chǔ)和顯示的分離。一般情況下,采用文檔/視結(jié)構(gòu)的應(yīng)用程序至少應(yīng)由以下對(duì)象組成:
。應(yīng)用程序是一個(gè)CwinApp派生對(duì)象,它充當(dāng)全部應(yīng)用程序的容器。應(yīng)用程序沿消息映射網(wǎng)絡(luò)分配消息給它的所有子程序。
。框架窗口是一CfrmeWnd派生對(duì)象。
。文檔是一個(gè)CDocument派生對(duì)象,它存儲(chǔ)應(yīng)用程序的數(shù)據(jù),并把這些信息提供給應(yīng)用程序的其余部分。
。視窗是Cview派生對(duì)象,它與其父框架窗口用戶區(qū)對(duì)齊。視窗接受用戶對(duì)應(yīng)用程序的輸入并顯示相關(guān)聯(lián)的文檔數(shù)據(jù)。
通常,應(yīng)用程序數(shù)據(jù)存在于簡(jiǎn)單模型中的框架窗口中。在文檔/視方式中,該數(shù)據(jù)移入稱為document的獨(dú)立數(shù)據(jù)對(duì)象。當(dāng)然,文檔不一定是文字,文檔是可以表現(xiàn)應(yīng)用程序使用的數(shù)據(jù)集的抽象術(shù)語(yǔ)。而用戶輸入處理及圖形輸出功能從框架窗口轉(zhuǎn)向視圖。單獨(dú)的視窗完全遮蔽框架窗口的客戶區(qū),這意味著即使程序員直接繪畫(huà)至框架窗口的客戶區(qū),視圖仍遮蔽繪畫(huà),在屏幕上不出現(xiàn)任何信息。所以輸出必須通過(guò)視圖。框架窗口僅僅是個(gè)視圖容器。
CDocument類(lèi)對(duì)文檔的建立及歸檔提供支持并提供應(yīng)用程序用于控制其數(shù)據(jù)的接口。MDI應(yīng)用程序可以處理多個(gè)類(lèi)型的文檔,每個(gè)類(lèi)型的文檔擁有一個(gè)相關(guān)聯(lián)的文檔模板對(duì)象。文檔對(duì)象駐留在場(chǎng)景后面,提供由視圖對(duì)象顯示的信息。文檔至少有一個(gè)相關(guān)聯(lián)的視圖。視圖只能與一個(gè)文檔相關(guān)聯(lián)。
在文檔/視方式中,對(duì)象的建立是由文檔模板來(lái)管理的,它是CDocTemplate派生對(duì)象,建立并維護(hù)框架窗口,文檔及視。
MFC調(diào)用命令處理程序以響應(yīng)發(fā)生在應(yīng)用程序中的事件。命令發(fā)送的優(yōu)先級(jí)是:
活動(dòng)的視圖->框架窗口->文檔->應(yīng)用程序->默認(rèn)窗口過(guò)程(DefWindowsProc)
總之,在文檔/視方式中,文檔和視是分離的,即:文檔用于保存數(shù)據(jù),而視是用來(lái)顯示這些數(shù)據(jù)。文檔模板維護(hù)它們之間的關(guān)西。這種文檔/視結(jié)構(gòu)在開(kāi)發(fā)大型軟件項(xiàng)目時(shí)特別有用。
(二)、了解與文檔/視結(jié)構(gòu)有關(guān)的各種類(lèi)之間的關(guān)系。
在文檔/視應(yīng)用程序中,CWinApp對(duì)象擁有并控制文檔模板,后者產(chǎn)生文檔、框架窗口及視窗。這種相互關(guān)系如圖(1)所示:
從用戶的角度來(lái)看,“視”實(shí)際上是一個(gè)普通的窗口。象其他基于Widnows應(yīng)用的窗口一樣,人們可以改變它的尺寸,對(duì)它進(jìn)行移動(dòng),也可以隨時(shí)關(guān)閉它。若從程序員的角度來(lái)看,視實(shí)際上是一個(gè)從MFC類(lèi)庫(kù)中的Cview類(lèi)所派生出的類(lèi)的對(duì)象。文檔對(duì)象是用來(lái)保存數(shù)據(jù)的,而視對(duì)象是用來(lái)顯示數(shù)據(jù)的,并且允許對(duì)數(shù)據(jù)進(jìn)行編輯。SDI或MDI的文檔類(lèi)是由Cdocument類(lèi)派生出來(lái)的,它可以有一個(gè)或多個(gè)視類(lèi),而這些視類(lèi)最終都是由Cview類(lèi)派生出來(lái)的。視對(duì)象只有一個(gè)與之相聯(lián)系的文檔對(duì)象,它所包含的CView::GetDocument函數(shù)允許應(yīng)用在視中得到與之相聯(lián)系的文檔,據(jù)此,應(yīng)用程序可以對(duì)文檔類(lèi)成員函數(shù)及公共數(shù)據(jù)成員進(jìn)行訪問(wèn)。如果視對(duì)象接受到了一條消息,表示用戶在編輯控制中輸入了新的數(shù)據(jù),此時(shí),視就必須通知文檔對(duì)象對(duì)其內(nèi)部數(shù)據(jù)進(jìn)行相應(yīng)的更新。
如果文檔數(shù)據(jù)發(fā)生了變化,則所有的視都必須被通知到,以便它們能夠?qū)λ@示的數(shù)據(jù)進(jìn)行相應(yīng)的更新。Cdocument::UpdateAllViews函數(shù)即可完成此功能。當(dāng)該函數(shù)被調(diào)用時(shí),派生視類(lèi)的CView::OnUpdate函數(shù)被觸發(fā)。通常OnUpdate函數(shù)要對(duì)文檔進(jìn)行訪問(wèn),讀取文檔數(shù)據(jù),然后再對(duì)視的數(shù)據(jù)成員或控制進(jìn)行更新,以便反映出文檔的變化。另外,還可以利用OnUpdate函數(shù)使視的部分客戶區(qū)無(wú)效,以便觸發(fā)Cview::OnDraw函數(shù),利用文檔數(shù)據(jù)來(lái)重新對(duì)窗口進(jìn)行繪制。
在MDI應(yīng)用程序中,可以處理多個(gè)文檔類(lèi)型,即多個(gè)文檔模板,每個(gè)模板又可以有多個(gè)文檔,每個(gè)文檔又可以多視顯示。為管理方便,上一級(jí)往往保留了下一級(jí)的指針列表。如圖(2)所示:
解釋如下:
(1)、每個(gè)應(yīng)用程序類(lèi)(CwinApp的派生類(lèi))都保留并維護(hù)了一份所有文檔模板的指針列表,這是一個(gè)鏈表結(jié)構(gòu)。應(yīng)用程序?yàn)樗С值拿總(gè)文檔類(lèi)型動(dòng)態(tài)分配一個(gè)CMultiDocTemplate 對(duì)象,
CmultiDocTemplate(UINT nIDResource,
CruntimeClass * pDocClass,
CruntimeClass * pFrameClass,
CruntimeClass * pViewClass );
并在應(yīng)用程序類(lèi)的CWinApp::InitInstance成員函數(shù)中將每個(gè)CMultiDocTemplate對(duì)象傳遞給CWinApp::AddDocTemplate。 該函數(shù)將一個(gè)文檔模板加入到應(yīng)用程序可用文檔模板的列表中。函數(shù)原形為:
void AddDocTemplate(CdocTemplate * pTemplate);
應(yīng)用程序可以用CWinApp::GetFirstDocTemplatePostion獲得應(yīng)用程序注冊(cè)的第一個(gè)文檔模板的位置,利用該值來(lái)調(diào)用CWinApp::GetNextDocTemplate函數(shù),獲得第一個(gè)CDocTemplate對(duì)象指針。函數(shù)原形如下:
POSITION GetFirstDocTemplate( ) const;
CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;
第二個(gè)函數(shù)返回由pos 標(biāo)識(shí)的文檔模板。POSITION是MFC定義的一個(gè)用于迭代或?qū)ο笾羔槞z索的值。通過(guò)這兩個(gè)函數(shù),應(yīng)用程序可以遍歷整個(gè)文檔模板列表。如果被檢索的文檔模板是模板列表中的最后一個(gè),則pos參數(shù)被置為NULL。
(2)、一個(gè)文檔模板可以有多個(gè)文檔,每個(gè)文檔模板都保留并維護(hù)了一個(gè)所有對(duì)應(yīng)文檔的指針列表。應(yīng)用程序可以用CDocTemplate::GetFirstDocPosition函數(shù)獲得與文檔模板相關(guān)的文檔集合中第一個(gè)文檔的位置,并用POSITION值作為CDocTemplate::GetNextDoc的參數(shù)來(lái)重復(fù)遍歷與模板相關(guān)的文檔列表。函數(shù)原形為:
viaual POSITION GetFirstDocPosition( ) const = 0;
visual Cdocument *GetNextDoc(POSITION & rPos) const = 0;
如果列表為空,則rPos被置為NULL.
(3)、在文檔中可以調(diào)用CDocument::GetDocTemplate獲得指向該文檔模板的指針。函數(shù)原形如下:
CDocTemplate * GetDocTemplate ( ) const;
如果該文檔不屬于文檔模板管理,則返回值為NULL。
(4)、一個(gè)文檔可以有多個(gè)視。每一個(gè)文檔都保留并維護(hù)一個(gè)所有相關(guān)視的列表。CDocument::AddView將一個(gè)視連接到文檔上,將該視加入到文檔相聯(lián)系的視的列表中,并將視的文檔指針指向該文檔。當(dāng)有File/New、File/Open、Windows/New或Window/Split的命令而將一個(gè)新創(chuàng)建的視的對(duì)象連接到文檔上時(shí), MFC會(huì)自動(dòng)調(diào)用該函數(shù),框架通過(guò)文檔/視的結(jié)構(gòu)將文檔和視聯(lián)系起來(lái)。當(dāng)然,程序員也可以根據(jù)自己的需要調(diào)用該函數(shù)。
Virtual POSITION GetFirstViewPosition( ) const;
Virtual CViw * GetNextView( POSITION &rPosition) cosnt;
應(yīng)用程序可以調(diào)用CDocument::GetFirstViewPosition返回與調(diào)用文檔相聯(lián)系的視的列表中的第一個(gè)視的位置,并調(diào)用CDocument::GetNextView返回指定位置的視,并將rPositon的值置為列表中下一個(gè)視的POSITION值。如果找到的視為列表中的最后一個(gè)視,則將rPosition置為NULL.
當(dāng)在文檔上新增一個(gè)視或刪除一個(gè)視時(shí),MFC會(huì)調(diào)用OnChangeViewList函數(shù)。如果被刪除的視是該文檔的最后一個(gè)視,則刪除該文檔。
(5)、一個(gè)視只能有一個(gè)文檔。在視中,調(diào)用CView::GetDocument可以獲得一個(gè)指向視的文檔的指針。函數(shù)原形如下:
CDocument *GetDocument ( ) const;
如果該視不與任何文檔相,則返回NULL.
(6)、MDI框架窗口通過(guò)調(diào)用CFrameWnd::GetActiveDocument 可以獲得與當(dāng)前活動(dòng)的視相連的CDocument 指針。函數(shù)原形如下:
virtual CDocument * GetActiveDocument( );
(7)、通過(guò)調(diào)用CFrameWnd::GetActiveView 可以獲得指向與CFrameWnd框架窗口連接的活動(dòng)視的指針,如果是被CMDIFrameWnd框架窗口調(diào)用,則返回NULL。MDI框架窗口可以首先調(diào)用MDIGetActive找到活動(dòng)的MDI子窗口,然后找到該子窗口的活動(dòng)視。函數(shù)原形如下:
virtual Cdocument * GetActiveDocument( );
(8)、MDI框架窗口通過(guò)調(diào)用CFrameWnd::GetActiveFrame, 可以獲得一個(gè)指向MDI框架窗口的活動(dòng)多文檔界面子窗口的指針。
(9)、CMDIChildWnd調(diào)用GetMDIFrame獲得MDI框架窗口(CMDIFrameWnd)。
(10)、CWinApp 調(diào)用AfxGetMainWnd得到指向應(yīng)用程序的活動(dòng)主窗口的指針。
下面一段代碼,就是利用CDocTemplate、CDocument和CView之間的存取關(guān)系,遍歷整個(gè)文檔模板、文檔以及視。
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
POSITION p = pMyApp->GetFirstDocTemplatePosition();
while(p!= NULL) {
CDocTemplate * pDocTemplate = pMyApp->GetNextDocTemplate(p);
POSITION p1 = pDocTemplate->GetFirstDocPosition();
while(p1 != NULL) {
CDocument * pDocument = pDocTemplate->GetNextDoc(p1);
POSITION p2 = pDocument->GetFirstViewPosition();
while(p2 != NULL) {
CView * pView = pDocument->GetNextView(p2);
}
}
}
(圖4)、遍歷整個(gè)文檔模板、文檔和視
在應(yīng)用程序的任何地方,程序員都可以調(diào)用AfxGetApp( )獲得應(yīng)用程序的對(duì)象指針。由于本文著重介紹文檔/視的關(guān)系,至于框架窗口之間的關(guān)系沒(méi)能列全,讀者可以查相應(yīng)的文檔。
(三)、了解CwinApp::OnFileNew、CwinApp::OnFileOpen和Window/New的程序流程。
(1)、CwinApp::OnFileNew和CwinApp::OnFileOpen函數(shù)的簡(jiǎn)單流程。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
在CWinApp::OnFile/new 或CwinApp::OnFileOpen函數(shù)中,核心操作是CDocTemplate::OpenDocument函數(shù)。其函數(shù)原型為:
virtual CDocument* CDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE ) = 0;
圖(4)中星號(hào)標(biāo)注之后即是該函數(shù)的流程,簡(jiǎn)要介紹如下:
(1)、CDocTemplate::CreateNewDocument函數(shù)創(chuàng)建一個(gè)新文檔,其類(lèi)型與文檔模板相關(guān),并通過(guò)函數(shù)CDocTemplate::AddDocument加入該文檔模板的文檔指針列表中。此時(shí),文檔類(lèi)的構(gòu)造函數(shù)被執(zhí)行,程序可以在此進(jìn)行文檔的初始化。
(2)、函數(shù)CDocTemplate::CreateNewFrame調(diào)用MDI子窗口類(lèi)(CMDIChildWnd)的構(gòu)造函數(shù),生成MDI子窗口對(duì)象。接著調(diào)用CMDIChildWnd::PreCreateWindow。然后,生成一個(gè)CCreateContext對(duì)象,(CcreateContext是MFC框架所使用的一種結(jié)構(gòu),它將構(gòu)成文檔和視的組件聯(lián)系起來(lái)。后文將詳細(xì)介紹之。)并將該對(duì)象值傳給CMDIChildWnd::OnCreateClient函數(shù)。MFC調(diào)用此函數(shù),用CCreateContext對(duì)象提供的信息創(chuàng)建一個(gè)或多個(gè)CView對(duì)象。此時(shí),各視的構(gòu)造函數(shù)被依次調(diào)用。
(3)、接著,判斷l(xiāng)pszPathName是否為空。分為兩種情況:
(a)、若為空,則表明要?jiǎng)?chuàng)建一個(gè)新文檔:調(diào)用SetDefaultTitle函數(shù)裝載文檔的缺省標(biāo)題,并顯示在文檔的標(biāo)題欄中;然后執(zhí)行CDocument::OnNewDocument。該函數(shù)調(diào)用DeleteContents以保證文檔為空,然后置新文檔為清潔。可以重載該函數(shù)。
(b)、否則,表明要打開(kāi)一個(gè)已存在的文檔:調(diào)用CDocument::OnOpenDocument打開(kāi)指定的文件;執(zhí)行DeleteContext,保證文檔為空;調(diào)用CObject::Serialize讀入該文件的內(nèi)容。(程序員可在此進(jìn)行文件的讀入操作。當(dāng)然,也可以在CDocument::OnOpenDocument中讀入文件)。然后置文檔為清潔;最后,調(diào)用CDocTemplate::SetPathName,并把文件名加入到最近文件列表中。
(4)、調(diào)用CDocTemplate::InitialUpdateFrame函數(shù),使框架窗口中的各個(gè)視收到OnInitialUpdate調(diào)用。框架窗口的主視(子窗ID等于AFX_IDW_PANE_FIRST的視)被激活。程序員可以在此對(duì)視對(duì)象進(jìn)行初始化。
(2)、Window/New命令的程序流程
當(dāng)主框架窗口上有子窗口時(shí),選擇Window/New命令可以生成該活動(dòng)子窗口的影象。它們有相同的文檔模板、相同的文檔。其流程如下:
執(zhí)行Window/New的過(guò)程與File/New的過(guò)程差不多。所不同的是,F(xiàn)ile/New須要?jiǎng)?chuàng)建一個(gè)新文檔,而Window/New則是獲得已存在的MDI子窗口的文檔。因此以前存在的視和New以后生成的視均為該文檔的視,都是該文檔的內(nèi)容的顯示。當(dāng)調(diào)用CDocument::UpdateAllViews函數(shù)時(shí),它們(視)的OnUpdate函數(shù)都將被激活。此時(shí),在該文檔的視指針列表中,將有多于一個(gè)的視(具體數(shù)目視Window/New執(zhí)行的次數(shù)而定)。讀者可以利用(圖3)中的代碼跟蹤程序結(jié)果。
(四)、幾種情況的討論
上面,筆者就MFC中文檔/視的關(guān)系進(jìn)行了分析,下面,筆者將結(jié)合具體情況進(jìn)行討論:
(1)、如何根據(jù)自己的要求來(lái)選擇文檔模板,及相應(yīng)的視和文檔。
在通常的MDI應(yīng)用程序中,只有一個(gè)文檔模板,程序員只能打開(kāi)一種類(lèi)型的文檔。因此,程序員只要調(diào)用File/New或者File/Open創(chuàng)建或者打開(kāi)文檔即可,至于文檔、視和框架窗口之間的關(guān)系,由文檔模板在幕后控制,不須要對(duì)文檔模板進(jìn)行操作。但是,如果應(yīng)用程序需要處理多種類(lèi)型的文檔,并且何時(shí)打開(kāi)何種文檔均需程序員手工控制,此時(shí),程序員必須對(duì)文檔模板進(jìn)行編程。
例如,筆者需要處理AVI和BMP兩種文件類(lèi)型。AVI和BMP的數(shù)據(jù)存放格式不同,不能用同一的數(shù)據(jù)結(jié)構(gòu)來(lái)描述,因此,把它們的數(shù)據(jù)都存入一個(gè)文檔是不合適的。同時(shí),由于AVI是圖象序列,BMP僅是一幅圖象,它們的顯示是肯定不一樣的,即它門(mén)的視不同。基于此,筆者決定分別建立兩套文檔模板,兩套框架窗口,兩套文檔和兩套視,分別用于AVI和BMP的數(shù)據(jù)存放和顯示。程序可以根據(jù)用戶選擇的文件名來(lái)分別處理AVI和BMP。具體步驟如下:
(Step 1)、在應(yīng)用程序類(lèi)(CWinApp)的派生類(lèi)中增加文檔模板成員變量,以便對(duì)文檔模板進(jìn)行操作。
class C3dlcsApp : public CWinApp
{ 。。。 。。。
public:
CMultiDocTemplate * m_pAVIDocTemplate;
CMultiDocTemplate * m_pBMPDocTemplate;
}
(Step 2)、在主框架中增加菜單響應(yīng):
void CMainFrame::OnFileOpen() {
CFileDialog my(true);
if(my.DoModal()==IDOK) {
CString FileName = my.GetPathName();
CString FileExt = my.GetFileExt();
if((FileExt == "AVI") (FileExt == "avi")) {
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate*pAVIDocTemplate=pMyApp->m_pAVIDocTemplate;
pAVIDocTemplate->OpenDocumentFile(FileName);
}
else if((FileExt == "BMP") (FileExt == "bmp")) {
CMyApp * p3dlcsApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate* pDATDocTemplate=pMyApp->m_pBMPDocTemplate;
pDATDocTemplate->OpenDocumentFile(FileName);
}
else {
AfxMessageBox("Yor select a file not supported!");
return;
}
}
}
筆者把用戶輸入文件名的后綴作為分支條件,如果是AVI文件,則先獲得關(guān)于AVI文件的文檔模板,然后調(diào)用CDocTemplate::OpenUpdateFrame (lpszFileName)函數(shù)打開(kāi)此文檔。正如前面所分析,此函數(shù)將依次生成新文檔,新框架,在CMDIChildWnd::OnCreateClient中創(chuàng)建視,最后向框架中所有的視發(fā)送初始化消息,使其顯示在屏幕上。如果是BMP文件,操作類(lèi)似。
當(dāng)然,程序員也可以在程序的任何位置實(shí)現(xiàn)此操作:通過(guò)全局函數(shù)AfxGetApp 獲得應(yīng)用程序?qū)ο笾羔槪瑥亩@得相應(yīng)的文檔模板指針。
由于由AppWizard生成的應(yīng)用程序會(huì)缺省調(diào)用CWinApp::OnFileNew,所以當(dāng)程序開(kāi)始執(zhí)行時(shí),會(huì)在主框架上顯示一個(gè)新的空窗口。如果想去掉這個(gè)空窗口,只須重載CWinApp::OnFileNew函數(shù),不許要任何代碼,即可。
(2)、切分窗口與文檔/視結(jié)構(gòu)
一個(gè)文檔可以有多個(gè)視,切分窗口即是表示多視的一種方法。 切分窗口是通過(guò)類(lèi)CSplitterWnd來(lái)表示的,對(duì)Window來(lái)說(shuō),CSplitterWnd對(duì)象是一個(gè)真正的窗口,它完全占據(jù)了框架窗口的客戶區(qū)域,而視窗口則占據(jù)了切分窗口的窗片區(qū)域。切分窗口并不參與命令傳遞機(jī)制,(窗片中)活動(dòng)的視窗從邏輯上來(lái)看直接被連到了它的框架窗口中。
切分窗口可以分為動(dòng)態(tài)和靜態(tài)兩種。前者較簡(jiǎn)單,本文僅討論后者。創(chuàng)建切分窗口的步驟如下:
(Step 1)、在自己的框架窗口中聲明成員變量,用以對(duì)切分窗口進(jìn)行操作。
class CMyFrame : public CMDIChildWnd
{ 。。。 。。。
CSplitterWnd m_Splitter;
CSplitterWnd m_Splitter2;
}
(Step 2)、重載CMDIChildWnd::OnCreateClient函數(shù),創(chuàng)建切分窗口。
BOOL CMyFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
BOOL btn = m_Splitter.CreateStatic(this,1,2);
btn = m_Splitter.CreateView(0,0, RUNTIME_CLASS(CAVIDispView), CSize(100,100), pContext);
m_Splitter2.CreateStatic(&m_Splitter,
2, 1,
WS_CHILD WS_VISIBLE WS_BORDER,
m_Splitter.IdFromRowCol(0, 1));
btn = m_Splitter2.CreateView(0, 0, RUNTIME_CLASS(CBMPView),
CSize(100,100), pContext);
btn = m_Splitter2.CreateView(1, 0, RUNTIME_CLASS(CAVIView),
CSize(100,100), pContext);
return btn;
//return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
CFrameWnd::OnCreateClient函數(shù)原形為:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CcreateContext * pContext);
缺省的CMDIChildWnd::OnCreateClient函數(shù)根據(jù)pContext參數(shù)提供的信息,調(diào)用CFrameWnd::CreateView函數(shù)創(chuàng)建一個(gè)視。可以重載該函數(shù),加載CCreateContext對(duì)象中傳遞的值,或改變框架窗口主客戶區(qū)中控制的創(chuàng)建方式。在上面的程序中,筆者 創(chuàng)建了3個(gè)切分窗口。比如打開(kāi)了一個(gè)名為“a.avi”的文檔,此時(shí)該文檔將有3個(gè)視,一個(gè)框架窗口。如果執(zhí)行了Window/New操作,則此時(shí)有一個(gè)文檔,6個(gè)視和2個(gè)框架窗口。若該文檔調(diào)用CDocument::UpdateAllViews函數(shù),則這6個(gè)視的CView::OnUpdate函數(shù)都會(huì)被激發(fā)。
(3)、關(guān)于CCreateContext的討論。
CCreateContext是MFC框架所使用的一種結(jié)構(gòu),它將構(gòu)成文檔/視的組件聯(lián)系起來(lái)。這個(gè)結(jié)構(gòu)包括指向文檔的指針,框架窗口,視以及文檔模板,它還包含一個(gè)指向CRuntimeClass的指針,以指明所創(chuàng)建的視的類(lèi)型。其數(shù)據(jù)成員如下:
m_pNewViewClass:指向創(chuàng)建上下文的視的CRuntimeClass的指針。
m_pCurrentDoc:指向文檔對(duì)象的指針,以和新視聯(lián)系起來(lái)。
m_pNewDocTemplate:指向與框架窗口的創(chuàng)建相聯(lián)系文檔模板的指針。
m_pLastView:指向已存在的視,它是新產(chǎn)生的視的模型。
m_pCurrentFrame:指向已存在的框架窗口,它是新產(chǎn)生的框架窗口的模型。
程序員可以通過(guò)改變CCreateContext對(duì)象的值,來(lái)創(chuàng)建更加靈活的視。由于過(guò)程較復(fù)雜,筆者不再贅許敘,讀者可參閱相關(guān)的Visual C++ Help文檔。
(五)、結(jié)束語(yǔ)
Visual C++ 6.0的文檔/視結(jié)構(gòu)代表了一種新的程序設(shè)計(jì)方式,其核心是文檔與視的分離,即數(shù)據(jù)存放與顯示(操作)的分離。在MFC類(lèi)庫(kù)中,各個(gè)對(duì)象之間的關(guān)系很復(fù)雜,但,只要深入了解后,會(huì)發(fā)現(xiàn)它們之間是相互聯(lián)系的,可以相互存取的。如果大家想設(shè)計(jì)出靈活、健壯的應(yīng)用程序,就必須深入了解MFC。跟蹤原代碼就是一個(gè)較好的方法。文檔/視的關(guān)系的確非常復(fù)雜,如果能知道每個(gè)函數(shù)是在哪調(diào)用的,執(zhí)行了何種操作,就能游人刃有余,寫(xiě)出優(yōu)美的應(yīng)用程序。