zoukankan      html  css  js  c++  java
  • 利用MFC Document/View框架完成图形显示和保存功能


    Document/View结构


    首先总结下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的文档/视类结构以及串行化完成图形重绘和保存功能。


  • 相关阅读:
    MS SQLSERVER 第三天
    MS SQLSERVER 第二天
    今天开始我的 MSSQLSERVER 之旅
    从今天开始就正式我的博客之旅
    mac 本地搭建mybatisGenerator代码生成环境
    idea中git远程版本回退
    Junit调试解决本地多线程异步调用
    Lambda表达式总结
    JDK8函数式编程之Stream API
    MySql分页查询慢的解决方案
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468329.html
Copyright © 2011-2022 走看看