免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
·教程:輕松玩轉(zhuǎn)MFC文檔視圖架構(gòu)編程
 引言

  MFC引入了"文檔/視圖"結(jié)構(gòu)的概念,理解這個結(jié)構(gòu)是編寫基于MFC編寫復(fù)雜Visual C++程序的關(guān)鍵。"文檔/視圖"中主要涉及到四種類:

 ?。?)文檔模板:

class CDocTemplate; // template for document creation
class CSingleDocTemplate; // SDI support
class CMultiDocTemplate; // MDI support

  (2)文檔:

class CDocument; // main document abstraction

 ?。?)視圖:

// views on a document
class CView; // a view on a document
class CScrollView; // a scrolling view

 ?。?)框架窗口:

// frame windows
class CFrameWnd; // standard SDI frame
class CMDIFrameWnd; // standard MDI frame
class CMDIChildWnd; // standard MDI child
class CMiniFrameWnd; // half-height caption frame wnd

  理解了這4個類各自的意義及它們縱橫交錯的關(guān)系也就理解了"文檔/視圖"結(jié)構(gòu)的基本概念,在此基礎(chǔ)上,我們還需要進一步研究"文檔/視圖"結(jié)構(gòu)的MFC程序消息流動的方向,這樣就完全徹底明白了基于"文檔/視圖"結(jié)構(gòu)MFC程序的"生死因果"。

  出于以上考慮,本文這樣組織了各次連載的內(nèi)容:

  第1次連載進行基本概念的介紹,第2~5次連載分別講述文檔模板、文檔、視圖和框架窗口四個類的功能和主要函數(shù),連載6則綜合闡述四個類之間的關(guān)系,接著以連載7講解消息流動的方向,最后的連載8則以實例剖析連載1~7所講述的所有內(nèi)容。

  本文所有的代碼基于WIN32平臺開發(fā),調(diào)試環(huán)境為Visual C++6.0。在本文的連載過程中,您可以通過如下方式聯(lián)系作者(熱忱歡迎讀者朋友對本文的內(nèi)容提出質(zhì)疑或給出修改意見):

  作者email:21cnbao@21cn.com(可以來信提問,筆者將力求予以回信解答);

  另外,對本文的轉(zhuǎn)載請務(wù)必注明作者和出處。未經(jīng)同意,不得用于任何形式的商業(yè)目的。

  架構(gòu)

  MFC"文檔/視圖"結(jié)構(gòu)被認為是一種架構(gòu),關(guān)于什么是架構(gòu),這是個"仁者見仁,智者見智"的問題。在筆者看來,成其為架構(gòu)者,必具備如下兩個特性:

 ?。?)它是一種基礎(chǔ)性平臺,是一個模型。通過這個平臺、這個模型,我們在上面進一步修飾,可以得到無窮無盡的新事物。譬如,建筑學(xué)上的鋼筋混凝土結(jié)構(gòu)、ISO(國際標準化組織)的OSI(開放式系統(tǒng)互連)七層模型。架構(gòu)只是一種基礎(chǔ)性平臺,不同于用這個架構(gòu)造出的實例。鋼筋混凝土結(jié)構(gòu)是架構(gòu),而用鋼筋混凝土結(jié)構(gòu)造出的房子就不能稱為架構(gòu)。

  這個特性強調(diào)了架構(gòu)的外部特征,即架構(gòu)具有可學(xué)習(xí)、可再生、可實例化的特點,是所有基于該架構(gòu)所構(gòu)造實例的共性,是貫串在它們體內(nèi)的一根"筋",但各個基于該架構(gòu)所構(gòu)造的實例彼此是存在差異的。

 ?。?)它是一個由內(nèi)部有聯(lián)系的事物所組成的一個有機整體。架構(gòu)中的內(nèi)部成員不是彼此松散的,并非各自"占山為王",它們歃血為盟,緊密合作,彼此都有明確的責(zé)任和分工,因此共同構(gòu)筑了一個統(tǒng)一的基礎(chǔ)性平臺、一個統(tǒng)一的模型。譬如,OSI模型從物理層到應(yīng)用層進行了良好的合作,雖然內(nèi)部包含了復(fù)雜的多個層次,但仍然脈絡(luò)清晰。

  由此可見,架構(gòu)的第2個特性是服務(wù)于第1個特性的。理解架構(gòu),關(guān)鍵是理解以上兩個特性。而針對特定的"文檔/視圖"結(jié)構(gòu),則需理解如下兩個問題:

  (1)學(xué)習(xí)這個架構(gòu),并學(xué)會在這個架構(gòu)上造房子(編寫基于"文檔/視圖"結(jié)構(gòu)的程序);

 ?。?)理解這個架構(gòu)內(nèi)部的工作機理(文檔模板、文檔、視圖和框架窗口四個類是如何聯(lián)系為一個有機整體的),并在造房子時加以靈活應(yīng)用(重載相關(guān)的類)。

  在這里,我們再引用幾位專家(或企業(yè))關(guān)于架構(gòu)的定義以供讀者進一步參考:

The key ideas of a commercial application framework : a generic app on steroids that provides a large amount of general-purpose functionality within a well-planned, welltested, cohesive structure.
(Application framework is) an extended collection of classes that cooperate to support a complete application architecture or application model, providing more complete application development support than a simple set of class libraries.
――MacApp(Apple's C++ application framework)
An application framework is an integrated object-oriented software system that offers all the application-level classes(documents, views, and commands)needed by a generic application.
An application framework is meant to be used in its entirety, and fosters both design reuse and code reuse. An application framework embodies a particular philosophy for structuring an application, and in return for a large mass of prebuilt functionality, the programmer gives up control over many architectural-design decisions.
――Ray Valdes


  什么是Application Framework?Framework 這個字眼有組織、框架、體制的意思,Application Framework 不僅是一般性的泛稱,它其實還是對象導(dǎo)向領(lǐng)域中的一個專有名詞。 

  基本上你可以說,Application Framework 是一個完整的程序模型,具備標準應(yīng)用軟件所需的一切基本功能,像是檔案存取、打印預(yù)視、數(shù)據(jù)交換...,以及這些功能的使用接口(工具列、狀態(tài)列、選單、對話盒)。如果更以術(shù)語來說,Application Framework 就是由一整組合作無間的"對象"架構(gòu)起來的大模型。喔不不,當(dāng)它還沒有與你的程序產(chǎn)生火花的時候,它還只是有形無體,應(yīng)該說是一組合作無間的"類別"架構(gòu)起來的大模型。

  ――侯捷


  最后,要強調(diào)的是,筆者之所以用一個較長的篇幅來連載關(guān)于"文檔/視圖"結(jié)構(gòu)的內(nèi)容,是因為"文檔/視圖"結(jié)構(gòu)是MFC中結(jié)構(gòu)最為復(fù)雜,體系最為龐大,而又最富有特色的部分,其中涉及到應(yīng)用、文檔模板、文檔、視圖、SDI窗口、MDI框架窗口、MDI子窗口等多種不同的類,如果不了解這些類及其盤根錯節(jié)的內(nèi)部聯(lián)系的話,就不可能編寫出高水平的文檔/視圖程序。當(dāng)然,學(xué)習(xí)"文檔/視圖"結(jié)構(gòu)的意義還不只于其本身,通過該架構(gòu)的學(xué)習(xí),一步步領(lǐng)略MFC設(shè)計者的神功奧妙,也將進一步增強我們自身對龐大程序框架的把握能力。一個優(yōu)秀的程序員是可以寫出一個個優(yōu)秀函數(shù)的程序員,而一個優(yōu)秀的系統(tǒng)設(shè)計師則需從全局把握軟件的架構(gòu),分析和學(xué)習(xí)"文檔/視圖"結(jié)構(gòu)相信將是我們成為系統(tǒng)設(shè)計師之旅的一個有利環(huán)節(jié)。

文檔模板管理者類CDocManager

  在"文檔/視圖"架構(gòu)的MFC程序中,提供了文檔模板管理者類CDocManager,由它管理應(yīng)用程序所包含的文檔模板。我們先看看這個類的聲明:

/////////////////////////////////////////////////////////////////////////////
// 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的設(shè)計者在MFC的實現(xiàn)中大量使用了鏈表這種數(shù)據(jù)結(jié)構(gòu)),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是對鏈表結(jié)構(gòu)體
struct CNode
{
 CNode* pNext; 
 CNode* pPrev;
 void* data;
};

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

  作為一個抽象的鏈表類型,CPtrList并未定義其中節(jié)點的具體類型,而以一個void指針(struct CNode 中的void* data)巧妙地實現(xiàn)了鏈表節(jié)點成員具體類型的"模板"化。很顯然,在Visual C++6.0開發(fā)的年代,C++語言所具有的語法特征"模板"仍然沒有得到廣泛的應(yīng)用。
而CDocManager類的成員函數(shù)

virtual void AddDocTemplate(CDocTemplate* pTemplate);
virtual POSITION GetFirstDocTemplatePosition() const;
virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const;

  則完成對m_TemplateList鏈表的添加及遍歷操作的封裝,我們來看看這三個函數(shù)的源代碼:

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);
}

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

1、文檔類CDocument

  在"文檔/視圖"架構(gòu)的MFC程序中,文檔是一個CDocument派生對象,它負責(zé)存儲應(yīng)用程序的數(shù)據(jù),并把這些信息提供給應(yīng)用程序的其余部分。CDocument類對文檔的建立及歸檔提供支持并提供了應(yīng)用程序用于控制其數(shù)據(jù)的接口,類CDocument的聲明如下:

/////////////////////////////////////////////////////////////////////////////
// class CDocument is the main document data abstraction
class CDocument : public CCmdTarget
{
 DECLARE_DYNAMIC(CDocument) 
public:
 // Constructors
 CDocument();

 // Attributes
public:
 const CString& GetTitle() const;
 virtual void SetTitle(LPCTSTR lpszTitle);
 const CString& GetPathName() const;
 virtual void SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU = TRUE);

 CDocTemplate* GetDocTemplate() const;
 virtual BOOL IsModified();
 virtual void SetModifiedFlag(BOOL bModified = TRUE);

 // Operations
 void AddView(CView* pView);
 void RemoveView(CView* pView);
 virtual POSITION GetFirstViewPosition() const;
 virtual CView* GetNextView(POSITION& rPosition) const;

 // Update Views (simple update - DAG only)
 void UpdateAllViews(CView* pSender, LPARAM lHint = 0L,
 CObject* pHint = NULL);

 // Overridables
 // Special notifications
 virtual void OnChangedViewList(); // after Add or Remove view
 virtual void DeleteContents(); // delete doc items etc

 // File helpers
 virtual BOOL OnNewDocument();
 virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
 virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);
 virtual void OnCloseDocument();
 virtual void ReportSaveLoadException(LPCTSTR lpszPathName,
 CException* e, BOOL bSaving, UINT nIDPDefault);
 virtual CFile* GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,
 CFileException* pError);
 virtual void ReleaseFile(CFile* pFile, BOOL bAbort);

 // advanced overridables, closing down frame/doc, etc.
 virtual BOOL CanCloseFrame(CFrameWnd* pFrame);
 virtual BOOL SaveModified(); // return TRUE if ok to continue
 virtual void PreCloseFrame(CFrameWnd* pFrame);

 // Implementation
protected:
 // default implementation
 CString m_strTitle;
 CString m_strPathName;
 CDocTemplate* m_pDocTemplate;
 CPtrList m_viewList; // list of views
 BOOL m_bModified; // changed since last saved

public:
 BOOL m_bAutoDelete; // TRUE => delete document when no more views
 BOOL m_bEmbedded; // TRUE => document is being created by OLE

 #ifdef _DEBUG
  virtual void Dump(CDumpContext&) const;
  virtual void AssertValid() const;
 #endif //_DEBUG
 virtual ~CDocument();

 // implementation helpers
 virtual BOOL DoSave(LPCTSTR lpszPathName, BOOL bReplace = TRUE);
 virtual BOOL DoFileSave();
 virtual void UpdateFrameCounts();
 void DisconnectViews();
 void SendInitialUpdate();

 // overridables for implementation
 virtual HMENU GetDefaultMenu(); // get menu depending on state
 virtual HACCEL GetDefaultAccelerator();
 virtual void OnIdle();
 virtual void OnFinalRelease();

 virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo);
 friend class CDocTemplate;

protected:
 // file menu commands
 //{{AFX_MSG(CDocument)
  afx_msg void OnFileClose();
  afx_msg void OnFileSave();
  afx_msg void OnFileSaveAs();
 //}}AFX_MSG
 // mail enabling
 afx_msg void OnFileSendMail();
 afx_msg void OnUpdateFileSendMail(CCmdUI* pCmdUI);
 DECLARE_MESSAGE_MAP()
};

  一個文檔可以有多個視圖,每一個文檔都維護一個與之相關(guān)視圖的鏈表(CptrList類型的 m_viewList實例)。CDocument::AddView將一個視圖連接到文檔上,并將視圖的文檔指針指向該文檔:

void CDocument::AddView(CView* pView)
{
 ASSERT_VALID(pView);
 ASSERT(pView->m_pDocument == NULL); // must not be already attached
 ASSERT(m_viewList.Find(pView, NULL) == NULL); // must not be in list

 m_viewList.AddTail(pView);
 ASSERT(pView->m_pDocument == NULL); // must be un-attached
 pView->m_pDocument = this;

 OnChangedViewList(); // must be the last thing done to the document
}

  CDocument::RemoveView則完成與CDocument::AddView相反的工作:

void CDocument::RemoveView(CView* pView)
{
 ASSERT_VALID(pView);
 ASSERT(pView->m_pDocument == this); // must be attached to us

 m_viewList.RemoveAt(m_viewList.Find(pView));
 pView->m_pDocument = NULL;

 OnChangedViewList(); // must be the last thing done to the document
}

  從CDocument::AddView和CDocument::RemoveView函數(shù)可以看出,在與文檔關(guān)聯(lián)的視圖被移走或新加入時CDocument::OnChangedViewList將被調(diào)用:

void CDocument::OnChangedViewList()
{
 // if no more views on the document, delete ourself
 // not called if directly closing the document or terminating the app
 if (m_viewList.IsEmpty() && m_bAutoDelete)
 {
  OnCloseDocument();
  return;
 }

 // update the frame counts as needed
 UpdateFrameCounts();
}

  CDocument::DisconnectViews將所有的視圖都與文檔"失連":

void CDocument::DisconnectViews()
{
 while (!m_viewList.IsEmpty())
 {
  CView* pView = (CView*)m_viewList.RemoveHead();
  ASSERT_VALID(pView);
  ASSERT_KINDOF(CView, pView);
  pView->m_pDocument = NULL;
 }
}

  實際上,類CDocument對視圖的管理與類CDocManager對文檔模板的管理及CDocTemplate對文檔的管理非常類似,少不了的,類CDocument中可遍歷對應(yīng)的視圖(出現(xiàn)GetFirstXXX和GetNextXXX兩個函數(shù)):

POSITION CDocument::GetFirstViewPosition() const
{
 return m_viewList.GetHeadPosition();
}

CView* CDocument::GetNextView(POSITION& rPosition) const
{
 ASSERT(rPosition != BEFORE_START_POSITION);
 // use CDocument::GetFirstViewPosition instead !
 if (rPosition == NULL)
  return NULL; // nothing left
 CView* pView = (CView*)m_viewList.GetNext(rPosition);
 ASSERT_KINDOF(CView, pView);
 return pView;
}

  CDocument::GetFile和CDocument::ReleaseFile函數(shù)完成對參數(shù)lpszFileName指定文檔的打開與關(guān)閉操作:

CFile* CDocument::GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError)
{
 CMirrorFile* pFile = new CMirrorFile;
 ASSERT(pFile != NULL);
 if (!pFile->Open(lpszFileName, nOpenFlags, pError))
 {
  delete pFile;
  pFile = NULL;
 }
 return pFile;
}

void CDocument::ReleaseFile(CFile* pFile, BOOL bAbort)
{
 ASSERT_KINDOF(CFile, pFile);
 if (bAbort)
  pFile->Abort(); // will not throw an exception
 else
  pFile->Close();
 delete pFile;
}

  CDocument類的OnNewDocument、OnOpenDocument、OnSaveDocument及OnCloseDocument這一組成員函數(shù)用于創(chuàng)建、打開、保存或關(guān)閉一個文檔。在這一組函數(shù)中,上面的CDocument::GetFile和CDocument::ReleaseFile兩個函數(shù)得以調(diào)用:

BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
{
 if (IsModified())
  TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");

 CFileException fe;
 CFile* pFile = GetFile(lpszPathName,
 CFile::modeRead|CFile::shareDenyWrite, &fe);
 if (pFile == NULL)
 {
  ReportSaveLoadException(lpszPathName, &fe,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
  return FALSE;
 }

 DeleteContents();
 SetModifiedFlag(); // dirty during de-serialize

 CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);
 loadArchive.m_pDocument = this;
 loadArchive.m_bForceFlat = FALSE;
 TRY
 {
  CWaitCursor wait;
  if (pFile->GetLength() != 0)
   Serialize(loadArchive); // load me
   loadArchive.Close();
   ReleaseFile(pFile, FALSE);
 }
 CATCH_ALL(e)
 {
  ReleaseFile(pFile, TRUE);
  DeleteContents(); // remove failed contents

  TRY
  {
   ReportSaveLoadException(lpszPathName, e,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
  }
  END_TRY
  DELETE_EXCEPTION(e);
  return FALSE;
 }
 END_CATCH_ALL

 SetModifiedFlag(FALSE); // start off with unmodified

 return TRUE;
}


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

視圖類CView

  在MFC"文檔/視圖"架構(gòu)中,CView類是所有視圖類的基類,它提供了用戶自定義視圖類的公共接口。在"文檔/視圖"架構(gòu)中,文檔負責(zé)管理和維護數(shù)據(jù);而視圖類則負責(zé)如下工作:

 ?。?) 從文檔類中將文檔中的數(shù)據(jù)取出后顯示給用戶;

 ?。?) 接受用戶對文檔中數(shù)據(jù)的編輯和修改;

  (3) 將修改的結(jié)果反饋給文檔類,由文檔類將修改后的內(nèi)容保存到磁盤文件中。

  文檔負責(zé)了數(shù)據(jù)真正在永久介質(zhì)中的存儲和讀取工作,視圖呈現(xiàn)只是將文檔中的數(shù)據(jù)以某種形式向用戶呈現(xiàn),因此一個文檔可對應(yīng)多個視圖。

  下面我們來看看CView類的聲明:

class CView : public CWnd
{
 DECLARE_DYNAMIC(CView) 
 // Constructors
protected:
 CView();

 // Attributes
public:
 CDocument* GetDocument() const;

 // Operations
public:
 // for standard printing setup (override OnPreparePrinting)
 BOOL DoPreparePrinting(CPrintInfo* pInfo);

 // Overridables
public:
 virtual BOOL IsSelected(const CObject* pDocItem) const; // support for OLE

 // OLE scrolling support (used for drag/drop as well)
 virtual BOOL OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll = TRUE);
 virtual BOOL OnScrollBy(CSize sizeScroll, BOOL bDoScroll = TRUE);

 // OLE drag/drop support
 virtual DROPEFFECT OnDragEnter(COleDataObject* pDataObject,DWORD dwKeyState, CPoint point);
 virtual DROPEFFECT OnDragOver(COleDataObject* pDataObject,DWORD dwKeyState, CPoint point);
 virtual void OnDragLeave();
 virtual BOOL OnDrop(COleDataObject* pDataObject,DROPEFFECT dropEffect, CPoint point);
 virtual DROPEFFECT OnDropEx(COleDataObject* pDataObject,
 DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point);
 virtual DROPEFFECT OnDragScroll(DWORD dwKeyState, CPoint point);

 virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = NULL);

 virtual void OnInitialUpdate(); // called first time after construct

protected:
 // Activation
 virtual void OnActivateView(BOOL bActivate, CView* pActivateView,CView* pDeactiveView);
 virtual void OnActivateFrame(UINT nState, CFrameWnd* pFrameWnd);

 // General drawing/updating
 virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint);
 virtual void OnDraw(CDC* pDC) = 0;

 // Printing support
 virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
 // must override to enable printing and print preview

 virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
 virtual void OnPrint(CDC* pDC, CPrintInfo* pInfo);
 virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);

 // Advanced: end print preview mode, move to point
 virtual void OnEndPrintPreview(CDC* pDC, CPrintInfo* pInfo, POINT point,CPreviewView* pView);

 // Implementation
public:
 virtual ~CView();
 #ifdef _DEBUG
  virtual void Dump(CDumpContext&) const;
  virtual void AssertValid() const;
 #endif //_DEBUG

 // Advanced: for implementing custom print preview
 BOOL DoPrintPreview(UINT nIDResource, CView* pPrintView,CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState);

 virtual void CalcWindowRect(LPRECT lpClientRect,UINT nAdjustType = adjustBorder);
 virtual CScrollBar* GetScrollBarCtrl(int nBar) const;
 static CSplitterWnd* PASCAL GetParentSplitter(const CWnd* pWnd, BOOL bAnyState);

protected:
 CDocument* m_pDocument;

public:
 virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo);
protected:
 virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
 virtual void PostNcDestroy();

 // friend classes that call protected CView overridables
 friend class CDocument;
 friend class CDocTemplate;
 friend class CPreviewView;
 friend class CFrameWnd;
 friend class CMDIFrameWnd;
 friend class CMDIChildWnd;
 friend class CSplitterWnd;
 friend class COleServerDoc;
 friend class CDocObjectServer;

 //{{AFX_MSG(CView)
  afx_msg int OnCreate(LPCREATESTRUCT lpcs);
  afx_msg void OnDestroy();
  afx_msg void OnPaint();
  afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message);
  // commands
  afx_msg void OnUpdateSplitCmd(CCmdUI* pCmdUI);
  afx_msg BOOL OnSplitCmd(UINT nID);
  afx_msg void OnUpdateNextPaneMenu(CCmdUI* pCmdUI);
  afx_msg BOOL OnNextPaneCmd(UINT nID);

  // not mapped commands - must be mapped in derived class
  afx_msg void OnFilePrint();
  afx_msg void OnFilePrintPreview();
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
};

  CView類首先要維護文檔與視圖之間的關(guān)聯(lián),它通過CDocument* m_pDocument保護性成員變量記錄關(guān)聯(lián)文檔的指針,并提供CView::GetDocument接口函數(shù)以使得應(yīng)用程序可得到與視圖關(guān)聯(lián)的文檔。而在CView類的析構(gòu)函數(shù)中,需將對應(yīng)文檔類視圖列表中的本視圖刪除:

CView::~CView()
{
 if (m_pDocument != NULL)
  m_pDocument->RemoveView(this);
}

  CView中地位最重要的函數(shù)是virtual void OnDraw(CDC* pDC) = 0;從這個函數(shù)的聲明可以看出,CView是一個純虛基類。這個函數(shù)必須被重載,它通常執(zhí)行如下步驟:

  (1) 以GetDocument()函數(shù)獲得視圖對應(yīng)文檔的指針;

 ?。?) 讀取對應(yīng)文檔中的數(shù)據(jù);

 ?。?) 顯示這些數(shù)據(jù)。

  以MFC向?qū)Ы⒌囊粋€初始"文檔/視圖"架構(gòu)工程將這樣重載OnDraw()函數(shù),注意注釋中的"add draw code for native data here(添加活動數(shù)據(jù)的繪制代碼)":

/////////////////////////////////////////////////////////////////////////////
// CExampleView drawing
void CExampleView::OnDraw(CDC* pDC)
{
 CExampleDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here
}
CView::PreCreateWindow負責(zé)View的初始化:
/////////////////////////////////////////////////////////////////////////////
// CView second phase construction - bind to document
BOOL CView::PreCreateWindow(CREATESTRUCT & cs)
{
 ASSERT(cs.style & WS_CHILD);

 if (cs.lpszClass == NULL)
 {
  VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
  cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
 }

 if (afxData.bWin4 && (cs.style & WS_BORDER))
 {
  cs.dwExStyle |= WS_EX_CLIENTEDGE;
  cs.style &= ~WS_BORDER;
 }

 return TRUE;
}

  CView::OnUpdate函數(shù)在文檔的數(shù)據(jù)被改變的時候被調(diào)用(即它被用來通知一個視圖的關(guān)聯(lián)文檔的內(nèi)容已經(jīng)被修改),它預(yù)示著我們需要重新繪制視圖以顯示變化后的數(shù)據(jù)。其中的Invalidate(TRUE)將整個窗口設(shè)置為需要重繪的無效區(qū)域,它會產(chǎn)生WM_PAINT消息,這樣OnDraw將被調(diào)用:

void CView::OnUpdate(CView* pSender, LPARAM /*lHint*/, CObject* /*pHint*/)
{
 ASSERT(pSender != this);
 UNUSED(pSender); // unused in release builds

 // invalidate the entire pane, erase background too
 Invalidate(TRUE);
}

  假如文檔中的數(shù)據(jù)發(fā)生了變化,必須通知所有鏈接到該文檔的視圖,這時候文檔類的UpdateAllViews函數(shù)需要被調(diào)用。

  此外,CView類包含一系列函數(shù)用于進行文檔的打印及打印預(yù)覽工作:

 ?。?)CView::OnBeginPrinting在打印工作開始時被調(diào)用,用來分配GDI資源;

  (2)CView::OnPreparePrinting函數(shù)在文檔打印或者打印預(yù)覽前被調(diào)用,可用來初始化打印對話框;

 ?。?)CView::OnPrint用來打印或打印預(yù)覽文檔;

 ?。?)CView::OnEndPrinting函數(shù)在打印工作結(jié)束時被調(diào)用,用以釋放GDI資源;

  (5)CView::OnEndPrintPreview在退出打印預(yù)覽模式時被調(diào)用。

  CView派生類

  MFC提供了豐富的CView派生類,各種不同的派生類實現(xiàn)了對不同種類控件的支持,以為用戶提供多元化的顯示界面。這些CView派生類包括:

  (1)CScrollView:提供滾動支持;

  (2)CCtrlView:支持tree、 list和rich edit控件;

  (3)CDaoRecordView:在dialog-box控件中顯示數(shù)據(jù)庫記錄;

  (4)CEditView:提供了一個簡單的多行文本編輯器;

  (5)CFormView:包含dialog-box控件,可滾動,基于對話框模板資源; 

  (6)CListView:支持list控件;

  (7)CRecordView:在dialog-box控件中顯示數(shù)據(jù)庫記錄;

  (8)CRichEditView:支持rich edit控件;

  (9)CTreeView:支持tree控件。

  其中,CRichEditView、CTreeView及CListView均繼承自CCtrlView類;CFormView繼承自CScrollView類;CRecordView、CDaoRecordView則進一步繼承自CFormView類。

  下圖描述了CView類體系的繼承關(guān)系:



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

從前文可知,在MFC中,文檔是真正的數(shù)據(jù)載體,視圖是文檔的顯示界面,對應(yīng)同一個文檔,可能存在多個視圖界面,我們需要另外一種東東來將這些界面管理起來,這個東東就是框架。

  MFC創(chuàng)造框架類的初衷在于:把界面管理工作獨立出來!框架窗口為應(yīng)用程序的用戶界面提供結(jié)構(gòu)框架,它是應(yīng)用程序的主窗口,負責(zé)管理其包容的窗口。一個應(yīng)用程序啟動時會創(chuàng)建一個最頂層的框架窗口。

  MFC提供二種類型的框架窗口:單文檔窗口SDI和多文檔窗口MDI(你可以認為對話框是另一種框架窗口)。單文檔窗口一次只能打開一個文檔框架窗口,而多文檔窗口應(yīng)用程序中可以打開多個文檔框架窗口,即子窗口(Child Window)。這些子窗口中的文檔可以為同種類型,也可以為不同類型。

  在Visual C++ AppWizard的第一個對話框中,會讓用戶選擇應(yīng)用程序是基于單文檔、多文檔還是基于對話框的,如圖5.1。


圖5.1 在AppWizard中選擇框架窗口

  MFC提供了三個類CFrameWnd、CMDIFrameWnd、CMDIChildWnd用于支持單文檔窗口和多文檔窗口,這些類的層次結(jié)構(gòu)如圖5.2。


圖5.2 CFrameWnd、CMDIFrameWnd、CMDIChildWnd類的層次

  (1)CFrameWnd類用于SDI應(yīng)用程序的框架窗口,SDI框架窗口既是應(yīng)用程序的主框架窗口,也是當(dāng)前文檔對應(yīng)的視圖的邊框;CFrameWnd類也作為CMDIFrameWnd和CMDIChildWnd類的父類,而在基于SDI的應(yīng)用程序中,AppWizard會自動為我們添加一個繼承自CFrameWnd類的CMainFrame類。

  CFrameWnd類中重要的函數(shù)有Create(用于創(chuàng)建窗口)、LoadFrame(用于從資源文件中創(chuàng)建窗口)、PreCreateWindow(用于注冊窗口類)等。Create函數(shù)第一個參數(shù)為窗口注冊類名,第二個參數(shù)為窗口標題,其余幾個參數(shù)指定了窗口的風(fēng)格、大小、父窗口、菜單名等,其源代碼如下:

BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext *pContext)
{
 HMENU hMenu = NULL;
 if (lpszMenuName != NULL)
 {
  // load in a menu that will get destroyed when window gets destroyed
  HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);
  if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
  {
   TRACE0("Warning: failed to load menu for CFrameWnd.\n");
   PostNcDestroy(); // perhaps delete the C++ object
   return FALSE;
  }
 } 
 m_strTitle = lpszWindowName; // save title for later

 if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left,
rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd
->GetSafeHwnd(), hMenu, (LPVOID)pContext))
 {
  TRACE0("Warning: failed to create CFrameWnd.\n");
  if (hMenu != NULL)
   DestroyMenu(hMenu);
  return FALSE;
 }
 return TRUE;
}

  LoadFrame函數(shù)用于從資源文件中創(chuàng)建窗口,我們通常只需要給其指定一個參數(shù),LoadFrame使用該參數(shù)從資源中獲取主邊框窗口的標題、圖標、菜單、加速鍵等,其源代碼為:

BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd
*pParentWnd, CCreateContext *pContext)
{
 // only do this once
 ASSERT_VALID_IDR(nIDResource);
 ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);

 m_nIDHelp = nIDResource; // ID for help context (+HID_BASE_RESOURCE)

 CString strFullString;
 if (strFullString.LoadString(nIDResource))
  AfxExtractSubString(m_strTitle, strFullString, 0);
  // first sub-string

 VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

 // attempt to create the window
 LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
 LPCTSTR lpszTitle = m_strTitle;
 if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd,
MAKEINTRESOURCE(nIDResource), 0L, pContext))
 {
  return FALSE; // will self destruct on failure normally
 }

 // save the default menu handle
 ASSERT(m_hWnd != NULL);
 m_hMenuDefault = ::GetMenu(m_hWnd);

 // load accelerator resource
 LoadAccelTable(MAKEINTRESOURCE(nIDResource));

 if (pContext == NULL)
  // send initial update
  SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);

 return TRUE;
}

  在SDI程序中,如果需要修改窗口的默認風(fēng)格,程序員需要修改CMainFrame類的PreCreateWindow函數(shù),因為AppWizard給我們生成的CMainFrame::PreCreateWindow僅對其基類的PreCreateWindow函數(shù)進行調(diào)用,CFrameWnd::PreCreateWindow的源代碼如下:

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT &cs)
{
 if (cs.lpszClass == NULL)
 {
  VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
  cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
 }

 if ((cs.style &FWS_ADDTOTITLE) && afxData.bWin4)cs.style |= FWS_PREFIXTITLE;

 if (afxData.bWin4)
  cs.dwExStyle |= WS_EX_CLIENTEDGE;
 return TRUE;
}


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


1、模板、文檔、視圖、框架的關(guān)系

  連載1~5我們各個擊破地講解了文檔、文檔模板、視圖和框架類,連載1已經(jīng)強調(diào)這些類有著親密的內(nèi)部聯(lián)系,總結(jié)1~5我們可以概括其聯(lián)系為:

 ?。?)文檔保留該文檔的視圖列表和指向創(chuàng)建該文檔的文檔模板的指針;文檔至少有一個相關(guān)聯(lián)的視圖,而視圖只能與一個文檔相關(guān)聯(lián)。

 ?。?)視圖保留指向其文檔的指針,并被包含在其父框架窗口中;

 ?。?)文檔框架窗口(即包含視圖的MDI子窗口)保留指向其當(dāng)前活動視圖的指針;

 ?。?)文檔模板保留其已打開文檔的列表,維護框架窗口、文檔及視圖的映射;

  (5)應(yīng)用程序保留其文檔模板的列表。 

  我們可以通過一組函數(shù)讓這些類之間相互可訪問,表6-1給出這些函數(shù)。

  表6-1 文檔、文檔模板、視圖和框架類的互相訪問

從該對象如何訪問其他對象
全局函數(shù)調(diào)用全局函數(shù)AfxGetApp可以得到CWinApp應(yīng)用類指針
應(yīng)用AfxGetApp()->m_pMainWnd為框架窗口指針;用CWinApp::GetFirstDocTemplatePostion、CWinApp::GetNextDocTemplate來遍歷所有文檔模板
文檔調(diào)用CDocument::GetFirstViewPosition,CDocument::GetNextView來遍歷所有和文檔關(guān)聯(lián)的視圖;調(diào)用CDocument:: GetDocTemplate 獲取文檔模板指針
文檔模板調(diào)用CDocTemplate::GetFirstDocPosition、CDocTemplate::GetNextDoc來遍歷所有對應(yīng)文檔
視圖調(diào)用CView::GetDocument 得到對應(yīng)的文檔指針; 調(diào)用CView::GetParentFrame 獲取框架窗口
文檔框架窗口調(diào)用CFrameWnd::GetActiveView 獲取當(dāng)前得到當(dāng)前活動視圖指針; 調(diào)用CFrameWnd::GetActiveDocument 獲取附加到當(dāng)前視圖的文檔指針
MDI 框架窗口調(diào)用CMDIFrameWnd::MDIGetActive 獲取當(dāng)前活動的MDI子窗口(CMDIChildWnd)

  我們列舉一個例子,綜合應(yīng)用上表中的函數(shù),寫一段代碼,它完成遍歷文檔模板、文檔和視圖的功能:

CMyApp *pMyApp = (CMyApp*)AfxGetApp(); //得到應(yīng)用程序指針
POSITION p = pMyApp->GetFirstDocTemplatePosition();//得到第1個文檔模板
while (p != NULL) //遍歷文檔模板
{
 CDocTemplate *pDocTemplate = pMyApp->GetNextDocTemplate(p);
 POSITION p1 = pDocTemplate->GetFirstDocPosition();//得到文檔模板對應(yīng)的第1個文檔
 while (p1 != NULL) //遍歷文檔模板對應(yīng)的文檔
 {
  CDocument *pDocument = pDocTemplate->GetNextDoc(p1);
  POSITION p2 = pDocument->GetFirstViewPosition(); //得到文檔對應(yīng)的第1個視圖
  while (p2 != NULL) //遍歷文檔對應(yīng)的視圖
  {
   CView *pView = pDocument->GetNextView(p2);
  }
 }
}

  由此可見,下面的管理關(guān)系和實現(xiàn)途徑都是完全類似的:

  (1)應(yīng)用程序之于文檔模板;

  (2)文檔模板之于文檔;

 ?。?)文檔之于視圖。

  圖6.1、6.2分別給出了一個多文檔/視圖框架MFC程序的組成以及其中所包含類的層次關(guān)系。


圖6.1 多文檔/視圖框架MFC程序的組成


圖6.2 文檔/視圖框架程序類的層次關(guān)系

  關(guān)于文檔和視圖的關(guān)系,我們可進一步細分為三類:

 ?。?)文檔對應(yīng)多個相同的視圖對象,每個視圖對象在一個單獨的 MDI 文檔框架窗口中;

 ?。?)文檔對應(yīng)多個相同類的視圖對象,但這些視圖對象在同一文檔框架窗口中(通過"拆分窗口"即將單個文檔窗口的視圖空間拆分成多個單獨的文檔視圖實現(xiàn)); 

 ?。?)文檔對應(yīng)多個不同類的視圖對象,這些視圖對象僅在一個單獨的 MDI 文檔框架窗口中。在此模型中,由不同的類構(gòu)造成的多個視圖共享單個框架窗口,每個視圖可提供查看同一文檔的不同方式。例如,一個視圖以字處理模式顯示文檔,而另一個視圖則以"文檔結(jié)構(gòu)圖"模式顯示文檔。

  圖6.3顯示了對應(yīng)三種文檔與視圖關(guān)系應(yīng)用程序的界面特點。


圖6.3文檔/視圖的三種關(guān)系


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


為了能夠把我們所學(xué)的所有知識都在實例中得以完整的體現(xiàn),我們來寫一個盡可能復(fù)雜的"文檔/視圖"架構(gòu)MFC程序,這個程序復(fù)雜到:

 ?。?)是一個多文檔/視圖架構(gòu)MFC程序;

 ?。?)支持多種文件格式(假設(shè)支持擴展名為BMP的位圖和TXT的文本文件);

  (3)一個文檔(BMP格式)對應(yīng)多個不同類型的視圖(圖形和二進制數(shù)據(jù))。

  相信上述程序已經(jīng)是一個包含"最復(fù)雜"特性的"文檔/視圖"架構(gòu)MFC程序了,搞定了這個包羅萬象的程序,還有什么簡單的程序搞不定呢?

  用Visual C++工程向?qū)?chuàng)建一個名為"Example"的多文檔/視圖框架MFC程序,最初的應(yīng)用程序界面如圖7.1。


圖7.1 最初的Example工程界面

  這個時候的程序還不支持任何文檔格式,我們需讓它支持TXT(由于本文的目的是講解框架而非具體的讀寫文檔與顯示,故將程序簡化為只顯示包含一行的TXT文件)和BMP文件。

  定義IDR_TEXTTYPE、IDR_BMPTYPE宏,并在資源文件中增加對應(yīng)IDR_TEXTTYPE、IDR_BMPTYPE文檔格式的字符串:

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by EXAMPLE.RC
//
#define IDD_ABOUTBOX 100
#define IDR_MAINFRAME 128
//#define IDR_EXAMPLTYPE 129
#define IDR_TEXTTYPE 10001 
#define IDR_BMPTYPE 10002 

#endif 
STRINGTABLE PRELOAD DISCARDABLE 
BEGIN
IDR_MAINFRAME "Example"
IDR_EXAMPLTYPE "\nExampl\nExampl\n\n\nExample.Document\nExampl Document"
IDR_TEXTTYPE "\nTEXT\nTEXT\nExampl 文件 (*.txt)\n.txt\nTEXT\nTEXT Document"
IDR_BMPTYPE "\nBMP\nBMP\nExampl 文件 (*.bmp)\n.bmp\nBMP\nBMP Document"
END

  我們讓第一個文檔模板(由VC向?qū)桑?yīng)TXT格式,修改CExampleApp::InitInstance函數(shù):

BOOL CExampleApp::InitInstance()
{
 …
 CMultiDocTemplate* pDocTemplate;
 pDocTemplate = new CMultiDocTemplate(
  IDR_TEXTTYPE, //對應(yīng)文本文件的字符串
  RUNTIME_CLASS(CExampleDoc),
  RUNTIME_CLASS(CChildFrame), // custom MDI child frame
  RUNTIME_CLASS(CExampleView));
 AddDocTemplate(pDocTemplate);
 …
}

  為了讓程序支持TXT文件的讀取和顯示,我們需要重載CexampleDoc文檔類和CExampleView視圖類。因為從文檔模板new CMultiDocTemplate中的參數(shù)可以看出,CExampleDoc和CExampleView分別為對應(yīng)TXT文件的文檔類和視圖類:

class CExampleDoc : public CDocument
{
 …
 CString m_Text; //在文檔類中定義成員變量用于存儲TXT文件中的字符串
 …
}

//重載文檔類的Serialize,讀取字符串到m_Text中
void CExampleDoc::Serialize(CArchive& ar)
{
 if (ar.IsStoring())
 {
  // TODO: add storing code here
 }
 else
 {
  // TODO: add loading code here
  ar.ReadString(m_Text); 
 }
}
//重載視圖類的OnDraw函數(shù),顯示文檔中的字符串
/////////////////////////////////////////////////////////////////////////////
// CExampleView drawing
void CExampleView::OnDraw(CDC* pDC)
{
 CExampleDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here
 pDC->TextOut(0,0,pDoc->m_Text);
}

  這個時候的程序已經(jīng)支持TXT文件了,例如我們打開一個TXT文件,將出現(xiàn)如圖7.2的界面。


圖7.2 打開TXT文件的界面






本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
文檔視圖框架總結(jié)--My Blog Homepage
如何聯(lián)系CMainFrame和CView各自的成員變量? VC/MFC / 界面
文檔與視圖的相互作用
理解MFC文檔/視圖框架
MFC 中獲得各個類的指針/句柄 ID的總結(jié)(轉(zhuǎn))
MFC多文檔和單文檔視結(jié)構(gòu)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服