(三)文档与视图之间的联系 在视图类有一个保护数据成员: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消息实现的
|
文档与视图的联系(三)
最新推荐文章于 2024-05-20 21:23:41 发布