MFC多文檔多視圖編程_第1頁
MFC多文檔多視圖編程_第2頁
MFC多文檔多視圖編程_第3頁
MFC多文檔多視圖編程_第4頁
MFC多文檔多視圖編程_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、深入了解MFC中的文擋/視結(jié)構(gòu)李澤宇金剛熊聯(lián)歡姜軍(華中理工大學(xué)圖象識別與人工智能研究所)VisualC+5.0以其功能強大、用戶界面友好而倍受程序員們的青睞。但是,在當前的Microsoft基本類庫4.2版本中,大約有將近200個類,數(shù)千個函數(shù),加之Microsoft公司隱藏了一些技術(shù)細節(jié),使得人們深入學(xué)習(xí)MFC變得十分困難。MFC的AppWizard可以生成三種類型的應(yīng)用程序:基于對話框的應(yīng)用、單文檔應(yīng)用(SDI)和多文檔應(yīng)用(MDI)。前兩者的結(jié)構(gòu)較簡單,本文不再贅敘。筆者擬從MFC中的文檔/視結(jié)構(gòu)入手,分析一些函數(shù)的流程,并解決編制MDI應(yīng)用程序過程中的一些常見問題。(一)、了解文檔/

2、視結(jié)構(gòu)MFC應(yīng)用程序模型歷經(jīng)多年以有了相當大的發(fā)展。有一個時期,它只是個使用應(yīng)用程序?qū)ο蠛椭鞔翱趯ο蟮暮唵文P?。在這個模型中,應(yīng)用程序的數(shù)據(jù)作為成員變量保持在框架窗口類中,在框架窗口的客戶區(qū)中,該數(shù)據(jù)被提交顯示器。隨著MFC2。0的問世,一種應(yīng)用程序結(jié)構(gòu)的新方式-MFC文檔/視結(jié)構(gòu)出現(xiàn)了。在這種結(jié)構(gòu)中,CFrameWnd繁重的任務(wù)被委派給幾個不同類,實現(xiàn)了數(shù)據(jù)存儲和顯示的分離。一般情況下,采用文檔/視結(jié)構(gòu)的應(yīng)用程序至少應(yīng)由以下對象組成:。應(yīng)用程序是一個CwinApp派生對象,它充當全部應(yīng)用程序的容器。應(yīng)用程序沿消息映射網(wǎng)絡(luò)分配消息給它的所有子程序??蚣艽翱谑且籆frmeWnd派生對象。文檔是一

3、個CDocument派生對象,它存儲應(yīng)用程序的數(shù)據(jù),并把這些信息提供給應(yīng)用程序的其余部分。視窗是Cview派生對象,它與其父框架窗口用戶區(qū)對齊。視窗接受用戶對應(yīng)用程序的輸入并顯示相關(guān)聯(lián)的文檔數(shù)據(jù)。通常,應(yīng)用程序數(shù)據(jù)存在于簡單模型中的框架窗口中。在文檔/視方式中,該數(shù)據(jù)移入稱為document的獨立數(shù)據(jù)對象。當然,文檔不一定是文字,文檔是可以表現(xiàn)應(yīng)用程序使用的數(shù)據(jù)集的抽象術(shù)語。而用戶輸入處理及圖形輸出功能從框架窗口轉(zhuǎn)向視圖。單獨的視窗完全遮蔽框架窗口的客戶區(qū),這意味著即使程序員直接繪畫至框架窗口的客戶區(qū),視圖仍遮蔽繪畫,在屏幕上不出現(xiàn)任何信息。所以輸出必須通過視圖。框架窗口僅僅是個視圖容器。CD

4、ocument類對文檔的建立及歸檔提供支持并提供應(yīng)用程序用于控制其數(shù)據(jù)的接口。MDI應(yīng)用程序可以處理多個類型的文檔,每個類型的文檔擁有一個相關(guān)聯(lián)的文檔模板對象。文檔對象駐留在場景后面,提供由視圖對象顯示的信息。文檔至少有一個相關(guān)聯(lián)的視圖。視圖只能與一個文檔相關(guān)聯(lián)。在文檔/視方式中,對象的建立是由文檔模板來管理的,它是CDocTemplate派生對象,建立并維護框架窗口,文檔及視。MFC調(diào)用命令處理程序以響應(yīng)發(fā)生在應(yīng)用程序中的事件。命令發(fā)送的優(yōu)先級是:活動的視圖-框架窗口-文檔-應(yīng)用程序-默認窗口過程(DefWindowsProc)總之,在文檔/視方式中,文檔和視是分離的,即:文檔用于保存數(shù)據(jù),

5、而視是用來顯示這些數(shù)據(jù)。文檔模板維護它們之間的關(guān)西。這種文檔/視結(jié)構(gòu)在開發(fā)大型軟件項目時特別有用。二)、了解與文檔/視結(jié)構(gòu)有關(guān)的各種類之間的關(guān)系。在文檔/視應(yīng)用程序中,CWinApp對象擁有并控制文檔模板,后者產(chǎn)生文檔、框架窗口及視窗。這種相互關(guān)系如圖(1)所示:圖(1)、應(yīng)用程序、文檔模板、文檔、框架窗口及視窗對象間的關(guān)系從用戶的角度來看,“視”實際上是一個普通的窗口。象其他基于Widnows應(yīng)用的窗口一樣,人們可以改變它的尺寸,對它進行移動,也可以隨時關(guān)閉它。若從程序員的角度來看,視實際上是一個從MFC類庫中的Cview類所派生出的類的對象。文檔對象是用來保存數(shù)據(jù)的,而視對象是用來顯示數(shù)據(jù)

6、的,并且允許對數(shù)據(jù)進行編輯。SDI或MDI的文檔類是由Cdocument類派生出來的,它可以有一個或多個視類,而這些視類最終都是由Cview類派生出來的。視對象只有一個與之相聯(lián)系的文檔對象,它所包含的CView:GetDocument函數(shù)允許應(yīng)用在視中得到與之相聯(lián)系的文檔,據(jù)此,應(yīng)用程序可以對文檔類成員函數(shù)及公共數(shù)據(jù)成員進行訪問。如果視對象接受到了一條消息,表示用戶在編輯控制中輸入了新的數(shù)據(jù),此時,視就必須通知文檔對象對其內(nèi)部數(shù)據(jù)進行相應(yīng)的更新。如果文檔數(shù)據(jù)發(fā)生了變化,則所有的視都必須被通知到,以便它們能夠?qū)λ@示的數(shù)據(jù)進行相應(yīng)的更新。Cdocument:UpdateAllViews函數(shù)即可完

7、成此功能。當該函數(shù)被調(diào)用時,派生視類的CView:OnUpdate函數(shù)被觸發(fā)。通常OnUpdate函數(shù)要對文檔進行訪問,讀取文檔數(shù)據(jù),然后再對視的數(shù)據(jù)成員或控制進行更新,以便反映出文檔的變化。另外,還可以利用OnUpdate函數(shù)使視的部分客戶區(qū)無效,以便觸發(fā)Cview:OnDraw函數(shù),利用文檔數(shù)據(jù)來重新對窗口進行繪制。在MDI應(yīng)用程序中,可以處理多個文檔類型,即多個文檔模板,每個模板又可以有多個文檔,每個文檔又可以多視顯示。為管理方便,上一級往往保留了下一級的指針列表。如圖(2)所示:圖(2)、MDI中的指針列表圖(3)。存取關(guān)系(箭頭表示獲得關(guān)系)解釋如下:、每個應(yīng)用程序類(CwinApp

8、的派生類)都保留并維護了一份所有文檔模板的指針列表,這是一個鏈表結(jié)構(gòu)。應(yīng)用程序為所要支持的每個文檔類型動態(tài)分配一個CMultiDocTemplate對象,CmultiDocTemplate(UINTnIDResource,CruntimeClass*pDocClass,CruntimeClass*pFrameClass,CruntimeClass*pViewClass);并在應(yīng)用程序類的CWinApp:InitInstance成員函數(shù)中將每個CMultiDocTemplate對象傳遞給CWinApp:AddDocTemplate。該函數(shù)將一個文檔模板加入到應(yīng)用程序可用文檔模板的列表中。函數(shù)原形

9、為:voidAddDocTemplate(CdocTemplate*pTemplate);應(yīng)用程序可以用CWinApp:GetFirstDocTemplatePostion獲得應(yīng)用程序注冊的第一個文檔模板的位置,利用該值來調(diào)用CWinApp:GetNextDocTemplate函數(shù),獲得第一個CDocTemplate對象指針。函數(shù)原形如下:POSITIONGetFirstDocTemplate()const;CDocTemplate*GetNextDocTemplate(POSITION&pos)const;第二個函數(shù)返回由pos標識的文檔模板oPOSITION是MFC定義的一個用于迭代或?qū)ο?/p>

10、指針檢索的值。通過這兩個函數(shù),應(yīng)用程序可以遍歷整個文檔模板列表。如果被檢索的文檔模板是模板列表中的最后一個,則pos參數(shù)被置為NULL。、一個文檔模板可以有多個文檔,每個文檔模板都保留并維護了一個所有對應(yīng)文檔的指針列表。應(yīng)用程序可以用CDocTemplate:GetFirstDocPosition函數(shù)獲得與文檔模板相關(guān)的文檔集合中第一個文檔的位置,并用POSITION值作為CDocTemplate:GetNextDoc的參數(shù)來重復(fù)遍歷與模板相關(guān)的文檔列表。函數(shù)原形為:viaualPOSITIONGetFirstDocPosition()const=0;visualCdocument*GetNe

11、xtDoc(POSITION&rPos)const=0;如果列表為空,則rPos被置為NULL.、在文檔中可以調(diào)用CDocument:GetDocTemplate獲得指向該文檔模板的指針。函數(shù)原形如下:CDocTemplate*GetDocTemplate()const;如果該文檔不屬于文檔模板管理,則返回值為NULLo、一個文檔可以有多個視。每一個文檔都保留并維護一個所有相關(guān)視的列表。CDocument:AddView將一個視連接到文檔上,將該視加入到文檔相聯(lián)系的視的列表中,并將視的文檔指針指向該文檔。當有File/New、File/Open、Windows/New或Window/Split

12、的命令而將一個新創(chuàng)建的視的對象連接到文檔上時,MFC會自動調(diào)用該函數(shù),框架通過文檔/視的結(jié)構(gòu)將文檔和視聯(lián)系起來。當然,程序員也可以根據(jù)自己的需要調(diào)用該函數(shù)。VirtualPOSITIONGetFirstViewPosition()const;VirtualCViw*GetNextView(POSITION&rPosition)cosnt;應(yīng)用程序可以調(diào)用CDocument:GetFirstViewPosition返回與調(diào)用文檔相聯(lián)系的視的列表中的第一個視的位置,并調(diào)用CDocument:GetNextView返回指定位置的視,并將rPositon的值置為列表中下一個視的POSITION值。如果

13、找到的視為列表中的最后一個視,則將Position置為NULL.當在文檔上新增一個視或刪除一個視時,MFC會調(diào)用OnChangeViewList函數(shù)。如果被刪除的視是該文檔的最后一個視,則刪除該文檔。、一個視只能有一個文檔。在視中,調(diào)用CView:GetDocument可以獲得一個指向視的文檔的指針。函數(shù)原形如下:CDocument*GetDocument()const;如果該視不與任何文檔相,則返回NULL.、MDI框架窗口通過調(diào)用CFrameWnd:GetActiveDocument可以獲得與當前活動的視相連的CDocument指針。函數(shù)原形如下:virtualCDocument*GetA

14、ctiveDocument();、通過調(diào)用CFrameWnd:GetActiveView可以獲得指向與CFrameWnd框架窗口連接的活動視的指針,如果是被CMDIFrameWnd框架窗口調(diào)用,則返回NULL。MDI框架窗口可以首先調(diào)用MDIGetActive找到活動的MDI子窗口,然后找到該子窗口的活動視。函數(shù)原形如下:virtualCdocument*GetActiveDocument();、MDI框架窗口通過調(diào)用CFrameWnd:GetActiveFrame,可以獲得一個指向MDI框架窗口的活動多文檔界面子窗口的指針。、CMDIChildWnd調(diào)用GetMDIFrame獲得MDI框架窗

15、口(CMDIFrameWnd)。、CWinApp調(diào)用AfxGetMainWnd得到指向應(yīng)用程序的活動主窗口的指針。下面一段代碼,就是利用CDocTemplateCDocument和CView之間的存取關(guān)系,遍歷整個文檔模板文檔以及視。CMyApp*pMyApp=(CMyApp*)AfxGetApp();POSITIONp=pMyApp-GetFirstDocTemplatePosition();while(p!=NULL)CDocTemplate*pDocTemplate=pMyApp-GetNextDocTemplate(p);POSITIONp1=pDocTemplate-GetFirst

16、DocPosition();while(p1!=NULL)CDocument*pDocument=pDocTemplate-GetNextDoc(p1);POSITIONp2=pDocument-GetFirstViewPosition();while(p2!=NULL)CView*pView=pDocument-GetNextView(p2);(圖4)遍歷整個文檔模板文檔和視在應(yīng)用程序的任何地方,程序員都可以調(diào)用AfxGetApp()獲得應(yīng)用程序的對象指針。由于本文著重介紹文檔/視的關(guān)系,至于框架窗口之間的關(guān)系沒能列全,讀者可以查相應(yīng)的文檔。三)、了解CwinApp:OnFileNew、Cw

17、inApp:OnFileOpen和Window/New的程序流程。(1)、CwinApp:OnFileNew和CwinApp:OnFileOpen函數(shù)的簡單流程。(圖5、FileNew/Open流程圖在CWinApp:OnFile/new或CwinApp:OnFileOpen函數(shù)中,核心操作是CDocTemplate:OpenDocument函數(shù)。其函數(shù)原型為:virtualCDocument*CDocTemplate:OpenDocumentFile(LPCTSTRlpszPathName,BOOLbMakeVisible=TRUE)=0;圖(4、中星號標注之后即是該函數(shù)的流程,簡要介紹如下

18、:(1、CDocTemplate:CreateNewDocument函數(shù)創(chuàng)建一個新文檔,其類型與文檔模板相關(guān),并通過函數(shù)CDocTemplate:AddDocument加入該文檔模板的文檔指針列表中。此時,文檔類的構(gòu)造函數(shù)被執(zhí)行,程序可以在此進行文檔的初始化。(2)、函數(shù)CDocTemplate:CreateNewFrame調(diào)用MDI子窗口類(CMDIChildWnd)的構(gòu)造函數(shù),生成MDI子窗口對象。接著調(diào)用CMDIChildWnd:PreCreateWindow。然后,生成一個CCreateContext對象,(CcreateContext是MFC框架所使用的一種結(jié)構(gòu),它將構(gòu)成文檔和視的組

19、件聯(lián)系起來。后文將詳細介紹之。)并將該對象值傳給CMDIChildWnd:OnCreateClient函數(shù)。MFC調(diào)用此函數(shù),用CCreateContext對象提供的信息創(chuàng)建一個或多個CView對象。此時,各視的構(gòu)造函數(shù)被依次調(diào)用。(3)、接著,判斷l(xiāng)pszPathName是否為空。分為兩種情況:(a)、若為空,則表明要創(chuàng)建一個新文檔:調(diào)用SetDefaultTitle函數(shù)裝載文檔的缺省標題,并顯示在文檔的標題欄中;然后執(zhí)行CDocument:OnNewDocument。該函數(shù)調(diào)用DeleteContents以保證文檔為空,然后置新文檔為清潔。可以重載該函數(shù)。(b)、否則,表明要打開一個已存在

20、的文檔:調(diào)用CDocument:OnOpenDocument打開指定的文件;執(zhí)行DeleteContext,保證文檔為空;調(diào)用CObject:Serialize讀入該文件的內(nèi)容。(程序員可在此進行文件的讀入操作。當然,也可以在CDocument:OnOpenDocument中讀入文件)。然后置文檔為清潔;最后,調(diào)用CDocTemplate:SetPathName,并把文件名加入到最近文件列表中。(4)、調(diào)用CDocTemplate:InitialUpdateFrame函數(shù),使框架窗口中的各個視收到OnInitialUpdate調(diào)用。框架窗口的主視(子窗ID等于AFX_IDW_PANE_FIRS

21、T的視)被激活。程序員可以在此對視對象進行初始化。(2)、Window/New命令的程序流程當主框架窗口上有子窗口時,選擇Window/New命令可以生成該活動子窗口的影象。它們有相同的文檔模板、相同的文檔。其流程如下:執(zhí)行Window/New的過程與File/New的過程差不多。所不同的是,F(xiàn)ile/New須要創(chuàng)建一個新文檔,而Window/New則是獲得已存在的MDI子窗口的文檔。因此以前存在的視和New以后生成的視均為該文檔的視,都是該文檔的內(nèi)容的顯示。當調(diào)用CDocument:UpdateAllViews函數(shù)時,它們(視)的OnUpdate函數(shù)都將被激活。此時,在該文檔的視指針列表中,

22、將有多于一個的視(具體數(shù)目視Window/New執(zhí)行的次數(shù)而定)。讀者可以利用(圖3)中的代碼跟蹤程序結(jié)果。(四)、幾種情況的討論上面,筆者就MFC中文檔/視的關(guān)系進行了分析,下面,筆者將結(jié)合具體情況進行討論:1)、如何根據(jù)自己的要求來選擇文檔模板,及相應(yīng)的視和文檔。在通常的MDI應(yīng)用程序中,只有一個文檔模板,程序員只能打開一種類型的文檔。因此,程序員只要調(diào)用File/New或者File/Open創(chuàng)建或者打開文檔即可,至于文檔、視和框架窗口之間的關(guān)系,由文檔模板在幕后控制,不須要對文檔模板進行操作。但是,如果應(yīng)用程序需要處理多種類型的文檔,并且何時打開何種文檔均需程序員手工控制,此時,程序員必

23、須對文檔模板進行編程。例如,筆者需要處理AVI和BMP兩種文件類型。AVI和BMP的數(shù)據(jù)存放格式不同,不能用同一的數(shù)據(jù)結(jié)構(gòu)來描述,因此,把它們的數(shù)據(jù)都存入一個文檔是不合適的。同時,由于AVI是圖象序列,BMP僅是一幅圖象,它們的顯示是肯定不一樣的,即它門的視不同?;诖?,筆者決定分別建立兩套文檔模板,兩套框架窗口,兩套文檔和兩套視,分別用于AVI和BMP的數(shù)據(jù)存放和顯示。程序可以根據(jù)用戶選擇的文件名來分別處理AVI和BMP。具體步驟如下:(Step1)、在應(yīng)用程序類(CWinApp)的派生類中增加文檔模板成員變量,以便對文檔模板進行操作。classC3dlcsApp:publicCWinApp

24、。public:CMultiDocTemplate*m_pAVIDocTemplate;CMultiDocTemplate*m_pBMPDocTemplate;(Step2)、在主框架中增加菜單響應(yīng):voidCMainFrame:OnFileOpen()CFileDialogmy(true);if(my.DoModal()=IDOK)CStringFileName=my.GetPathName();CStringFileExt=my.GetFileExt();if(FileExt=AVI)|(FileExt=avi)CMyApp*pMyApp=(CMyApp*)AfxGetApp();CMul

25、tiDocTemplate*pAVIDocTemplate=pMyApp-m_pAVIDocTemplate;pAVIDocTemplate-OpenDocumentFile(FileName);elseif(FileExt=BMP)|(FileExt=bmp)CMyApp*p3dlcsApp=(CMyApp*)AfxGetApp();CMultiDocTemplate*pDATDocTemplate=pMyApp-m_pBMPDocTemplate;pDATDocTemplate-OpenDocumentFile(FileName);elseAfxMessageBox(Yorselectaf

26、ilenotsupported!);return;筆者把用戶輸入文件名的后綴作為分支條件,如果是AVI文件,則先獲得關(guān)于AVI文件的文檔模板,然后調(diào)用CDocTemplate:OpenUpdateFrame(IpszFileName)函數(shù)打開此文檔。正如前面所分析,此函數(shù)將依次生成新文檔,新框架,在CMDIChildWnd:OnCreateClient中創(chuàng)建視,最后向框架中所有的視發(fā)送初始化消息,使其顯示在屏幕上。如果是BMP文件,操作類似。當然,程序員也可以在程序的任何位置實現(xiàn)此操作:通過全局函數(shù)AfxGetApp獲得應(yīng)用程序?qū)ο笾羔?,從而獲得相應(yīng)的文檔模板指針。由于由AppWizard生成

27、的應(yīng)用程序會缺省調(diào)用CWinApp:OnFileNew,所以當程序開始執(zhí)行時,會在主框架上顯示一個新的空窗口。如果想去掉這個空窗口,只須重載CWinApp:OnFileNew函數(shù),不許要任何代碼,即可。2)、切分窗口與文檔/視結(jié)構(gòu)一個文檔可以有多個視,切分窗口即是表示多視的一種方法。切分窗口是通過類CSplitterWnd來表示的,對Window來說,CSplitterWnd對象是一個真正的窗口,它完全占據(jù)了框架窗口的客戶區(qū)域,而視窗口則占據(jù)了切分窗口的窗片區(qū)域。切分窗口并不參與命令傳遞機制,(窗片中)活動的視窗從邏輯上來看直接被連到了它的框架窗口中。切分窗口可以分為動態(tài)和靜態(tài)兩種。前者較簡單

28、,本文僅討論后者。創(chuàng)建切分窗口的步驟如下:(Step1)、在自己的框架窗口中聲明成員變量,用以對切分窗口進行操作。classCMyFrame:publicCMDIChildWnd。CSplitterWndm_Splitter;CSplitterWndm_Splitter2;(Step2)、重載CMDIChildWnd:OnCreateClient函數(shù),創(chuàng)建切分窗口。BOOLCMyFrame:OnCreateClient(LPCREATESTRUCTlpcs,CCreateContext*pContext)BOOLbtn=m_Splitter.CreateStatic(this,1,2);btn

29、|=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);returnbtn;/returnCMDIChildWnd:OnCreateClient(lpcs,pContext);CFrameWnd:OnCreateClient函數(shù)原形為:virt

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論