Click here to Skip to main content
Click here to Skip to main content

Unlimited number of switchable views within a Splitter window

, 19 Feb 2004
Rate this:
Please Sign up or sign in to vote.
Allows someone to have an unlimited number of views associated with a splitter pane

Introduction

This article details how one can use my CMultiSplitterView class that allows one to implement multiple switchable views within a splitter pane. In the image above the grey area is the first view. By going to Menu bar and clicking 'View > Show 2nd View' the program dynamically changes the first view to the second view.

This class allows you to switch to any view you create in one function call, without doing anything ! Adding a view is as simple as one function call as well. Now, on to the details ...

Code Details

The core of the everything is located in MultiSplitterView.cpp and MultiSplitterView.h.

Adding a view to the splitter pane is done here:

 bool CMultiSplitterView::AddSwitchableView(UINT id, 
                 CRuntimeClass * pView,
                 CCreateContext* pContext,
                 CRect & size, bool isFirstView, UINT altId)
{
   CWnd* pWin;
   DWORD style;

   pWin  = (CWnd*) pView->CreateObject();
   style = WS_CHILD ;
   
   if (isFirstView) 
   {
     style |=  WS_VISIBLE ;
   }

   pWin->Create(NULL, NULL, style, size , this, id, pContext);

   if (isFirstView) // id provided is usally diff. so use alternate
   {
     views[pWin] =altId ;
   }
   else 
   {
     views[pWin] = id;
   }

   return true;
}

The first param is the id of the view that you associate with the view so you can look it up easily. The second param is created by calling RUNTIME_CLASS(SomeViewClass) which returns a pointer to a CRuntimeClass class. The third param is the CCreateContext given to you by the OnCreateClient funtion in your CMainFrame class. The fourth param is the dimensions of the window. Now the final two are optional and will only be used with the first call to AddSwitchableView(). Because the id for the first param is the id of the pane the first view will be set to, I needed to pass in the real id that the user of this class is associating with the view, hence the last param called altId.

Ok, so we pass in all these params, then we create an object that is of the type of the runtime class that was passed in and cast it to its base class CWnd for creation and storage. Notice in the call to Create for the CWnd object I always use the id passed and the this pointer which associates the view with the splitter. I then store the pointer to the CWnd as the 'key' within a map<> and use the id or alternative id as the value for later lookup.

Next we need to switch the view dynamically so that one is seen and another is hidden.

The following code handles the switching of any number of views:

bool CMultiSplitterView::SwitchView(UINT id, int paneRow, int paneCol)
{

    CView* pOldView = (CView*) GetPane(paneRow, paneCol); // get current view

   if (pOldView == NULL) // serious prob
  {
     return false;
  }

   CView* pNewView = (CView*) GetDlgItem(id); // get new view

   if(pNewView == NULL ) // bad view id or this is already the view we requested
   {
      return false;
   }

   CFrameWnd * mainWnd = (CFrameWnd *)AfxGetMainWnd();

   if (mainWnd == NULL) // serious prob
   {
     ASSERT(false);
     return false;
   }
  
   if(mainWnd->GetActiveView() == pOldView)
    mainWnd->SetActiveView(pNewView);
  
   pNewView->ShowWindow(SW_SHOW);
   pOldView->ShowWindow(SW_HIDE);

   pNewView->SetDlgCtrlID(  IdFromRowCol(paneRow, paneCol));

   CWnd * bCwnd =(CWnd *)pOldView; // upcast to CWnd ptr

  if (views.find(bCwnd) == views.end()) // search for CWnd ptr
  {
  return false;
  }

   UINT oldId = views[bCwnd]; // get id of this view for future lookup

   pOldView->SetDlgCtrlID(oldId); // reset view id, so we can look it up

}

Ok, so now the user or the GUI has called SwitchView() with some id and the row and col of the splitter to which the view belongs. We first get a pointer to the current view using the row and col provided by the user. Next we get the new view ( the view we are about to show which is associated with the id passed in). We verify that they aren't NULL and then get the CFrameWnd pointer from the main window. We then compare the active view to the old view to see if they are the same (which they should be) and set the new view to the view requested. After we hide and show the old and new view, it is essential that we set the id of the new view to that of the id from the row and col that the view is associated with since it is the child window of the pane. We next lookup the id of the old view in the map<> because it's control id is still the id from IdFromRowCol(). We then reset the control id to that stored in the map<> so that the view pointer can be retrieved later.

How to use:

Within your CMainFrame you must first include the MultiViewSplitter.h header file. Next declare a member var of type CMultiViewSplitter. Next in OnCreateClient() within CMainFrame:

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{

CRect r;
GetWindowRect(r);

m_SplitterFirst.CreateStatic(this,1,2); 

m_SplitterFirst.CreateView(0,0, RUNTIME_CLASS( CSomeView) ,
               CSize(r.Width() *0.14, r.Height()), pContext); 

m_SplitterSeconde.CreateStatic(&m_SplitterFirst, 2, 1, WS_CHILD | WS_VISIBLE |
                 WS_BORDER, m_SplitterFirst.IdFromRowCol(0, 1));

/******************* Add switchable views **************************/

   m_SplitterSeconde.AddSwitchableView(m_SplitterSeconde.IdFromRowCol(0, 0),
            RUNTIME_CLASS(CFirstView) ,pContext, CRect(0,0,r.Width(), r.Height()) ,
      true , FIRST_VIEW);

   m_SplitterSeconde.AddSwitchableView(SECOND_VIEW, 
      RUNTIME_CLASS( CSecondView), pContext,
      CRect(0,0,r.Width(), r.Height()*0.60) );

/******************************************************************/

  ...

}

The only part that really matters for this example is the part after the comment that says 'Add Switchable views'. I first split the first splitter called m_SplitterFirst into 2 columns. Then I create the second splitter as a child of the first splitter and split that into two rows. Now this is all immaterial to you because you may decide to split your window however you see fit.

For the first call to AddSwitchableView(), be sure to use the id from the splitter provided by calling m_SplitterSeconde.IdFromRowCol(x, x), and provide the alternate view ID as the last param and true as the second to last param to indicate this is the first view.

Inside of the call to RUNTIME_CLASS( x ), add the class name of whatever class is encapsulating your view also.
Wow, that's it ! Now whenever you want to switch views dynamically, just call SwitchView( x ), where x is the id of the view. Please download the example demo project for a full illustration of how this works.

If anyone votes this article as a low score (less that 4 or 5) can you give me the courtesy of telling me why you gave me that score and what I can do better. Suggestions, kudos, problems are welcome. Thanks for your time.

Other Useful Functions

GetViewPtr(UINT id, int paneRow, int paneCol) - Gets a base class CWnd ptr associated with the splitter.

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

About the Author

Dan Clark
Web Developer
United States United States
Dan is originally from (Philadelphia, PA) and now resides in Maryland. He was a philosophy major at Univ. of Maryland but has since graduated.
He also enjoys writing about himself in the 3rd person (like now).


Dan loves to travel and meet new people and experience different cultures. He plans on traveling to Europe in the coming months to backpack for 2 months.


Currently Dan is interested in CLisp, ATL, Java Servlets/JSP/EJB, C , COM/DCOM, MFC, API and of course Perl programming


My kickass website !


@{${$dan}{c}{clark}}[1].

Comments and Discussions

 
GeneralListView problem Pinmemberrkshdixit22-Aug-07 3:38 
QuestionI cannot insert program icon in your smpla app PinmemberZBPELZER9-May-07 4:28 
AnswerRe: I cannot insert program icon in your smpla app PinmemberPelzer9-May-07 10:55 
GeneralNot Quite PinmemberJohn Simmons / outlaw programmer13-Dec-06 10:49 
GeneralMissing OnInitialUpdate for Switchable Views - Solution Pinmemberoocode17-Nov-04 21:25 
GeneralIt's the one Pinmemberpank00721-Oct-04 14:54 
GeneralThank you for the great work PinmemberNathan Holt at CCEI18-Mar-04 5:36 
QuestionDid you say this is a VC6 sample? PinmemberWREY23-Feb-04 7:34 
AnswerRe: Did you say this is a VC6 sample? PinmemberDan Clark23-Feb-04 7:51 
GeneralRe: Did you say this is a VC6 sample? PinmemberWREY23-Feb-04 11:39 
GeneralRe: Did you say this is a VC6 sample? PinmemberNeta77725-Feb-04 6:34 
GeneralRe: URL to VS6 sample I just made PinmemberDan Clark25-Feb-04 13:39 
GeneralRe: URL to VS6 sample I just made PinmemberNeta77725-Feb-04 19:36 
GeneralRe: URL to VS6 sample I just made Pinmemberxablackandblue5-Nov-07 14:27 
GeneralRe: URL to VS6 sample I just made PinmemberFlaviu220-Nov-11 7:16 

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.140721.1 | Last Updated 20 Feb 2004
Article Copyright 2004 by Dan Clark
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid