wxSizer

From WxWiki
Jump to: navigation, search
Official Classes SmallBlocks.png Archive Containers Controls Data Structures Database Date & Time Debug Device Contexts Dialogs Document & Views Drag & Drop Events Filesystem Frames Graphics Grid Cell Help HTML Logging Miscellaneous Networking Printing Sizers Streams Threading Windows

wxSizer is the abstract base class used for laying out subwindows in a window.

You cannot use wxSizer directly; instead, you will have to use one of the sizer classes derived from it. Currently there are wxBoxSizer, wxStaticBoxSizer, wxGridSizer, wxFlexGridSizer, wxWrapSizer and wxGridBagSizer.

The layout algorithm used by sizers in wxWidgets is closely related to layout in other GUI toolkits, such as Java's AWT, the GTK toolkit or the Qt toolkit. It is based upon the idea of the individual subwindows reporting their minimal required size and their ability to get stretched if the size of the parent window has changed.

This will most often mean that the programmer does not set the original size of a dialog in the beginning, rather the dialog will be assigned a sizer and this sizer will be queried about the recommended size. The sizer in turn will query its children, which can be normal windows, empty space or other sizers, so that a hierarchy of sizers can be constructed. Note that wxSizer does not derive from wxWindow and thus does not interfere with tab ordering and requires very little resources compared to a real window on screen.

What makes sizers so well fitted for use in wxWidgets is the fact that every control reports its own minimal size and the algorithm can handle differences in font sizes or different window (dialog item) sizes on different platforms without problems. If e.g. the standard font as well as the overall design of Motif widgets requires more space than on Windows, the initial dialog size will automatically be bigger on Motif than on Windows.

Sizers may also be used to control the layout of custom drawn items on the window. The wxSizer::Add(), wxSizer::Insert(), and wxSizer::Prepend() functions return a pointer to the newly added wxSizerItem. Just add empty space of the desired size and attributes, and then use the wxSizerItem::GetRect() method to determine where the drawing operations should take place.

Please notice that sizers, like child windows, are owned by the library and will be deleted by it which implies that they must be allocated on the heap. However if you create a sizer and do not add it to another sizer or window, the library wouldn't be able to delete such an orphan sizer and in this, and only this, case it should be deleted explicitly.

Some Member Functions

Layout

This one positions the controls; after its call they have their correct size and positions. Normally called from OnSize() and so doesn't have to be called explicitly--but only for top-level windows such as wxFrame and wxDialog, and only if you use SetSizer or SetSizerAndFit. Otherwise, you should call Layout() (the wxWindow function, not the sizer function of the same name) within your window's OnSize() handler.

SetSizeHints

Says that the top level window shouldn't shrink beyond its minimal size. It also calls Fit() to make the window of the right size initially. You only have to call SetSizeHints() and not Fit().

wind->SetSizerAndFit

A wxWindow function equivalent to SetSizer( sizer ); sizer->SetSizeHints(this);

wind->SetSizer

(Setting the sizer for the window). Actually a wxWindow function, it is used to associate a sizer with a window. Implicitly calls SetAutoLayout(true) if sizer is !NULL.

Spacing

If you want to add some manual spacing between two children of a sizer, you might want to use the Sizer::AddSpacer member function that allows you to specify a rectangular size.

Pitfalls

Sizer Does Not Grow or Expand

Your implementation of a sizer may not expand even though you include wxGROW or wxEXPAND when adding it to the parent window or sizer. Beware of the pitfall in this code.

myPanel->Add(mySizer, wxEXPAND);

That form of the Add statement will compile and your sizer will appear on the screen. Alas, it will not grow. wxWidgets is looking for a reference to a wxSizerFlags object as the second argument but will happily accept and ignore the wxEXPAND. Use another form of the Add function.

myPanel->Add(mySizer, 0, wxEXPAND, 0);

Optionally you may provide the wxSizerFlags object in the first form. See the "wxSizerFlags Class Reference" for the official documentation.

Window Size is Very Small

If you have a sizer for your frame to which you want to add a panel, be sure that you add it after you add everything to the panel and call panel->SetSizerAndFit( panelsizer ). If you first add the panel, then add controls to it, the window will not size correctly.

Optionally you can call panel->GetSizer()->Fit(panel) after you add the panels controls to re-fit the sizer.

Sizer Doesn't Work When Making a Custom Control/Window (no autolayout)

If you have a custom window class that doesn't resize its children properly, you must implement an OnSize() event handler, hook it in in your event table (through EVT_SIZE), and within that event handler, call Layout().

Usually sizers are used in top-level windows such as wxFrame and wxDialog. Both of these inherit internally from wxTopLevelWindow. wxTopLevelWindow has its own OnSize handler, which looks like this:

if ( GetAutoLayout() )
{
    Layout();
}
else
  /* if there's only a single child window, fill it to the full size */

AutoLayout only works automatically in top-level windows. If you want it to work in your own window, you need to handle OnSize() yourself and call Layout.

Everything is Shown at (0,0)

Call SetSizerAndFit(MySizer)

This could also be a parenting issue; e.g. you added a panel to the frame, then you added widgets to the panel and also to a sizer, but then use SetSizer on the frame instead of doing so on the panel (i.e. the widgets added in a sizer must have the same parent as the sizer) A variant of this is that the parent of the controls is itself badly parented.

This could also be that you catch size events but don't let them continue with event.Skip().

Reducing the Size of a Dialog Window

Say you have a dialog that you laid out with sizers. You built the dialog and did all the usual SetAutoLayout(), SetSizeHints() stuff. Now you want to allow resizing of the dialog. For example, you want a button on the dialog called Details that will toggle between showing or hiding the bottom half of the dialog.

As of wx 2.3.4, you can use wxSizer::Show()/Hide() to collapse and expand sizers with less trouble.

For earlier versions, you still need to manually reset the min and max values for the size (buried down as members of the wxWidgets class unfortunately), otherwise the size constraints will prevent you from making the dialog smaller than it was. For example, if you wanted to include a function in your sizer-laid-out-dialog to set its height to a certain height in pixels:

void MyCollapsingDialog::UpdateMyHeight( int desired_dialog_height )
{
   // We have to reset min size constraints or would never reduce the
   // dialog size when collapsing it and we have to reset max constraint
   // because it wouldn't expand it otherwise. Note: these are members of the 
   // wxWindow class.
 
   m_minHeight = -1;
   m_maxHeight = -1;
 
   // Set the new size hints of the dialog, before we try to set the size otherwise
   // won't be able to set the size, since it won't let us set to a size that is 
   // not within the range of size hints. Note that we set m_maxHeight to -1 earlier
   // in this function.
 
   SetSizeHints( GetSize().x, desired_dialog_height, m_maxWidth, m_maxHeight );
 
   wxLogDebug( "SetSizeHints=%d,%d,%d,%d", GetSize().x, 
              desired_dialog_height, m_maxWidth, m_maxHeight ); 
 
   // Set the new size of the dialog. The -1 parameter means leave the width to 
   // whatever it was before.
 
   SetSize( -1, desired_dialog_height );
 
#ifdef __WXGTK__
 
   // Vaclav Slavic: This Show(TRUE) is necessary in order to force dialog redraw under
   // WindowMaker or fvwm2 (and probably other broken WMs). Otherwise, the newly exposed
   // parts of the dialog won't be displayed.
 
   Show( TRUE );
 
#endif // wxGTK
}

Size of a wxToolBar

In wxWidgets 2.4.0, if you change the size of a ToolBar using

wxToolBar::SetToolBitmapSize(,)

The sizer hierarchy, if it was set up with default settings, will not be aware of this size change. To ensure that the toolbar does resize, use the flag wxADJUST_MINSIZE when adding the wxToolBar into the sizer of its parent window.

Two Hierarchies

There are TWO hierarchies involved:

  • The windows hierarchy - which window is a parent of each window.
  • The sizer hierarchy - which sizer is a parent of each sizer or window.

If you have a dialog with many controls, these will all have the dialog window as their window's parent though there may be several sizers among them and the dialog window.

See Also