none
在CMainFrame的OnCreate中调用GetActiveView()总是返回NULL RRS feed

  • 问题

  • VS2010 SDI doc/view项目,CMainFrame是主框架类,derived from CFrameWnd。我试着在CMainFrame::OnCreate的最后加入CView* pActiveView = GetActiveView(), 但是pActiveView总是NULL。请问在CmainFrame中,View在哪一个函数里最终生成?
    2012年10月25日 9:41

答案

  • CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
    {
    	ASSERT(m_hWnd != NULL);
    	ASSERT(::IsWindow(m_hWnd));
    	ENSURE_ARG(pContext != NULL);
    	ENSURE_ARG(pContext->m_pNewViewClass != NULL);
    
    	// Note: can be a CWnd with PostNcDestroy self cleanup
    	CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();
    	if (pView == NULL)
    	{
    		TRACE(traceAppMsg, 0, "Warning: Dynamic create of view type %hs failed.\n",
    			pContext->m_pNewViewClass->m_lpszClassName);
    		return NULL;
    	}
    	ASSERT_KINDOF(CWnd, pView);
    
    	// views are always created with a border!
    	if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
    		CRect(0,0,0,0), this, nID, pContext))
    	{
    		TRACE(traceAppMsg, 0, "Warning: could not create view for frame.\n");
    		return NULL;        // can't continue without a view
    	}
    
    	if (pView->GetExStyle() & WS_EX_CLIENTEDGE)
    	{
    		// remove the 3d style from the frame, since the view is
    		//  providing it.
    		// make sure to recalc the non-client area
    		ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
    	}
    	return pView;
    }
    
    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;
    }
    
    int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)
    {
    	ENSURE_ARG(lpcs != NULL);
    	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))
    	{
    		TRACE(traceAppMsg, 0, "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
    }
    


    Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.

    • 已标记为答案 David_LS 2012年11月4日 5:50
    2012年10月25日 12:24
    版主
  • OnCreate函数实质是响应WM_CREATE消息,窗口的创建是在Create函数中完成的,更准确的说法是 CreateWindowEx中发送WM_CREATE消息,消息响应后再返到CreateWindowEx函数,这个时候窗口产生了,但是没有显示。所以调用GetActiveView()返回的值为空。

    xiao

    • 已标记为答案 David_LS 2012年11月4日 5:49
    2012年10月31日 15:55

全部回复

  • CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
    {
    	ASSERT(m_hWnd != NULL);
    	ASSERT(::IsWindow(m_hWnd));
    	ENSURE_ARG(pContext != NULL);
    	ENSURE_ARG(pContext->m_pNewViewClass != NULL);
    
    	// Note: can be a CWnd with PostNcDestroy self cleanup
    	CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();
    	if (pView == NULL)
    	{
    		TRACE(traceAppMsg, 0, "Warning: Dynamic create of view type %hs failed.\n",
    			pContext->m_pNewViewClass->m_lpszClassName);
    		return NULL;
    	}
    	ASSERT_KINDOF(CWnd, pView);
    
    	// views are always created with a border!
    	if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
    		CRect(0,0,0,0), this, nID, pContext))
    	{
    		TRACE(traceAppMsg, 0, "Warning: could not create view for frame.\n");
    		return NULL;        // can't continue without a view
    	}
    
    	if (pView->GetExStyle() & WS_EX_CLIENTEDGE)
    	{
    		// remove the 3d style from the frame, since the view is
    		//  providing it.
    		// make sure to recalc the non-client area
    		ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
    	}
    	return pView;
    }
    
    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;
    }
    
    int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)
    {
    	ENSURE_ARG(lpcs != NULL);
    	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))
    	{
    		TRACE(traceAppMsg, 0, "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
    }
    


    Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.

    • 已标记为答案 David_LS 2012年11月4日 5:50
    2012年10月25日 12:24
    版主
  • 看起来在CFrameWnd::OnCreate中已经调用了CreateView了,而在重载的int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)中第一件事就是调用CFrameWnd::OnCreate(lpCreateStruct),所以可否认为此时view已经成功生成了呢?如果是的话,在CFrameWnd::OnCreate(lpCreateStruct)最后调用CView* pActiveView = GetActiveView()总是返回NULL,是否可以解释为尽管view已经创建,但是并不是active view?那么生成的view是在什么地方成为active view的?
    2012年10月26日 2:54
  • 单文档模版类调用OpenDocumentFile()函数,在OpenDocumentFile函数中会调用CDocTemplate类的InitialUpdateFrame()函数,之后会调用CFrameWnd框架类的同名的InitialUpdateFrame函数,之后您就可以SetActiveView函数的调用了。只有这样以后,再当您调用GetActiveView返回的就不为NULL了。
    void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,
     BOOL bMakeVisible)
    {
     // just delagate to implementation in CFrameWnd
     pFrame->InitialUpdateFrame(pDoc, bMakeVisible);
    }
    void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible)
    {
    	// if the frame does not have an active view, set to first pane
    	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)
    	{
    		// send initial update to all views (and other controls) in the frame
    		SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
    		// give view a chance to save the focus (CFormView needs this)
    		if (pView != NULL)
    			pView->OnActivateFrame(WA_INACTIVE, this);
    		// finally, activate the frame
    		// (send the default show command unless the main desktop window)
    		int nCmdShow = -1;      // default
    		CWinApp* pApp = AfxGetApp();
    		if (pApp != NULL && pApp->m_pMainWnd == this)
    		{
    			nCmdShow = pApp->m_nCmdShow; // use the parameter from WinMain
    			pApp->m_nCmdShow = -1; // set to default after first time
    		}
    		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);
    }



    Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.


    2012年10月26日 8:08
    版主
  • 另外把CFrameWnd::GetActiveView/SetActiveView的实现也贴出来
    CView* CFrameWnd::GetActiveView() const
    {
    	ASSERT(m_pViewActive == NULL ||
    		m_pViewActive->IsKindOf(RUNTIME_CLASS(CView)));
    	return m_pViewActive;
    }
    void CFrameWnd::SetActiveView(CView* pViewNew, BOOL bNotify)
    {
    #ifdef _DEBUG
    	if (pViewNew != NULL)
    	{
    		ASSERT(IsChild(pViewNew));
    		ASSERT_KINDOF(CView, pViewNew);
    	}
    #endif //_DEBUG
    	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 the OnActivateView moves the active window,
    	//    that will veto this change
    	if (m_pViewActive != NULL)
    		return;     // already set
    	m_pViewActive = pViewNew;
    	// activate
    	if (pViewNew != NULL && bNotify)
    		pViewNew->OnActivateView(TRUE, pViewNew, pViewOld);
    }

    Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.

    • 已建议为答案 wh_xiao 2012年11月1日 7:41
    2012年10月26日 8:12
    版主
  • 或许我应该把问这个问题的出发点先解释一下。在SDI single doc, multi views应用中,switching views通常放在主框架类中(例如collect例子)。我希望基于collect例子做一些改变,用view指针数组保存所有的view,这样每次切换就更容易一些。我想既然view的切换是在主框架类中,那么同样在主框架类中得到初始的view是比较逻辑的选择。但是从我找到的所有例子中都如你所讲,初始view在app的InitInstance()中进行。所以我才想问有没有办法在主框架类中得到所有view的指针,包括现在active的view。

    还请VisualEleven版主不吝赐教。如果你能点评一下用view指针数组的办法我将更加感激。

    2012年10月26日 10:05
  • OnCreate函数实质是响应WM_CREATE消息,窗口的创建是在Create函数中完成的,更准确的说法是 CreateWindowEx中发送WM_CREATE消息,消息响应后再返到CreateWindowEx函数,这个时候窗口产生了,但是没有显示。所以调用GetActiveView()返回的值为空。

    xiao

    • 已标记为答案 David_LS 2012年11月4日 5:49
    2012年10月31日 15:55