当前位置:文档之家› 12文档与视图(windows 编程技术)

12文档与视图(windows 编程技术)

12文档与视图(windows 编程技术)
12文档与视图(windows 编程技术)

第12章文档与视图

MFC提供了将应用程序的数据与显示分开的文档-视图结构,它为应用程序提供了统一的框架,参见图12-1和图12-2。MFC的文档-视图结构是MVC模式的一种部分实现。

图12-1 SDI应用程序中的对象图12-2 文档-视图结构中各种对象及其创建与关系

12.1 MVC

MVC是一种软件架构模式,通过分解程序的不同功能,达到降低程序设计的复杂度、利于程序员专业分工、简化程序的维护和扩展的目的。MVC是1979年挪威计算机科学家Trygve Reenskaug在Xerox(施乐公司)PARC(Palo Alto Research Center,帕洛阿尔托研究中心)工作时,为(历史上第二个面向对象程序设计语言和第一个真正的集成开发环境)SmallTalk提出的。

12.1.1 概念

MVC(Model-View-Controller,模型—视图—控制器)是一种软件架构模式,它把软件系统分为如下三个基本部分(参见图12-3):

●模型(Model)——数据(库)。

●视图(View)——图形界面(表示)。

控制器(Controller)——程序功能(算法)。

图12-3 MVC模式的关系图

MVC模式的目的是实现一种动态的程序设计,简化对程序的后续修改和扩展,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观。

软件系统通过对自身基本部份分离的同时,也赋予了各个基本部分应有的功能,专业人员可以通过自身的专长进行分组合作:数据库专家利用“模型”进行数据管理和数据库设计、界面设计人员利用“视图”进行图形界面设计、而程序员则利用“控制器”编写程序应有的功能(实现算法等等)。

12.1.2 层次

模型(Model,数据模型)用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“模型”有对数据直接访问的权利,例如对数据库的访问。“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改变。(比较:观察者模式(软件设计模式))

视图(View)视图层能够实现数据有目的的显示(理论上,这不是必需的)。在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册。

控制器(Controller)控制器起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据模型上的改变。

12.1.3 实现

●S mallTalk

1980年MVC最早被应用于Xerox PARC的面向对象、动态类型、自反射的编程语言SmallTalk-80环境中,运行在首个具有GUI的原型计算机Alto(男高音)上。

●M acApp

1985年Apple将MVC用于其推出的Mac OS系统的面向对象的应用程序框架MacApp 中,这是MVC首次在商用产品中的实际应用。

●M FC

1993年2月微软在其随Visual C++ 1.0推出的MFC 2.0中,引入了文档-视图(Document / View)架构,它也是一种对于MVC的早期部分实现。MFC将程序分成视图(View)和文档(Document)两大类,其中的CDocument对应MVC中的数据模型(Model),CView相当于MVC中的视图+控制(View+Controller),再加上应用程序类CWinApp,合成三大项。

但是MFC基本上是一个失败的MVC作品。由于MFC对Document/View的定义过于模糊,未将Controller(消息映射)部份取出,因此Controller既可置入View也可置入Document,但不管置入哪一方面,都会与View或Document绑死而缺乏弹性。

●J ava EE——Struts

1999年12月Sun推出的Java EE(Java Enterprise Edition,Java企业版,原来叫J2EE)和其他的各种框架不一样,它为模型对象(Model Objects)定义了一个规范。典型例子是由Craig McClanahan于2000年5月所开发的开源Java EE轻型Web应用框架Apache Struts。

?视图(View) ——在JA V A EE应用程序中,视图(View)可能由JSP(Java Server

Page,爪哇服务器网页)承担。生成视图的代码则可能是Servlet的一部分,

特别是在客户端服务端交互的时候。

?控制器(Controller) ——JA V A EE应用中,控制器可能是一个Servlet。

?模型(Model) ——模型则是由一个实体Bean来实现。

●.NET——Windows Forms

2002年2月微软所推出的.NET框架中还包含有WinForms(视窗窗体),这个针对视图(View)和控制器(Controller)的模式已被很好地定义,而模型(Model)则留给开发者去设计。

?视图(View)——由Form或者Control类继承来的一个类处理视图的职责。

在WinForm这个例子中视图和控制器被编译在同一个类中,这个和https://www.doczj.com/doc/9610643354.html,

不同。

?控制器(Controller)——控制器的职责被分割成三部分。事件(Event)的产

生和传输是操作系统的一部分。在.Net框架中Form和Control类将不同的事

件转发给相应的事件处理器。而事件的处理则在分离的代码中实现。

?模型(Model)——就像https://www.doczj.com/doc/9610643354.html,一样,WinForm不严格需要一个模型。开

发者可以自行选择创建一个模型类,但是很多人选择放弃这一步,直接把事件

处理放在控制器里处理任何计算、数据保存等等。也就是说用模型来包含商业

逻辑和数据访问。

●.NET——https://www.doczj.com/doc/9610643354.html,

2002年2月微软随.NET推出了https://www.doczj.com/doc/9610643354.html,,针对视图(View)和控制器(Controller)的模式并没有被很好地定义,模型(Model)也留给开发者去设计。

?视图(View)——ASPX和ASCX文件被用来处理视图的职责。在这个设计中

视图实际上是从控制器继承而来。这个和Smalltalk的实施有所不同,在

Smalltalk中不同的类都有指针互相指向对方。

?控制器(Controller)——控制器的职责被分割成两部分。事件(Event)的产生

和传输是框架的一部分,更明确的说是Page和Control两个类。而事件的处理

则在分离的代码中实现。

?模型(Model)——https://www.doczj.com/doc/9610643354.html, 不严格需要一个模型。开发者可以自行选择创

建一个模型类,但是很多人选择放弃这一步,直接把事件处理放在控制器里处

理任何计算、数据保存等等。但用模型来包含商业逻辑和数据访问是可实现的。

2009年4月9日微软推出了https://www.doczj.com/doc/9610643354.html, MVC 1.0,它在https://www.doczj.com/doc/9610643354.html, 3.5运行库之上提供了一个新的MVC架构。此架构为Web应用程序文件夹(folder)结构定义了一个特别模式,并提供了一个控制器基类来处理“动作(actions)”请求。

2010年3月10日微软推出了https://www.doczj.com/doc/9610643354.html, MVC 2.0,可用于.NET 3.5 SP1和Visual Studio 2008及其SP1,并将集成进.NET 4.0和Visual Studio 2010中。

12.2 文档-视图体系

文档(document)对应于用户的数据(可以是文本、数值、图像、声音、视频等),它

可以从磁盘文件中读入,也可写入磁盘文件,用户还可以创建、修改和管理这些数据。文档对应的MFC类为CDocument。

视图(view)是一种窗口对象,对应于框架窗口的客户区,

它负责在屏幕和打印机上显示和输出数据,为用户提供观察、

选择、编辑文档数据的交互界面(参见图12-4)。视图对应的

MFC类为CView。

文档-视图结构有两种主要的方式:SDI(Single Document

Interface,单文档界面)和MDI(Multiple Document Interface,

多文档界面)。从MFC 7.0起新增加了一种MTDI(Multiple Top-level Document Interface,多顶级文档界面),参见图11-5。

SDI 传统MDI 选项卡式MDI MTDI

图11-5 不同的文档界面

SDI应用程序只有一个框架窗口(类)和一个视

图窗口(对应于框架窗口的客户区),且只有一个文档

类,每次只能打开一个文档。这里的文档和视图一般

是一一对应的。例如Windows中的记事本、写字板和

画图等软件,是典型的SDI应用程序。

MDI应用程序有一个主框架窗口(类),可有任

意多个子框架窗口和对应的视图客户区窗口,也可有

多个文档类,可以同时打开多个文档/窗口。这里,每

个视图对应于一个文档,而每个文档则可对应于多个

视图,参见图12-6。例如Word 2000和IE 8,分别是

传统和选卡式MDI应用程序。

MTDI类似于MDI,只是MDI中的每个文档视图窗口都是主框架窗口的子窗口(只能位于主框架窗口的客户区内)。而MTDI的文档视图窗口都是顶层窗口,位于主框架窗口之外。例如新版Word和老版IE,就是典型的MTDI应用程序。

应用程序的文档-视图结构种类,可以在创建MFC应用程序时,在“MFC应用程序向

图12-4 文档与视图

图12-6 一个文档可对应多个视图

导”对话框的“应用程序类型”页中设置(默认为MDI),在该对话框页中还可以选择是否具有“文档/视图结构支持”(默认是选中的),参见图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] ( // 文档模板构造函数

UINT nIDResource, // 文档类型的资源ID

CRuntimeClass* pDocClass, // 派生文档类对象的指针

CRuntimeClass* pFrameClass, // [派生]框架窗口类对象的指针

CRuntimeClass* pViewClass // 派生视图类对象的指针

);

CWinApp类创建文档模板的操作分两步进行,首先用文档模板类的构造函数创建一个SDI或MDI文档模板的实例,然后调用CWinApp类的成员函数AddDocument将该模板添加到应用程序的模板列表中。创建文档模板的操作,一般在派生应用程序类的InitInstance 成员函数中完成。

例如(SDI) :

BOOL CDrawApp::InitInstance() {

……

// 注册应用程序的文档模板。文档模板

// 将用作文档、框架窗口和视图之间的连接

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

RUNTIME_CLASS(CDrawDoc),

RUNTIME_CLASS(CMainFrame), // 自定义 MDI 子框架

RUNTIME_CLASS(CDrawView));

if (!pDocTemplate) return FALSE;

AddDocTemplate(pDocTemplate);

……

}

其中,RUNTIME_CLASS宏返回一个指向CRunTimeClass类的指针:

CRuntimeClass* RUNTIME_CLASS( class_name )

又例如(MDI) :

BOOL CImageApp::InitInstance() {

……

// 注册应用程序的文档模板。文档模板

// 将用作文档、框架窗口和视图之间的连接

CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate(

IDR_BMPTYPE,

RUNTIME_CLASS(CImageDoc),

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

一个类的实例另一个类的实例

图12-9 具有两个文档类型的MDI应用程序

12.2.2 文档类

所有用户的文档类都是从文档基类CDocument派生的,参见图12-10。

图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修改了文档数据后,应调用该函数通

知所有与文档相关联的其他视图窗口。若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类中。

12.2.3 框架窗口类

因为视图窗口是框架窗口的子窗口,位于框架窗口的客户区。视图窗口本身只是一个没

有边框、没有标题条、更没有菜单条和控制条的矩形区域。视图窗口不能单独存在,必须依

附于一个框架窗口,参见图12-11 a)。主框架窗口负责管理标题条、菜单条、控制条、加速

键和视图窗口或文档子窗口,参见图12-11 b)。

a) 框架窗口与视图b) MDI框架窗口与子窗口

图12-11 框架窗口

框架窗口(类)也是通过文档模板(类)与视图(类)Array及文档(类)联系在一起的。MFC中框架窗口类为从窗

口类CWnd派生的类CFrameWnd,参见图12-12。

SDI应用程序的主框架窗口一般直接以CFrameWnd

为基类,例如:

class CMainFrame : public CFrameWnd

{

protected: // 仅从序列化创建

CMainFrame();

DECLARE_DYNCREATE(CMainFrame)

... ...

}

但MDI应用程序的主框架窗口则一般以其派生类

图12-12 框架窗口类的层次结构CMDIFrameWnd为基类,例如:

class CMainFrame : public CMDIFrameWnd {

DECLARE_DYNAMIC(CMainFrame)

public:

CMainFrame();

... ...

}

而MDI中文档子窗口的框架窗口,则从另一个框架窗口类CMDIChildWnd派生。例如:class CChildFrame : public CMDIChildWnd {

DECLARE_DYNCREATE(CChildFrame)

public:

CChildFrame();

... ...

}

12.2.4 视图类

视图是文档与用户的接口,为用户提供观察、选择、编辑文档数据的交互界面。MFC 中的视图类为从CWnd派生的CView类及其派生类。

MFC应用程序的视图类的基类一般为CView,其他常用的视图基类有CScrollView、C[Rich]EditView、CFormView、CHtmlView、(MFC 8.0新加的)CWndFormsView和(MFC

9.0功能包新加的)CTabView,参见图12-13。

1.CView类

CView是用户视图类的默认基类,也是其他各种

MFC视图类的基类。例如:

class CTestView : public CView {

protected: // 仅从序列化创建

CTestView();

DECLARE_DYNCREATE(CTestView)

// 属性

public:

CTestDoc* GetDocument() const;

图12-13 视图类的层次结构……

// 重写

public:

virtual void OnDraw(CDC* pDC); // 重写以绘制该视图……

protected:

CDocument* m_pDocument;

// 生成的消息映射函数

protected:

DECLARE_MESSAGE_MAP()

};

1)CView类的常用成员函数有:

CDocument* GetDocument( ) const; // 获得对应文档类对象的指针

virtual void OnInitialUpdate( ); // 初始化虚消息响应函数,可覆盖

virtual void OnActivateView( BOOL bActivate, CView* pActivateView,

CView* pDeactiveView ); // 当视图窗口被激活或非激活时被调用virtual void OnDraw( CDC* pDC ) = 0; // 纯虚函数,必须覆盖

2)视图类从其基类CWnd继承的常用成员函数有:

CDC* GetDC( ); // 获得视图窗口的DC

int ReleaseDC( CDC* pDC ); // 释放视图窗口的DC

void GetClientRect( LPRECT lpRect ) const; // 获得客户区矩形

void GetWindowRect( LPRECT lpRect ) const; // 获得窗口矩形

// 获得需重绘的最小矩形

BOOL GetUpdateRect( LPRECT lpRect, BOOL bErase = FALSE );

// 改变窗口的位置和尺寸

void MoveWindow( int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE );

void MoveWindow( LPCRECT lpRect, BOOL bRepaint = TRUE );

CFrameWnd* GetParentFrame( ) const; // 获得父框架窗口的指针

CFrameWnd* GetTopLevelFrame( ) const; // 获得顶层框架窗口的指针

void Invalidate( BOOL bErase = TRUE ); // 使整个客户区矩形无效重画

void UpdateWindow( ); // 通过发送WM_PAINT消息来更新客户区

// 重绘窗口,相当于调用Invalidate和UpdateWindow

BOOL RedrawWindow(LPCRECT lpRectUpdate = NULL, CRgn* prgnUpdate = NULL, UINT flags = RDW_INV ALIDATE | RDW_UPDATENOW | RDW_ERASE);

CWnd* SetCapture( ); // 设置鼠标捕获

:: BOOL ReleaseCapture(VOID); // 释放设置鼠标捕获

CWnd* GetDlgItem( int nID ) const; // 获得nID对应的控件窗口指针

UINT GetDlgItemInt( int nID, BOOL* lpTrans = NULL, BOOL bSigned = TRUE ) const;

// 获得/设置控件中显示的整数

void SetDlgItemInt( int nID, UINT nValue, BOOL bSigned = TRUE );

int GetDlgItemText( int nID, LPTSTR lpStr, int nMaxCount ) const;

int GetDlgItemText( int nID, CString& rString ) const;

// 获得/设置控件中显示的串

void SetDlgItemText( int nID, LPCTSTR lpszString );

2.GetDocument

默认情况下,MFC会自动在派生的视图类中覆盖其成员函数GetDocument,使其能够获得派生的文档类指针。应用程序中的GetDocument函数一般有两个版本:

●在头文件中定义的正式版(内联函数):

#ifndef _DEBUG // TestView.cpp 中的非调试版本

inline CTestDoc* CTestView::GetDocument() const

{ return reinterpret_cast(m_pDocument); }

#endif

●在代码文件定义的测试版:

#ifdef _DEBUG

... ...

CTestDoc* CTestView::GetDocument() const // 调试版本是内联的

{

ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTestDoc)));

return (CTestDoc*)m_pDocument;

}

#endif //_DEBUG

在视图类中可以调用GetDocument函数来获得对应的文档对象,从而可显示、输出、编辑和修改文档数据。例如在OnDraw函数中MFC会自动加入的语句:

// CTestView 绘制

void CTestView::OnDraw(CDC* /*pDC*/) {

CTestDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if (!pDoc)

return;

// TODO: 在此处为本机数据添加绘制代码

}

12.2.5 创建过程

MFC应用程序的文档-视图结构中各个对象的创建,完全由系统控制,对用户是透明(黑箱)的。但是,为了能正确使用窗口和文档对象,我们需要了解它们创建的顺序和过程,参见图12-14。

图12-14 创建SDI应用程序的主要过程

框架窗口的创建过程如图12-15所示。

文档模板:OpenDocumentFile

图12-15 框架窗口的创建过程

应用程序

ID_FILE_OPEN命令ID_FILE_NEW命令

文档模板在此点被选择

(MDI或SDI)

图12-16 创建和打开文档的过程

MFC应用程序封装了创建和打开文档的过程,可用默认“文件”菜单中的“新建”和“打开”菜单项启动。MFC应用程序创建和打开文档的过程如图12-16所示。

3.初始化视图的过程

若希望在文档数据被显示之前,根据文档中的参数做一些初始化工作(如按图像的大小改变窗口的尺寸),可以在视图类中添加(重写型)消息响应函数OnInitialUpdate,并在此函数中作一些必要的文档操作。该函数是在窗口已经创建但还没有显示时会被系统调用,这时文档对象已经存在,而且文档文件已经被读取。需要说明的是,OnInitialUpdate函数不仅在程序启动(窗口创建)时会被调用,而且在每次打开或创建新文件时,也会被被系统调用。

视图初始化的过程如图12-17所示。

视图在此点被初始化

图12-17 视图初始化的过程

4.创建应用程序过程

需要注意的是,应用程序中的文档对象虽然在视图对象创建之前已经被创建,但是文档类中(Serialize函数)对文件的读取则是在视图对象创建之后,而且是在视图类的OnCreate 和OnSize函数被调用之后,但是是在视图类的OnInitialUpdate函数之前(参见图12-14和图12-18)。所以不能在视图类的构造函数中或OnSize的首次被调用时,使用GetDocument 来获得用户文档的指针,不然得到的将是一个空文档的指针,利用该指针对文档类数据的任何操作都会导致系统的致命错误(因为其变量还没有初始化)。

另外,在系统第一次调用(窗口消息WM_SIZE的响应函数,需要自己添加)OnSize 之前,文档还没有就绪,窗口也未初始化。所以,不能在首次调用OnSize函数时,进行窗

口操作或使用文档数据。解决办法是,在视图类中定义一个bool型类变量如m_bInit,在构造函数中将其初始化为false,在(重写型函数,需要自己添加)OnInitialUpdate中再将其设置为true,然后在OnSize函数中判断m_bInit是否为真。

图12-18 创建SDI和MDI应用程序的过程

(实线箭头表示实际过程,虚线箭头表示逻辑过程)

5.例子

下面是一个如何在OnSize中正确使用文档和窗口对象的例子代码框架:

class CTestView : public CView {

……

bool m_Init;

……

}

CTestView::CTestView() {

// TODO: 在此处添加构造代码

m_Init = false;

}

void CTestView::OnInitialUpdate() {

CView::OnInitialUpdate();

// TODO: 在此添加专用代码和/或调用基类

m_Init = true;

……

}

void CTestView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy);

// TODO: 在此处添加消息处理程序代码

if (m_Init) ……// 使用文档和窗口对象的代码

}

12.2.6 滚动和编辑视图类

除了标准的视图类外,MFC还提供了许多其他功能更强大的视图类,参见图12-13。由于篇幅的限制,这里只简单介绍可自动支持窗口滚动的视图类CScrollView和能自动支持文

本文件的读写和编辑的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())

相关主题
文本预览
相关文档 最新文档