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 本身并无任何具体数据,它只是提供一个空壳 ...
随机推荐
- CS Academy #32 G
题意: 分析: 考虑如何求方案数 dp[i][j]表示i个数字的和为j的方案数,这是个经典问题,转移有两种,一个是填一个数字1,一个是整体加1 然后这个问题并不是求方案数,而是求对应的权值和 我们很容 ...
- jinjia2模板学习
http://docs.jinkan.org/docs/jinja2/templates.html#
- poj2482--Stars in Your Window(扫描线)
题目链接:点击打开链接 链接题目大意:给出n个星星的坐标,每一个星星有一个亮度.给出一个矩形的长和宽,问矩形能包含的星星的最大亮度和(不包含边框). 如果每个星星都是矩形的最左下点.那么每个星星都能够 ...
- MBProgressHUD 显示方向异常
一直在iphone上使用MBProgressHUD做提示信息视图.一直都没有什么问题,但用在ipad上使用时.却有时会出现显示方向不正常.如ipad屏幕是横的,但当MBProgressHUD出现时却依 ...
- requirejs中的define
关于requirejs中的define的原理理解 我们已经了解到模块模式是为单例创建私有变量和特权方法的.一个最基本的例子: var foo=(function(){ var something= ...
- 创建一个zookeeper的会话(实现watcher)
在先前的章节中,我们利用zkCli去了解了一下主要的zookeeper的操作.在接下来的章节中,我们将会学习一下在应用中是怎样利用zookeeper的api的.接下来我们将利用一个程序展示一下,怎样来 ...
- 把握linux内核设计思想(五):下半部机制之工作队列及几种机制的选择
[版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 工作队列是下半部的第二种将工作推后运行形式.和软中断.task ...
- TCP连接建立与终止,及状态转换
TCP连接建立 三路握手 三路握手发生在客户端发起connect请求到服务端accept返回中,在三路握手发生前,服务端必须准备好接受外来连接,这通常通过服务端调用 (socket.bind.list ...
- PandoraBox 支持3G无线上网卡(联通卡3G卡)(一)
一:笔者采用的是系统是OpenWrt之PandoraBox,内核版本3.3.8:硬件设备是MTK的7620开发板. 其中怎么搭建openwrt开发环境在此不用多说,因为既然想实现3G无线上网卡拨号上网 ...
- Android安装应用后点击"打开"(Open)带来的问题及解决方案
MainActivity例如以下: package cc.cc; import android.app.Activity; import android.content.Intent; import ...