簡單而完整:MFC骨幹程式(深入淺出MFC之讀書筆記)

xringm發表於2016-03-29

Document/VIew是MFC的靈魂。CDocument可以內嵌其他物件(用來處理基本資料型別如連結串列、陣列等等)。有關檔案讀寫的操作在CDocument的Serialize函式中進行,有關畫面顯示的操作在CView的OnDraw或OnPaint函式中進行。

改寫虛擬函式InitInstance:

new一個CMultiDocTemplate物件,此物件規劃Document、View以及Document Frame視窗三者之間的關係。new一個CMyMDIFrameWnd物件,作為主視窗物件。

呼叫LoadFrame,產生主視窗並加掛選單等諸元素,並指定視窗標題、檔案標題、副檔名等(關鍵在IDR_MAINFRAME)。LoadFrame內部將呼叫Create。後者呼叫CreateWindowEx,於是觸發WM_CREATE訊息。

由於我們曾在CMainFrame之中攔截WM_CREATE,所以WM_CREATE產生之際FrameWork會呼叫OnCreate。我們在此為主視窗掛上工具欄和狀態列。

回到InitInstance,執行ShowWindow顯示視窗。

WM_COMMAND/ID_FILE_OPEN訊息將由CWinApp::OnFileOpen函式處理。此函式在顯示過【File Open】對話方塊後呼叫Serialize函式。

Document Template的意義:
View本身雖然已經是一個視窗,但其外圍必須再封裝一個外框視窗作為舞臺。Document Frame視窗是View視窗的一個容器。也就說程式每開啟一份檔案,就應該產生三份物件:Document物件;View物件;CMDIChildWnd物件(作為外框視窗)。這三份物件由Document Template物件來管理。

如果程式支援不同的資料格式(例如一為TEXT,一為BITMAP),那麼就需要不同的Document Template:

BOOL CMyWinApp::InitInstance()

{

   pDocTemplate=new CMultiDocTemplate(

                IDR_TEXTTYPE,...);

   AddDocTemplate(pDocTemplate);

   pDocTemplate=new CMultiDocTemplate(

                IDR_BMPTYPE,...);

   AddDocTemplate(pDocTemplate);

}

CMultiDocTemplate::CMultiDocTemplate(UINT nIDResource,//資源ID,表示這一檔案型別所使用的資源,代表RC檔中的選單、圖示、字串三種資源,用來表示此Document顯現時應該採用的UI物件;

                               CRuntimeClass* pDocClass,

                               CRuntimeClass* pFrameClass,

                               CRuntimeClass* pViewClass);

任何一個類只要在宣告時使用DECLARE_DYNAMIC或DECLARE_DYNCREATE或DECLARE_SERIAL巨集,就會擁有一個靜態(static)CRuntimeClass內嵌物件。

Document Template接受了三種型別的CRuntimeClass指標,於是每當使用者開啟一份檔案時,Document Template就能夠根據“類別型錄網”,動態生成出三個物件(document、view、document frame window)。

字串資源以“\n”分隔為七個子字串,每一個都可以在程式進行過程中取得,只要呼叫CDocTemplate::GetDocString並在其第二個引數中指定索引值即可。但是最好以CDocTemplate所定義的七個常量來代替沒有字面意義的索引值。例如:CString strDefExt;pDocTemplate->GetDocString(strDefExt,CDocTemplate::filterExt);

我們借CDocument管理資料,借Collection Class處理實際的資料;借CView負責資料的顯示,借CDC和CGdiObject實際繪圖。View並不能完全獨立,必須依存在一個所謂的Document Frame視窗內。

檔案讀寫在CMyDoc的Serialize中完成。使用者對Document的任何編輯操作都必須通過Document Frame視窗,訊息隨後傳到CView。在MDI中主視窗採用CMDIFrameWnd類。構建MDI主視窗,有兩個步驟,首先new一個CMDIFrameWnd物件,然後呼叫其LoadFrame函式。

視窗產生之際會發出WM_CREATE訊息,因此CMainFrame::OnCreate會被執行起來,那裡將進行工具欄和狀態列的建立工作。LoadFrame->CFrameWnd::LoadFrame->CFrameWnd::Create->CWnd::CreateEx->::CreateWindowEx->觸發WM_CREATE->CMainFrame::OnCreate。

工具欄和狀態列的誕生:

工具欄和狀態列分別由CToolBar和CStatusBar掌管,兩個物件隸屬於主視窗,所以在CMainFrame中以兩個變數表示之,m_wndStatusBar和m_wndToolBar。

m_wndToolBar.Create(this)表示要產生一個隸屬於this(也就是當前物件,即主視窗)的工具欄。

m_wndToolBar.LoadToolBar(IDR_MAINFRAME)將RC檔中的工具欄資源載入。IDR_MAINFRAME在RC檔中代表兩種與工具欄有關的資源:點陣圖和按鈕。

當使用者從shell中拖放一個檔案到程式A,shell就配置一塊全域性記憶體,填入被拖拽的檔名稱(包含路徑),然後發出WM_DROPFILES傳到程式A的訊息佇列。程式A取得此訊息後,應該把記憶體(記憶體handle放在WM_DROPFILES訊息wParam中)的內容取出,再想辦法開檔讀檔。

只有具備WS_EX_ACCEPTFILES風格的視窗才能收到WM_DROPFILES訊息。欲讓視窗具備此一風格,必須使用CreateWindowEx,並指定第一個引數為WS_EX_ACCEPTFILES。

程式如下:

BOOL CScribeApp::InitInstance()

{

   m_pMainWnd->DragAcceptFiles();//預設引數為TRUE,表示主視窗以及每一個子視窗(檔案視窗)都可以接受來自shell的拖放文件。CFrameWnd內有一個OnDropFiles成員函式,負責對WM_DROPFILES訊息作出響應,它會通知application物件的OnOpenDocument函式,並夾帶被拖放的檔案的名稱。

   EnableShellOpen();

   RegisterShellFileTypes(TRUE);//此函式想shell註冊本程式的檔案型別。這個函式搜尋Document Template連結串列中的每一種檔案型別,然後把他加到系統所維護的registry中。

}

每一個派生自CCmdTarget的類都可以有自己的Message Map用於處理訊息。

各種MDI程式幾乎都有兩組選單。一組是當沒有任何子視窗(檔案視窗)存在時出現,另一組相反。

CEditView是一個已具備文字編輯能力的類,它所使用的視窗是Windows的標準控制元件之一Edit,其SerializeRaw成員函式可以把Edit控制元件中的raw text(而非“物件”所持有的資料)寫到檔案中。當在Appwizard選擇了它的時候,程式程式碼中CView會統統變成CEditView,而最重要的虛擬函式則變成:

void CScribbleDoc::Serialize()

{

    ((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);

}

相關文章