首先總結下MFC的Document/View結構相關的知識點:
(1)在MFC中,文檔類負責管理數據,提供保存和加載數據的功能;視類負責數據的顯示,以及提供給用戶對數據編輯和修改功能。
(2)有關文件的讀寫操作在CDocument的Serialize函數中進行,有關數據和圖形顯示的操作在CView的OnDraw函數中進行,相關的函數都是虛函數,我們只需要在派生類中重寫這些函數即可。
(3)在單擊“文件/打開”菜單命令后,應用程序框架會激活“文件打開”對話框,讓用戶指定將要打開的文件名。然后程序自動調用文檔類的Serialize函數,完成數據的加載,同時還會調用視類的OnDraw函數,提供窗口重繪的機會。
(4)在應用程序啟動會創建文檔類對象,同時還創建了框架類對象和視類對象。這是MFC Doc/View的特定,即每當有一個文檔產生,總是會產生一個文檔類對象、框架類對象、視類對象,它們三位一個為這份文檔服務。
(5)對于一個文檔類對象來說,可以有多個視類對象與之相關;對一個視類對象來說,只有一個文檔類對象相關。
(6)若需要保存類對象數據,我們需要設計可串行化的類,數據的保存和加載實際上都是在可串行化的類對象中進行。
(7)可串行化的類均是派生于CObject類,在該類中有Serialize函數,我們可利用該函數做為串行化的入口。
(8)在可串行化的類中,我們“IMPLEMENT_SERIAL宏”會幫助我們在堆上申請對象,因此我們在提取類對象數據時,我們不用分配類對象空間,我們傳入該類的指針即可。
(9)在堆上申請的內存,需要我們手動去釋放,文檔類提供了DeleteContents虛函數,用于文檔對象數據的銷毀。在新建文件和打開文件都會調用這個DeleteContents函數。
(10)MFC為我們提供的文檔/視類/框架結構中,已經為我們設計好了CDoucment類,CView類,CMainFrame類這三個類的相互調用接口。
根據以上特點,我們將繪圖三要素作為數據,保存在文檔類中,在視類中完成圖形的顯示,這個功能基于“圖形重繪”這篇文件進行修改的,部分關鍵代碼如下:
Graph.h 中是圖形類的數據和操作方法
//(1)派生于CObject類class CGraph:public CObject{public: //(3)聲明中使用DECLARE_SERIAL宏 DECLARE_SERIAL(CGraph) //(4)定義無參的構造函數 CGraph(void); CGraph(CPoint ptBegin, CPoint ptEnd, int DrawType); ~CGraph(void); //(2)重寫Serialize成員函數 virtual void Serialize(CArchive& ar);public: enum { EN_RECT = 0, EN_ELLipSE, EN_LINE, }; void SetDrawType(int nType); void SetBeginPoint(CPoint ptBegin); void SetEndPont(CPoint ptEnd); CPoint GetBeginPoint(); CPoint GetEndPont(); int GetDrawType(); //new CGraph對象 CGraph* CreateGraphObj(); //完成畫圖功能 void DrawItem(CDC *pDC);PRivate: CPoint m_ptBegin; CPoint m_ptEnd; int m_DrawType;}; //Graph.cpp是實現部分#include "StdAfx.h"#include "Graph.h"//(5)實現文件中使用“IMPLEMENT_SERIAL”宏IMPLEMENT_SERIAL(CGraph, CObject, 1)CGraph::CGraph(void){ m_DrawType = 0;}CGraph::CGraph(CPoint ptBegin, CPoint ptEnd, int DrawType){ this->m_ptBegin = ptBegin; this->m_ptEnd = ptEnd; this->m_DrawType = DrawType;}CGraph::~CGraph(void){}//Call your base class version of Serialize to make sure that the inherited portion of the object is serialized. //Insert or extract the member variables specific to your class. void CGraph::Serialize(CArchive& ar){ // call base class function first // base class is CObject in this case CObject::Serialize(ar); // now do the stuff for our specific class if (ar.IsStoring()) { ar << m_DrawType << m_ptBegin << m_ptEnd; } else { ar >> m_DrawType >> m_ptBegin >> m_ptEnd; }}void CGraph::SetDrawType(int nType){ m_DrawType = nType;}void CGraph::SetBeginPoint(CPoint ptBegin){ m_ptBegin = ptBegin;}void CGraph::SetEndPont(CPoint ptEnd){ m_ptEnd = ptEnd;} CPoint CGraph::GetBeginPoint(){ return m_ptBegin;}CPoint CGraph::GetEndPont(){ return m_ptEnd;}int CGraph::GetDrawType(){ return m_DrawType;}CGraph* CGraph::CreateGraphObj(){ CGraph *pObj = NULL; try { pObj = new CGraph(m_ptBegin, m_ptEnd, m_DrawType); } catch (...) { return NULL; } return pObj;}//圖形繪制顯示功能void CGraph::DrawItem(CDC *pDC){ //開始繪圖操作流程 CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); CBrush* pOldBrush = pDC->SelectObject(pBrush); //畫圖選擇 switch (m_DrawType) { case CGraph::EN_RECT : { pDC->Rectangle(CRect(m_ptBegin, m_ptEnd)); break; } case CGraph::EN_LINE : { pDC->MoveTo(m_ptBegin); pDC->LineTo(m_ptEnd); break; } case CGraph::EN_ELLIPSE : { pDC->Ellipse(CRect(m_ptBegin, m_ptEnd)); break; } default: break; } pDC->SelectObject(pOldBrush);}//CGraphicview.cpp 主要是圖形數據保存和顯示 m_obArray是CObject類型,是文檔類的成員數據
/*****************************************************************函數名稱: *功 能:畫圖顯示到屏幕*作 者:Jin*日 期:2017年2月12日****************************************************************/void CGraphicView::OnDraw(CDC* pDC){ CGraphicDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; //窗口變化或者窗口生成時開始繪制歷史圖形 int nCount = pDoc->m_obArray.GetCount(); for (int i = 0; i < nCount; i++) { ((CGraph*)pDoc->m_obArray.GetAt(i))->DrawItem(pDC); }}/*****************************************************************函數名稱: 主要完成數據保存和顯示*功 能:*作 者:Jin*日 期:2017年2月12日****************************************************************/void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point){ // TODO: 在此添加消息處理程序代碼和/或調用默認值 m_GrahpInfo.SetEndPont(point); //顯示本次圖形,但是存在延遲效果不佳 //CClientDC dc(this); //m_GrahpInfo.DrawItem(&dc); //記錄本次畫圖數據 CGraph *pNewGraph = m_GrahpInfo.CreateGraphObj(); if (pNewGraph != NULL) { CGraphicDoc* pDoc = GetDocument(); if (pDoc!= NULL) { pDoc->m_obArray.Add(pNewGraph); } } //引發客戶窗口無效,發生重繪, //圖形能立即顯示,但大量數據情況下頻繁重繪效率下 Invalidate(TRUE); CView::OnLButtonUp(nFlags, point);}GraphicDoc.cpp主要是完成文檔數據的銷毀、數據加載和保存功能//重新新建或者打開文檔對象之前需要數據銷毀,防止堆溢出void CGraphicDoc::DeleteContents(){ int nCount = m_obArray.GetCount(); for (int i = 0; i < nCount; i++) { if (NULL != m_obArray.GetAt(i)) { //刪除指針對象的所指的內存 delete m_obArray.GetAt(i); m_obArray.SetAt(i, NULL); } } //清空數據 m_obArray.RemoveAll();}// CGraphicDoc 序列化void CGraphicDoc::Serialize(CArchive& ar){ CGraphicView *pView = NULL; CGraph *pGraph = NULL; POSITION pos = GetFirstViewPosition(); if (pos != NULL) { pView = (CGraphicView*)GetNextView(pos); } if (ar.IsStoring()) { } else { } //使用這種方式更加簡便 m_obArray.Serialize(ar);}運行結果:我們繪制一些圖案后,然后點擊保存,會有“文件保存”提示框,我們命名為GraphData.txt,關閉應用程序后;使用打擊工具欄的“打開”后,會有“文件打開對話”,我們選中GraphData.txt,界面上就會顯示我們之前繪制的圖案了,如下所示:

以上我們是使用MFC的文檔/視類結構以及串行化完成圖形重繪和保存功能。
新聞熱點
疑難解答