(三)文档与视图之间的联系
在视图类有一个保护数据成员: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消息实现的
作者Blog:http://blog.csdn.net/coordinate/
转自:http://dev.csdn.net/article/55/55362.shtm
本文地址:http://com.8s8s.com/it/it24197.htm