1.创建框架窗口的过程
框架窗口的创建过程如图12-15所示。
文档模板:OpenDocumentFile
构造文档对象 CMyDoc 构造框架窗口对象 CMainFrame 框架 否 打开? 创建文档框架 是 调用CFrameWnd::Create 创建框架窗口 处理WM_CREATE消息 CMainFrame::OnCreate调用 CFrameWnd::OnCreateClient 创建客户区 打开文件并创建存档 调用CMyDoc:: OnNewDocument 调用CMyDoc:: OnOpenDocument 调用CMyDoc::Serialize 读取文档文件 关闭存档和文件 文档就绪可用 文档 CMyView::OnCreate 处理WM_CREATE消息 创建视图窗口 构造视图对象 CMyView 图12-15 框架窗口的创建过程
应用程序 ID_FILE_OPEN命令
调用处理函数 CWinApp::OnFileOpen 从用户获取文件名 使用文件扩展名来 选择文档模板 ID_FILE_NEW命令 调用处理函数 CWinApp::OnFileNew 一个文档 模板? 否 从用户获取文档类型 是 ●
文档模板在此点被选择 (MDI或SDI)
图12-16 创建和打开文档的过程
16
2.创建和打开文档的过程
MFC应用程序封装了创建和打开文档的过程,可用默认“文件”菜单中的“新建”和“打开”菜单项启动。MFC应用程序创建和打开文档的过程如图12-16所示。
3.初始化视图的过程
若希望在文档数据被显示之前,根据文档中的参数做一些初始化工作(如按图像的大小改变窗口的尺寸),可以在视图类中添加(重写型)消息响应函数OnInitialUpdate,并在此函数中作一些必要的文档操作。该函数是在窗口已经创建但还没有显示时会被系统调用,这时文档对象已经存在,而且文档文件已经被读取。需要说明的是,OnInitialUpdate函数不仅在程序启动(窗口创建)时会被调用,而且在每次打开或创建新文件时,也会被被系统调用。
视图初始化的过程如图12-17所示。
视图 WM_INITIALUPDATE 消息发送到视图 CMyView::OnInitialUpdate 处理消息 ●
视图在此点被初始化
默认OnInitialUpdate 调用CView::OnUpdate 图12-17 视图初始化的过程
4.创建应用程序过程
需要注意的是,应用程序中的文档对象虽然在视图对象创建之前已经被创建,但是文档类中(Serialize函数)对文件的读取则是在视图对象创建之后,而且是在视图类的OnCreate和OnSize函数被调用之后,但是是在视图类的OnInitialUpdate函数之前(参见图12-14和图12-18)。所以不能在视图类的构造函数中或OnSize的首次被调用时,使用GetDocument来获得用户文档的指针,不然得到的将是一个空文档的指针,利用该指针对文档类数据的任何操作都会导致系统的致命错误(因为其变量还没有初始化)。
另外,在系统第一次调用(窗口消息WM_SIZE的响应函数,需要自己添加)OnSize之前,文档还没有就绪,窗口也未初始化。所以,不能在首次调用OnSize函数时,进行窗
17
口操作或使用文档数据。解决办法是,在视图类中定义一个bool型类变量如m_bInit,在构造函数中将其初始化为false,在(重写型函数,需要自己添加)OnInitialUpdate中再将其设置为true,然后在OnSize函数中判断m_bInit是否为真。
构造应用程序对象 调用应用程序类的InitInstance函数 SDI 新建 构造文档对象 打开 构造[主/子]框架窗口对象 MDI 构造主框架窗口对象 调用主框架窗口类的PreCreateWindow函数 创建主档框架窗口 调用[主/子]框架窗口类的PreCreateWindow函数 调用主框架窗口类的OnCreate函数 创建[主/子]框架窗口 创建主框架窗口的客户区 调用[主/子]框架窗口类的OnCreate函数 调用主框架窗口类的OnCreateClient函数 创建[主/子]框架窗口的客户区 调用文档类的On[New|Open]Document函数 调用[主/子]框架窗口类的OnCreateClient函数 打开 打开文件并创建存档对象 新建 构造视图对象 调用文档类的Serialize函数 读取文档文件[后关闭文件] 调用视图类的PreCreateWindow函数 文档就绪 调用视图类的 创建视图窗口 OnInitialUpdate函数 调用视图类的OnDraw函数 调用视图类的OnCreate函数 调用视图类的OnSize函数 调用视图类的各种消息响应函数 18
图12-18 创建SDI和MDI应用程序的过程 (实线箭头表示实际过程,虚线箭头表示逻辑过程)
5.例子
下面是一个如何在OnSize中正确使用文档和窗口对象的例子代码框架:
class CTestView : public CView { }
CTestView::CTestView() { }
void CTestView::OnInitialUpdate() { }
void CTestView::OnSize(UINT nType, int cx, int cy) { }
CView::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码
if (m_Init) …… // 使用文档和窗口对象的代码 CView::OnInitialUpdate();
// TODO: 在此添加专用代码和/或调用基类 m_Init = true; ……
// TODO: 在此处添加构造代码 m_Init = false; ……
bool m_Init; ……
12.2.6 滚动和编辑视图类
除了标准的视图类外,MFC还提供了许多其他功能更强大的视图类,参见图12-13。由于篇幅的限制,这里只简单介绍可自动支持窗口滚动的视图类CScrollView和能自动支持文
19
本文件的读写和编辑的CEditView类。
其他视图类,如CRichEditView(富文本编辑视图)、CHtmlView(网页浏览器视图)、CFormView(窗体视图)和MFC 8.0新加的CWndFormsView(Windows窗体视图)等,已经超过本书的范围,有兴趣的同学,可以自己找资料看。
1.CScrollView类
如果文档较大(如大尺寸图像),不能一次在视图窗口中完整显示,则必须使用滚动窗口。此时,用户视图类必须以CView的派生类CScrollView为基类,该类可以自动支持文档的滚动。
滚动视图类CScrollView是MFC提供的一种自动化程度非常高的滚动窗口类,可在创建项目时,在“MFC应用程序向导”最后一步的“生成的类”页,将C*View的基类从默认的CView改成CScrollView。
对已经存在的项目,可在*View.h和*View.cpp文件中,将所有的CView全部替换成CScrollView。替换方法和步骤:可以选“编辑\\查找和替换\\在文件中替换”菜单项或按“Ctrl+Shift+H”组合键,打开“查找和替换”对话框,在“查找内容”栏中输入“CView”、在“替换为”栏中输入“CScrollView”,保持“查找范围”栏的“整个解决方案”选项不变,按该对话框右下角的“全部替换”按钮。
在OnInitialUpdate函数中或其他需要的地方调用CScrollView类的成员函数SetScrollSizes来设置滚动的范围和参数,该函数的原型为:
void SetScrollSizes( int nMapMode, SIZE sizeTotal, const SIZE& sizePage = sizeDefault,
const SIZE& sizeLine = sizeDefault );
例如(其中m_iWidth、m_iHeight为视图类的整型类变量,m_bInit为视图类的布尔型类变量(初始化为false);img为文档类中定义的CImage对象,在文档类的序列化函数中装入图像文件,参见9.3.4小节2.中的例子):
void CImageView::OnInitialUpdate() {
CScrollView::OnInitialUpdate(); CImageDoc* pDoc = GetDocument(); // 对滚动视图类,必须设置滚动的尺寸 if(pDoc->img.IsNull())
20