bitbucket: C++ MFC ATL WTL Win32 COM ActiveX Samples Tutorials Source Code Controls

MultiSplitterWnd

Download Sample Project

Abstract

CMultiSplitWnd is an extended CSplitterWnd MFC implementation, that allows changing number of panes and split orientation on the fly even in a so called "static" splitter.

Overview

Features:

How to use:

User Documentation

Instead of CSplitterWnd, dereive from CMultiSplitterWnd. (Since the control does not use it's own message map, you don't even need to change the base class entry in your classes Message Map)

To keep the implementation fairly easy, the following restriction applies:

AddView CWnd * AddView(int row, int col, CRuntimeClass * pViewClass, CCreateContext* pContext = NULL)
bool AddView(int row, int col, CWnd * newWnd)
Adds a new view, and makes it the current view in the (row, col) pane. The prototype is simular to CSplitterWnd::CreatePane, except for the intial size parameter. You can use the SetRowHeight, SetColWidth, SetPaneSize functions after adding all views.
The second prototype allows to add a window that has already been created.
The first prottype returns a pointer to the newly created window, or NULL if an error occurs. The second prototype returns true for success, or false if an error occurs.
ShowView bool ShowView(CWnd * wnd)
bool ShowView(CWnd * wnd, int newRow, int newCol)
Switch to another view. The view passed in wnd must have been added with AddView before.The second prototype allows moving the view to another pane.
SwitchPanes void SwitchPanes(int row1, int col1, int row2, int col2)
Switch all views that belong to the two given panes 8row1, col1) and (row2, col2). This affects both visible and invisible panes.
GetCurrentView CWnd * GetCurrentView(int nRow, int nCol)
Returns the view currently visible in the given pane. If no view has been created for the pane, or the default background is displayed, this method returns NULL.
RemoveView bool RemoveView(CWnd * wnd)
Removes a view from the splitter window. The window is not destroyed.
SetStaticSplit void SetStaticSplit(int newRows, int newCols)
Change the number of splitters.
ShowDefaultView void ShowDefaultView(int nRow, int nCol)
display the default background in the given pane
IsDefaultView bool IsDefaultView(int row, int col)
Returns true, if the default background is displayed in the given pane.
Pane Sizing
int SetRowHeight(int row, int newHeight) int GetRowHeight(int row)
int SetColWidth(int col, int newWidth) int GetColWidth(int col)
void SetPaneSize(int row, int col, int newWidth, int newHeight)

These functions do what they promise. SetRowheight and SetColWidth return the previous value. Note that SetPaneSize affects the entire row and entire column.

 

Implementation Notes:

Switching the Views
The idea was taken from Caroline Engelbienne's article on CodeGuru. To hide a view, its DlgCtrlID is set to 0, and ShowWindow(SW_HIDE) is called. To show another view, set the correct DlgCtrlID, and call ShowWindow(SW_SHOW).
The std::map<HWND, POINT> m_map holds the last (or current) pane location for each view.

The major problem in tuning the CSplitterWnd was the MFC implementation relying on the DlgCtrlID of each pane views to identify the location of each view.  Playing around with this caused an epic "ASSERT feast".

Default Background
For empty panes, CMultiSplitterWnd creates a new window for the pane. These windows are kept in std::map<UINT paneID, HWND wnd> m_defaultPane. To reduce overhead, default panes are created only when required. The CheckDefaultPane protected member function verifies for the given pane if the default background needs to be displayed, and creates the window if necessary.
The default background was used to work around the follwing problem: CSplitterWnd::RecalcLayout fails with an assertion if no view is visible for a pane. However, RecalcLayout is not virtual, and is called by many functions; and some uses (like moving multiple view around in sequence) would require much more coding from the user.

Changing Splitter Count
To do this, the spitter Window is destroyed and re-created with the new row+column count. To keep the views alive, their parent window is temporary set to the parent of the splitter itself. (Otherwise, CSplitterWnd::DestroyWindow would destroy the views as well).
The implementation relies on the internal implementation of CSplitterWnd, but I guess it isn't likely to change.

 

5.5.2002: brushed the article up a bit


bitbucket c++ site © Peter Hauptmann. Questions & Comments to  cherea@cherea.de
page updated 29/02/04