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

The Ultimate Toolbox Layout Manager

0.00/5 (No votes)
25 Aug 2007 1  
The Ultimate Toolbox provides a Layout Manager that can be configured to manage control placement on resizing of forms and dialogs

Visit the Ultimate Toolbox main page for an overview and configuration guide to the Ultimate Toolbox library.

Source code and project files for this sample can be found in the samples\gui\LayoutManager directory of the sample projects download.

Overview

The COXLayoutManager class can be attached to a container window (dialog, form etc.). From that moment on, the layout manager will manage the layout of the child windows of this container. So imagine you have a dialog with several controls on it. When the dialog is resized, the layout manager moves and resizes the controls of this dialog. The programmer can specify certain constraints that have to be fulfilled at all times. By using these constraints and the new size of the container window (dialog), the layout manager is able to move and resize the controls appropriately.

Features

  • Easy, flexible and complete when setting constraints on child windows.
  • Run-time adding or removing constraints.
  • Optimized codes for faster resizing.

Container Example

The samples\gui\LayoutManager sample declares a member COXLayoutManger m_LayoutManager. In the views OnCreate handler, a member dialog with a number of buttons is created as a child of the view, and in OnnitialUpdate the dialog is shown and the layout manager is attached:

void CLayoutManagerView::OnInitialUpdate() 
{
    CView::OnInitialUpdate();

    m_dlgDemo.ShowWindow(SW_SHOW);
    m_LayoutManager.Attach(this);
    m_LayoutManager.SetConstraint(&m_dlgDemo, OX_LMS_ANY, OX_LMT_SAME);
    m_LayoutManager.RedrawLayout();
}

To provide a better understanding of the layout manager it we'll illustrate with an example. In this example a window is to be laid out in a tiled format, with three windows at the top and one at the bottom. A layout manager is used because all child windows must accept any size their parent determines.

It is obvious that some sides must be connected to the container borders to make the child windows resize with the container. The top sides of windows A, B, and C, the left sides of A and D, the right sides of C and D, and the bottom side of D are affected.

Now you have to decide in which way the added or subtracted space of a resize operation is distributed. First the horizontal direction will be examined. There are several ways of distributing the space:

SetConstraint (BUTTONB,OX-LMS-LEFT,OX-LMT-OPPOSITE,0, BUTTONA);

SetConstraint (BUTTONB,OX-LMS-RIGHT,OX-LMT-OPPOSITE,0,BUTTONC);

Localized Distribution: All the space may be absorbed by one of the child windows, the others keeping their size. A child window keeps its preferred size if only one side is attached and the other is left alone (no constraint). The child which serves as a space buffer has both sides constrained The attachments are to either the neighbour child window or the container.

Therefore, if the middle child window is the buffer, it is attached to the left and right child, and the outer children are constrained by the container. As a result the sides of the middle child window are dragged or pushed by the unconstrained sides of the outer two. Unconstrained means that the side itself is not attached to a position (i.e. it is a free variable), but other sides may be constrained by it.

To give a physical analogy to this behavior, imagine that child windows with two sides attached are made of rubber, while the other ones are solid (with respect to one direction).

SetConstraint (BUTTONA,OX-LMS-RIGHT,OX-LMT-POSITION,40);

SetConstraint(BUTTONB,OX-LMS-LEFT,OX-LMS-OPPOSITE,O,BUTTONA);

SetConstraint(BUTTONB,OX-LMS-RIGHT,OX-LMS,POSITION,60);

SetConstraint(BUTTONC,OX-LMS-LEFT,OX-LMS-OPPOSITE,O,BUTTONB);

Even Distribution: The space may he distributed proportionally between the child windows. In this case the middle sides must be attached to a relative position in the container. You should attach only one of two coinciding sides to the relative position in the container and make the other one dependent on it. This ensures that the exact value of the relative position is set only once, and you can later define a spacing between the two.

The disadvantage of relative positions is that you must know the approximate relative sizes of the child windows to set appropriate relative positions.

Absolute Positions: Some sides may he set to absolute positions by attaching them to the container sides with a fixed offset. However, a good design avoids encoding fixed values in the program, because the child window sizes may be different in different program versions as a result of different texts and fonts.

With absolute positions (and with localized distribution when the buffering space is exhausted) it may be possible that parts of a child are clipped if the container is very small, because there is no way to reposition the child without breaking the constraints.

In the above figure B and C are constrained by D, which in turn is constrained by A.

Another possibility is to align A, B, and C directly, which would be necessary if D didn't exist. It shows a situation where the child windows are aligned on their left sides (the same can be applied to all sides). Because constraints work only in one dimension, a left side may be attached to another left side which is above or below it.

By specifying an offset, one child may be indented relative to the other. By aligning the left and right sides to the first child in a column you can achieve a row-column-like effect. The difference is, however, that the preferred width of a row-column is calculated as the maximum of all child windows, while in the container the first child acts as a leader and determines the width.

The base window can be specified by using the window pointer or the resource ID of the referenced child window. The reference must be unique and only direct children of the container can be referenced in this way.

SetConstraint(BUTTONB,OX-LMS-LEFT,OX-LMT-SAME,O,BUTTONA);

Getting Started

A container is a CWnd derived class that appears as a parent window of some child windows or controls. When an end-user resizes the container, you expect those child windows will be repositioned according to a set of constraints, under which all positions of the child windows will be uniquely determined. Each child window has to have a child ID to be identified by COXLayoutManager. A constraint can be either horizontal or vertical, and constraints on these two dimensions are absolutely independent of each other. A constraint is always posted onto one side of a child window at a time, and there are three types of constraints:

OX_LIMIT_SAME

The side is constraint by the same side of the base window with a fixed distance specified by an offset field. The constraint is directed, meaning that the child window moves if the side of the container moves, but not the other way round.

OX_LIMIT_OPPOSITE

The side is constraint by the opposite side of the base window with a fixed distance specified by an offset field. The constraint is also directed.

OX_LIMIT_POSITION

The side is constrained by a relative position in the base window. The behavior in this case can be illustrated by nailing the side to a specific point in the base window. Imagine the base window being made of rubber, such that the nail point stays at a certain relative position when the base window resizes. The nail position is specified by giving the ratio of the relative distances to the base window sides.

Both OX_LMT_SAME and OX_LMT_OPPOSITE use one side of a base window, which could be another child window or container, to determine one side of a child window. In these two types of constraints, all you need to specify is the base window and an offset. COXLayoutManager can then deduct the position of the side of a child window being constrained by adding the position of either the same side (in OX_LMT_SAME) or the opposite side (in OX_LMT_OPPOSITE) of the base window, with the offset.

OX_LMT_POSITION uses two sides of a base window to determine the position of the side of a child window being constrained. In this case, all you need to specify is the base window and a relative offset to the major side (left or top) of the base window. To determine the position of the constrained side, the relative offset is transformed into an absolute offset using a fraction base. For example, the fraction base is 100 by default, and if the relative offset is 20, it means the constrained side will be repositioned at a 20% distance to the left (or top) side of the base window as a fraction of the width (or height) of the base window. You will be able to change the fraction base at your convenience.

In all the above three types, base window's sides that are used as starting point of calculation are always in the same dimension as the constrained side of a child window. In other words, you cannot use top or bottom side of a base window to constraint a left or right side of a child window.

When a child window's one side is constrained and the other side in the same dimension is not constrained, the other side will be repositioned to maintain its original width (or height), giving an impression of sticking the child window to one side of its base window. When both sides of a child window are constrained, the child window will be resized to conform to the constraints, giving an impression of stretching or shrinking the child window accordingly.

By following these simple steps, you will be able to use COXLayoutManager easily:

1. Attach a Container Window

Call Attach to set up the container window, or specify it in the constructor. This function registers a container window (dialog, form) with the layout manager. Once a container is attached, its WM_SIZE message will be intercepted by the COXLayoutManager to resize child windows.

Detach is provided for reverse operation. This function unregisters a container window from the layout manager. From this moment onwards child windows of the container window are not managed anymore. All constraints that have been added for these child windows are removed as well.

The layout manager also detaches a container window when that window is destroyed. In this case an explicit call to Detach is not needed.

2. Add Child Windows (optional)

Call AddChild with a child window's ID or CWnd*, or call AddAllChildren to add all child windows of the container window. These added child windows will then be considered for repositioning when the container is resized. When adding a child window, its major sides (left and top) will be constrained to the container's left or top using current distances. This default behavior can be turned off by specifying FALSE in the bSetDefaultConstraints parameter. RemoveChild and RemoveAllChildren are provided for the reverse operations.

3. Set Constraints

Call SetConstraint, SetMinMax or RemoveConstraint to modify constraints on child windows. GetConstraint and GetMinMax are provided for checking constraints and min/max.

Calling SetConstraint(someChildID, ... ) will cause an internal call of AddChild(someChildID, FALSE) without setting default constraints, if no AddChild(someChildID [, ...]) was called previously. This provides a shortcut when you do not want a default constraint behavior on some child windows.

4. RedrawLayout( )

Finally, call RedrawLayout at a proper time. It will read the current positions of the container window and apply constraints on child windows.

A full COXLayoutManager class reference can be found in the Graphical User Interface | Layout Manager section of the compiled HTML help documentation.

History

Initial CodeProject release August 2007.

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