MFC之document与view实践总结

1.视图的同步更新UpdateAllViews>OnUpDate>WM_PAINT>OnDrawUpdateAllView(NULL)表示更新所有视图同时也可以根据U

大家好,又见面了,我是全栈君,今天给大家准备了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之document与view实践总结

  从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();
};

MFC之document与view实践总结

  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;
}

 MFC之document与view实践总结

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)上,坐标系如下:

MFC之document与view实践总结

  将逻辑坐标转换为设备坐标: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)效果如下

MFC之document与view实践总结

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)效果

MFC之document与view实践总结

MFC之document与view实践总结

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120122.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(1)
blank

相关推荐

  • Chain of Responsibility职责链[通俗易懂]

    Chain of Responsibility职责链[通俗易懂]职责链 Chain of Responsibility动机模式定义实例结构要点总结笔记动机在软件构建过程中,一个请求可能被多个对象处理.但是每个请求在运行时只能有一个请求者.如果显示指定.将必不可少地带来请求发送者与接收者地紧耦合如何使请求地发送者不需要指定具体地接收者?让请求地接收者自己在运行时决定来处理请求,从而使两者解耦模式定义使多个对象都有机会处理请求 从而避免请求地发送者和接收者之间地耦合关系.将这些对象形成一条链.并沿着这条链传递请求.直到有一个对象处理它为止实例#include&

  • 沃舍尔算法求传递闭包_离散数学传递闭包

    沃舍尔算法求传递闭包_离散数学传递闭包给定 n 个变量和 m 个不等式。其中 n 小于等于 26,变量分别用前 n 的大写英文字母表示。不等式之间具有传递性,即若 A>B 且 B>C,则 A>C。请从前往后遍历每对关系,每次遍历时判断:如果能够确定全部关系且无矛盾,则结束循环,输出确定的次序;如果发生矛盾,则结束循环,输出有矛盾;如果循环结束时没有发生上述两种情况,则输出无定解。输入格式输入包含多组测试数据。每组测试数据,第一行包含两个整数 n 和 m。接下来 m 行,每行包含一个不等式,不等式全部为小于关系

  • Vue.js 入门教程[通俗易懂]

    Vue.js 入门教程[通俗易懂]Vue.js教程Vue.js(读音/vjuː/,类似于view)是一套构建用户界面的渐进式框架。Vue只关注视图层,采用自底向上增量开发的设计。Vue的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件。MVVM模式下图不仅概括了MVVM模式(Model-View-ViewModel),还描述了在Vue.js中ViewModel是如何和View以及Model进行交互…

  • pycharm 2021永久激活码破解方法

    pycharm 2021永久激活码破解方法,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • goland 激活码2021.5[在线序列号]

    goland 激活码2021.5[在线序列号],https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 用WriteProcessMemory函数注入进程的流程

    用WriteProcessMemory函数注入进程的流程
    CreateProcessA    创建挂起进程
    GetThreadContext
    ReadProcessMemory
    VirtualAllocEx      分配空间 
    WriteProcessMemory写入PE头
    WriteProcessMemory循环写入各节表
    WriteProcessMemory
    SetThreadContex

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号