Container Windows
Container windows are designed to hold further visual elements, either child windows or graphics drawn on the window.
wxPanel
wxPanel is essentially a wxWindow with some dialog-like properties. This is usually the class to use when you want to arrange controls on a window that's not a wxDialog, such as a wxFrame. It's often used for pages of a wxNotebook. wxPanel normally takes the system dialog color.As with wxDialog, you can use InitDialog to send a wxInitDialogEvent to the panel, transferring data to the panel via validators or other means. wxPanel also handles navigation keys such as the Tab key to provide automatic traversal between controls, if the wxTAB_TRAVERSAL style is provided.wxPanel has the following constructor in addition to the default constructor:
For example:
wxPanel(wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxTAB_TRAVERSAL|wxNO_BORDER,
const wxString& name = wxT("panel"));
wxPanel* panel = new wxPanel(frame, wxID_ANY,
wxDefaultPosition, (500, 300));
wxPanel Styles
There are no specific styles for wxPanel, but see the styles for wxWindow.
wxPanel Member Functions
There are no distinct wxPanel functions; please refer to the wxWindow member functions, inherited by wxPanel.
wxNotebook
This class represents a control with several pages, switched by clicking on tabs along an edge of the control. A page is normally a wxPanel or a class derived from it, although you may use other window classes.Chapter 10), and specified by position in the list.To use a notebook, create a wxNotebook object and call AddPage or InsertPage, passing a window to be used as the page. Do not explicitly destroy the window for a page that is currently managed by wxNotebook; use DeletePage instead, or let the notebook destroy the pages when it is itself destroyed.Here's an example of creating a notebook with three panels, and a text label and icon for each tab:
Figure 4-9 shows the result on Windows.
#include "wx/notebook.h"
#include "copy.xpm"
#include "cut.xpm"
#include "paste.xpm"
// Create the notebook
wxNotebook* notebook = new wxNotebook(parent, wxID_ANY,
wxDefaultPosition, wxSize(300, 200));
// Create the image list
wxImageList* imageList = new wxImageList(16, 16, true, 3);
imageList->Add(wxIcon(copy_xpm));
imageList->Add(wxIcon(paste_xpm));
imageList->Add(wxIcon(cut_xpm));
// Create and add the pages
wxPanel1* window1 = new wxPanel(notebook, wxID_ANY);
wxPanel2* window2 = new wxPanel(notebook, wxID_ANY);
wxPanel3* window3 = new wxPanel(notebook, wxID_ANY);
notebook->AddPage(window1, wxT("Tab one"), true, 0);
notebook->AddPage(window2, wxT("Tab two"), false, 1);
notebook->AddPage(window3, wxT("Tab three"), false 2);
Figure 4-9. A wxNotebook

Notebook Theme Management
On Windows XP, the default theme paints a gradient on the notebook's pages. Although this is the expected native behavior, it can slow down performance, and you may prefer a solid background for aesthetic reasons, especially when the notebook is not being used in a dialog. If you want to suppress themed drawing, there are three ways of doing it. You can use the wxNB_NOPAGETHEME style to disable themed drawing for a particular notebook, you can call wxSystemOptions::SetOption to disable it for the whole application, or you can disable it for individual pages by using SetBackgroundColour. To disable themed pages globally, do this:
Set the value to 1 to enable it again. To give a single page a solid background that matches the current theme, use
wxSystemOptions::SetOption(wxT("msw.notebook.themed-background"), 0);
On platforms other than Windows, or if the application is not using Windows themes, GetThemeBackgroundColour will return an uninitialized color object, and this code will therefore work on all platforms. Please note that this syntax and behavior is subject to change, so refer to the wxNotebook documentation in your wxWidgets distribution for the latest information.
wxColour col = notebook->GetThemeBackgroundColour();
if (col.Ok())
{
page->SetBackgroundColour(col);
}
wxNotebook Styles
wxNotebook can have the window styles listed in Table 4-11, in addition to those described for wxWindow.
wxNB_TOP | Place tabs on the top side. |
wxNB_LEFT | Place tabs on the left side. Not supported under Windows XP for all themes. |
wxNB_RIGHT | Place tabs on the right side. Not supported under Windows XP for all themes. |
wxNB_BOTTOM | Place tabs under instead of above the notebook pages. Not supported under Windows XP for all themes. |
wxNB_FIXEDWIDTH | All tabs will have same width. Windows only. |
wxNB_MULTILINE | There can be several rows of tabs. Windows only. |
wxNB_NOPAGETHEME | On Windows, suppresses the textured theme painting for the notebook's pages, drawing a solid color to match the current theme instead. This can improve performance in addition to giving an aesthetic choice. |
wxNotebook Events
wxNotebook generates wxNotebookEvent propagating events (events that can be handled by the notebook or its ancestors) specified in Table 4-12.
EVT_NOTEBOOK_PAGE_CHANGED(id, func) | The page selection has changed. |
EVT_NOTEBOOK_PAGE_CHANGING(id, func) | The page selection is about to change. You can veto the selection change with Veto. |
wxNotebook Member Functions
These are the major wxNotebook functions.AddPage adds a page, and InsertPage inserts a page at the given position. You can use a text label for the tab, or an image (specified by index into an image list), or both. For example:
DeletePage removes and destroys the specified page, while RemovePage just removes the page without deleting the page. Call DeleteAllPages to delete all the pages. When the wxNotebook is deleted, it will delete all its pages.AdvanceSelection cycles through the pages, and SetSelection sets the specified page by zero-based index. Use GetSelection to get the index of the selected page, or wxNOT_FOUND if none was selected.SetImageList sets a wxImageList to be used by the notebook but does not take ownership of it. Call AssignImageList if you want the notebook to delete the image list when it is destroyed. GetImageList returns the associated image list. An image list stores images to be shown on each page tab, if required. wxImageList is described in Chapter 10.Use GetPage to return the page window for a given index, and use GetPageCount to return the number of pages in the notebook.SetPageText and GetPageText are accessors for the label for a given page (by index).SetPageImage and GetPageImage are accessors for the index of a page's image index in the notebook's image list.
// Adds an unselected page with a label and an image
// (index 2 in the associated image list).
notebook->AddPage(page, wxT("My tab"), false, 2);
Alternatives to wxNotebook
wxNotebook is derived from a base class wxBookCtrlBase, which abstracts the concept of a control that manages pages. There are two API-compatible variations of the wxNotebook concept, wxListbook and wxChoicebook, and you can implement your own, such as wxtreebook.wxListbook uses a wxListCtrl to change pages; the list control displays icons with text labels underneath them, and can be on any of the four sides, defaulting to the left side. This is an attractive alternative to wxNotebook, and it has the advantage of being able to cope with an arbitrary number of pages even on Mac OS X because the list control can scroll.wxChoicebook uses a choice control (a drop-down list) and is particularly handy for small devices with restricted screen space, such as smartphones. It does not display images, and by default, it will display the choice control at the top.The include files for these classes are wx/listbook.h and wx/choicebk.h. Event handlers for these two classes take a wxListbookEvent or wxChoicebookEvent argument, respectively, and you can use the event macros EVT_XXX_PAGE_CHANGED(id, func) and EVT_XXX_PAGE_CHANGING(id, func) where XXX is LISTBOOK or CHOICEBOOK.You can use the same window styles as wxNotebook, or you can use the equivalents, such as wxCHB_TOP or wxLB_TOP instead of wxNB_TOP.
wxScrolledWindow
All windows can have scrollbars, but extra code is required to make scrolling work. This gives the flexibility to define appropriate scrolling behaviors for different kinds of windows. wxScrolledWindow implements commonly required scrolling behavior by assuming that scrolling happens in consistent units, not different-sized jumps, and that page size (the amount scrolled when "paging" up, down, left, or right) is represented by the visible portion of the window. It is suited to drawing applications but is not so suitable for a sophisticated editor in which the amount scrolled may vary according to the size of text on a given line. For this, you would derive from wxWindow and implement scrolling yourself. wxGrid is an example of a class that implements its own scrolling, largely because columns and rows can vary in size.To use a scrolled window, you need to define the number of pixels per logical scroll unit (how much the window is scrolled for a line up or down scroll event) and provide the virtual size in logical units. wxScrolledWindow will then take care of showing the scrollbars with appropriately sized "thumbs" (the parts you can drag) and will show or hide scrollbars as appropriate, according to the actual size of the window.The following fragment shows how to create a scrolled window:
A second way to specify the virtual size is to use SetVirtualSize, which takes the virtual size in pixels, plus a call to SetScrollRate to set the horizontal and vertical scrolling increments. A third way is to set a sizer for the window, and the scrolled window will calculate the required scrollbar dimensions from the space taken up by the child windows. You will still need to call SetScrollRate to specify the scrolling increments.You can provide a paint event handler as normal, but in order to draw the graphics at the appropriate position for the current positions of the scrollbars, call DoPrepareDC before drawing. This sets the device context's device origin. For example:
#include "wx/scrolwin.h"
wxScrolledWindow* scrolledWindow = new wxScrolledWindow(
this, wxID_ANY, wxPoint(0, 0), wxSize(400, 400),
wxVSCROLL|wxHSCROLL);
// Set up virtual window dimensions. It will be 1000x1000
// and will scroll 10 pixels at a time
int pixelsPerUnixX = 10;
int pixelsPerUnixY = 10;
int noUnitsX = 1000;
int noUnitsY = 1000;
scrolledWindow->SetScrollbars(pixelsPerUnitX, pixelsPerUnitY,
noUnitsX, noUnitsY);
Alternatively, you can override the OnDraw virtual function; wxScrolledWindow creates a paint device context and calls DoPrepareDC for you before calling your OnDraw function, so the code simplifies to the following:
void MyScrolledWindow::OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(this);
DoPrepareDC(dc);
dc.SetPen(*wxBLACK_PEN);
dc.DrawLine(0, 0, 100, 100);
}
Note that you will need to call DoPrepareDC if you draw on the window from outside the paint event, such as within a mouse event handler.You can provide your own DoPrepareDC function. The default function simply shifts the device origin according to the current scroll positions so that subsequent drawing will appear at the right place:
void MyScrolledWindow::OnDraw(wxDC& dc)
{
dc.SetPen(*wxBLACK_PEN);
dc.DrawLine(0, 0, 100, 100);
}
For more on painting on a wxScrolledWindow, including using buffered drawing, please see the section on wxPaintDC in Chapter 5, "Drawing and Printing."
void wxScrolledWindow::DoPrepareDC(wxDC& dc)
{
int ppuX, ppuY, startX, startY;
GetScrollPixelsPerUnit(& ppuX, & ppuY);
GetViewStart(& startX, & startY);
dc.SetDeviceOrigin( - startX * ppuX, - startY * ppuY );
}
wxScrolledWindow Styles
There are no special styles for wxScrolledWindow, but usually you will supply wxVSCROLL|wxHSCROLL (the default style for wxScrolledWindow). On some platforms, if these styles are not present, no scrollbars will be provided for efficiency reasons.
wxScrolledWindow Events
wxScrolledWindow generates wxScrollWinEvent events (see Table 4-13). These events do not propagate up the window parent-child hierarchy, so if you want to intercept these events, you must derive a new class or plug an event handler object into the window object. Normally you will not need to override the existing handlers for these events.
EVT_SCROLLWIN(func) | Handles all scroll events. |
EVT_SCROLLWIN_TOP(func) | Handles wxEVT_SCROLLWIN_TOP scroll-to-top events. |
EVT_SCROLLWIN_BOTTOM(func) | Handles wxEVT_SCROLLWIN_TOP scroll-to-bottom events. |
EVT_SCROLLWIN_LINEUP(func) | Handles wxEVT_SCROLLWIN_LINEUP line up events. |
EVT_SCROLLWIN_LINEDOWN(func) | Handles wxEVT_SCROLLWIN_LINEDOWN line down events. |
EVT_SCROLLWIN_PAGEUP(func) | Handles wxEVT_SCROLLWIN_PAGEUP page up events. |
EVT_SCROLLWIN_PAGEDOWN(func) | Handles wxEVT_SCROLLWIN_PAGEDOWN page down events. |
wxScrolledWindow Member Functions
These are the major wxScrolledWindow functions.CalcScrolledPosition and CalcUnscrolledPosition both take four arguments: two integers for the position input in pixels, and two pointers to integers for the transformed position output, also in pixels. CalcScrolledPosition calculates the device position from the logical position. For example, if the window is scrolled 10 pixels down from the top, the logical first visible position is 0, but the device position is -10. CalcUnscrolledPosition does the inverse, calculating the logical position from the device position.EnableScrolling enables or disables physical scrolling in horizontal and vertical directions independently. Physical scrolling is the physical transfer of bits up or down the screen when a scroll event occurs. If the application scrolls by a variable amount (for example, if there are different font sizes), then physical scrolling will not work, and you should switch it off. If physical scrolling is disabled, you will have to reposition child windows yourself. Physical scrolling may not be available on all platforms, but it is enabled by default where it is available.GetScrollPixelsPerUnit returns the horizontal and vertical scroll unit sizes in two pointers to integers. A value of zero indicates that there is no scrolling in that direction.GetViewStart returns the position of the first visible position on the window, in logical units. Pass two pointers to integers to receive the values. You will need to multiply by the values returned by GetScrollPixelsPerUnit to get pixel values.GetVirtualSize returns the size in device units (pixels) of the scrollable window area. Pass two pointers to integers to receive the virtual width and height.DoPrepareDC prepares the device context by setting the device origin according to the current scrollbar positions.Scroll scrolls a window so that the view is at the given point in scroll units (not pixels), passed as two integers. If either parameter is -1, that position will be unchanged.SetScrollbars sets the pixels per unit in each direction, the number of units for the virtual window in each direction, the horizontal and vertical position to scroll to (optional), and a boolean to indicate whether the window should be refreshed (false by default).SetScrollRate sets the horizontal and increment scroll rate (the same as the pixels per unit parameters in SetScrollbars).SetTargetWindow can be used to scroll a window other than the wxScrolledWindow.
Scrolling Without Using wxScrolledWindow
If you want to implement your own scrolling behavior, you can derive a class from wxWindow and use wxWindow::SetScrollbar to set scrollbar properties.SetScrollbar takes the arguments listed in Table 4-14.
int orientation | The scrollbar to set: wxVERTICAL or wxHORIZONTAL. |
int position | The position of the scrollbar "thumb" in scroll units. |
int visible | The size of the visible portion of the scrollbar, in scroll units. Normally, a scrollbar is capable of indicating the visible portion visually by showing a different length of thumb. |
int range | The maximum value of the scrollbar, where zero is the start position. You choose the units that suit you, so if you wanted to display text that has 100 lines, you would set this to 100. Note that this doesn't have to correspond to the number of pixels scrolledit is up to you how you actually show the contents of the window. |
bool refresh | true if the scrollbar should be repainted immediately. |
Note that with the window at this size, the thumb position can never go above 50 minus 16, or 34.You can determine how many lines are currently visible by dividing the current view size by the character height in pixels.When defining your own scrollbar behavior, you will always need to recalculate the scrollbar settings when the window size changes. You could therefore introduce a new function AdjustScrollbars into which you place your scrollbar calculations and SetScrollbar call. AdjustScrollbars can be called initially, and also from your wxSizeEvent handler function.It's instructive to look at the implementations of wxScrolledWindow and wxGrid if you're thinking of implementing your own scrolling behavior.You may want to look at wxVScrolledWindow in the wxWidgets reference manual; this can be used to build a scrolled window class that can scroll by lines of unequal height in the vertical direction.
SetScrollbar(wxVERTICAL, 0, 16, 50)
wxSplitterWindow
This class manages up to two subwindows (use nested splitter windows if you need more splits). The current view can be split into two by the application, for example, from a menu command. It can be unsplit either by the application or via the splitter window user interface by double-clicking on the sash or dragging the sash until one of the panes has zero size (override the latter behavior with SetMinimumPaneSize).On most platforms, when the sash is dragged, a reverse-video line will be drawn to show where the sash will end up. You can pass wxSP_LIVE_UPDATE to let the sash move in "real time" instead, resizing the child windows. This is the default (and only) mode on Mac OS X.The following fragment shows how to create a splitter window, creating two subwindows and hiding one of them.
This fragment shows how the splitter window can be manipulated after creation:
#include "wx/splitter.h"
wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY,
wxPoint(0, 0), wxSize(400, 400), wxSP_3D);
leftWindow = new MyWindow(splitter);
leftWindow->SetScrollbars(20, 20, 50, 50);
rightWindow = new MyWindow(splitter);
rightWindow->SetScrollbars(20, 20, 50, 50);
rightWindow->Show(false);
splitter->Initialize(leftWindow);
// Unncomment this to prevent unsplitting
// splitter->SetMinimumPaneSize(20);
Figure 4-10 shows how the wxWidgets splitter sample looks on Windows without the wxSP_NO_XP_THEME style. If you use this style, the splitter will take on a more traditional look with a sunken border and 3D sash.
void MyFrame::OnSplitVertical(wxCommandEvent& event)
{
if ( splitter->IsSplit() )
splitter->Unsplit();
leftWindow->Show(true);
rightWindow->Show(true);
splitter->SplitVertically( leftWindow, rightWindow );
}
void MyFrame::OnSplitHorizontal(wxCommandEvent& event)
{
if ( splitter->IsSplit() )
splitter->Unsplit();
leftWindow->Show(true);
rightWindow->Show(true);
splitter->SplitHorizontally( leftWindow, rightWindow );
}
void MyFrame::OnUnsplit(wxCommandEvent& event)
{
if ( splitter->IsSplit() )
splitter->Unsplit();
}
Figure 4-10. A wxSplitterWindow

wxSplitterWindow Styles
wxSplitterWindow can have the window styles shown in Table 4-15 in addition to those described for wxWindow.
wxSP_3D | Draws a 3D-effect border and sash. |
wxSP_3DSASH | Draws a 3D-effect sash. |
wxSP_3DBORDER | Synonym for wxSP_BORDER. |
wxSP_BORDER | Draws a standard border. |
wxSP_NOBORDER | No border (the default). |
wxSP_NO_XP_THEME | Add a sunken border and 3D sash on Windows, if you don't like the minimal (but more native) look. |
wxSP_PERMIT_UNSPLIT | Always enable the window to unsplit, even with the minimum pane size other than zero. |
wxSP_LIVE_UPDATE | Resize the child windows immediately as the splitter is being moved. |
wxSplitterWindow Events
wxSplitterWindow generates wxSplitterEvent propagating events, as listed in Table 4-16.
EVT_SPLITTER_SASH_POS_CHANGING(id, func) | Processes a wxEVT_COMMAND_SPLITTER_SASH_ POS_CHANGING event, generated when the sash position is in the process of being changed. Call Veto to stop the sash position changing, or call the event's SetSashPosition function to change the sash position. |
EVT_SPLITTER_SASH(id, func) | Processes a wxEVT_COMMAND_SPLITTER_ SASH_POS_CHANGED event, generated when the sash position is changed. May be used to modify the sash position before it is set, or to prevent the change from taking place, by calling the event's SetSashPosition function. |
EVT_SPLITTER_UNSPLIT(id, func) | Processes a wxEVT_COMMAND_SPLITTER_UNSPLIT event, generated when the splitter is unsplit. |
EVT_SPLITTER_DCLICK(id, func) | Processes a wxEVT_COMMAND_SPLITTER_ DOUBLECLICKED event, generated when the sash is double-clicked. |
wxSplitterWindow Member Functions
These are the major wxSplitterWindow functions.GetMinimumPaneSize and SetMinimumPaneSize are accessors for the minimum pane size. The default minimum pane size is zero, which means that either pane can be reduced to zero by dragging the sash, thus removing one of the panes. To prevent this behavior (and veto out-of-range sash dragging), set a minimum size, for example 20 pixels. However, if the wxSP_PERMIT_UNSPLIT style is used when a splitter window is created, the window may be unsplit even if the minimum size is non-zero.GetSashPosition and SetSashPosition are accessors for the sash position. Passing true to SetSashPosition resizes the pane and redraws the sash and border.GetSplitMode and SetSplitMode are accessors for the split orientation, which can be wxSPLIT_VERTICAL or wxSPLIT_HORIZONTAL.GetWindow1 and GetWindow2 get the pointers to the two panes.Initialize can be called with a pointer to a window if you only want to have one pane initially.IsSplit tests whether the window is split.ReplaceWindow replaces one of the windows managed by the wxSplitter Window with another one. Generally, it's better to use this function instead of calling Unsplit and then resplitting the window.SetSashGravity takes a floating-point argument, which determines the position of the sash as the window is resized. A value of 0.0 (the default) means that only the bottom or right child window will be resized, and a value of 1.0 means that only the top or left child window will be resized. Values inbetween indicate that the change in size should be distributed between both child windows (a value of 0.5 distributes the size evenly). Use GetSashGravity to return the current setting.SplitHorizontally and SplitVertically initialize the splitter window with two panes and optionally an initial sash size.Unsplit removes the specified pane.UpdateSize causes the splitter to update its sash position immediately (normally, this is done in idle time).
Sizing Issues with wxSplitterWindow
There are subtleties to be aware of when using a splitter window as part of a sizer hierarchy. If you don't need the sash to be moveable, you can create both child windows with absolute sizes. This will fix the minimum size of both child windows, and the sash will therefore not be free to move. If you need the sash to be moveable, as is normally the case, pass default values to the child windows and specify an initial minimum size in the splitter window constructor. Then add the splitter window to its sizer, passing the wxFIXED_MINSIZE flag to Add, which tells wxWidgets to treat the specified size as the minimum size.Another issue is that a splitter does not set its sash position (and therefore the sizes of its child windows) until idle time, when it can be sure that sizing has been finalized and the sash position won't be set prematurely. This can result in a sash that visibly repositions itself just after the window has been shown. To fix this, call wxSplitterWindow::UpdateSize as soon as you have done your layout, for example after a wxSizer::Fit call. The splitter will update its sash and child window sizes immediately.By default, when the user or application resizes the splitter, only the bottom (or right) window is adjusted to take into account the new size. If you need different behavior, use SetSashGravity as documented in the previous section.
Alternatives to wxSplitterWindow
If you have a lot of "split" windows in your application, consider using wxSashWindow. This is a window that allows any of its edges to have a sash (as specified by the application) that can be dragged to resize the window. The actual content window is normally created by the application as a child of wxSashWindow.When a sash is dragged, it notifies the application with a wxSashEvent so the handler can change the window size accordingly before laying out the windows. Layout is achieved via a class called wxLayoutAlgorithm, which provides LayoutWindow, LayoutFrame, and LayoutMDIFrame methods for arranging the sash windows on different kinds of parent windows.You can also use the class wxSashLayoutWindow, which responds to events of type wxQueryLayoutInfoEvent to provide orientation and size information to wxLayoutAlgorithm.Please see the reference manual for further details. wxSashWindow doesn't permit moving or undocking windows, and it's likely that these classes will be superceded by a general docking and layout framework in the near future.Figure 4-11 shows a view of the wxSashWindow sample provided in samples/sashtest.
Figure 4-11. The wxSashWindow demo

