MFC文档视图架构编程.docx
《MFC文档视图架构编程.docx》由会员分享,可在线阅读,更多相关《MFC文档视图架构编程.docx(44页珍藏版)》请在冰豆网上搜索。
MFC文档视图架构编程
MFC文档/视图架构编程
理解了MFC文档视图类的意义及它们纵横交错的关系也就理解了“文档/视图”结构的基本概念,在此基础上,我们再进一步研究“文档/视图”结构的MFC程序消息流动的方向,这样就完全彻底明白了基于"文档/视图"结构MFC程序的“生死因果”。
“文档/视图”结构是MFC中结构最为复杂,体系最为庞大,而又最富有特色的部分,其中涉及到应用、文档模板、文档、视图、SDI窗口、MDI框架窗口、MDI子窗口等多种不同的类,如果不了解这些类及其盘根错节的内部联系的话,就不可能编写出高水平的文档/视图程序。
学习"文档/视图"结构的意义还不只于其本身,通过该架构的学习,一步步领略MFC设计者的神功奥妙,也将进一步增强我们自身对庞大程序框架的把握能力。
一个优秀的程序员是可以写出一个个优秀函数的程序员,而一个优秀的系统设计师则需从全局把握软件的架构,分析和学习"文档/视图"结构将是我们成为软件系统设计师之旅的一个重要阶段。
第一讲基本概念
MFC引入了"文档/视图"结构的概念,理解这个结构是编写基于MFC编写复杂VisualC++程序的关键。
MFC"文档/视图"结构被认为是一种架构,关于什么是架构,这是个"仁者见仁,智者见智"的问题。
引言
MFC引入了"文档/视图"结构的概念,理解这个结构是编写基于MFC编写复杂VisualC++程序的关键。
"文档/视图"中主要涉及到四种类:
(1)文档模板:
classCDocTemplate;//templatefordocumentcreation
classCSingleDocTemplate;//SDIsupport
classCMultiDocTemplate;//MDIsupport
(2)文档:
classCDocument;//maindocumentabstraction
(3)视图:
//viewsonadocument
classCView;//aviewonadocument
classCScrollView;//ascrollingview
(4)框架窗口:
//framewindows
classCFrameWnd;//standardSDIframe
classCMDIFrameWnd;//standardMDIframe
classCMDIChildWnd;//standardMDIchild
classCMiniFrameWnd;//half-heightcaptionframewnd
理解了这4个类各自的意义及它们纵横交错的关系也就理解了"文档/视图"结构的基本概念,在此基础上,我们还需要进一步研究"文档/视图"结构的MFC程序消息流动的方向,这样就完全彻底明白了基于"文档/视图"结构MFC程序的"生死因果"。
出于以上考虑,本文这样组织了各次连载的内容:
第1次连载进行基本概念的介绍,第2~5次连载分别讲述文档模板、文档、视图和框架窗口四个类的功能和主要函数,连载6则综合阐述四个类之间的关系,接着以连载7讲解消息流动的方向,最后的连载8则以实例剖析连载1~7所讲述的所有内容。
本文所有的代码基于WIN32平台开发,调试环境为VisualC++6.0。
在本文的连载过程中,您可以通过如下方式联系作者(热忱欢迎读者朋友对本文的内容提出质疑或给出修改意见):
作者email:
21cnbao@(可以来信提问,笔者将力求予以回信解答);
另外,对本文的转载请务必注明作者和出处。
未经同意,不得用于任何形式的商业目的。
架构
MFC"文档/视图"结构被认为是一种架构,关于什么是架构,这是个"仁者见仁,智者见智"的问题。
在笔者看来,成其为架构者,必具备如下两个特性:
(1)它是一种基础性平台,是一个模型。
通过这个平台、这个模型,我们在上面进一步修饰,可以得到无穷无尽的新事物。
譬如,建筑学上的钢筋混凝土结构、ISO(国际标准化组织)的OSI(开放式系统互连)七层模型。
架构只是一种基础性平台,不同于用这个架构造出的实例。
钢筋混凝土结构是架构,而用钢筋混凝土结构造出的房子就不能称为架构。
这个特性强调了架构的外部特征,即架构具有可学习、可再生、可实例化的特点,是所有基于该架构所构造实例的共性,是贯串在它们体内的一根"筋",但各个基于该架构所构造的实例彼此是存在差异的。
(2)它是一个由内部有联系的事物所组成的一个有机整体。
架构中的内部成员不是彼此松散的,并非各自"占山为王",它们歃血为盟,紧密合作,彼此都有明确的责任和分工,因此共同构筑了一个统一的基础性平台、一个统一的模型。
譬如,OSI模型从物理层到应用层进行了良好的合作,虽然内部包含了复杂的多个层次,但仍然脉络清晰。
由此可见,架构的第2个特性是服务于第1个特性的。
理解架构,关键是理解以上两个特性。
而针对特定的"文档/视图"结构,则需理解如下两个问题:
(1)学习这个架构,并学会在这个架构上造房子(编写基于"文档/视图"结构的程序);
(2)理解这个架构内部的工作机理(文档模板、文档、视图和框架窗口四个类是如何联系为一个有机整体的),并在造房子时加以灵活应用(重载相关的类)。
在这里,我们再引用几位专家(或企业)关于架构的定义以供读者进一步参考:
Thekeyideasofacommercialapplicationframework:
agenericapponsteroidsthatprovidesalargeamountofgeneral-purposefunctionalitywithinawell-planned,welltested,cohesivestructure.
(Applicationframeworkis)anextendedcollectionofclassesthatcooperatetosupportacompleteapplicationarchitectureorapplicationmodel,providingmorecompleteapplicationdevelopmentsupportthanasimplesetofclasslibraries.
――MacApp(Apple'sC++applicationframework)
Anapplicationframeworkisanintegratedobject-orientedsoftwaresystemthatoffersalltheapplication-levelclasses(documents,views,andcommands)neededbyagenericapplication.
Anapplicationframeworkismeanttobeusedinitsentirety,andfostersbothdesignreuseandcodereuse.Anapplicationframeworkembodiesaparticularphilosophyforstructuringanapplication,andinreturnforalargemassofprebuiltfunctionality,theprogrammergivesupcontrolovermanyarchitectural-designdecisions.
――RayValdes
什么是ApplicationFramework?
Framework这个字眼有组织、框架、体制的意思,ApplicationFramework不仅是一般性的泛称,它其实还是对象导向领域中的一个专有名词。
基本上你可以说,ApplicationFramework是一个完整的程序模型,具备标准应用软件所需的一切基本功能,像是档案存取、打印预视、数据交换...,以及这些功能的使用接口(工具列、状态列、选单、对话盒)。
如果更以术语来说,ApplicationFramework就是由一整组合作无间的"对象"架构起来的大模型。
喔不不,当它还没有与你的程序产生火花的时候,它还只是有形无体,应该说是一组合作无间的"类别"架构起来的大模型。
――侯捷
最后,要强调的是,笔者之所以用一个较长的篇幅来连载关于"文档/视图"结构的内容,是因为"文档/视图"结构是MFC中结构最为复杂,体系最为庞大,而又最富有特色的部分,其中涉及到应用、文档模板、文档、视图、SDI窗口、MDI框架窗口、MDI子窗口等多种不同的类,如果不了解这些类及其盘根错节的内部联系的话,就不可能编写出高水平的文档/视图程序。
当然,学习"文档/视图"结构的意义还不只于其本身,通过该架构的学习,一步步领略MFC设计者的神功奥妙,也将进一步增强我们自身对庞大程序框架的把握能力。
一个优秀的程序员是可以写出一个个优秀函数的程序员,而一个优秀的系统设计师则需从全局把握软件的架构,分析和学习"文档/视图"结构相信将是我们成为系统设计师之旅的一个有利环节。
第二讲文档模板
在"文档/视图"架构的MFC程序中,提供了文档模板管理者类CDocManager,由它管理应用程序所包含的文档模板。
本讲的内容,您只需要建立基本的印象。
最初的浅尝辄止是为了最终的深入脊髓!
文档模板管理者类CDocManager
在"文档/视图"架构的MFC程序中,提供了文档模板管理者类CDocManager,由它管理应用程序所包含的文档模板。
我们先看看这个类的声明:
/////////////////////////////////////////////////////////////////////////////
//CDocTemplatemanagerobject
classCDocManager:
publicCObject
{
DECLARE_DYNAMIC(CDocManager)
public:
//Constructor
CDocManager();
//Documentfunctions
virtualvoidAddDocTemplate(CDocTemplate*pTemplate);
virtualPOSITIONGetFirstDocTemplatePosition()const;
virtualCDocTemplate*GetNextDocTemplate(POSITION&pos)const;
virtualvoidRegisterShellFileTypes(BOOLbCompat);
voidUnregisterShellFileTypes();
virtualCDocument*OpenDocumentFile(LPCTSTRlpszFileName);//opennamedfile
virtualBOOLSaveAllModified();//savebeforeexit
virtualvoidCloseAllDocuments(BOOLbEndSession);//closedocumentsbeforeexiting
virtualintGetOpenDocumentCount();
//helperforstandardcommdlgdialogs
virtualBOOLDoPromptFileName(CString&fileName,UINTnIDSTitle,
DWORDlFlags,BOOLbOpenFileDialog,CDocTemplate*pTemplate);
//Commands
//Advanced:
processasyncDDErequest
virtualBOOLOnDDECommand(LPTSTRlpszCommand);
virtualvoidOnFileNew();
virtualvoidOnFileOpen();
//Implementation
protected:
CPtrListm_templateList;
intGetDocumentCount();//helpertocountnumberoftotaldocuments
public:
staticCPtrList*pStaticList;//forstaticCDocTemplateobjects
staticBOOLbStaticInit;//TRUEduringstaticinitialization
staticCDocManager*pStaticDocManager;//forstaticCDocTemplateobjects
public:
virtual~CDocManager();
#ifdef_DEBUG
virtualvoidAssertValid()const;
virtualvoidDump(CDumpContext&dc)const;
#endif
};
从上述代码可以看出,CDocManager类维护一个CPtrList类型的链表m_templateList(即文档模板链表,实际上,MFC的设计者在MFC的实现中大量使用了链表这种数据结构),CPtrList类型定义为:
classCPtrList:
publicCObject
{
DECLARE_DYNAMIC(CPtrList)
protected:
structCNode
{
CNode*pNext;
CNode*pPrev;
void*data;
};
public:
//Construction
CPtrList(intnBlockSize=10);
//Attributes(headandtail)
//countofelements
intGetCount()const;
BOOLIsEmpty()const;
//peekatheadortail
void*&GetHead();
void*GetHead()const;
void*&GetTail();
void*GetTail()const;
//Operations
//getheadortail(andremoveit)-don'tcallonemptylist!
void*RemoveHead();
void*RemoveTail();
//addbeforeheadoraftertail
POSITIONAddHead(void*newElement);
POSITIONAddTail(void*newElement);
//addanotherlistofelementsbeforeheadoraftertail
voidAddHead(CPtrList*pNewList);
voidAddTail(CPtrList*pNewList);
//removeallelements
voidRemoveAll();
//iteration
POSITIONGetHeadPosition()const;
POSITIONGetTailPosition()const;
void*&GetNext(POSITION&rPosition);//return*Position++
void*GetNext(POSITION&rPosition)const;//return*Position++
void*&GetPrev(POSITION&rPosition);//return*Position--
void*GetPrev(POSITION&rPosition)const;//return*Position--
//getting/modifyinganelementatagivenposition
void*&GetAt(POSITIONposition);
void*GetAt(POSITIONposition)const;
voidSetAt(POSITIONpos,void*newElement);
voidRemoveAt(POSITIONposition);
//insertingbeforeorafteragivenposition
POSITIONInsertBefore(POSITIONposition,void*newElement);
POSITIONInsertAfter(POSITIONposition,void*newElement);
//helperfunctions(note:
O(n)speed)
POSITIONFind(void*searchValue,POSITIONstartAfter=NULL)const;
//defaultstostartingattheHEAD
//returnNULLifnotfound
POSITIONFindIndex(intnIndex)const;
//getthe'nIndex'thelement(mayreturnNULL)
//Implementation
protected:
CNode*m_pNodeHead;
CNode*m_pNodeTail;
intm_nCount;
CNode*m_pNodeFree;
structCPlex*m_pBlocks;
intm_nBlockSize;
CNode*NewNode(CNode*,CNode*);
voidFreeNode(CNode*);
public:
~CPtrList();
#ifdef_DEBUG
voidDump(CDumpContext&)const;
voidAssertValid()const;
#endif
//localtypedefsforclasstemplates
typedefvoid*BASE_TYPE;
typedefvoid*BASE_ARG_TYPE;
};
很显然,CPtrList是对链表结构体
structCNode
{
CNode*pNext;
CNode*pPrev;
void*data;
};
本身及其GetNext、GetPrev、GetAt、SetAt、RemoveAt、InsertBefore、InsertAfter、Find、FindIndex等各种操作的封装。
作为一个抽象的链表类型,CPtrList并未定义其中节点的具体类型,而以一个void指针(structCNode中的void*data)巧妙地实现了链表节点成员具体类型的"模板"化。
很显然,在VisualC++6.0开发的年代,C++语言所具有的语法特征"模板"仍然没有得到广泛的应用。
而CDocManager类的成员函数
virtualvoidAddDocTemplate(CDocTemplate*pTemplate);
virtualPOSITIONGetFirstDocTemplatePosition()const;
virtualCDocTemplate*GetNextDocTemplate(POSITION&pos)const;
则完成对m_TemplateList链表的添加及遍历操作的封装,我们来看看这三个函数的源代码:
voidCDocManager:
:
AddDocTemplate(CDocTemplate*pTemplate)
{
if(pTemplate==NULL)
{
if(pStaticList!
=NULL)
{
POSITIONpos=pStaticList->GetHeadPosition();
while(pos!
=NULL)
{
CDocTemplate*pTemplate=(CDocTemplate*)pStaticList->GetNext(pos);
AddDocTemplate(pTemplate);
}
deletepStaticList;
pStaticList=NULL;
}
bStaticInit=FALSE;
}
else
{
ASSERT_VALID(pTemplate);
ASSERT(m_templateList.Find(pTemplate,NULL)==NULL);//mustnotbeinlist
pTemplate->LoadTemplate();
m_templateList.AddTail(pTemplate);
}
}
POSITIONCDocManager:
:
GetFirstDocTemplatePosition()const
{
returnm_templateList.GetHeadPosition();
}
CDocTemplate*CDocManager:
:
GetNextDocTemplate(POSITION&pos)const
{
return(CDocTemplate*)m_templateList.GetNext(pos);
}
第三讲文档
在"文档/视图"架构的MFC程序中,文档是一个CDocument派生对象,它负责存储应用程序的数据,并把这些信息提供给应用程序的其余部分。
1、文档类CDocument
在"文档/视图"架构的MFC程序中,文档是一个CDocument派生对象,它负责存储应用程序的数据,并把这些信息提供给应用程序的其余部分。
CDocument类对文档的建立及归档提供