文档模板类(CDocTemplate)将原本独立的文档、视图和框架窗口对象组织在一起。文档模板的很多接口都是由CWinApp应用类调用以提供部分标准菜单命令的默认实现的。单文档模板只支持一种文档模板,多文档界面可以支持、定义多种文档模板支持不同的文档类型,或者仅一种文档模板在一个主框架窗口中创建多个文档实例的视图。文档模板通过CWinApp:: AddDocTemplate加入到应用中。CDocTemplate是从CCmdTarget类派生的,可以在其中处理部分菜单命令,但不能处理一般的窗口消息。
BOOL CEx_MdiApp::InitInstance()
{ …
CMultiDocTemplate*pDocTemplate;
pDocTemplate= new CMultiDocTemplate(
IDR_EX_MDITYPE,
RUNTIME_CLASS(CEx_MdiDoc),
RUNTIME_CLASS(CChildFrame),// MDI文档子窗口
RUNTIME_CLASS(CEx_MdiView));
AddDocTemplate(pDocTemplate);
//创建MDI主框架窗口
CMainFrame*pMainFrame = new CMainFrame;
if(!pMainFrame->LoadFrame(IDR_MAINFRAME))
returnFALSE;
m_pMainWnd= pMainFrame;
…
}
1. 一个视图对象只有一个与之相关联的文档对象。视图对象通过调用成员函数函数GetDocument()返回与视图相关联的文档对象的指针,利用这个指针可以访问文档类及其派生类的公有成员,函数原型:
a) CDocument* GetDocument( ) const;
2. 派生类中的函数代码:
CMysdiDoc* CMysdiView::GetDocument() {
ASSERT(m_pDocument->
IsKindOf(RUNTIME_CLASS(CMysdiDoc)));
return (CMysdiDoc*)m_pDocument;
// m_pDocument是CArchive类的数据成员,
// 指向当前文档对象
}
1. CDocument::UpdateAllViews函数,函数的原型如下。
2. void UpdateAllViews( CView*pSender, LPARAM lHint = 0L, CObject* pHint = NULL );
3. CView::OnUpdate函数:应用程序调用了CDocument::UpdateAllViews函数时,应用程序框架就会相应地调用该函数。
4. virtual void OnUpdate( CView*pSender, LPARAM lHint, CObject* pHint );
5. CView::OnInitialUpdate函数:应用程序被启动时,或从“文件”菜单中选择了“新建”或“打开”时,CView虚函数都会被自动调用。该函数除了调用无提示参数(lHint = 0, pHint = NULL)的OnUpdate函数之外,没做其他任何事情。可以重载此函数对文档所需信息进行初始化操作。如果应用程序中的文档大小是动态的,那么就可在文档每次改变时调用OnUpdate来更新视图的滚动范围。
6. CDocument::OnNewDocument函数:在SDI应用程序中,从“文件”菜单中选择“新建”命令时,框架将先构造一个文档对象,然后调用该虚函数。MFCAppWizard为用户的派生文档类自动产生了重载的OnNewDocument函数,如下面的代码:
BOOL CMyDoc::OnNewDocument(){
if (!CDocument::OnNewDocument()) // 注意一定要保证对基类函数的调用,
return FALSE;
// Do initialization of new documenthere.
return TRUE;
}
1. 一个文档对象可以有多个与之相关联的视图对象。当文档数据发生改变时,与它关联的每一个视图都必须反映出这些修改(重绘)。
2. 更新与该文档有关的所有视图的方法是调用成员函数CDocument::UpdateAllViews()
a) 函数原型:void UpdateAllViews(CView* pSender, LPARAM lHint = 0L, CObject* pHint=NULL );
b) 参数:第一个参数pSender设为NULL,表示所有与当前文档相关的视图都要重绘;如果使用this指针,代表当前视图,例如:GetDocument()->UpdateAllViews(this)
CDocument::UpdateAllViews()
→CView::OnUpdate()
→CWnd::Invalidate()// 使整个窗口矩形无效
→OnPaint()
→OnDraw()
1. MFC基于文档/视图结构的应用程序分为单文档和多文档两种类型,一个多文档应用程序有一个主窗口,但在主窗口中可以同时打开多个子窗口,每一个子窗口对应一个不同的文档。
2. 利用MFC AppWizard[exe]向导可以很方便地建立一个多文档应用程序,只需在MFC AppWizard向导第1步选择Multiple documents程序类型。
3. SDI和MDI使用不同框架窗口:
a) SDI的框架窗口是唯一的主框架窗口,窗口类是CMainFrame,由CFrameWnd派生而来。
b) MDI的框架窗口分为主框架窗口和子框架窗口,区别于SDI,MDI的主框架窗口不包含视图,分别由每个子框架窗口包含一个视图。MDI的主框架窗口类不与某个打开的文档相关联,而只与子框架窗口相关联。
c) MDI主框架窗口类CMainFrame由CMDIFrameWnd派生而来,MDI子框架窗口类CChildFrame由CMDIChildWnd派生而来。
1. MFC为应用程序提供了多种不同的视图,除了我们平常使用最多的一般视图CView,我们还可以使用其它视图,如滚动视图CScrollView、文本编辑视图CEditView、对话框视图CFormView、列表视图CListView和树型视图CTreeView等,这些视图都是从类CView派生而来。在利用应用程序向导创建一个文档/视图结构的应用程序时,在向导的第6步我们可以为应用程序选择不同的视图。
1. CEditView类对象是一种视图,提供窗口编辑控制功能,可以执行简单文本操作。由于CEditView类自动封装上述功能的映射函数,因此只要在文档模板中使用CEditView类,那么应用程序的“编辑”菜单和“文件”菜单里的菜单项都可自动激活。
2. 但CEditView仍然摆脱不了所有编辑控件的限制,如:
a) CEditView不具有所见即所得编辑功能。
b) CEditView只能将文本作单一字体的显示,不支持特殊格式的字符。
c) CEditView可容纳的文本总数有限,在32位Windows中最多不超过1M字节。
4. 在使用类CScrollView时,一般情况下,我们使用默认的滚动值,且不需要程序员自己处理滚动消息。编程时可使用CScrollView类的一些常用成员函数:
a) SetScrollSizes():用于设置整个滚动视图的大小、每一页和每一行的大小;
b) GetTotalSize():用于获取滚动视图的大小;
c) GetScrollPosition():用于获取当前可见视图左上角的坐标。
void CMyDoc::OnRepaintAllViews()
{ POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{ CView* pView = GetNextView(pos);
pView->UpdateWindow();
}
}// 实现上述功能也可直接调用UpdateAllViews(NULL);
1. 函数参数ar是一个CArchive类的对象,文档数据的序列化操作通过CArchive类对象作为中介来完成。CArchive类对象由应用程序框架创建,并与用户正在使用的文件关联在一起。CArchive类包含一个类CFile指针的成员变量,当创建一个CArchive类对象时,该对象与一个类CFile或其派生类的对象联系在一起,代表一个已打开的文件。
2. C++主要通过文件句柄来实现磁盘输入和输出,一个文件句柄与一个磁盘文件相关联。而MFC中物理文件的读写操作是由CFile类及其派生类来完成的,它们对文件句柄进行了封装。CArchive类对象为读写CFile类对象中的可序列化数据提供了一种安全的缓冲机制,它们之间形成了如下关系:
a) Serialize()函数¬®CArchive类对象¬®CFile类对象¬®磁盘文件
1. 向导为项目Mysdi生成了文档类的头文件MysdiDoc.h,该头文件用于定义文档类CMysdiDoc。CMysdiDoc类是MFC的CDocument类的派生类,它主要负责应用程序数据的保存和装载,实现文档的序列化功能
2. 文档类的成员函数
a) AssertValid()
b) Dump()
c) OnNewDocument():当用户执行File菜单中New命令时,MFC应用程序框架会调用函数OnNewDocument()来完成新建文档的工作。
d) Serialize():负责文档数据的磁盘读写操作。
1. 成员函数Create用来创建“动态切分”,函数原型:
a) BOOL Create( CWnd* pParentWnd,int nMaxRows, int nMaxCols, SIZE sizeMin, CCreateContext* pContext, DWORDdwStyle = WS_CHILD | WS_VISIBLE |WS_HSCROLL | WS_VSCROLL | PLS_DYNAMIC_SPLIT, UINT nID = AFX_IDW_PANE_FIRST );
2. CreateStatic用来创建“静态切分”的文档窗口,函数原型:
a) BOOL CreateStatic( CWnd*pParentWnd, int nRows, int nCols, DWORD dwStyle = WS_CHILD | WS_VISIBLE, UINTnID = AFX_IDW_PANE_FIRST );
将SDI文档窗口静态分成3 x 2个窗格:
(1)用MFC AppWizard创建一个单文档项目Ex_SplitSDI。
(2)打开框架窗口类MainFrm.h头文件,为CMainFrame类添加一个保护型的切分窗口的数据成员,如下面的定义:
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
CSplitterWndm_wndSplitter;
(3)用ClassWizard创建一个新的视图类CDemoView(基类为CView)用于与静态切分的窗格相关联。
(4)在类视图中双击CMainFrame类,添加视图类CDemoView的包含文件:#include"DemoView.h“
(5)为CMainFrame类添加OnCreateClient消息函数
切分窗口的应用实例2-2
BOOLCMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
CRectrc;
GetClientRect(rc);
CSizepaneSize(rc.Width()/2-16,rc.Height()/3-16);
m_wndSplitter.CreateStatic(this,3,2);
m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CDemoView),paneSize,pContext);
m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CDemoView),paneSize,pContext);
m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CDemoView),paneSize,pContext);
m_wndSplitter.CreateView(1,1,RUNTIME_CLASS(CDemoView),paneSize,pContext);
m_wndSplitter.CreateView(2,0,RUNTIME_CLASS(CDemoView),paneSize,pContext);
m_wndSplitter.CreateView(2,1,RUNTIME_CLASS(CDemoView),paneSize,pContext);
returnTRUE;
}
1. MFC对于“一档多视”提供下列三个模式:
a) (1)在各自MDI文档窗口中包含同一个视图类的多个视图对象。有时,想要应用程序能为同一个文档打开另一个文档窗口,以便同时使用两个文档窗口来查看文档的不同部分内容。用MFC AppWizard创建的MDI应用程序支持这种模式,选择“窗口”菜单的“新建窗口”命令时,系统就会为第一个文档窗口创建一个副本。
b) (2)在同一个文档窗口中包含同一个视图类的多个视图对象。这种模式实际上是使用“切分窗口”机制使SDI应用程序具有多视的特征。
c) (3)在单独一个文档窗口中包含不同视图类的多个视图对象。