Introduction
This article describes the basic use of WTL's CSplitterWindow
as an application window divider and CPaneContainer
as a host for
child windows. The included demonstration project is 3-pane SDI application
using vertical and horizontal splitters. Each splitter pane holds a pane
container and one of the pane containers holds an edit control.
Splitters
Splitters can be either vertical or horizontal and are used in popular
multipane applications such as Microsoft Outlook to divide the application's
main window into functional sections. The splitter window template is found in
atlsplit.h
. This header file offers two ready to use splitter
implementations. CSplitterWindow
is a standard vertical splitter
while CHorSplitterWindow
provides a horizontal splitter.
3-pane Layout
A Microsoft Outlook style 3-pane layout requires a vertical splitter, which
divides the screen into left and right segments, and a horizontal splitter,
which divides the right segment of the vertical splitter into top and bottom.
The main frame of the application is parent to the vertical splitter while the
vertical splitter is parent to the horizontal. Basic setup coding, using the
m_vSplit
and m_hzSplit
member variables, is as
follows:
CRect rcVert;
GetClientRect(&rcVert);
m_vSplit.Create(m_hWnd, rcVert, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
CRect rcHorz;
GetClientRect(&rcHorz);
m_hzSplit.Create(m_vSplit.m_hWnd, rcHorz, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
m_vSplit.SetSplitterPane(1, m_hzSplit);
In addition, several options were set for both splitters. The code snippet
below shows how to set a minimum split size of 35 pixels. This limits how close
to the left or right edge of the main window the splitter can be moved. Also,
the initial splitter position is set 85 pixels from the left and the "ghost bar"
effect is enabled. In ghost bar mode, a dark gray bar is displayed while the
splitter is being dragged. In normal mode, the entire splitter and pane contents
are dragged.
m_vSplit.m_cxyMin = 35;
m_vSplit.SetSplitterPos(85);
m_vSplit.m_bFullDrag = false;
At this point, the framework of a 3-pane application is in place. The next
section of this article describes how to add content to the splitters. Splitter
panes can contain controls, such as a treelist or listview, or a child window,
such as a dialog or pane container. In our sample project we add a pane
container to each section of the application, setting various options, and
then add an edit control to one of the pane containers.
Pane Containers
Pane Containers provide an area where child windows and controls can be
hosted. They also provide a header bar with a title and close button. A common
use for pane containers is to provide helpful titles and a logical grouping of
related program elements. The CPaneContainer
class is found in the
atlctrlx.h
header file.
Initializing a Pane Container
There are three basic steps needed to use a pane container. First, create
the container using the appropriate splitter handle as parent. Second, add the
container to the appropriate splitter section. Third, set the container title.
These steps are shown in the code below. This code sets up the pane container
for the left section of the vertical split.
m_lPane.Create(m_vSplit.m_hWnd);
m_vSplit.SetSplitterPane(0, m_lPane);
m_lPane.SetTitle("Left Pane");
If desired, the title can be set when the pane container is created by
supplying the title text or a resource ID for the title string as the second
parameter to the pane container create statement.
After the container is created and assigned to a splitter, you may set
extended options. The extended options are PANECNT_NOCLOSEBUTTON
and PANECNT_VERTICAL
. The first option controls whether or not a
Close button is displayed in the container header and the second controls
whether the header is layed out horizontally, at the top of the container, or
vertically, on the left side of the container. Note that header titles are not
displayed when the header is set to vertical orientation. Extended options are
set as follows:
m_tPane.SetPaneContainerExtendedStyle(PANECNT_NOCLOSEBUTTON);
Adding a Container Child
As mentioned earlier, a pane container can host a child window or a child
control, such as the edit control in the sample program. The steps for adding a
child control to a pane container are:
- Create the control as usual
- Configure the control per your needs
- Add the control to the pane container
You would use similar steps to add any kind of window to the container. This
code shows how an edit control is added to the bottom pane container of the
sample project:
m_edit.Create(m_bPane.m_hWnd, rcDefault, NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
m_edit.SetFont((HFONT)GetStockObject(DEFAULT_GUI_FONT), TRUE);
m_edit.SetWindowText(" Bottom Pane -- with vertical header and edit as child");
m_bPane.SetClient(m_edit.m_hWnd);
Handling the Close Button
When a user click on the pane container's Close button (marked with an X),
the button sends an ID_PANE_CLOSE
notification. The sample project
catches the notification in the main frame's message map and uses the following
routine to process it:
LRESULT OnPaneClose(WORD, WORD, HWND hWndCtl, BOOL&)
{
::ShowWindow(hWndCtl, SW_HIDE);
HWND hWnd = ::GetParent(hWndCtl);
CSplitterWindow* pWnd;
pWnd = (CSplitterWindow*)::GetWindowLong(hWnd, GWL_ID);
int nCount = pWnd->m_nPanesCount;
for(int nPane = 0; nPane < nCount; nPane++)
{ if (hWndCtl == pWnd->m_hWndPane[nPane])
{ pWnd->SetSinglePaneMode(nCount - nPane - 1);
break; } }
return 0; }
Use DestroyWindow(hWndCtl)
instead of ShowWindow
if you want to totally remove the container instead of just hiding it. You may
also want to use SetSplitterPane(nPane, NULL)
instead of
SetSinglePaneMode
if you want to stay in multipane mode instead of
changing to single pane mode.
In addition, you might want to replace an existing child window or control
with another. Do so by creating it and adding it to the container after the old
one is removed. If you do implement that as a feature, you may also want to
override the container's DrawButtonImage
method to provide an
appropriate button image.
Terms Of Use
The sample project available with this article is free. Use the code however
you wish.
THIS SOFTWARE IS DISTRIBUTED AS-IS, WITHOUT WARRANTIES OF ANY KIND.