您的位置:首页 > 其它

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

2005-02-02 16:46 459 查看
(三)文档与视图之间的联系

在视图类有一个保护数据成员: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消息实现的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: