1. 程式人生 > >MFC介面程式設計基礎(16):文件、檢視、框架

MFC介面程式設計基礎(16):文件、檢視、框架

上一篇:MFC介面程式設計基礎(15):程式舉例+遮蔽或接收鍵盤事件 下一篇:MFC介面程式設計基礎(17):文件序列化

MFC應用程式模型歷經多年以有了相當大的發展。有一個時期,它只是個使用應用程式物件和主視窗物件的簡單模型。在這個模型中,應用程式的資料作為成員變數保持在框架視窗類中,在框架視窗的客戶區中,該資料被提交顯示器。隨著MFC2.0的問世,一種應用程式結構的新方式----MFC文件/視結構出現了。在文件/檢視結構中,CFrameWnd繁重的任務被委派給幾個不同類,實現了資料儲存和顯示的分離。

文件/檢視應用程式組成

一般情況下,採用文件/視結構的應用程式至少應由以下物件組成:

  • 應用程式是一個CWinApp派生物件,它充當全部應用程式的容器。應用程式沿訊息對映網路分配訊息給它的所有子程式。
  • 框架視窗是一CFrmeWnd派生物件。
  • 文件是一個CDocument派生物件,它儲存應用程式的資料,並把這些資訊提供給應用程式的其餘部分。
  • 視窗是CView派生物件,它與其父框架視窗使用者區對齊。視窗接受使用者對應用程式的輸入並顯示相關聯的文件資料。

通常,應用程式資料存在於簡單模型中的框架視窗中。在文件/視方式中,該資料移入稱為document的獨立資料物件。當然,文件不一定是文字,文件是可以表現應用程式使用的資料集的抽象術語

。而使用者輸入處理及圖形輸出功能從框架視窗轉向檢視。單獨的視窗完全遮蔽框架視窗的客戶區,這意味著即使程式設計師直接繪畫至框架視窗的客戶區,檢視仍遮蔽繪畫,在螢幕上不出現任何資訊。所以輸出必須通過檢視。框架視窗僅僅是個檢視容器。

CDocument類對文件的建立及歸檔提供支援並提供應用程式用於控制其資料的介面。MDI應用程式可以處理多個型別的文件,每個型別的文件擁有一個相關聯的文件模板物件。文件物件駐留在場景後面,提供由檢視物件顯示的資訊。文件至少有一個相關聯的檢視。檢視只能與一個文件相關聯。

在文件/視方式中,物件的建立是由文件模板來管理的,它是CDocTemplate派生物件,建立並維護框架視窗,文件及視。

總之,在文件/視模式中,文件和檢視是分離的,即:文件用於儲存資料,而檢視是用來顯示這些資料。文件模板維護它們之間的關西。這種文件/視結構在開發大型軟體專案時特別有用。

文件、檢視、框架之間的關聯.

MFC SDI/MDI 中的核心就在於文件、檢視、框架之間的關聯,形成了一個有機的可運作的整體。 MFC 提供了預設的關聯關係,但是在實際的專案開發中很多時候需要動態進行他們的之間的關聯。

與文件/視結構有關的各種類之間的關係

CWinApp物件擁有並控制文件模板,後者產生文件、框架視窗及視窗。這種相互關係如圖所示:
在這裡插入圖片描述

從使用者的角度來看,“視”實際上是一個普通的視窗。像其他基於Widnows應用的視窗一樣,人們可以改變它的尺寸,對它進行移動,也可以隨時關閉它。若從程式設計師的角度來看,視實際上是一個從MFC類庫中的CView類所派生出的類的物件。文件物件是用來儲存資料的,而視物件是用來顯示資料的,並且允許對資料進行編輯SDI或MDI的文件類是由CDocument類派生出來的,它可以有一個或多個視類,而這些視類最終都是由CView類派生出來的。視物件只有一個與之相聯絡的文件物件,它所包含的 CView::GetDocument函式允許應用在視中得到與之相聯絡的文件, 據此,應用程式可以對文件類成員函式及公共資料成員進行訪問。如果視物件接受到了一條訊息,表示使用者在編輯控制中輸入了新的資料,此時,視就必須通知文件物件對其內部資料進行相應的更新。

如果文件資料發生了變化,則所有的視都必須被通知到,以便它們能夠對所顯示的資料進行相應的更新。CDocument::UpdateAllViews函式即可完成此功能。當該函式被呼叫時,派生視類的CView::OnUpdate函式被觸發。通常OnUpdate函式要對文件進行訪問,讀取文件資料,然後再對視的資料成員或控制進行更新,以便反映出文件的變化。另外,還可以利用OnUpdate函式使視的部分客戶區無效,以便觸發CView::OnDraw函式,利用文件資料來重新對視窗進行繪製。Invalidate()函式也可以使當前視窗無效,導致視窗重繪。

MDI中的指標列表
在MDI應用程式中,可以處理多個文件型別,即多個文件模板,每個模板又可以有多個文件,每個文件又可以多視顯示。為管理方便,上一級往往保留了下一級的指標列表。如下圖所示:
在這裡插入圖片描述

MDI中的存取關係
在這裡插入圖片描述
解釋如下:

①: 每個應用程式類(CWinApp的派生類)都保留並維護了一份所有文件模板的指標列表,這是一個連結串列結構。應用程式為所要支援的每個文件型別動態分配一個CMultiDocTemplate 物件,

CMultiDocTemplate(UINT nIDResource, 
CRuntimeClass * pDocClass,
CRuntimeClass * pFrameClass,
CRuntimeClass * pViewClass );

並在應用程式類的CWinApp::InitInstance成員函式中將每個CMultiDocTemplate物件傳遞給CWinApp::AddDocTemplate。 該函式將一個文件模板加入到應用程式可用文件模板的列表中。函式原形為:

void AddDocTemplate(CdocTemplate * pTemplate);

應用程式可以用CWinApp::GetFirstDocTemplatePostion獲得應用程式註冊的第一個文件模板的位置,利用該值來呼叫CWinApp::GetNextDocTemplate函式,獲得第一個CDocTemplate物件指標。函式原形如下:

POSITION GetFirstDocTemplate( ) const;
CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;

第二個函式返回由pos 標識的文件模板。POSITION是MFC定義的一個用於迭代或物件指標檢索的值。通過這兩個函式,應用程式可以遍歷整個文件模板列表。如果被檢索的文件模板是模板列表中的最後一個,則pos引數被置為NULL。
使用 MFC 類嚮導生成 MFC SDI/MDI 程式,在 App 類的 InitInstance ()方法中有如下程式碼(假設 Project 名稱均為 Test ):
SDI (單文件介面)中

CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame),// main SDI frame window
RUNTIME_CLASS(CTestView)
);
AddDocTemplate(pDocTemplate);

MDI(多文件介面) 中

CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_TESTTYPE,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CTestView)
);
AddDocTemplate(pDocTemplate);

這裡通過 CDocTemplate (無論是 SDI 中的 CSingleDocTemplate 還是 MDI 中的 CMultiDocTemplate )的建構函式,將文當、檢視和框架( SDI 中與主框架, MDI 中與自框架)關聯在一起了,形成了一個整體。

②: 一個文件模板可以有多個文件,每個文件模板都保留並維護了一個所有對應文件的指標列表。應用程式可以用CDocTemplate::GetFirstDocPosition函式獲得與文件模板相關的文件集合中第一個文件的位置,並用POSITION值作為CDocTemplate::GetNextDoc的引數來重複遍歷與模板相關的文件列表。函式原形為:

viaual  POSITION	 GetFirstDocPosition( ) const = 0;
isual  CDocument *GetNextDoc(POSITION & rPos) const = 0;

如果列表為空,則rPos被置為NULL.
③: 在文件中可以呼叫CDocument::GetDocTemplate獲得指向該文件模板的指標。函式原形如下:

CDocTemplate * GetDocTemplate ( ) const;

如果該文件不屬於文件模板管理,則返回值為NULL。
④: 一個文件可以有多個檢視。每一個文件都保留並維護一個所有相關檢視的列表。CDocument::AddView將一個檢視連線到文件上,將該檢視加入到文件相聯絡的檢視的列表中,並將檢視的文件指標指向該文件。當有File/New、File/Open、Windows/New或Window/Split的命令而將一個新建立的檢視的物件連線到文件上時, MFC會自動呼叫該函式,框架通過文件/檢視的結構將文件和檢視聯絡起來。當然,程式設計師也可以根據自己的需要呼叫該函式。

virtual POSITION GetFirstViewPosition( ) const;
virtual CViw * GetNextView( POSITION &rPosition) cosnt;

應用程式可以呼叫CDocument::GetFirstViewPosition返回與呼叫文件相聯絡的檢視的列表中的第一個檢視的位置,並呼叫CDocument::GetNextView返回指定位置的檢視,並將rPositon的值置為列表中下一個檢視的POSITION值。如果找到的檢視為列表中的最後一個檢視,則將rPosition置為NULL.

當在文件上新增一個檢視或刪除一個檢視時,MFC會呼叫OnChangeViewList函式。如果被刪除的檢視是該文件的最後一個檢視,則刪除該文件。

⑤: 一個檢視只能有一個文件。在檢視中,呼叫CView::GetDocument可以獲得一個指向檢視的文件的指標。函式原形如下:

CDocument *GetDocument ( ) const;

如果該檢視沒有與任何文件相連線,則返回NULL.
⑥: MDI框架視窗通過呼叫CFrameWnd::GetActiveDocument 可以獲得與當前活動的檢視相連的CDocument 指標。函式原形如下:

virtual CDocument * GetActiveDocument( );

⑦: 通過呼叫CFrameWnd::GetActiveView 可以獲得指向與CFrameWnd框架視窗連線的活動檢視的指標,如果是被CMDIFrameWnd框架視窗呼叫,則返回NULL。MDI框架視窗可以首先呼叫MDIGetActive找到活動的MDI子視窗,然後找到該子視窗的活動視。函式原形如下:

virtual Cdocument * GetActiveDocument( );

⑧: MDI框架視窗通過呼叫CFrameWnd::GetActiveFrame, 可以獲得一個指向MDI框架視窗的活動多文件介面子視窗的指標。
⑨: CMDIChildWnd呼叫GetMDIFrame獲得MDI框架視窗(CMDIFrameWnd)。
⑩: CWinApp 呼叫AfxGetMainWnd得到指向應用程式的活動主視窗的指標。

遍歷整個文件模板、文件和檢視

下面一段程式碼,就是利用CDocTemplate、CDocument和CView之間的存取關係,遍歷整個文件模板、文件以及檢視。

//獲取應用程式的物件指標
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);
			}
		}
	}

MFC SDI/MDI 各個類之間的互訪

在實際專案開發中用的最多就是各個類之間的互訪問,這裡將網路上和書籍中提到的做了一個總結,也是在實際開發中比較常用的。
在這裡插入圖片描述

上一篇:MFC介面程式設計基礎(15):程式舉例+遮蔽或接收鍵盤事件 下一篇:MFC介面程式設計基礎(17):文件序列化