具有“文档/视图结构支持”(默认是选中的),参见图12-7。
图12-7 MFC应用程序向导中的“文档/视图结构支持”选项
12.2.1 文档模板类
文档、框架窗口与视图通过文档模板联系在一起,MFC的文档模板类为CDocTemplate。对SDI与MDI,它有两个对应的派生类CSingleDocTemplate与CMultiDocTemplate,在MFC功能包中又增加了多文档模板的扩展类CMultiDocTemplateEx,参见图12-8。
图12-8 文档模板类的层次结构
它们的构造函数的参数都一样:
C[Single|Multi]DocTemplate[Ex] ( // 文档模板构造函数 );
6
UINT nIDResource, // 文档类型的资源ID // 派生文档类对象的指针
CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass, // [派生]框架窗口类对象的指针 CRuntimeClass* pViewClass
// 派生视图类对象的指针
CWinApp类创建文档模板的操作分两步进行,首先用文档模板类的构造函数创建一个SDI或MDI文档模板的实例,然后调用CWinApp类的成员函数AddDocument将该模板添加到应用程序的模板列表中。创建文档模板的操作,一般在派生应用程序类的InitInstance成员函数中完成。
例如 (SDI) :
BOOL CDrawApp::InitInstance() { }
其中,RUNTIME_CLASS宏返回一个指向CRunTimeClass类的指针:
CRuntimeClass* RUNTIME_CLASS( class_name )
又例如 (MDI) :
BOOL CImageApp::InitInstance() {
……
// 注册应用程序的文档模板。文档模板 // 将用作文档、框架窗口和视图之间的连接 CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CDrawDoc),
RUNTIME_CLASS(CMainFrame), // 自定义 MDI 子框架 RUNTIME_CLASS(CDrawView));
if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); ……
……
// 注册应用程序的文档模板。文档模板 // 将用作文档、框架窗口和视图之间的连接 CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate(
IDR_BMPTYPE,
RUNTIME_CLASS(CImageDoc),
7
}
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 RUNTIME_CLASS(CImageView));
if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate);
pDocTemplate = new CMultiDocTemplate(
IDR_GIFTYPE,
RUNTIME_CLASS(CImageDoc),
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架 RUNTIME_CLASS(CImageView));
if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); ……
可见一个MDI应用程序可有多个MDI模板,每个MDI模板在运行时又可有多个实例,对应于同一模板中同一文档类型/视图类型的多个文档对象/视图窗口,参见图12-9。
CMyApp 应用程序对象 CMultiDocTemplate 文档模板A 文档模板B 打开文档 CMultiDocTemplate 文档1 CMyDocA 文档2 CMyDocA 文档3 CMyDocA 文档1 CMyDocB 一个类的实例 另一个类的实例
图12-9 具有两个文档类型的MDI应用程序
12.2.2 文档类
所有用户的文档类都是从文档基类CDocument派生的,参见图12-10。
8
图12-10 文档类的层次结构
CDocument类的常用成员函数有(其中粗体表示最常用的):
? GetFirstViewPosition——获得视图列表中与本文档关联的第一个视图的位置,该位
置可用于GetNextView函数,原型为:
virtual POSITION GetFirstViewPosition( ) const;
? GetNextView——返回rPosition所指的视图的指针,获得下一个本文档关联的视图
的位置到rPosition中,原型为:
virtual CView* GetNextView( POSITION& rPosition ) const; 使用GetFirstViewPosition与GetNextView可遍历文档的所有视图。 ? GetTitle——返回文档(窗口)的标题,一般为相关联的文件名,原型为:
const CString& GetTitle( ) const;
? SetTitle——设置文档(窗口)的标题,原型为:
virtual void SetTitle( LPCTSTR lpszTitle );
? GetPathName——返回与文档相关联的文件路径串,无关联文件时返回NULL,原
型为:const CString& GetPathName( ) const;
? SetPathName——设置存取文档的默认路径(与文档窗口的标题),若bAddToMRU
= TRUE,则将该路径添加到最近使用 (most recently used,MRU) 文件的列表,原型为:virtual void SetPathName( LPCTSTR lpszPathName,
BOOL bAddToMRU = TRUE );
? IsModified——判断文档在最后一次存储后是否被修改过。若被修改过,则在用户
关闭文档窗口或应用程序时,会提示保存文件,原型为:BOOL IsModified( ); ? SetModifiedFlag——设置文档在最后一次存储后是否被修改过,原型为:
void SetModifiedFlag( BOOL bModified = TRUE );
? UpdateAllViews——在用户通过视图pSender修改了文档数据后,应调用该函数通
9
知所有与文档相关联的其他视图窗口。若pSender = NULL,则通知与文档相关联的所有视图窗口,该函数会调用每个视图类的OnUpdate成员函数,一般是在调用SetModifiedFlag后调用该函数。原型为:
void UpdateAllViews( CView* pSender, LPARAM lHint = 0L,
CObject* pHint = NULL );
? Serialize——默认时,派生的文档类会覆盖根类CObject的序列化成员函数Serialize
以支持文档的读写,原型为:
virtual void Serialize( CArchive& ar );
例如:
void CTestDoc::Serialize(CArchive& ar) {
if (ar.IsStoring()) { // 写入(对应于选中“文件”菜单中的“保存”项)
// TODO: add storing code here ar << m_nWidth << m_nHeight;
} else { // 读取(对应于选中“文件”菜单中的“打开”项)
// TODO: add loading code here ar >> m_nWidth >> m_nHeight;
}
? LoadStdProfileSettings——在派生应用程序类C*App的InitInstance成员函数中,默
认会调用该成员函数来支持MRU文件列表功能,原型为:
void LoadStdProfileSettings( UINT nMaxMRU = _AFX_MRU_COUNT ); 其中,_AFX_MRU_COUNT = 4,nMaxMRU可取的最大值为_AFX_MRU_MAX_ COUNT = 16,若nMaxMRU =0,则不支持MRU。例如:
LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU) LoadStdProfileSettings(10); // 自己设置 }
也可调用应用程序类的成员函数
virtual void AddToRecentFileList( LPCTSTR lpszPathName );
来向MRU文件列表中添加指定的文件路径串。MFC将MRU功能封装在从CObject类派生的CRecentFileList类中。
10