大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。
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(0,0,rc.Width(),rc.Height(), &MemDC,0,0,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, 1, 2); m_wndSplit1.CreateView(0,0,RUNTIME_CLASS(CTextView), CSize(300, 0), pContext); m_wndSplit2.CreateStatic(&m_wndSplit1, 2, 1, WS_CHILD|WS_VISIBLE, m_wndSplit1.IdFromRowCol(0,1)); m_wndSplit2.CreateView(0, 0, RUNTIME_CLASS(CBarView), CSize(0, 260), pContext); //m_wndSplit2.CreateView(1, 0, RUNTIME_CLASS(CGraphView), CSize(0, 0), pContext); // 为了不添加CGraphView头文件引起编译错误 m_wndSplit2.CreateView(1, 0, pContext->m_pNewViewClass, CSize(0, 0), pContext); SetActiveView((CView* )m_wndSplit1.GetPane(0,0)); return TRUE; }
5. 映射模式(SetMapMode)
具体的映射模式种类可以百度或参考映射模式之SetMapMode,下面只是总结SetMapMode(MM_ANISOTROPIC)的使用,使用MM_ANISOTROPIC并结合SetWindoworg、SetViewportorg、SetWindowExt和SetViewportExt我们可以自定义坐标系(比例和方向)
CRect rc; GetClientRect(&rc); // 设置坐标系 pDC->SetMapMode(MM_ANISOTROPIC); // 设置窗口原点 pDC->SetWindowOrg(0, 0); // 对应视口的右下角 pDC->SetViewportOrg(rc.left + 20, rc.bottom - 20); pDC->SetWindowExt(100, 100); pDC->SetViewportExt(rc.Width()- 40, -(rc.Height() - 40));
上面代码将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,纯为测试代码!
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120122.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...