Click here to Skip to main content
Click here to Skip to main content
Go to top

Replacing a view in a doc-view application

, 28 Dec 1999
Rate this:
Please Sign up or sign in to vote.
Using different View classes to represent data differently within Doc/View applications

Introduction

Sometimes the way a document is being visualized needs to be significantly changed. For example, in PowerPoint, you can see the slides in a WYSIWYG form or view only the text in a text edit window. In the MFC world, one can find an amazingly large quantity of programs that implement this behavior defining one CView descendant and making it responsible for all visualization changes. This path has several disadvantages:

  • Big, difficult to manage class definition file.
  • Diminished reusability: you could reuse one big CView descendant or nothing.
  • Hard to maintain (what if you want to modify or add a new "look" to the document?).
  • Wasted memory: some variables (objects) will exist in memory and won’t be used.

For an application using the MFC document-view architecture, it is more appropriate to define different view classes and switch between them when necessary. This shall overcome all the disadvantages listed before. There probably will be some features common to all views for the same type of document, so it is a good idea to have a direct CView descendant that implement all the functionality common to all view types. The views used by the document should be descendants of this class ("grandchildren" of CView).

The code needed to implement view switching depends on the frame window containing the view. There are three common cases: the view is contained within a CFrameWnd (SDI application), the view is contained within a CMDIChildWnd (MDI application) and the view is a pane of a splitter window, either in SDI or MDI applications. In all cases what we need is a method in our document class to switch to the desired view. This method should receive the new view type as a parameter and return a success flag. The advantage of having this method in the document class becomes obvious when there are several document types each of which can have different view types. Lets start with an SDI application that doesn’t have splitters:

BOOL CMyDocument::SwitchToView(CRuntimeClass* pNewViewClass)
{
   CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd();
   CView* pOldActiveView = pMainWnd->GetActiveView();

   // If we're already displaying this kind of view, no need to go further.
   if (pOldActiveView->IsKindOf(pNewViewClass))
      return TRUE;
	
   // Set the child window ID of the active view to AFX_IDW_PANE_FIRST.
   // This is necessary so that CFrameWnd::RecalcLayout will allocate
   // this "first pane" to that portion of the frame window's client
   // area not allocated to control bars.  Set the child ID of
   // the previously active view to some other ID.
   ::SetWindowLong(pOldActiveView->m_hWnd, GWL_ID, 0);

   // create the new view
   CCreateContext context;
   context.m_pNewViewClass = pNewViewClass;
   context.m_pCurrentDoc = this;
   CView* pNewView = STATIC_DOWNCAST(CView, pMainWnd->CreateView(&context));
   if (pNewView != NULL)
   {
      // the new view is there, but invisible and not active...
      pNewView->ShowWindow(SW_SHOW);
      pNewView->OnInitialUpdate();
      pMainWnd->SetActiveView(pNewView);
      pMainWnd->RecalcLayout();

      // destroy the old view...
      pOldActiveView->DestroyWindow();
      return TRUE;
   }

   return FALSE;
}

In the case of an MDI application (again without splitters):

BOOL CMyDocument::SwitchToView(CRuntimeClass* pNewViewClass)
{
   CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd();

   // Get the active MDI child window.
   CMDIChildWnd* pChild = (CMDIChildWnd*)pMainWnd->MDIGetActive();

   // Get the active view attached to the active MDI child window.
   CView* pOldActiveView = pChild->GetActiveView();

   // If we're already displaying this kind of view, no need to go further.
   if (pOldActiveView->IsKindOf(pNewViewClass))
      return TRUE;
	
   // Set flag so that document will not be deleted when view is destroyed.
   BOOL bAutoDelete = m_bAutoDelete;
   m_bAutoDelete = FALSE;

   // Delete existing view
   pOldActiveView->DestroyWindow();

   // restore flag
   m_bAutoDelete = bAutoDelete;
	
   // Create new view.
   CView* pNewView = (CView *)pNewViewClass->CreateObject();
   if (pNewView == NULL)
   {
      TRACE1("Warning: Dynamic create of view type %Fs failed\n",
              pNewViewClass->m_lpszClassName);
      return FALSE;
   }

   // Draw new view.
   CCreateContext context;
   context.m_pNewViewClass = pNewViewClass;
   context.m_pCurrentDoc = this;
   context.m_pNewDocTemplate = NULL;
   context.m_pLastView = NULL;
   context.m_pCurrentFrame = pChild;
   if (!pNewView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, 
               CRect(0, 0, 0, 0), pChild, AFX_IDW_PANE_FIRST, &context))
   {
      TRACE0("Warning: couldn't create view for frame\n");
      delete pNewView;
      return FALSE; 
   }

   // WM_INITIALUPDATE is defined in afxpriv.h
   pNewView->SendMessage(WM_INITIALUPDATE, 0, 0);     
   pChild->RecalcLayout();
   pNewView->UpdateWindow();
   pChild->SetActiveView(pNewView);
   return TRUE;
}

When the view to replace is a pane of a splitter window, there is also a small difference between SDI and MDI applications, related to the retrieval of the current active view. In the method below you must comment out what you don’t need depending on your application type:

BOOL CMyDocument::SwitchToView(CRuntimeClass* pNewViewClass)
{
/* Uncomment this if this is a SDI application
   CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd();
   CView* pOldActiveView = pMainWnd->GetActiveView();
*/

/* Uncomment this if this a MDI application
   CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd();

   // Get the active MDI child window.
   CMDIChildWnd* pChild = (CMDIChildWnd*)pMainWnd->MDIGetActive();

   // Get the active view attached to the active MDI child window.
   CView* pOldActiveView = pChild->GetActiveView();
*/

   // If we're already displaying this kind of view, no need to go further.
   if (pOldActiveView->IsKindOf(pNewViewClass))
      return TRUE;

   CSplitterWnd* pSplitter = (CSplitterWnd *)pOldActiveView->GetParent();
   int row, col;
   ASSERT(pSplitter->IsChildPane(pOldActiveView, row, col));
   CRect viewrect;
   pOldActiveView->GetWindowRect(&viewrect);

   // set flag so that document will not be deleted when view is destroyed
   m_bAutoDelete = FALSE;    

   // Delete existing view 
   pOldActiveView->DestroyWindow();

   // set flag back to default 
   m_bAutoDelete = TRUE;
 
   // Create new view                      
   CCreateContext context;
   context.m_pNewViewClass = pNewViewClass;
   context.m_pCurrentDoc = this;
   context.m_pNewDocTemplate = NULL;
   context.m_pLastView = NULL;
   context.m_pCurrentFrame = NULL;
   if (!pSplitter->CreateView(row, col, pNewViewClass, viewrect.Size(),
        &context))
      return FALSE;

   // Set active 
   CView* pNewView = (CView *)pSplitter->GetPane(row, col);
   pSplitter->GetParentFrame()->SetActiveView(pNewView);
   
   pSplitter->RecalcLayout(); 
   pNewView->SendMessage(WM_PAINT); 
   return TRUE;
}

Now that we have a method in our document class that will replace the current view, lets use it. The new view type should be decided (in response to a menu selection, for instance), and the function must be called as follows:

   CRuntimeClass* pNewViewClass = RUNTIME_CLASS(CMyView);
   if (!SwitchToView(pNewViewClass))
      // failed
   else
      // succeeded

One final word to the class wizard fans. When you have a descendant of a CView descendant, the class wizard won’t allow you to edit this class. To change this behavior, change all class wizard comments replacing the name of your direct CView descendant with CView. Class wizard will now work.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

No Biography provided

Comments and Discussions

 
Newsvery good Pinmemberwuliangqun15-Aug-13 21:39 
QuestionFantastic! PinmemberBruff14-Aug-12 9:49 
GeneralThanks Pinmembersevenrui13-May-09 18:56 
GeneralWYSIWYG Pinmember123456uio9-Aug-06 3:15 
GeneralCRuntimeClass Pinmemberquangi24-Aug-05 23:38 
GeneralPrinting problem... PinmemberMVH15-Aug-03 22:56 
GeneralRe: Printing problem... PinmemberMVH16-Aug-03 0:47 
QuestionHow to have Splitter in one of the many views being switched PinsussReneJohn9-Apr-03 19:45 
GeneralMDI version crashes / Exception error PinmemberMark F.26-Mar-03 3:45 
GeneralRe: MDI version crashes / Exception error - The Fix PinmemberAli Rafiee15-Dec-05 9:50 
Generalstill muddled... Pinmembersuzanne27-Aug-02 5:39 
GeneralAssert Error PinsussDark17-Jul-02 0:07 
GeneralCREDIT WHERE CREDIT IS DUE PinmemberRick York3-Jan-02 8:47 
GeneralRe: CREDIT WHERE CREDIT IS DUE PinmemberBubba214618-Jul-02 1:35 
GeneralSplitter in splitter PinmemberFredo1-Aug-01 22:01 
GeneralCatch ya ! :rolleyes: PinmemberUChar8-Mar-01 4:05 
GeneralRe: Catch ya ! - Bug fixed PinmemberUChar8-Mar-01 5:42 
GeneralRe: Catch ya ! :rolleyes: PinmemberAnonymous8-Mar-01 7:22 
GeneralSlight problem when using with "Persistent Frames" by Stefan Chekanov (this section) PinmemberGary2-Mar-01 17:14 
GeneralGood, Simple but space for optimization PinmemberFarhan Noor Qureshi26-Jan-01 11:12 
GeneralRe: Good, Simple but space for optimization Pinmemberakraus1-Oct-01 23:55 
GeneralBug Fix PinmemberJoseph Dempsey13-Nov-00 5:49 
GeneralRe: Bug Fix PinmemberAnonymous18-Jan-01 10:12 
GeneralRe: Bug Fix PinmemberGary2-Mar-01 15:14 
GeneralRe: Bug Fix PinmemberJohn Golding12-Sep-02 8:38 
GeneralRe: Bug Fix PinmemberJohn Golding12-Sep-02 8:39 
GeneralRe: Bug Fix Pinmemberprthilagavathi28-Apr-04 22:58 
GeneralMight Have Overheads PinsussShrikant Dhumal9-Oct-00 23:25 
GeneralRe: Might Have Overheads PinmemberpimOOsse4-Mar-03 12:00 
Generalyou rock!!! PinsussArkon9-Aug-00 10:23 
GeneralExcellent code, but be careful with those ASSERTa PinsussSteve7-Feb-00 4:48 
GeneralRe: Excellent code, but be careful with those ASSERTa PinsussAnonymous16-Sep-02 22:02 
GeneralRe: Excellent code, but be careful with those ASSERTa PinmemberStefan Ungureanu17-Sep-02 3:13 
GeneralRe: Excellent code, but be careful with those ASSERTa PinmemberJoão Paulo Figueira28-Oct-03 4:00 
GeneralRe: Excellent code, but be careful with those ASSERTa PinmemberStefan Ungureanu5-Nov-03 0:28 
GeneralRe: Excellent code, but be careful with those ASSERTa Pinmemberdrhani3339-Jan-09 5:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140921.1 | Last Updated 29 Dec 1999
Article Copyright 1999 by Jorge Lodos
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid