2009-9-3
========================================================
《深入解析MFC》筆記 7.MFC的文檔/視圖結構
========================================================
文檔/視圖相互依賴關系
· CWinApp包含一個 CDocManager 指針。
· CDocManager 維護一個文檔模板鏈表,須在 CWinApp::IniInstance()中 創建并加入這些模板。
· 文檔模板將這 3 個類綁定在一起: CView / CDocument / CFrameWnd。這些類都根據傳遞給文檔模板構造函數的CRuntimeClass信息創建
· MDI 專有的文檔模板維護一個打開文檔鏈表,而 SDI 專有的文檔模板只有一個指向打開文檔的指針。
· 文檔維護一個打開視圖的鏈表。使用這個鏈表與視圖通信、更新視圖。
· 文檔有一個指針指向它們的文檔模板。用這個指針設置標題,進行命令路由選擇,在被刪除時通知文檔模板
· 框架有一個指針指向當前的活躍視圖
· 被創建時,框架等到一個 CCreateContext 結構,包含了在文檔模板中可以找到的CRuntimeClass信息和一個指向文檔的指針。
· 視圖有一個指針指向它的文檔。當該視圖被刪除時,用該指針通知文檔。
· 視圖可以通過調用 CView::GetParentFrame() 獲得它的框架窗口。
· 如果文檔要訪問它的所有視圖的框架,可以遍歷它的視圖鏈表,并調用 CView::GetParentFrame().
· CDocTemplate —— 在 CWinApp 中創建,保存在 CWinApp::m_pDocManage中
· CDocManager —— 在CWinApp::InitInstance() 中創建
· CFrameWnd —— 在 CDocTemplate::CreateNewFrame() 中創建
· CDocument —— 在 CDocTemplate::CreateNewDocument() 中創建
· CView —— 通過 CFrameWnd::OnCreate() 創建
ID_FILE_OPEN → CWinApp::OnFileOpen() CWinApp::OnFileNew() ← ID_FILE_NEW
命令 ↓ ↓
獲取文件名 一個文檔模板? →否→ 從用戶獲取文檔類型
↓ ↓ ↓
使用文件拓展名,選擇文檔模板 是 ↓
↓ ↓ ↓
------------------------------選擇的模板-------------------------------
選擇模板
↓ → →→打開? →→Yes→→ CMyDoc::OnOpenDocument() →
Construct document object: CMyDoc ↑ ↓ ↓ ↓
↓ ↑ ↓ ↓ ↓
Construct frame window object : CMainFrame ↑ →→→→→→→CMyDoc::OnNewDocument() ↓
↓ ↑ ↓ ↓
CframeWnd::Create() ↑ 文檔準備好 ←←←←←←
CFrameWnd::OnCreateClient() ↑
↓ ↑
Create CMyView →→→→→→→→→→→→ 選擇模板之后的控制流程
CWinApp::OnFileOpen() CWinApp::OnFileNew()
↓ ↓
CDocManager::OnFileOpen() CDocManager::OnFileNew()
↓ ↓
CDocTemplate::OpenDocumentFile()
CDocTemplate::CreateNewDocument()
CDocTemplate::CreateNewFrame()
WM_CREATE
CFrameWnd::OnCreate()
CFrameWnd::OnCreateHelper()
CFrameWnd::OnCreateClient()
CFrameWnd::CreateView()
CMyDocument::OnOpenDocument()
[if file was specified to OpenDocumentFile()]
CMyDocument::OnNewDocument()
創建文檔/視圖體系中所有對象過程中調用的函數
-----------
體系結構
文檔與視圖
1)文檔; 2)視圖; 3)文檔/視圖框架; 4)文檔模版
文檔
由CDocument類體現,派生自CCmdTarget,具有CObject提供的所有支持,且可以接受命令消息,支持組件對象模型(COM)接口和 OLE 自動化。
視圖
文檔/視圖框架
文檔模板 《深入解析MFC》 P 198
將整個機制綁定在一起。這3個組件由 CDocTemplate 的類管理。
CDocTemplate是一個抽象基類,定義了處理文檔、框架和視圖的基本操作。
CSingleDocTemplate: 4個參數①一個資源ID,②運行時的CDocument派生類;③ 運行時的試圖框架; ④ 運行時的文檔視圖。
資源ID:①窗口標題;②文檔名稱;③當創建一個新文檔時使用的文檔類型描述符;④對標準的打開文件對話框中文件類型的描述符
⑤文檔擴展名過濾器; ⑥ 文件管理器使用的文件類型描述符; ⑦ 在Windows注冊表中注冊的 ProgID。
SMultiDocTemplage
可以支持一組文檔的鏈表,而CSingleDocTemplate只能支持一種文檔
CWinApp的角色
文檔模板由 CWinApp 對象管理。
---------------
文檔/視圖結構內幕 《深入解析MFC》P 199
------------------------------------
CWinApp 管理文檔模板,文檔模板管理 框架/視圖/文檔。
CWinApp / CDocTemplate 接口: CDocManager
· CPtrList m_templateList —— 維護一個文檔模板列表的指針。 通過GetFirstDocTemplatePosition()和GetNextDocTemplate()來遍歷模板列表。
CDocManager::OnFileNew()
負責創建一個新的文檔模板
· ①OnFileNew() 檢查 m_templateList 中是是否有多于一個文檔模板,若是,則創建一個 CNewTypeDlg 對話框,列出一個文檔鏈表以供用戶選擇。
· ② 調用CDocTemplate::OpenDocumentFile(),
tag:CNewTypeDlg的構造函數讀入一個指向文檔模板指針鏈表的指針,即 m_templateList。在DoModal() 之后,OnFileNew()從
CNewTypeDlg::m_pSelectdeTemplate 中得到選中的對話框模板。
CNewTypeDlg (DOCMGR.CPP)
CNewTypeDlg::OnInitDialog()
· ①, 調用GetDlgItem() 得到一個指向 CListBox 指針,并將之類型轉換成 CListBox 指針。
· ②,得到指針后,遍歷它的文檔模板鏈表,遍歷時,通過調用CDocTemplate::GetDocString()得到文檔類型的字符串版本,
OnInitDialog獲取每個文檔的這個字符串并加入鏈表,再調用 CListBox::SetItemDataPtr() 將這個文檔模板指針附加到這項上。
· ③,返回到CDialog::OnInitDialog()。
tag:在CNewType::OnOK()中,當用戶選擇從鏈表中選擇了一項并按下OK按鈕時, CNewType::OnOK() 從量表中得到被選中的項,并調用
CListBox::SetItemDataPtr() 獲得指向該文檔模板的一個指針。 OnOK() 將這個被選中的模板指針放在 pSelectedTemplate 中,這樣,
DoModal() 返回后就可以獲取這個指針
----------------------------------------
CDocTemplate: CDocument、CView 和 CFrameWnd 管理器
· m_nIDResource —— 存儲傳給 CDocTemplate 構造函數的第一個參數,是一個資源ID,
· m_pDocClass —— 指向該文檔模板的CDocument(或派生類)的CRuntimeClass結構,在構造函數中設定
· m_pFrameClass —— 指向該文檔模板的 CFrameWnd(或派生類)的CRuntimeClass 結構。在構造函數中設定
· m_pViewClass —— 指向該文檔模板的CView (或派生類)的CRuntimeClass結構,在構造函數中設定。
· m_strDocStrings —— 一個CString,包含用來定義與每個文檔模板聯系的7個字符串的字符串表資源。在LoadTemplate()中初始化。
創建新的 文檔/視圖/框架
CDocTemplate::CreateNewDocument() (DOCTEMPL.CPP) 《深入解析MFC》P205
· 首先,通過CRuntimeClass 指針的數據成員 m_pDocClass 調用 CRuntimeClass::CreateObject() 創建一個新的文檔
· 在pDocument 中保存了 新的 CDocument(或派生類 ) 指針之后,驗證 CreateObject 是否已經工作,若失敗,返回NULL。
若成功,調用 AddDocument() 將該文檔加到打開文檔鏈表中。 CreateNewDocument() 返回指向新文檔的指針。
CDocTemplate::CreateNewFrame()
· 首先,填充一個CCreateContext(存放創建各種文檔/視圖結構元素所需的關鍵信息)。
1>. m_pNewViewClass —— 一個 CRuntimeClass 指針,用來創建視圖
2>. m_pCurrentDoc —— 指向當前文檔對象的指針
3>. m_pCurrentFrame —— 指向當前框架對象的指針
4>. m_pNewDocTemplate —— 如果有多個文檔,指向最后一個;
5>. m_pLastView —— 如果有多個視圖,指向最后一個視圖。
· 通過m_pFrameClass 中的 CRuntimeClass 結構調用 CRuntimeClass::CreateObject() 創建一個框架,再調用 LoadFrame()裝入
這個框架的資源,根據這些值創建該框架。 最后,CreateNewFrame()返回一個指向新框架的指針。
CSingleDocTemplate 《深入解析MFC》P 207
: CDocument* m_pOnlyDoc;
AddDocument() 將 m_pOnlyDoc 設置成參數;
CMultiDocTemplate
: AddDocument() 通過調用 m_docList.AddTail( pDocument ) 將新文檔加入自己的鏈表中。
CPtrList m_docList;
int m_nUntitledCount; //記錄沒有標題的窗口數
CMultiDocTemplate::OpenDocumentFile()
· 調用CreateNewDocument() 創建一個新的空文檔(一個CDocument派生對象),然后通過 CreateNewFrame() 創建一個新的框架,
· 若參數 lpszPathName 為NULL, 則OpenDocumentFile() 知道要創建的事一個空的新文檔,調用SetDefaultTitle()完成這個功能。
這個函數為新文檔確認名稱[使用Untitled與m_nUntitledCount中未命名計數的組合],然后通過新文檔指針調用CDocument::SetTitle()
設置新文檔名字,組后,OpenDocumentFile()調用 OnOpenDocumentFile(),并將未命名計數的數據成員加1.
· 若lpszPathName 不等于NULL,則 OpenDocumentFile() 嘗試打開指定的文檔,首先,OpenDocumentFile() 顯示一個等待光標,然后
調用CDocument::OnOpenDocumentFile(),將文檔名傳入作為參數。若調用失敗,則銷毀新創建的對話框,返回一個NULL。若調用成功,
則OpenDocumentFile() 調用 CDocument::SetPathName() 更新CDocument 的路徑。
·不管lpszPathName的狀態時說明,OpenDocumentFile() 最后調用 CDocTemplate::InitializeUpdateFrame(),再調用
CFrameWnd::InitialUpdateFrame(), 并傳遞一個指向新文檔的指針。
****///---------------------------------------------
CFrameWnd (WINFRM.CPP)
CFrameWnd 和 CView 的創建 《深入解析MFC》P208
視圖的創建:CFrameWnd::CreateView
在文檔模板在CreateNewFrame() 中創建框架時,Windows發出一個 WM_CREATE,進行如下調用
OnCreate -> OnCreateHelper() -> OnCreateClient() -> CreateView()
CreateView() 使用 CCreateContext::m_pNewViewClass 中存儲的 CRuntimeClass 創建一個視圖,創建完成后,調用 CWnd::Create()完成創建工作。
CFrameWnd::InitialUpdateFrame()
使框架中的所有視圖都被更新。
· 首先,InitialUpdateFrame() 獲得一個活躍視圖的指針,并存放在 pView中,
· 若 bMakeVisible 參數為TRUE, 則InitialUpdateFrame() 發出 WM_INITIALUPDATE 給他所有后代,這個消息被映射到 CView::OnInitialUpadte()。
發送消息后,通過CView::OnActiveView() 激活視圖。
****///---------------------------------------------
CDocument (AFXWIN.H)
· m_strTitle —— 文檔名,通過 SetTitle()設置,保存時用它來設定框架窗口的標題和文件的名字
· m_strPathName —— 當前文檔的路徑(包括文件名)。用 SetPathname() 設置,是對MRU菜單的補充,保存文件時用到。
· m_pDocTemplate —— 指向文檔的文檔模板的指針。 CDocTemplate::AddDocument()中設置,通過CDocTemplate::GetDocTemplate()訪問
· m_viewList —— 記錄所有在該文檔下打開的所有視圖的指針。 文檔改變時,通過這個指針通知視圖。
視圖通過AddView()加入到m_viewList中,RemoveView()刪除。
GetFirstViewPosition() 和 GetNextView() 來遍歷
· m_bModified —— “修改”標志位,被修改就設為 1。為1是框架在用戶關閉文檔時提示。 IsModified() SetModifiedFlag()
· m_bAutoDelete —— 被框架設置為 TRUE時,所有視圖關閉時該文檔對象要被刪除??稍O為 FALSE,文檔在沒有任何打開視圖時仍會保留
· DisconnectViews() —— 該成員函數遍歷 m_viewlist 中保存的視圖鏈表,并將 CView::m_pDocument 指針設置為 NULL,從而將所有視圖從文檔上斷開。
· DoSave() —— CDocument成員 OnFileSaveAs() 和 OnFileSave() 在提示輸入文件名或驗證文件名后,最終都調用 DoSave(),DoSave調用了 OnSaveDocument()
· DoFileSave() —— 先檢查文檔要被存儲到的文件的一些性質,在調用 DoSave()。
· UpdateFrameCounts() —— 計數為該文檔的所有視圖打開的框架,并根據新的技術通知更新它們的窗口標題。
· SendInitialUpdate() —— 遍歷視圖鏈表并調用 CView::OnInitialUpdate()。
創建文檔: 《深入解析MFC》P213
-> 創建一個空的文檔,會調用 CDocument::OnNewDocument()。
首先調用DeleteContents() 清空文檔(默認什么都不做),然后設置修改標志位為 FALSE,并確保 m_strPathName為空
-> CDocument::OnOpenDocument() 從一個文件創建文檔
①. 首先調用 CDocument::GetFile() 打開一個文件,該函數用給出的文件名調用 CFile::Open()。若打開成功,OnOpenDocument()調用DeleteContents()
為文件的發序列化(de-serialization)作準備(從永久存儲設備讀入一個新的文檔)。調用SetModified()。 若序列化失敗,文檔被標記為“已修改”。
②. 根據pFile 創建一個 CArchive,并將這個 CArchive 傳遞給 CDocument::Serialize()。 若發序列化成功,關閉檔案并釋放文件
③. 最后,關閉修改標志位并返回TRUE
保存文檔:
CDocument::OnSaveDocument()
通過GetFile()打開一個文件,使用標志指定文件是打開用來保存的(CFile:: mode ReadWrite|CFile::modeCreate)。
創建CArchive,并將之床底給 Serialize() 成員函數。
若成功序列化,關閉CArchive,標志位設置成 FALSE,返回TRUE。
與視圖通信:
m_viewlist 是 CDocument 與正在“視”這個文檔的視圖進行交互的中介。
(需要交互的狀況)
1. 通知視圖文檔被銷毀。 CDocument析構函數調用 DisconnectView()
2. 從視圖出發,通過調用CView::GetParentFrame() 訪問框架,
3. 通知視圖文檔被修改、需要更新, UpdateAllViews()完成這個功能
CDocument::UpdateAllViews()
首先調用 GetFirstViewPosition() 獲得 m_viewlist的第一個視圖;
然后,遍歷整個視圖鏈表,除發送者的視圖外調用 CView::OnUpdate()。
****///---------------------------------------------
CView (AFXWIN.H)
CView 繪圖
CView::OnPaint()
· 首先創建一個CPaintDC,包含要重畫的區域,
· 將這個 paint DC 傳遞給 OnPrepareDC(),最后將 paint DC 傳給 OnDraw()。
tag:默認的 OnPrepareDC() 和 OnDraw() 實現不做任何事,
posted on 2010-03-15 23:24
Euan 閱讀(2701)
評論(0) 編輯 收藏 引用 所屬分類:
windows