深入浅出MFC“文档/视图”架构(2)――文档模板

简介:
深入浅出MFC“文档/视图”架构(2)

――文档模板

作者:宋宝华  e-mail:21cnbao@21cn.com

1.文档模板管理者类CDocManager

在“文档/视图”架构的MFC程序中,提供了文档模板管理者类CDocManager,由它管理应用程序所包含的文档模板。我们先看看这个类的声明:

/////////////////////////////////////////////////////////////////////////////

// CDocTemplate manager object

 

class CDocManager : public CObject

{

       DECLARE_DYNAMIC(CDocManager)

public:

 

// Constructor

       CDocManager();

 

       //Document functions

       virtual void AddDocTemplate(CDocTemplate* pTemplate);

       virtual POSITION GetFirstDocTemplatePosition() const;

       virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const;

       virtual void RegisterShellFileTypes(BOOL bCompat);

       void UnregisterShellFileTypes();

       virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file

       virtual BOOL SaveAllModified(); // save before exit

       virtual void CloseAllDocuments(BOOL bEndSession); // close documents before exiting

       virtual int GetOpenDocumentCount();

 

       // helper for standard commdlg dialogs

       virtual BOOL DoPromptFileName(CString& fileName, UINT nIDSTitle,

                     DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate);

 

//Commands

       // Advanced: process async DDE request

       virtual BOOL OnDDECommand(LPTSTR lpszCommand);

       virtual void OnFileNew();

       virtual void OnFileOpen();

 

// Implementation

protected:

       CPtrList m_templateList;

       int GetDocumentCount(); // helper to count number of total documents

 

public:

       static CPtrList* pStaticList;       // for static CDocTemplate objects

       static BOOL bStaticInit;            // TRUE during static initialization

       static CDocManager* pStaticDocManager;  // for static CDocTemplate objects

 

public:

       virtual ~CDocManager();

#ifdef _DEBUG

       virtual void AssertValid() const;

       virtual void Dump(CDumpContext& dc) const;

#endif

};

从上述代码可以看出,CDocManager类维护一个CPtrList类型的链表m_templateList(即文档模板链表,实际上,MFC的设计者在MFC的实现中大量使用了链表这种数据结构),CPtrList类型定义为:

class CPtrList : public CObject

{

       DECLARE_DYNAMIC(CPtrList)

 

protected:

       struct CNode

       {

              CNode* pNext;

              CNode* pPrev;

              void* data;

       };

public:

 

// Construction

       CPtrList(int nBlockSize = 10);

 

// Attributes (head and tail)

       // count of elements

       int GetCount() const;

       BOOL IsEmpty() const;

 

       // peek at head or tail

       void*& GetHead();

       void* GetHead() const;

       void*& GetTail();

       void* GetTail() const;

 

// Operations

       // get head or tail (and remove it) - don't call on empty list!

       void* RemoveHead();

       void* RemoveTail();

 

       // add before head or after tail

       POSITION AddHead(void* newElement);

       POSITION AddTail(void* newElement);

 

 

       // add another list of elements before head or after tail

       void AddHead(CPtrList* pNewList);

       void AddTail(CPtrList* pNewList);

 

       // remove all elements

       void RemoveAll();

 

       // iteration

       POSITION GetHeadPosition() const;

       POSITION GetTailPosition() 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/modifying an element at a given position

       void*& GetAt(POSITION position);

       void* GetAt(POSITION position) const;

       void SetAt(POSITION pos, void* newElement);

 

       void RemoveAt(POSITION position);

 

       // inserting before or after a given position

       POSITION InsertBefore(POSITION position, void* newElement);

       POSITION InsertAfter(POSITION position, void* newElement);

 

 

       // helper functions (note: O(n) speed)

       POSITION Find(void* searchValue, POSITION startAfter = NULL) const;

                                          // defaults to starting at the HEAD

                                          // return NULL if not found

       POSITION FindIndex(int nIndex) const;

                                          // get the 'nIndex'th element (may return NULL)

 

// Implementation

protected:

       CNode* m_pNodeHead;

       CNode* m_pNodeTail;

       int m_nCount;

       CNode* m_pNodeFree;

       struct CPlex* m_pBlocks;

       int m_nBlockSize;

 

       CNode* NewNode(CNode*, CNode*);

       void FreeNode(CNode*);

 

public:

       ~CPtrList();

#ifdef _DEBUG

       void Dump(CDumpContext&) const;

       void AssertValid() const;

#endif

       // local typedefs for class templates

       typedef void* BASE_TYPE;

       typedef void* BASE_ARG_TYPE;

};

很显然,CPtrList是对链表结构体

struct CNode

{

       CNode* pNext;

       CNode* pPrev;

       void* data;

};

本身及其GetNext、GetPrev、GetAt、SetAt、RemoveAt、InsertBefore、InsertAfter、Find、FindIndex等各种操作的封装。

作为一个抽象的链表类型,CPtrList并未定义其中节点的具体类型,而以一个void指针(struct CNode 中的void* data)巧妙地实现了链表节点成员具体类型的“模板”化。很显然,在Visual C++6.0开发的年代,C++语言所具有的语法特征“模板”仍然没有得到广泛的应用。

而CDocManager类的成员函数

virtual void AddDocTemplate(CDocTemplate* pTemplate);

virtual POSITION GetFirstDocTemplatePosition() const;

virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const;

则完成对m_TemplateList链表的添加及遍历操作的封装,我们来看看这三个函数的源代码:

void CDocManager::AddDocTemplate(CDocTemplate* pTemplate)

{

       if (pTemplate == NULL)

       {

              if (pStaticList != NULL)

              {

                     POSITION pos = pStaticList->GetHeadPosition();

                     while (pos != NULL)

                     {

                            CDocTemplate* pTemplate =

                                   (CDocTemplate*)pStaticList->GetNext(pos);

                            AddDocTemplate(pTemplate);

                     }

                     delete pStaticList;

                     pStaticList = NULL;

              }

              bStaticInit = FALSE;

       }

       else

       {

              ASSERT_VALID(pTemplate);

              ASSERT(m_templateList.Find(pTemplate, NULL) == NULL);// must not be in list

              pTemplate->LoadTemplate();

              m_templateList.AddTail(pTemplate);

       }

}

POSITION CDocManager::GetFirstDocTemplatePosition() const

{

       return m_templateList.GetHeadPosition();

}

CDocTemplate* CDocManager::GetNextDocTemplate(POSITION& pos) const

{

       return (CDocTemplate*)m_templateList.GetNext(pos);

}

2.文档模板类CDocTemplate

文档模板类CDocTemplate是一个抽象基类(这意味着不能直接用它来定义对象而必须用它的派生类),它定义了文档模板的基本处理函数接口。对一个单文档界面程序,需使用单文档模板类CSingleDocTemplate,而对于一个多文档界面程序,需使用多文档模板类CMultipleDocTemplate。我们首先来看看CDocTemplate类的声明:

class CDocTemplate : public CCmdTarget

{

       DECLARE_DYNAMIC(CDocTemplate)

 

// Constructors

protected:

       CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,

              CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);

 

public:

       virtual void LoadTemplate();

 

// Attributes

public:

       // setup for OLE containers

       void SetContainerInfo(UINT nIDOleInPlaceContainer);

 

       // setup for OLE servers

       void SetServerInfo(UINT nIDOleEmbedding, UINT nIDOleInPlaceServer = 0,

              CRuntimeClass* pOleFrameClass = NULL, CRuntimeClass* pOleViewClass = NULL);

 

       // iterating over open documents

       virtual POSITION GetFirstDocPosition() const = 0;

       virtual CDocument* GetNextDoc(POSITION& rPos) const = 0;

 

// Operations

public:

       virtual void AddDocument(CDocument* pDoc);      // must override

       virtual void RemoveDocument(CDocument* pDoc);   // must override

 

       enum DocStringIndex

       {

              windowTitle,        // default window title

              docName,            // user visible name for default document

              fileNewName,        // user visible name for FileNew

              // for file based documents:

              filterName,         // user visible name for FileOpen

              filterExt,          // user visible extension for FileOpen

              // for file based documents with Shell open support:

              regFileTypeId,      // REGEDIT visible registered file type identifier

              regFileTypeName,    // Shell visible registered file type name

       };

       virtual BOOL GetDocString(CString& rString,

              enum DocStringIndex index) const; // get one of the info strings

       CFrameWnd* CreateOleFrame(CWnd* pParentWnd, CDocument* pDoc,

              BOOL bCreateView);

 

// Overridables

public:

       enum Confidence

       {

              noAttempt,

              maybeAttemptForeign,

              maybeAttemptNative,

              yesAttemptForeign,

              yesAttemptNative,

              yesAlreadyOpen

       };

       virtual Confidence MatchDocType(LPCTSTR lpszPathName,

                                   CDocument*& rpDocMatch);

       virtual CDocument* CreateNewDocument();

       virtual CFrameWnd* CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther);

       virtual void InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,

              BOOL bMakeVisible = TRUE);

       virtual BOOL SaveAllModified();     // for all documents

       virtual void CloseAllDocuments(BOOL bEndSession);

       virtual CDocument* OpenDocumentFile(

              LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE) = 0;

                                   // open named file

                                   // if lpszPathName == NULL => create new file with this type

       virtual void SetDefaultTitle(CDocument* pDocument) = 0;

 

// Implementation

public:

       BOOL m_bAutoDelete;

       virtual ~CDocTemplate();

 

       // back pointer to OLE or other server (NULL if none or disabled)

       CObject* m_pAttachedFactory;

 

       // menu & accelerator resources for in-place container

       HMENU m_hMenuInPlace;

       HACCEL m_hAccelInPlace;

 

       // menu & accelerator resource for server editing embedding

       HMENU m_hMenuEmbedding;

       HACCEL m_hAccelEmbedding;

 

       // menu & accelerator resource for server editing in-place

       HMENU m_hMenuInPlaceServer;

       HACCEL m_hAccelInPlaceServer;

 

#ifdef _DEBUG

       virtual void Dump(CDumpContext&) const;

       virtual void AssertValid() const;

#endif

       virtual void OnIdle();             // for all documents

       virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,

              AFX_CMDHANDLERINFO* pHandlerInfo);

 

protected:

       UINT m_nIDResource;                 // IDR_ for frame/menu/accel as well

       UINT m_nIDServerResource;           // IDR_ for OLE inplace frame/menu/accel

       UINT m_nIDEmbeddingResource;        // IDR_ for OLE open frame/menu/accel

       UINT m_nIDContainerResource;        // IDR_ for container frame/menu/accel

 

       CRuntimeClass* m_pDocClass;         // class for creating new documents

       CRuntimeClass* m_pFrameClass;       // class for creating new frames

       CRuntimeClass* m_pViewClass;        // class for creating new views

       CRuntimeClass* m_pOleFrameClass;    // class for creating in-place frame

       CRuntimeClass* m_pOleViewClass;     // class for creating in-place view

 

       CString m_strDocStrings;    // '\n' separated names

              // The document names sub-strings are represented as _one_ string:

              // windowTitle\ndocName\n ... (see DocStringIndex enum)

};

文档模板挂接了后面要介绍的文档、视图和框架窗口,使得它们得以互相关联。通过文档模板,程序确定了创建或打开一个文档时,以什么样的视图和框架窗口来显示。文档模板依靠保存相互对应的文档、视图和框架窗口的CRuntimeClass对象指针来实现上述挂接,这就是文档模板类中的成员变量m_pDocClass、m_pFrameClass、m_pViewClass的由来。实际上,对m_pDocClass、m_pFrameClass、m_pViewClass的赋值在CDocTemplate类的构造函数中实施:

CDocTemplate::CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,

       CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)

{

       ASSERT_VALID_IDR(nIDResource);

       ASSERT(pDocClass == NULL ||

              pDocClass->IsDerivedFrom(RUNTIME_CLASS(CDocument)));

       ASSERT(pFrameClass == NULL ||

              pFrameClass->IsDerivedFrom(RUNTIME_CLASS(CFrameWnd)));

       ASSERT(pViewClass == NULL ||

              pViewClass->IsDerivedFrom(RUNTIME_CLASS(CView)));

 

       m_nIDResource = nIDResource;

       m_nIDServerResource = NULL;

       m_nIDEmbeddingResource = NULL;

       m_nIDContainerResource = NULL;

 

       m_pDocClass = pDocClass;

       m_pFrameClass = pFrameClass;

       m_pViewClass = pViewClass;

       m_pOleFrameClass = NULL;

       m_pOleViewClass = NULL;

 

       m_pAttachedFactory = NULL;

       m_hMenuInPlace = NULL;

       m_hAccelInPlace = NULL;

       m_hMenuEmbedding = NULL;

       m_hAccelEmbedding = NULL;

       m_hMenuInPlaceServer = NULL;

       m_hAccelInPlaceServer = NULL;

 

       // add to pStaticList if constructed as static instead of on heap

       if (CDocManager::bStaticInit)

       {

              m_bAutoDelete = FALSE;

              if (CDocManager::pStaticList == NULL)

                     CDocManager::pStaticList = new CPtrList;

              if (CDocManager::pStaticDocManager == NULL)

                     CDocManager::pStaticDocManager = new CDocManager;

              CDocManager::pStaticList->AddTail(this);

       }

       else

       {

              m_bAutoDelete = TRUE;   // usually allocated on the heap

              LoadTemplate();

       }

}

文档模板类CDocTemplate还保存了它所支持的全部文档类的信息,包括所支持文档的文件扩展名、文档在框架窗口中的名字、图标等。

CDocTemplate类的AddDocument、RemoveDocument成员函数使得CDocument* pDoc参数所指向的文档归属于本文档模板(通过将this指针赋值给pDoc所指向CDocument对象的m_pDocTemplate成员变量)或脱离与本文档模板的关系:

void CDocTemplate::AddDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

       ASSERT(pDoc->m_pDocTemplate == NULL);   // no template attached yet

       pDoc->m_pDocTemplate = this;

}

void CDocTemplate::RemoveDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

       ASSERT(pDoc->m_pDocTemplate == this);   // must be attached to us

       pDoc->m_pDocTemplate = NULL;

}

而CDocTemplate类的CreateNewDocument成员函数则首先调用CDocument运行时类的CreateObject函数创建一个CDocument对象,再调用AddDocument成员函数将其归属于本文档模板类:

CDocument* CDocTemplate::CreateNewDocument()

{

       // default implementation constructs one from CRuntimeClass

       if (m_pDocClass == NULL)

       {

              TRACE0("Error: you must override CDocTemplate::CreateNewDocument.\n");

              ASSERT(FALSE);

              return NULL;

       }

       CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();

       if (pDocument == NULL)

       {

              TRACE1("Warning: Dynamic create of document type %hs failed.\n",

                     m_pDocClass->m_lpszClassName);

              return NULL;

       }

       ASSERT_KINDOF(CDocument, pDocument);

       AddDocument(pDocument);

       return pDocument;

}

文档类对象由文档模板类构造生成,单文档模板类CSingleDocTemplate只能生成一个文档类对象,并用成员变量 m_pOnlyDoc 指向该对象;多文档模板类可以生成多个文档类对象,用成员变量 m_docList 指向文档对象组成的链表。

CSingleDocTemplate的构造函数、AddDocument及RemoveDocument成员函数都在CDocTemplate类相应函数的基础上增加了对m_pOnlyDoc指针的处理:

CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource,

       CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass,

       CRuntimeClass* pViewClass)

              : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)

{

       m_pOnlyDoc = NULL;

}

void CSingleDocTemplate::AddDocument(CDocument* pDoc)

{

       ASSERT(m_pOnlyDoc == NULL);     // one at a time please

       ASSERT_VALID(pDoc);

 

       CDocTemplate::AddDocument(pDoc);

       m_pOnlyDoc = pDoc;

}

void CSingleDocTemplate::RemoveDocument(CDocument* pDoc)

{

       ASSERT(m_pOnlyDoc == pDoc);     // must be this one

       ASSERT_VALID(pDoc);

 

       CDocTemplate::RemoveDocument(pDoc);

       m_pOnlyDoc = NULL;

}

同样,CMultiDocTemplate类的相关函数也需要对m_docList所指向的链表进行操作(实际上AddDocument和RemoveDocument成员函数是文档模板管理其所包含文档的函数):

// CMultiDocTemplate document management (a list of currently open documents)

void CMultiDocTemplate::AddDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

 

       CDocTemplate::AddDocument(pDoc);

       ASSERT(m_docList.Find(pDoc, NULL) == NULL); // must not be in list

       m_docList.AddTail(pDoc);

}

void CMultiDocTemplate::RemoveDocument(CDocument* pDoc)

{

       ASSERT_VALID(pDoc);

 

       CDocTemplate::RemoveDocument(pDoc);

       m_docList.RemoveAt(m_docList.Find(pDoc));

}

由于CMultiDocTemplate类可包含多个文档,依靠其成员函数GetFirstDocPosition和GetNextDoc完成对文档链表m_docList的遍历:

POSITION CMultiDocTemplate::GetFirstDocPosition() const

{

       return m_docList.GetHeadPosition();

}

CDocument* CMultiDocTemplate::GetNextDoc(POSITION& rPos) const

{

       return (CDocument*)m_docList.GetNext(rPos);

}

而CSingleDocTemplate的这两个函数实际上并无太大的意义,仅仅是MFC要玩的某种“招数”,这个“招数”高明吗?相信看完MFC的相关源代码后你或许不会这么认为,实际上CSingleDocTemplate的GetFirstDocPosition、GetNextDoc函数仅仅只能判断m_pOnlyDoc的是否为NULL:

POSITION CSingleDocTemplate::GetFirstDocPosition() const

{

       return (m_pOnlyDoc == NULL) ? NULL : BEFORE_START_POSITION;

}

 

CDocument* CSingleDocTemplate::GetNextDoc(POSITION& rPos) const

{

       CDocument* pDoc = NULL;

       if (rPos == BEFORE_START_POSITION)

       {

              // first time through, return a real document

              ASSERT(m_pOnlyDoc != NULL);

              pDoc = m_pOnlyDoc;

       }

       rPos = NULL;        // no more

       return pDoc;

}

笔者认为,MFC的设计者们将GetFirstDocPosition、GetNextDoc作为基类CDocTemplate的成员函数是不合理的,一种更好的做法是将GetFirstDocPosition、GetNextDoc移至CMultiDocTemplate派生类。

CDocTemplate还需完成对其对应文档的关闭与保存操作:

BOOL CDocTemplate::SaveAllModified()

{

       POSITION pos = GetFirstDocPosition();

       while (pos != NULL)

       {

              CDocument* pDoc = GetNextDoc(pos);

              if (!pDoc->SaveModified())

                     return FALSE;

       }

       return TRUE;

}

void CDocTemplate::CloseAllDocuments(BOOL)

{

       POSITION pos = GetFirstDocPosition();

       while (pos != NULL)

       {

              CDocument* pDoc = GetNextDoc(pos);

              pDoc->OnCloseDocument();

       }

}

前文我们提到,由于MFC的设计者将CSingleDocTemplate和CMultiDocTemplate的行为未进行规范的区分,它对仅仅对应一个文档的CSingleDocTemplate也提供了所谓的GetFirstDocPosition、GetNextDoc遍历操作,所以基类CDocTemplate的SaveAllModified和CloseAllDocuments函数(都是遍历)就可统一CSingleDocTemplate和CMultiDocTemplate两个本身并不相同类的SaveAllModified和CloseAllDocuments行为(实际上,对于CSingleDocTemplate而言,SaveAllModified和CloseAllDocuments中的“All”是没有太大意义的。教室里有1个老师和N个同学,老师可以对同学们说“所有同学”,而学生对老师说“所有老师”相信会被当成神经病)。MFC的设计者们特意使用了“将错就错”的方法意图简化CSingleDocTemplate和CMultiDocTemplate类的设计,读者朋友可以不认同他们的做法。

CDocTemplate还提供了框架窗口的创建和初始化函数:

/////////////////////////////////////////////////////////////////////////////

// Default frame creation

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)

{

       if (pDoc != NULL)

              ASSERT_VALID(pDoc);

       // create a frame wired to the specified document

 

       ASSERT(m_nIDResource != 0); // must have a resource ID to load from

       CCreateContext context;

       context.m_pCurrentFrame = pOther;

       context.m_pCurrentDoc = pDoc;

       context.m_pNewViewClass = m_pViewClass;

       context.m_pNewDocTemplate = this;

 

       if (m_pFrameClass == NULL)

       {

              TRACE0("Error: you must override CDocTemplate::CreateNewFrame.\n");

              ASSERT(FALSE);

              return NULL;

       }

       CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();

       if (pFrame == NULL)

       {

              TRACE1("Warning: Dynamic create of frame %hs failed.\n",

                     m_pFrameClass->m_lpszClassName);

              return NULL;

       }

       ASSERT_KINDOF(CFrameWnd, pFrame);

 

       if (context.m_pNewViewClass == NULL)

              TRACE0("Warning: creating frame with no default view.\n");

 

       // create new from resource

       if (!pFrame->LoadFrame(m_nIDResource,

                     WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles

                     NULL, &context))

       {

              TRACE0("Warning: CDocTemplate couldn't create a frame.\n");

              // frame will be deleted in PostNcDestroy cleanup

              return NULL;

       }

 

       // it worked !

       return pFrame;

}

void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,

       BOOL bMakeVisible)

{

       // just delagate to implementation in CFrameWnd

       pFrame->InitialUpdateFrame(pDoc, bMakeVisible);

}

3. CWinApp与CDocManager/CDocTemplate类

应用程序CWinApp类对象与CDocManager和CDocTemplate类的关系是:CWinApp对象中包含一个CDocManager指针类型的共有数据成员m_pDocManager,CWinApp::InitInstance函数调用CWinApp::AddDocTemplate函数向链表m_templateList添加模板指针(实际上是调用前文所述CDocManager的AddDocTemplate函数)。另外,CWinApp也提供了GetFirstDocTemplatePosition和GetNextDocTemplate函数实现来对m_templateList链表进行访问(实际上也是调用了前文所述CDocManager的GetFirstDocTemplatePosition、GetNextDocTemplate函数)。我们仅摘取CWinApp类声明的一小部分:

class CWinApp : public CWinThread

{



       CDocManager* m_pDocManager;

 

// Running Operations - to be done on a running application

       // Dealing with document templates

       void AddDocTemplate(CDocTemplate* pTemplate);

       POSITION GetFirstDocTemplatePosition() const;

       CDocTemplate* GetNextDocTemplate(POSITION& pos) const;

 

       // Dealing with files

       virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file

       void CloseAllDocuments(BOOL bEndSession); // close documents before exiting

 

// Command Handlers

protected:

       // map to the following for file new/open

       afx_msg void OnFileNew();

       afx_msg void OnFileOpen();

       int GetOpenDocumentCount();



};

来看CWinApp派生类CSDIExampleApp(单文档)、CMDIExampleApp(多文档)的InitInstance成员函数的例子(仅仅摘取与文档模板相关的部分):

BOOL CSDIExampleApp::InitInstance()

{



       CSingleDocTemplate* pDocTemplate;

       pDocTemplate = new CSingleDocTemplate(

              IDR_MAINFRAME,

              RUNTIME_CLASS(CSDIExampleDoc),

              RUNTIME_CLASS(CMainFrame),       // main SDI frame window

              RUNTIME_CLASS(CSDIExampleView));

       AddDocTemplate(pDocTemplate);



       return TRUE;

}

BOOL CMDIExampleApp::InitInstance()

{



       CMultiDocTemplate* pDocTemplate;

       pDocTemplate = new CMultiDocTemplate(

              IDR_MDIEXATYPE,

              RUNTIME_CLASS(CMDIExampleDoc),

              RUNTIME_CLASS(CChildFrame), // custom MDI child frame

              RUNTIME_CLASS(CMDIExampleView));

       AddDocTemplate(pDocTemplate);



}

读者朋友,看完本次连载,也许您有许多不明白的地方,这是正常的。因为其所讲解的内容与后续几次连载息息相关,我们愈往后看,就会愈加清晰。对于本次连载的内容,您只需要建立基本的印象。最初的浅尝辄止是为了最终的深入脊髓!

我们试图对MFC的深层机理刨根究底,“拨开云雾见月明”的过程是艰辛的 





 本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120813,如需转载请自行联系原作者





相关文章
|
7月前
|
SQL NoSQL 前端开发
基于BS架构的饰品购物平台设计与实现(程序+文档+数据库)
基于BS架构的饰品购物平台设计与实现(程序+文档+数据库)
|
7月前
|
存储 人工智能 缓存
AI 提示词模板相关的架构设计
现在很多企业纷纷研发大语言模型以解决业务问题。提示词在与模型交互中起到关键作用。为优化提示词模板的修改、提高渲染效率及确保安全性,架构设计注重可修改性、安全性、可靠性和性能。设计包括:将提示词存储在OSS以方便修改和版本控制;使用本地缓存提升读取性能;模板引擎增强灵活性;秘钥安全存储在加密系统中;并通过配置中心动态调整。此设计旨在提供高效、安全且可靠的AI交互体验等。
717 78
AI 提示词模板相关的架构设计
|
4月前
|
JSON 前端开发 API
Django 后端架构开发:通用表单视图、组件对接、验证机制和组件开发
Django 后端架构开发:通用表单视图、组件对接、验证机制和组件开发
65 2
|
4月前
|
JSON 数据处理 API
Django后端架构开发:视图与模板的正确使用
Django后端架构开发:视图与模板的正确使用
37 1
|
4月前
|
JSON 数据可视化 数据格式
【Azure 环境】中国区Azure是否可以根据资源组的模板,生成一个可视化的架构图呢?
【Azure 环境】中国区Azure是否可以根据资源组的模板,生成一个可视化的架构图呢?
【Azure 环境】中国区Azure是否可以根据资源组的模板,生成一个可视化的架构图呢?
|
7月前
|
存储 设计模式 前端开发
请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
【2月更文挑战第26天】【2月更文挑战第89篇】请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
53 0
|
16天前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
14天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
15天前
|
Dubbo Java 应用服务中间件
服务架构的演进:从单体到微服务的探索之旅
随着企业业务的不断拓展和复杂度的提升,对软件系统架构的要求也日益严苛。传统的架构模式在应对现代业务场景时逐渐暴露出诸多局限性,于是服务架构开启了持续演变之路。从单体架构的简易便捷,到分布式架构的模块化解耦,再到微服务架构的精细化管理,企业对技术的选择变得至关重要,尤其是 Spring Cloud 和 Dubbo 等微服务技术的对比和应用,直接影响着项目的成败。 本篇文章会从服务架构的演进开始分析,探索从单体项目到微服务项目的演变过程。然后也会对目前常见的微服务技术进行对比,找到目前市面上所常用的技术给大家进行讲解。
33 1
服务架构的演进:从单体到微服务的探索之旅
|
13天前
|
消息中间件 监控 安全
后端架构演进:从单体到微服务####
在数字化转型的浪潮中,企业应用的后端架构经历了从传统单体架构到现代微服务架构的深刻变革。本文探讨了这一演进过程的背景、驱动力、关键技术及面临的挑战,揭示了如何通过微服务化实现系统的高可用性、扩展性和敏捷开发,同时指出了转型过程中需克服的服务拆分、数据管理、通信机制等难题,为读者提供了一个全面理解后端架构演变路径的视角。 ####
31 8