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(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,純為測試代碼!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM