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时,所有视图关闭时该文档对象要被删除。可设为 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 阅读(2552)
评论(0) 编辑 收藏 引用 所属分类:
windows