友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第61部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
Tile ID_WINDOW_TILE_HORZ MDIFrameWnd::OnWindowCmd Yes
Arrange Icons ID_WINDOW_ARRANGE MDIFrameWnd::OnWindowCmd Yes
Help
About AppName ID_APP_ABOUT None
上表的最后一字段为No 者有五笔,表示虽然那些命令项有预设的处理例程,但你必须
在自己的Message Map 中设定映射项目,它们才会起作用。噢,AppWizard 此时又表现
出了它的善体人意,自动为我们做出了这些码:
BEGIN_MESSAGE_MAP(CScribbleApp; CWinApp)
。。。
ON_MAND(ID_FILE_NEW; CWinApp::OnFileNew)
ON_MAND(ID_FILE_OPEN; CWinApp::OnFileOpen)
ON_MAND(ID_FILE_PRINT_SETUP; CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CScribbleView; CView)
。。。
ON_MAND(ID_FILE_PRINT; CView::OnFilePrint)
ON_MAND(ID_FILE_PRINT_PREVIEW; CView::OnFilePrintPreview)
END_MESSAGE_MAP()
448
…………………………………………………………Page 511……………………………………………………………
第7章 簡單而完整:MFC 骨幹程式
对话框
Scribble 可以激活许多对话框,前一节提了许多。唯一要程序员自己动手(我的意思是
出现在我们的程序代码中)的只有About 对话框。
为了拦截WM_MAND 的ID_APP_ABOUT 项目,首先我们必须设定其Message
Map :
BEGIN_MESSAGE_MAP(CScribbleApp; CWinApp)
ON_MAND(ID_APP_ABOUT; OnAppAbout)
ON_MAND(ID_FILE_NEW; CWinApp::OnFileNew)
ON_MAND(ID_FILE_OPEN; CWinApp::OnFileOpen)
ON_MAND(ID_FILE_PRINT_SETUP; CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
当消息送来,就由OnAppAbout 处理:
void CScribbleApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg。DoModal();
}
其中CAboutDlg 是CDialog 的衍生类别:
class CAboutDlg : public CDialog
{
enum { IDD = IDD_ABOUTBOX }; // IDD_ABOUTBOX 是RC 文件中的对话框模板资源
。。。
DECLARE_MESSAGE_MAP()
};
比之于SDK 程序中的对话框,这真是方便太多了。传统SDK 程序要在RC 文件中定义
对话框模板(dialog template,也就是其外形),在C 程序中设计对话框函数。现在只
需从CDialog 衍生出一个类别,然后产生该类别之对象,并指定RC 文件中的对话框面
板资源,再调用对话框对象的DoModal 成员函数即可。
第10 章一整章将讨论所谓的对话框数据交换(DDX )与对话框数据确认(DDV)。
449
…………………………………………………………Page 512……………………………………………………………
第篇 湷觥 FC 程式設計
改用CEditView
Scribble step0 除了把一个应用程序的空壳做好,不能再贡献些什么。如果我们在
AppWizard 步骤六中把CScribbleView 的基础类别从CView 改为CEditView,那可就
有大妙用了:
CEditView 是一个已具备文字编辑能力的类别,它所使用的窗口是Windows 的标准控
制组件之一Edit ,其SerializeRaw 成员函数可以把Edit 控制组件中的raw text (而非
「对象」所持有的资料)写到文件中。当我们在AppWizard 步骤六选择了它,程序代码中
所有的CView 统统变成CEditView,而最重要的两个虚拟函数则变成:
void CScribbleDoc::Serialize(CArchive& ar)
{
// CEditView contains an edit control which handles all serialization
((CEditView*)m_viewList。GetHead())…》SerializeRaw(ar);
}
void CScribbleView::OnDraw(CDC* pDC)
{
450
…………………………………………………………Page 513……………………………………………………………
第7章 簡單而完整:MFC 骨幹程式
CScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
就这样,我们不费吹灰之力获得了一个多窗口的文字编辑器:
并拥有读写档能力以及预视能力:
451
…………………………………………………………Page 514……………………………………………………………
第篇 湷觥 FC 程式設計
452
…………………………………………………………Page 515……………………………………………………………
4
深入MFC 程 序 设 计
深入湷觥FC
2nd Edition
453
…………………………………………………………Page 516……………………………………………………………
MFC
第篇 深入 程式設計
454
…………………………………………………………Page 517……………………………………………………………
第8章
Document…View 深入探讨
形而上者谓之道,形而下者谓之器。
对于 View 而言,很少有人能够先道而后器。
Document/
完全由AppWziard 代劳做出的Scribble step0,应用程序的整个架构(空壳)都已经建
构起来了,但是Document Document 和VView iew 还空着好几个最重要的函数(都是虚拟函数)等着
你设计其实体。这就像一部汽车外面的车体以及内部的油路电路都装配好了,但还等着
最重要的发动机(引擎)植入,才能够产生动力,开始「有所为」。
我已经在第7章概略介绍了Document/View 以及Document Template ,还有更多的秘密将
在本章揭露。
为什么需要Document…View (形而上)
MFC 之所以为Application Framework ,最重要的一个特征就是它能够将管理资料的程序
码和负责资料显示的程序代码分离开来,这种能力由MFC 的Document/View 提供。
Document/View 是MFC 的基石,了解它,对于有效运用MFC 有极关键的影响。甚至
OLE 复合文件(pound document )都是建筑在Document/View 的基础上呢!
几乎每一个软件都致力于资料的处理,毕竟信息以及资料的管理是计算机技术的主要用途。
把数据管理和显示方法分离开来,需要考虑下列几个议题:
455
…………………………………………………………Page 518……………………………………………………………
第篇 深入 MFC 程式設計
1。 程序的哪一部份拥有资料
2。 程序的哪一部份负责更新资料
3。 如何以多种方式显示资料
4。 如何让资料的更改有一致性
5。 如何储存资料(放到永久储存装置上)
6。 如何管理使用者接口。不同的数据类型可能需要不同的使用者接口,而一个程
序可能管理多种类型的资料。
其实Document / View 不是什么新主意,Xerox PARC 实验室是这种观念的滥觞。它是
Smalltalk 环境中的关键性部份,在那里它被称为Model…View…Controller (MVC )。其
中的Model 就是MFC 的Document ,而Controller 相当于MFC 的Document Template。
回想在没有Application Framework 帮助的时代(并不太久以前),你如何管理资料?只
要程序需要,你就必须想出各种表现资料的方法;你有责任把资料的各种表现方法和资
料本体调解出一种关系出来。100 位程序员,有100 种作法!如果你的程序只处理一种
数据类型,情况还不至于太糟。举个例,字处理软件可以使用巨大的字符串数组,把文
字统统含括进来,并以ASCII 型式显示之,顶多嘛,变换一下字形!
但如果你必须维护一种以上的数据类型,情况又当如何?想象得到,每一种数据类型可
能需要独特的处理方式,于是需要一套功能菜单;每一种数据类型显现在窗口中,应该
有独特的窗口标题以及缩小图标;当资料编辑完毕要存盘,应该有独特的扩展名;登录
在Registry 之中应该有独特的型号。再者,如果你以不同的窗口,不同的显现方式,秀
出一份资料,当资料在某一窗口中被编辑,你应该让每一窗口的资料显像与实际资料之
间常保一致。吧啦吧啦吧啦! K ,繁杂事务不胜枚举。
」
很快地,问题就浮显出来了。程序不仅要做数据管理,更要做「与数据类型相对应的UI
的管理。幸运的是,解决之道亦已浮现,那就是对象导向观念中的Model…View…Controller
(MVC ),也就是MFC 的Document / View 。
456
…………………………………………………………Page 519……………………………………………………………
第8章 Document…View 深入探討
Document
名称有点令人惧怕…Document 令我们想起文字处理软件或电子表格软件中所谓的「文件」。
但,这里的Document 其实就是资料。的确是,不必想得过份复杂。有人用data set 或data
source 来表示它的意义,都不错。
Document 在MFC 的CDocument 里头被具体化。CDocument 本身并无实务贡献,它
只是提供一个空壳。当你开发自己的程序,应该从CDocument 衍生出一个属于自己的
Document 类别,并且在类别中声明一些成员变量,用以承载(容纳)数据。然后再(至
少)改写专门负责文件读写动作的Serialize 函数。事实上,AppWizard 为我们把空壳都
准备好了,以下是Scribble step0 的部份内容:
class CScribbleDoc : public CDocument
{
DECLARE_DYNCREATE(CScribbleDoc)
。。。
virtual void Serialize(CArchive& ar);
DECLARE_MESSAGE_MAP()
};
void CScribbleDoc::Serialize(CArchive& ar)
{
if (ar。IsStoring())
{
// TODO: add storing code here
}
else
{
// TODO: add loading code here
}
}
由于CDocument 衍生自CObject,所以它就有了CObject 所支持的一切性质,包括执行
时期型别信息(RTTI )、动态生成(Dynamic Creation )、文件读写(Serialization)。又
由于它也衍生自CCmdTarget,所以它可以接收来自菜单或工具栏的WM_MAND 讯
息。
457
…………………………………………………………Page 520……………………………………………………………
第篇 深入 MFC 程式設計
View
View 负责描述(呈现)Document 中的资料。
View 在MFC 的CView 里头被具体化。CView 本身亦无实务贡献,它只是提供一个空
壳。当你开发自己的程序,应该从CView 衍生出一个属于自己的View 类别,并且在
类别中(至少)改写专门负责显示资料的OnDraw 函数(针对屏幕)或OnPrint 函数
(针对打印机)。事实上,AppWizard 为我们把空壳都准备好了,以下是Scribble step0 的
部份内容:
class CScribbleView : public CView
{
DECLARE_DYNCREATE(CScribbleView)
。。。
virtual void OnDraw(CDC* pDC);
DECLARE_MESSAGE_MAP()
};
void CScribbleView::OnDraw(CDC* pDC)
{
CScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
由于CView 衍生自CWnd,所以它可以接收一般Windows 消息(如WM_SIZE、
WM_PAINT 等等),又由于它也衍生自CCmdTarget,所以它可以接收来自菜单或工具
列的WM_MAND 消息。
在传统的C/SDK 程序中,当窗口函数收到WM_PAINT ,我们(程序员)就调用
BeginPaint ,获得一个Device Context (DC ),然后在这个DC 上作画。这个DC 代表萤
幕装置。在MFC 里头,一旦WM_PAINT 发生,Framework 会自动调用OnDraw 函数。
View 事实上是个没有边框的窗口。真正出现时,其外围还有一个有边框的窗口,我们称
为Frame 窗口。
458
…………………………………………………………Page 521……………………………………………………………
第8章 Document…View 深入探討
Document Frame (View Frame)
如果你的程序管理两种不同类型的资料,譬如说一个是TEXT ,一个是BITMAP ,作为
一位体贴的程序设计者,我想你很愿意为你的使用者考虑多一些:你可能愿意在使用者
操作TEXT 资料时,换一套TEXT 专属的使用者接口,在使用者操作BITMAP 资料时,
换一套BITMAP 专属的使用者接口。这份工作正是由Frame 窗口负责。
乍见这个观念,我想你会惊讶为什么UI 的管理不由View 直接负责,却要交给Frame
窗口?你知道,有时候机能与机能之间要有点黏又不太黏才好,把UI 管理机能隔离出来,
可以降低彼此之间的依存性,也可以使机能重复使用于各种场合如SDI、MDI 、OLE in…place
editing (即地编辑)之中。如此一来View 的弹性也会大一些。
Document Template
MFC 把Document/View/Frame 视为三位一体。可不是吗!每当使用者欲打开(或新增)
一份文件,程序应该做出Document 、View 、Frame 各一份。这个「三口组」成为一个
运作单元,由所谓的Document Template 掌管。MFC 有一个CDocTemplate 负责此事。
它又有两个衍生类别,分别是CMultiDocTemplate 和CSingleDocTemplate。
所以我在上一章说了,如果你的程序能够处理两种数据类型,你必须制造两个Document
Template 出来,并使用AddDocTemplate 函数将它们一一加入系统之中。这和程序是不
是MDI 并没有关系。如果你的程序支持多种数据类型,但却是个SDI,那只不过表示
你每次只能开启一份文件罢了。
但是,逐渐地,MDI 这个字眼与它原来的意义有了一些出入(要知道,这个字眼早在SDK
时代即有了)。因此,你可能会看到有些书籍这么说:『MDI 程序使用CMultiDocTemplate,
SDI 程序使用CSingleDocTemplate』,那并不是很精准。
CDocTemplate 是个抽象类别,定义了一些用来处理「Document/View/Frame 三口组」的
基础函数。
459
…………………………………………………………Page 522……………………………………………………………
第篇 深入 MFC 程式設計
CDocTemplate 管理CDocument / CView / CFrameWnd
好,我们说Document Template 管理「三口组」,谁又来管理Document Template 呢?
答案是CWinApp。下面就是InitInstance 中应有的相关作为:
BOOL CScribbleApp::InitInstance()
{
。。。
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_SCRIBTYPE;
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!