现在的位置: 首页 > 综合 > 正文

文档/视图结构中的各个部分是如何联系到一起的(2)

2013年02月28日 ⁄ 综合 ⁄ 共 6434字 ⁄ 字号 评论关闭

(三)文档与视图之间的联系

在视图类有一个保护数据成员:CDocument* m_pDocument;,这个文档指针指向视图对象所属的文档,视图里常用的函数GetDocument()就是返回的这个指针;在文档类有一个保护数据成员:CDocument* m_viewList;,它保存的是所有正在显示该文档的视图的指针,通过CDocument的成员函数GetFirstViewPosition和GetNextView函数可以实现对这些视图的访问。

在视图被创建的时候,在OnCreate函数里视图和文档发生了关联:

int CView::OnCreate(LPCREATESTRUCT lpcs)

{

       if (CWnd::OnCreate(lpcs) == -1)

              return -1;

       CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;

 

       if (pContext != NULL && pContext->m_pCurrentDoc != NULL)

       {

              pContext->m_pCurrentDoc->AddView(this);

              ASSERT(m_pDocument != NULL);

       }

       else

       {

              TRACE0("Warning: Creating a pane with no CDocument./n");

       }

 

       return 0;  

}

这个关联是通过文档类的AddView函数实现的:

void CDocument::AddView(CView* pView)

{

    ……

       m_viewList.AddTail(pView);

       pView->m_pDocument = this;

 

       OnChangedViewList();  

}

在这个函数里,首先文档对象先把所添加的视图指针加到自己的视图链表里,然后指向自己的指针赋給了所添加的视图的m_pDocument成员。

众所周知,文档与视图进行通信的方式先调用文档的UpdateAllViews函数,从而调用视图的OnUpdate函数:

void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)

       // walk through all views

{

    //视图链表不能为空且发送者不能为空

       ASSERT(pSender == NULL || !m_viewList.IsEmpty());

       POSITION pos = GetFirstViewPosition();

       while (pos != NULL)

       {

              CView* pView = GetNextView(pos);

              ASSERT_VALID(pView);

      //不调用发送者的OnUpdate函数

              if (pView != pSender)

                     pView->OnUpdate(pSender, lHint, pHint);

       }

}

在视图的OnUpdate函数里默认的实现仅是通知视图进行重画:

Invalidate(TRUE);

我们一般重载这个更新视图的某些数据或进行其他操作,比如更新视图滚动条的滚动范围。

(四)框架窗口与文档、视图之间的联系

在框架窗口被创建的时候,创建了视图,相关的函数如下:

int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)

{

       CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;

       return OnCreateHelper(lpcs, pContext);

}

int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext)

{

       if (CWnd::OnCreate(lpcs) == -1)

              return -1;

 

       // create special children first

       if (!OnCreateClient(lpcs, pContext))

       {

              TRACE0("Failed to create client pane/view for frame./n");

              return -1;

       }

 

       // post message for initial message string

       PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);

 

       // make sure the child windows have been properly sized

       RecalcLayout();

 

       return 0;   // create ok

}

BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)

{

       // default create client will create a view if asked for it

       if (pContext != NULL && pContext->m_pNewViewClass != NULL)

       {

              if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)

                     return FALSE;

       }

       return TRUE;

}

CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)

{

 

       CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();

       if (pView == NULL)

       {

              return NULL;

       }

       ASSERT_KINDOF(CWnd, pView);

 

       if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,

              CRect(0,0,0,0), this, nID, pContext))

       {

              return NULL;        // can't continue without a view

       }

 

       if (afxData.bWin4 && (pView->GetExStyle() & WS_EX_CLIENTEDGE))

       {

              ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);

       }

       return pView;

}

在文档模板的OpenDocumentFile函数发生了如下的调用:

InitialUpdateFrame(pFrame, pDocument, bMakeVisible);

文档模板的这个函数的实现为:

pFrame->InitialUpdateFrame(pDoc, bMakeVisible);

实际是调用了框架窗口的同名函数:

void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible)

{

       CView* pView = NULL;

       if (GetActiveView() == NULL)

       {

              //取主视图

              CWnd* pWnd = GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);

              if (pWnd != NULL && pWnd->IsKindOf(RUNTIME_CLASS(CView)))

              {

        //主视图存在且合法,把当前的主视图设置为活动视图

                     pView = (CView*)pWnd;

                     SetActiveView(pView, FALSE);

              }

       }

 

       if (bMakeVisible)

       {

              SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);

 

              if (pView != NULL)

                     pView->OnActivateFrame(WA_INACTIVE, this);

        ……

              ActivateFrame(nCmdShow);

              if (pView != NULL)

                     pView->OnActivateView(TRUE, pView, pView);

       }

 

       // update frame counts and frame title (may already have been visible)

       if (pDoc != NULL)

              pDoc->UpdateFrameCounts();

       OnUpdateFrameTitle(TRUE);

}

上面的函数中对视图的操作主要是用SetActiveView设置了活动视图,并且调用了视图的OnActivateFrame函数。在CFrameWnd类中维护着一个保护成员:CView* m_pViewActive;,SetAcitveView函数主要就是对它进行操作:

void CFrameWnd::SetActiveView(CView* pViewNew, BOOL bNotify)

{

       CView* pViewOld = m_pViewActive;

       if (pViewNew == pViewOld)

              return;     // do not re-activate if SetActiveView called more than once

 

       m_pViewActive = NULL;   // no active for the following processing

 

       // deactivate the old one

       if (pViewOld != NULL)

              pViewOld->OnActivateView(FALSE, pViewNew, pViewOld);

 

       if (m_pViewActive != NULL)

              return;     // already set

       m_pViewActive = pViewNew;

 

       // activate

       if (pViewNew != NULL && bNotify)

              pViewNew->OnActivateView(TRUE, pViewNew, pViewOld);

}

CFrameWnd还有另一个函数返回这个成员:

CView* CFrameWnd::GetActiveView() const

{

       ASSERT(m_pViewActive == NULL ||

              m_pViewActive->IsKindOf(RUNTIME_CLASS(CView)));

       return m_pViewActive;

}

CframeWnd还有一个函数能取得当前活动的文档,它是通过活动视图间接得到的:

CDocument* CFrameWnd::GetActiveDocument()

{

       ASSERT_VALID(this);

       CView* pView = GetActiveView();

       if (pView != NULL)

              return pView->GetDocument();

       return NULL;

}

(五)MDI主窗口和子窗口之间的关联:

在MDI子窗口创建的时候,指定了它与MDI之间的关系:

BOOL CMDIChildWnd::Create(LPCTSTR lpszClassName,

       LPCTSTR lpszWindowName, DWORD dwStyle,

       const RECT& rect, CMDIFrameWnd* pParentWnd,

       CCreateContext* pContext)

{

       if (pParentWnd == NULL)

       {

              CWnd* pMainWnd = AfxGetThread()->m_pMainWnd;

              ASSERT(pMainWnd != NULL);

              ASSERT_KINDOF(CMDIFrameWnd, pMainWnd);

              pParentWnd = (CMDIFrameWnd*)pMainWnd;

       }

    ……

       pParentWnd->RecalcLayout();

 

       CREATESTRUCT cs;

……

//指定了所属的MDI子窗口

       cs.hwndParent = pParentWnd->m_hWnd;

    ……

       cs.lpCreateParams = (LPVOID)pContext;

 

       if (!PreCreateWindow(cs))

       {

              PostNcDestroy();

              return FALSE;

       }

       MDICREATESTRUCT mcs;

    ……

       mcs.style = cs.style & ~(WS_MAXIMIZE | WS_VISIBLE);

       mcs.lParam = (LONG)cs.lpCreateParams;

 

       AfxHookWindowCreate(this);

    //发送WM_MDICREATE消息,创建了MDI子窗口

       HWND hWnd = (HWND)::SendMessage(pParentWnd->m_hWndMDIClient,

              WM_MDICREATE, 0, (LPARAM)&mcs);

       if (!AfxUnhookWindowCreate())

              PostNcDestroy();        // cleanup if MDICREATE fails too soon

    ……

       return TRUE;

}

当MDI子窗口创建了以后,MDI主窗口就可以用自己的函数实现对子窗口的管理,例如取得当前的活动子窗口是通过发送WM_MDIGETACTIVE消息实现的

抱歉!评论已关闭.