Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Splitter Window Tutorial

0.00/5 (No votes)
13 Jan 2003 1  
Implementing CSplitterWnd controls in MFC SDI applications

Sample Image - SplitterWindowTutorial.gif

Introduction

This article was written to cover the basics of using the default MFC CSplitterWnd objects. I looked for a long time for a basic introduction to using the objects but could only find subclasses or otherwise adapted CSplitterWnd tutorials. I hope this article helps those who, like me, just want a basic introduction to the basic object.

Background

A CSplitterWnd or splitter window is a way to split the screen into two or more different panes (see screenshot or applications such as 3D editors). A pane itself is usually filled with a CView or CWnd derived object to provide different views of a document (or user interface functionality).

Splitter windows come in two forms - static and dynamic. This tutorial will only cover static splitters since the dynamic ones are slightly more complex. The main difference between the two is that a dynamic splitter can be split and collapsed by the user while a static splitter cannot.

Using the Code

Step 1 - Declare the Data

Use AppWizard to create a new single document application with document/view support. Add a CSplitterWnd object to your Main Frame header file and a boolean flag for splitter management.

CSplitterWnd m_mainSplitter;
BOOL m_bInitSplitter;

m_mainSplitter will be used to split the window into two panes like the screenshot above. The boolean flag will be used for checking that the splitter has been setup before managing resizing. Ensure that in the constructor of your Main Frame class, you initialize the boolean value to FALSE.

Step 2 - Create the Splitter

A splitter window should be created during the initialization of the client area of the main frame window. Add a function for the OnCreateClient message in your Main Frame class and insert splitter creation code as follows:

//calculate client size 
CRect cr; 
GetClientRect( &cr);

if ( !m_mainSplitter.CreateStatic( this, 1, 2 ) ) 
{ 
    MessageBox( "Error setting up splitter frames!", 
                  "Init Error!", MB_OK | MB_ICONERROR ); 
    return FALSE; 
}

The parameters to CreateStatic() are the parent window (this) and the number or rows and columns. The client rectangle size is obtained to help with sizing later on.

Step 3 - Create the Views

Each pane in the splitter, unless it has a splitter nested inside, must have a view attached to it before it can be displayed. After the CreateStatic call, use the following code to create a default view for the pane. Notice that each column in the splitter must have a view created since it doesn't have a nested splitter inside. This caused me a lot of problems (understanding the requirements on views), so take some time to look at various code samples to gain a deeper understanding. The boolean flag is then updated to TRUE to indicate that the splitters have been created.

if ( !m_mainSplitter.CreateView( 0, 0, 
       RUNTIME_CLASS(CSplitterWindowTutorialView), 
       CSize(cr.Width()/2, cr.Height()), pContext ) ) 
{ 
    MessageBox( "Error setting up splitter frames!", 
                "Init Error!", MB_OK | MB_ICONERROR );
    return FALSE; 
}

if ( !m_mainSplitter.CreateView( 0, 1, 
        RUNTIME_CLASS(CSplitterWindowTutorialView), 
        CSize(cr.Width()/2, cr.Height()), pContext ) ) 
{ 
    MessageBox( "Error setting up splitter frames!", 
        "Init Error!", MB_OK | MB_ICONERROR );
    return FALSE; 
}

m_bInitSplitter = TRUE;

To use RUNTIME_CLASS(CSplitterWindowTutorialView), you must include your view class header file in MainFrm.cpp and include your document class in your view class header just before:

#endif // _MSC_VER > 1000.

This will allow you to use your view class in the Main Frame class. Lots of problems occur if you do not include your document class in the view class header though - another thing to watch out for!

Step 4 - Replace the Return Value

Replace:

return CFrameWnd::OnCreateClient(lpcs, pContext);

with:

return TRUE;

The default return value from the existing code will not show our work since it returns the default method to the frame window.

Step 5 - Manage Resizing

SetRowInfo and SetColumnInfo are responsible for managing the size of the splitters. Add a handler for the WM_ONSIZE message and add the following code:

void CMainFrame::OnSize(UINT nType, int cx, int cy) 
{
    CFrameWnd::OnSize(nType, cx, cy);
    CRect cr;
    GetWindowRect(&cr);

    if (  m_bInitSplitter && nType != SIZE_MINIMIZED )
    {
        m_mainSplitter.SetRowInfo( 0, cy, 0 );
        m_mainSplitter.SetColumnInfo( 0, cr.Width() / 2, 50);
        m_mainSplitter.SetColumnInfo( 1, cr.Width() / 2, 50);

        m_mainSplitter.RecalcLayout();
    }
}

In the above code, we first check that the splitters have been initialized by checking the boolean value. This check is required since I think a WM_SIZE message is passed to the frame before the create method is ran - therefore, the object won't exist the first time this code runs and would crash if you didn't check for its existence.

The application should now run and provide something resembling the screenshot at the start of the article! I will probably add a second article in this series to add further nested splitters and other views based on forms! Stay tuned...

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.

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