Document/View是MFC的基石,负责程序数据的管理和显示,Doculent和Viewd的关系有一档一视,一档多视和多档多视,下面将分别对实现过程中的重点知识进行总结。
  1. 视图的同步更新
  2. 序列化和反序列化
  3. 双缓存绘图
  4. CSplitterWnd视图切割
  5. 映射模式(SetMapMode)
  6. 使用CMDIFrameWnd::OnWindowNew实现多视图显示
  7. 多重文件类型

1. 视图的同步更新

  UpdateAllViews----->OnUpDate----->WM_PAINT------->OnDraw

  UpdateAllView(NULL) 表示更新所有视图

  同时也可以根据UpdateAllViews中的第二个第三个参数实现只更新需要重绘区域,提高效率

  UpdateAllViews源码如下:

void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)
// walk through all views
{
ASSERT(pSender == NULL || !m_viewList.IsEmpty());
// must have views if sent by one of them POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
if (pView != pSender)
pView->OnUpdate(pSender, lHint, pHint);
}
}

  重载OnUpdate,实现无效区域重绘代码

void CScribbleView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
// TODO: 在此添加专用代码和/或调用基类
if (pHint != NULL)
{
if (pHint->IsKindOf(RUNTIME_CLASS(CStroke)))
{
CStroke *pStroke = (CStroke *)pHint;
CClientDC dc(this);
OnPrepareDC(&dc);
CRect rectInvalid = pStroke->GetBoundingRect();
dc.LPtoDP(&rectInvalid);
InvalidateRect(&rectInvalid);
return;
}
}return;
}

  OnDraw关键代码

void CScribbleView::OnDraw(CDC* pDC)
{
CScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return; // 得到窗口的无效区域
CRect rectClip;
pDC->GetClipBox(&rectClip);
  // 得到已变化的区域
  rectStroke = pStroke->GetBoundingRect();
  // 判断两区域是否有交集,有则更新视图
  if (!rectStroke.IntersectRect(&rectStroke, &rectClip)){continue;}
}

2. 序列化和反序列化

  Serialize机制作为MFC六大关键技术之一,实现对象、资料的序列化和反序列化

  所有派生于CObject的类都拥有RunTimeClass、IsKindof和IsSerialBle功能,Serialize机制的具体实现这里不作说明,只介绍如何使用。

  继承于CObject的类若要具备Serialize机制需具备以下条件:

  (1)类必须要有默认的无参数构造函数

  (2)类的声明中添加DECLARE_SERIAL(CNewClass)

  (3) 类的实现中添加IMPLEMENT_SERIAL(CNewClass, CObject, 1),注意第三个参数为版本号,版本号如为0XFFFF,则无Serialize

  (4) 类中重载Serialize(CArchive &ar)虚函数

void CStroke::Serialize(CArchive &ar)
{
if (ar.IsStoring()) // 写文件
{
ar << a;
}
else  // 读文件
{
ar >> a;
}
}

3. 双缓存绘图,防止闪烁

(1)在OnDraw函数中创建内存DC,在内存DC上绘图,然后再将内存DC通过BitBlt或StrechBlt贴到原DC上

(2)重写OnEraseBkgnd虚函数,return TRUE;

CRect rc;
GetClientRect(&rc);
CDC MemDC;
MemDC.CreateCompatibleDC(pDC);
CBitmap BitMap;
BitMap.CreateCompatibleBitmap(pDC,rc.Width(),rc.Height());
CBitmap* pOldBitMap = MemDC.SelectObject(&BitMap);
MemDC.FillSolidRect(rc, pDC->GetBkColor());
.....绘图
pDC->BitBlt(,,rc.Width(),rc.Height(), &MemDC,,,SRCCOPY);
MemDC.DeleteDC();
BitMap.DeleteObject();
BOOL CScribbleView::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//return CView::OnEraseBkgnd(pDC);
return TRUE;
}

4. CSplitterWnd视图切割

(1)视图创建过程

  从MFC源码看出View产生的整个流程如下:

  OpenDocumentFile----->CreateNewDocument----->CreateNewFrame----->LoadFrame

  LoadFrame的最后一个参数为CCreateContext

struct CCreateContext   // Creation information structure
// All fields are optional and may be NULL
{
// for creating new views
CRuntimeClass* m_pNewViewClass; // runtime class of view to create or NULL
CDocument* m_pCurrentDoc; // for creating MDI children (CMDIChildWnd::LoadFrame)
CDocTemplate* m_pNewDocTemplate; // for sharing view/frame state from the original view/frame
CView* m_pLastView;
CFrameWnd* m_pCurrentFrame; // Implementation
CCreateContext();
};

  Document Frame窗口产生之际由于WM_CREATE的发生导致

  CFrameWnd::OnCreate------>CFrameWnd::OnCreateHelper----->CFrameWnd::OnCreateClient----->CFrameWnd::CreateView

(2)视图切割

  由上我们看出视图的产生在虚函数OnCreateClient中,所以进行视图的切割操作应在该函数中发生

// 重写OnCreateClient虚函数,创建视图
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
m_wndSplit1.CreateStatic(this, , );
m_wndSplit1.CreateView(,,RUNTIME_CLASS(CTextView), CSize(, ), pContext); m_wndSplit2.CreateStatic(&m_wndSplit1, , , WS_CHILD|WS_VISIBLE, m_wndSplit1.IdFromRowCol(,));
m_wndSplit2.CreateView(, , RUNTIME_CLASS(CBarView), CSize(, ), pContext);
//m_wndSplit2.CreateView(1, 0, RUNTIME_CLASS(CGraphView), CSize(0, 0), pContext); // 为了不添加CGraphView头文件引起编译错误
m_wndSplit2.CreateView(, , pContext->m_pNewViewClass, CSize(, ), pContext); SetActiveView((CView* )m_wndSplit1.GetPane(,));
return TRUE;
}

5. 映射模式(SetMapMode)

  具体的映射模式种类可以百度或参考映射模式之SetMapMode,下面只是总结SetMapMode(MM_ANISOTROPIC)的使用,使用MM_ANISOTROPIC并结合SetWindoworg、SetViewportorg、SetWindowExt和SetViewportExt我们可以自定义坐标系(比例和方向)

CRect rc;
GetClientRect(&rc);
// 设置坐标系
pDC->SetMapMode(MM_ANISOTROPIC);
// 设置窗口原点
pDC->SetWindowOrg(, );
// 对应视口的右下角
pDC->SetViewportOrg(rc.left + , rc.bottom - );
pDC->SetWindowExt(, );
pDC->SetViewportExt(rc.Width()- , -(rc.Height() - ));

  上面代码将CSize(100,100)映射到(rc.Width()- 40, -(rc.Height() - 40)上,坐标系如下:

  将逻辑坐标转换为设备坐标:LPtoDP

  将设备坐标转换为逻辑坐标:DPtoLP

6. 使用CMDIFrameWnd::OnWindowNew实现多视图显示

  如果你不喜欢分裂窗口,我们可以来点新鲜的,重写OnWindowNew函数

(1)添加新的CMultiDocTemplate

m_pTemplateTxt= new CMultiDocTemplate(IDR_TextTYPE,
RUNTIME_CLASS(CTextDoc),
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
RUNTIME_CLASS(CTextView));

m_pTemplateHex = new CMultiDocTemplate(IDR_TextTYPE,
      RUNTIME_CLASS(CTextDoc),
      RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
      RUNTIME_CLASS(CHexView));

(2)添加菜单项进行视图创建测试

void CMainFrame::OnTesta()
{
CMDIChildWnd *pActiveChild= MDIGetActive();
CDocument *pDocument;
if (pActiveChild == NULL
|| (pDocument = pActiveChild->GetActiveDocument()) == NULL)
{
return;
} CDocTemplate *pTemplate = ((CTextApp *)AfxGetApp())->m_pTemplateTxt;
if (pTemplate == NULL)
{
return;
} CFrameWnd *pFrame = pTemplate->CreateNewFrame(pDocument, pActiveChild);
if (pFrame == NULL)
{
return;
} pTemplate->InitialUpdateFrame(pFrame, pDocument);
}
// 关键点:重写CMDIFrameWnd::OnWindowNew创建视图
void CMainFrame::OnTest01()
{
CMDIChildWnd *pActiveChild = MDIGetActive();
CDocument *pDocument;
if (pActiveChild == NULL
|| (pDocument = pActiveChild->GetActiveDocument()) == NULL)
{
return;
} CDocTemplate *pTemplate = ((CTextApp *)AfxGetApp())->m_pTemplateHex;
if (pTemplate == NULL)
{
return;
} CFrameWnd *pFrame = pTemplate->CreateNewFrame(pDocument, pActiveChild);
if (pFrame == NULL)
{
return;
} pTemplate->InitialUpdateFrame(pFrame, pDocument);
}

(3)效果如下

7. 多重文件类型

  以下为在多文档视图中实现多文档类型:

(1)添加新建UI系统

CMultiDocTemplate(UINT nIDResource,CRuntimeClass* pDocClass,

CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);

  nIDResource是一个资源ID,表示此一文件类型(文件格式)所使用的资源。内容都在.rc文件中,包含ICON/MENU等等信息,可以自己查看

(2)添加新DOC

(3)添加新View,添加相应显示内容

(4)添加CMultiDocPlate

pDocTemplate = new CMultiDocTemplate(IDR_NEWTYPE,
RUNTIME_CLASS(CNewDoc),
RUNTIME_CLASS(CMDIChildWnd), // 自定义 MDI 子框架
RUNTIME_CLASS(CNewDocView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);

(5)效果

上述内容代码链接:https://pan.baidu.com/s/117eij5osBlopdjVdUbXT1A,纯为测试代码!

MFC之document与view实践总结的更多相关文章

  1. Android显示框架:自定义View实践之绘制篇

    文章目录 一 View 二 Paint 2.1 颜色处理 2.2 文字处理 2.3 特殊处理 三 Canvas 3.1 界面绘制 3.2 范围裁切 3.3 集合变换 四 Path 4.1 添加图形 4 ...

  2. MFC + XToolKit的使用 ( 亲自实践 )

    1. 变量声明: 在Dlg.h    的public下 CXTPButton m_Button2; 2. 变量交换并设置按钮风格: Dlg.cpp下 void CXT_VS2010Dlg::DoDat ...

  3. 深入分析MFC文档视图结构(项目实践)

    k_eckel:http://www.mscenter.edu.cn/blog/k_eckel 文档视图结构(Document/View Architecture)是MFC的精髓,也是Observer ...

  4. MFC笔记

    一.Win32基本程序概念 所有的windows程序都必须载入windows.h MFC程序都有一个Stdafx.h文件,它载入了MFC框架必须的文件. Windows程序以消息为基础,以事件驱动之. ...

  5. 深入浅出MFC——Document-View深入探讨(五)

    1. MFC之所以为Application Framework,最重要的一个特征就是它能够将管理数据的程序代码和负责数据显示的程序代码分离开来,这种能力由MFC的Document/View提供.Doc ...

  6. 【笔记】《深入浅出MFC》第5章 总观Application Framework

    凝聚性强.组织化强的类库就是Application Framework.一组合作无间的对象,彼此藉消息的流动而沟通,并且互相调用对方的函数以求完成任务,这就是Application Framework ...

  7. 关于获得MFC窗口其它类指针的方法(csdn)

    转自:http://tieba.baidu.com/p/252804018 访问应用程序的其它类 获得CWinApp: -在CMainFrame,CChildFrame,CDocument,CView ...

  8. 《深入浅出MFC》– Document-View深入探讨

    1.其实Document/View不是什么新东西,Xerox PARC实验室是这种观念的滥觞.它是Smalltalk环境中的关键性部分,在那里它被称为Model-View-Controller(MVC ...

  9. MFC深入浅出读书笔记第三部分1

    第八章 Document-View 深入探讨(总结) 1.Document Document 在MFC 的CDocument 里头被具体化.CDocument 本身并无任何具体数据,它只是提供一个空壳 ...

随机推荐

  1. ZOJ 3717 二分+2-sat判定。

    好久没有2-sat了,此题当复习之用,二分求最大值+2-sat判断可行,此题主要跪于题意:The results should be rounded to three decimal places. ...

  2. What should do in Production

    Using Compose in production https://docs.docker.com/compose/production/

  3. HDU 6149 Valley Numer II(状压DP)

    题目链接 HDU6149 百度之星复赛的题目……比赛的时候并没有做出来. 由于低点只有15个,所以我们可以考虑状压DP. 利用01背包的思想,依次考虑每个低点,然后枚举每个状态. 在每个状态里面任意枚 ...

  4. 心脏滴血漏洞复现(CVE-2014-0160)

    漏洞范围: OpenSSL 1.0.1版本 漏洞成因: Heartbleed漏洞是由于未能在memcpy()调用受害用户输入内容作为长度参数之前正确进 行边界检查.攻击者可以追踪OpenSSL所分配的 ...

  5. iOS 内存管理实践

    内存管理实践 尽管基本的概念在内存管理策略文章中简单得阐述了,但是还有一些实用的步骤让你更容易管理内存:有助于确保你的程序最大限度地减少资源需求的同时,保持可靠和强大. 使用“访问器方法”让内存管理更 ...

  6. 深度神经网络多任务学习(Multi-Task Learning in Deep Neural Networks)

    https://cloud.tencent.com/developer/article/1118159 http://ruder.io/multi-task/ https://arxiv.org/ab ...

  7. Intent传递简单对象与集合

    我们在Intent传递传递对象.能够有三种方式,实现Serializable接口.实现Parcelable接口,使用json格式序列化与反序列化. 在此我们使用第二方式,现实Parcelable接口, ...

  8. HDU 2825 Wireless Password (AC自己主动机,DP)

    pid=2825">http://acm.hdu.edu.cn/showproblem.php? pid=2825 Wireless Password Time Limit: 2000 ...

  9. scrollReveal 使用

    传统的layzload只能适用于图片懒加载,而我们现在需要的是全部元素的懒加载! 官网:https://scrollrevealjs.org/ gitHub:https://github.com/jl ...

  10. Microsoft Dynamics CRM Server 2013软件安装要求

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveV9mMTIz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...