简体   繁体   中英

MFC SDI GetActiveView() always returns NULL

I need help with switching between CFormViews in my MFC SDI C++ project. I have been digging for a long time, and can't figure out why my code isn't working. Through searching the internet (and this site included) I came across several tutorials for switching forms by adding two functions to MainFrm.cpp (a CMainFrame object that inherits from CFrameWnd ). One of them is passed an id of the form I want to switch to, then gets a pointer to the active view, and runs some other code from there. However, GetActiveView() always returns a NULL pointer value. I know there's an active view because I'm clicking a button from an active form. My code is below. This is just the function I'm referring to. It resides in MainFrm.cpp (the default window file created when you start a new MFC project).

So far I've tried the code from the Microsoft Knowledge Base that talks about How to Get Current CDocument or CView from Anywhere, I tried to get the active frame first, then called GetActiveView from CFrameWnd , and I tried the code below. All to no avail. I clearly do not know enough about MFC to figure anything out. If you need more information from me, please ask. I probably didn't mention everything I should have. I chose to do MFC for a school project, and can't proceed to create a UML or writing any other code until I know that I can get these forms to work.

void CMainFrame::SelectView(UINT ViewID)
{
    // If the view the user selected is already displaying, do nothing
    if (ViewID == m_CurrentView)
        return;

    // Get a pointer to the current view
    CView* pCurrentView = GetActiveView();

    // We are about to change the view, so we need a pointer to the runtime class
    CRuntimeClass* pNewView = NULL; // Added = NULL because it wouldn't allow program to be run without initialization of pNewView

    // We will process a form
    // First, let's change the identifier of the current view to our integer
    ::SetWindowLong(pCurrentView->m_hWnd, GWL_ID, m_CurrentView);

    // Now we will identify what form the user selected
    switch (ViewID)
    {
    case IDD_CHOOSE_ITEM:
        pNewView = RUNTIME_CLASS(CChooseItemView);
        break;

    case IDD_ITEM_INFORMATION:
        pNewView = RUNTIME_CLASS(CItemInformationView);
        break;
    }

    // We will deal with the frame
    CCreateContext crtContext;

    // We have a new view now. So we initialize the context
    crtContext.m_pNewViewClass = pNewView;
    // No need to change the document. We keep the current document
    crtContext.m_pCurrentDoc = GetActiveDocument();

    CView* pNewViewer = STATIC_DOWNCAST(CView, CreateView(&crtContext));

    // Now we can create a new view and get rid of the previous one
    if (pNewViewer != NULL)
    {
        pNewViewer->ShowWindow(SW_SHOW);
        pNewViewer->OnInitialUpdate();
        SetActiveView(pNewViewer);
        RecalcLayout();
        m_CurrentView = ViewID;
        pCurrentView->DestroyWindow();
    }
}

The following code is working for me:

 virtual CView* SwitchToView(CView* pNewView); 

and in cpp:

 CView* CMyDoc::SwitchToView(CView* pNewView) { CMDIFrameWndEx* pMainWnd = (CMDIFrameWndEx*)AfxGetMainWnd(); // Get the active MDI child window CMDIChildWndEx* pChild = (CMDIChildWndEx*)pMainWnd->MDIGetActive(); // Get the active view attached to the active MDI child window. CView* pOldActiveView = pChild->GetActiveView(); // Exchange control ID of old view // note: if you have more than two view you have to remember which view you switched to // so you can set it's old control ID correctly if(pNewView == m_pMyView) pOldActiveView->SetDlgCtrlID(CTRLID_MYVIEW2); if(pNewView == m_pMyView2) pOldActiveView->SetDlgCtrlID(CTRLID_MYVIEW); // Exchange control ID of new new // note: the control ID of the active view must always be AFX_IDW_PANE_FIRST pNewView->SetDlgCtrlID(AFX_IDW_PANE_FIRST); // Set flag so that document will not be deleted when view is dettached. BOOL bAutoDelete = m_bAutoDelete; m_bAutoDelete = FALSE; // Dettach existing view RemoveView(pOldActiveView); // restore flag m_bAutoDelete = bAutoDelete; // Show the newly active view and hide the inactive view. pNewView->ShowWindow(SW_SHOW); pOldActiveView->ShowWindow(SW_HIDE); // Attach new view AddView(pNewView); pChild->RecalcLayout(); pNewView->UpdateWindow(); pChild->SetActiveView(pNewView); return pOldActiveView; } 

I hope it help you.

To get the a non active View but associated CView from CDocument, you can implement this schema in Doc

// ----- GetCChooseItemView() -- -Search the first associated CView in  INACTIVE Views too ! ------ 
CView* CMyDoc::GetCChooseItemView(void)
{
  CRuntimeClass* prt = RUNTIME_CLASS(CChooseItemView);
  CView* pView = NULL;

  // Continue search in inactive View by T(o)m

  POSITION pos = GetFirstViewPosition();
  while (pos != NULL)
  {
    pView = GetNextView(pos);
    if (pView->GetRuntimeClass() == prt)
    {
        if (pView->IsKindOf(RUNTIME_CLASS(CChooseItemView)))
            break;
    }
    pView = NULL;       // not valid vie
  }

  return static_cast<CChooseItemView*>(pView);
}

then add in your SelectView Code

void CMainFrame::SelectView(UINT ViewID)
{
  : (code as before)
  :      
  // Get a pointer to the current view
  CView* pCurrentView = GetActiveView();

  // Get a pointer to the current view
  CView* pCurrentView = GetActiveView();
  if (pCurrentView == NULL
  { 
    CMyDoc* pDoc = static_cast<CMyDoc*>(GetActiveDocument());
    if (pDoc)
    {
      pCurrentView = pDoc->GetChhoseItemView(); 
      if (pCurrentView == NULL)
         mpCurrentView = pDoc->GetCItemInformationView()  // let as exercise for the OP

      if (pCurrentView == NULL
      {
          DebugBreak();     // Errror No View found..
      }
    } 

  :  (code as befeore)
  :   
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM