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