文档类、子框架类及视图类的关系及如何相互调用 收藏
了解文档和视图的相互作用关系是编写MFC程序的基本功。但是MFC的应用程序框架把文档和视图之间的关系封装了起来,初学的朋友往往不得要领,因此写程序往往被局限于在用向导生成的框架中。本文希望能够尽可能说明白文档视图框架之间是如何进行作用,希望能给一些朋友带来小小的帮助。
几个概念:
(虽然大家都知道了,雷神还是要重申一次)
文档对象:是用来保存数据的。
视图对象:是用来显示和编辑数据的。
应用程序框架:框架是用来管理不同文档显示界面的。例如你有一个数据网格显示界面,还有一个图形显示界面,它们的数据可能都来自你的文档,但是视图不同,怎么办用框架。为什么不用视图?为的是把界面管理独立的拿出来。
文档模板:MFC把文档/视图/框架视为一体,只要你创建文档/视图框架结构的程序,必定会为你创建这三个类。这个工作在在应用程序初始化时完成,如下:
BOOL CMyHtmlApp::InitInstance()
{
//。。。。。。
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMyHtmlDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CMyHtmlView));
AddDocTemplate(pDocTemplate);
//。。。。。。
}
单文档:就是一次只能打开一个文件,和你的文档类型支持的多少无关。你完全可以做一个单文档的支持所有图象格式的程序,只不过它一次只能打开一个文档罢了。
多文档:就是你可以打开多个文件,和文档类型也无关。你也可以作一个可以同时打开多个文档的程序,但它只支持一种文档类型。
何时需要文档/视图框架结构?
首先你可以不使用文档视图这种框架结构,即便是在MFC中。你可以在你需要的时候选择使用这种方式。你可以完成一个只有视图没有文档的程序,例如一个基于对话框的应用。
哪什么时候需要呢?
当你想将你的数据层和界面层分开的时候。
通常我们对数据的操作放在文档类中,例如存取,打开,关闭。在这里你可以尽情的对你的数据进行操作,如果你需要,在对数据进行了改变后,对视图做一下更新,那么程序会将你对数据所做的改变呈现给你的程序的用户。由此可见视图的作用就是提供一个用户和数据之间进行数据交换的界面,它的作用就是在需要的时候显示数据,并在需要的时候提供输入界面。当用户输入后实际的数据操作工作是由文档类来做的。那框架类有在做什么呢?
框架类是为了便于管理你的文档类和视图类而存在的。通常我们的操作都是通过视图窗口完成,消息由视图进行接收并且进行处理。所以消息映射定义一般在视图中。但如果一个应用同时拥有多个视图而当前活动视图没有对消息进行处理则消息会发往框架窗口。另外框架窗口可以方便的处理非窗口消息。
再来说一边典型的单文档程序的生成过程(不完整,只挑有用的)
1、 CwinApp对象被建立,这个对象是全局的且只能有一个,名字叫theApp。这时你可以完成一些工作,例如对注册表的操作,(如果你想写一个不修改注册表的软件,需要在这里做写工作)
2、 在InitInstance()函数中创建文档模板,文档模板以CruntimClass静态成员指针做构造参数。
3、 执行MFC框架默认的命令行参数。命令行参数有很多其中之一是,Cmd1它会创建一个新文件。(如果没有命令行参数则执行默认的ID_FILE_NEW)
4、 文档模板的实例根据三个类的动态创建信息创建出文档、视图、框架。
5、 对文档、视图、框架进行初始化。
我们对文档,视图,框架如何产生以及他们的用途有了一定的了解,如何有效的使用它们呢。
文档,视图,框架之间的相互作用。
由上面的典型的单文档程序的生成过程可以看出一个完整的应用一般由四个类组成:CWinApp应用类,CFrameWnd框架类,CDocument文档类,CView视图类。我将四个类常用的成员函数列出,大家一看便知。不过参数,返回值均未列出,大家可以从MSDN上了解更多。几个重要的虚函数也未做说明。大家自己看吧。
通过全局函数AfxGetApp可以得到CWinApp应用类的全局对象theApp.
CwinApp
数据成员:
m_pszAppName 应用程序名称
m_pszExeName 可执行文件的名称
m_pszProfileName INI文件的名
m_pszRegistryKey 注册表或INI文件的KEY
m_hInstance 实例的句柄
m_pMainWnd 为框架窗口指针
成员函数:
InitInstance() //初始化
ParseCommandLine() //完成命令行的解析处理
CFrameWnd
GetActiveDocument() //得到当前活动文档指针
GetActiveView() //得到当前活动视图指针
SetActiveView() //设置当前视图为活动视图
CDocument
OnNewDocument()
OnOpenDocument()
OnSaveDocument()
OnFileClose()
//以上是用来对文档的操作
GetFirstViewPosition() //文档对象链表中的第一个文档位置
GetNextView() //下一个
//以上是用来遍历所有和文档关联的视图
GetDocTemplate()得到文档模板指针
AddView() //增加一个视图
RemoveView() //删除一个视图
UpdateAllView() //更新所有视图
Cview
GetDocument()得到对应的文档指针
其他的就不列出了,大家还是看MSDN。你可以直接查看CWinApp应用类,CFrameWnd框架类,CDocument文档类,CView视图类的类成员。
最后说说几个常见到的问题。
1,为什么在对话框的应用程序中没有发现文档模板?
默认的对话框程序没有使用文档/视图框架结构。
2 ,如果我使用数据库作为数据源是否意味着可以不需要文档类?
看你自己,但是我建议使用。因为可以文档,视图这一个清晰方便的框架结构,以及方便完成三者之间的相互作用。
------------------------------------------------------------------------------------------------------------
框架、文档、视图类之间的调用关系
1、主框架(CFrameWnd)中访问视图(CView)
CView* GetActiveView() const;
通常定义的视图为CView的派生类,在调用自定义视图对象的方法时
应该这样写:((CMouseKeyView*)GetActiveView())->MyFunc();
2、主框架(CFrameWnd)中访问文档(CDocument)
GetActiveDocument,返回CDocument对象;
3、在视图(CView)中访问文档(CDocument)
inline CMouseKeyDoc* CMouseKeyView::GetDocument()
{return (CMouseKeyDoc*)m_pDocument;}
4、在视图(CView)中访问框架(CFrameWnd)
CFrameWnd* GetParentFrame() const;
5、在文档(CDocument)中访问框架(CFrameWnd)
CWnd* AfxGetMainWnd();
CWnd* AfxGetApp()->m_pMainWnd;
6、在文档(CDocument)中访问视图(CView)
UpdateAllViews
功能:通知所有的视图文档已被修改的信息
原型:
void UpdateAllViews(
CView* pSender, // 要更新的视图指针,如果希望更新所有视
图,将此参数设为NULL
LPARAM lHint=0L, // 包含更改消息的通知
CObject* pHint=NULL // 保管更改消息的对象
}
7、在其他类中访问文档类(CDocument)
CDocument* GetDocument()
{
CFrameWnd* frm=(CFrameWnd*)::AfxGetMainWnd();
ASSERT(frm);
CDocument* pDoc=frm->GetActiveDocument();
ASSERT(pDoc);
ASSERT(pDoc->IsKindOf(RUNTIME_CLASS(CMouseKeyDoc)));
return (CMouseKeyDoc*)pDoc;
}
[注:资料转载自网上]
————————————————————————————————————————————————————————————
MFC中文档和视图的关系 收藏
1、应用程序对象有一个文档模板管理器CDocManager* m_pDocManager(第一次调用AddDocTemplate时new出来)
2、文档模板管理器有一个文档模板对象列表CPtrList m_templateList(AddDocTemplate 函数负责添加该列表)
3、文档模板对象拥有文档、视图、框架的静态CRuntimeClass成员指针用于动态创建,还有一个m_nIDResource用来表示应采用的UI对象
4、每个文档模板对象拥有 m_pOnlyDoc 或 m_docList (文档指针或文档指针列表),OnFileNew 和 OnFileOpen都调用文档模板对象的OpenDocumentFile,OpenDocumentFile 调用文档模板的 CreateNewDocument,CreateNewDocument再调用文档模板的 AddDocument 填充该文档列表或文档指针
5、文档对象有一个文档模板指针 m_pDocTemplate (回指文档对象所属模板对象).同上,也是文档模板的 AddDocument 成员函数把 this 指针(文档模板自身).塞给刚刚创建的文档对象
6、文档对象有一个 m_viewList(视图列表),OnFileNew 和 OnFileOpen 都调用文档模板对象的OpenDocumentFile,该函数调用 CreateNewDocument 创建文档,然后调用 CreateNewFrame 创建框架对象.
CreateNewFrame 构造CCreateContext对象
CCreateContext两个重要字段:(1)刚创建的文档指针(2)视图的CRuntimeClass指针
CreateNewFrame 创建框架对象后由该对象调用 LoadFrame
LoadFrame 的最后一个参数即为 CCreateContext 指针
LoadFrame 调用 Create,Create 再调用 CreateEx 最后一个参数均为此CCreateContext指针
Create的调用由消息映射表引发CFrameWnd::OnCreate被调用
OnCreate的LPCREATESTRUCT的一个字段lpCreateParams 仍然是这个CCreateContext指针
则在CFrame::OnCreate中,由这个CCreateContext的CRuntimeClass(视图的)来调用CreateObject
产生视图对象后,由该对象调用Create(最后一个参数仍然是这个CCreateContext指针)
视图对象的Create由消息映射表引发视图对象的OnCreate被调用
视图的OnCreate的参数 LPCREATESTRUCT 的 lpCreateParams 还是这个CCreateContext指针)
于是利用 CCreateContext 的成员 m_pCurrentDoc (当前文档)
来调用 CDocument::AddView 把视图加入文档的视图列表
7、视图有一个文档指针m_pDocument (指向所属文档)
同上,也是CDocument::AddView函数初始化的,如下所示:
pView->m_pDocument = this;
8、框架有一个m_pViewActive(活动视图)
由框架的SetActiveView进行设置