MFC之document与view实践总结
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实践总结的更多相关文章
- Android显示框架:自定义View实践之绘制篇
文章目录 一 View 二 Paint 2.1 颜色处理 2.2 文字处理 2.3 特殊处理 三 Canvas 3.1 界面绘制 3.2 范围裁切 3.3 集合变换 四 Path 4.1 添加图形 4 ...
- MFC + XToolKit的使用 ( 亲自实践 )
1. 变量声明: 在Dlg.h 的public下 CXTPButton m_Button2; 2. 变量交换并设置按钮风格: Dlg.cpp下 void CXT_VS2010Dlg::DoDat ...
- 深入分析MFC文档视图结构(项目实践)
k_eckel:http://www.mscenter.edu.cn/blog/k_eckel 文档视图结构(Document/View Architecture)是MFC的精髓,也是Observer ...
- MFC笔记
一.Win32基本程序概念 所有的windows程序都必须载入windows.h MFC程序都有一个Stdafx.h文件,它载入了MFC框架必须的文件. Windows程序以消息为基础,以事件驱动之. ...
- 深入浅出MFC——Document-View深入探讨(五)
1. MFC之所以为Application Framework,最重要的一个特征就是它能够将管理数据的程序代码和负责数据显示的程序代码分离开来,这种能力由MFC的Document/View提供.Doc ...
- 【笔记】《深入浅出MFC》第5章 总观Application Framework
凝聚性强.组织化强的类库就是Application Framework.一组合作无间的对象,彼此藉消息的流动而沟通,并且互相调用对方的函数以求完成任务,这就是Application Framework ...
- 关于获得MFC窗口其它类指针的方法(csdn)
转自:http://tieba.baidu.com/p/252804018 访问应用程序的其它类 获得CWinApp: -在CMainFrame,CChildFrame,CDocument,CView ...
- 《深入浅出MFC》– Document-View深入探讨
1.其实Document/View不是什么新东西,Xerox PARC实验室是这种观念的滥觞.它是Smalltalk环境中的关键性部分,在那里它被称为Model-View-Controller(MVC ...
- MFC深入浅出读书笔记第三部分1
第八章 Document-View 深入探讨(总结) 1.Document Document 在MFC 的CDocument 里头被具体化.CDocument 本身并无任何具体数据,它只是提供一个空壳 ...
随机推荐
- Android开发把项目打包成apk-(转)
做完一个Android项目之后,如何才能把项目发布到Internet上供别人使用呢?我们需要将自己的程序打包成Android安装包文件--APK(Android Package),其后缀名为" ...
- (46)C#注册表及读写
启动注册表:regedit 结构: 注册表一共有7个配置单元用regedit只能看到5个 HKEY_CLASSES_ROOT 包含系统上文件类型的细节(.txt,.doc)等.以及使用那些应用程序可以 ...
- Codeforces 961 D Pair Of Lines
题目描述 You are given nn points on Cartesian plane. Every point is a lattice point (i. e. both of its c ...
- springboot jetty替换tomcat
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...
- Java中常量定义在interface和class的区别(转)
最终结论:定义常量在interface和class中其实都行,关键是看你的设计和个人爱好. Java中interface中定义变量默认都是"public static final" ...
- uibutton去掉点击后背景有阴影的方法
1,将normal和highlight两种方式都设置上图片即可 UIButton *goback = [[UIButton alloc]initWithFrame:CGRectMake(5.0f, 5 ...
- 将一个文件从gbk编码转换为utf8编码
用django展示模板时,出现如下错误: 'utf8' codec can't decode byte 0xd3 in position 197: invalid continuation byte ...
- PPAPI+Skia实现的涂鸦板
在PPAPI插件中使用Skia画图介绍了怎样在PPAPI中使用Skia,文末说回头要提供一个简单的涂鸦板插件,这次我来兑现承诺了. foruok原创,关注微信订阅号"程序视界"可联 ...
- MRP Force Reservation的作用
生产单根据BOM计算出相应的物料需求,生产领料单stock.picking ( internal moves) Stock.picking使用工作流自动计算库存量,如果库存量够,则使用 test_as ...
- 图片3d轮放查看效果(V2.0):使用鼠标拖动实现图片的轮放
上面的版本号为通过左右button实现图片轮放,这个版本号.是通过在窗体拖动鼠标.左右滑动图片. 关键点在于选择一个合适的值.使鼠标拖动时.全部图片均可显示,可是不会滑动过快或离开窗体. 不多说,直接 ...