Control Layout Engines
As you've seen, the .NET forms architecture provides support for laying out controls using coordinates in a grid. This approach, combined with the built-in support for docking and anchoring, gives developers a rich layout environment.However, there are times when grid layout is not necessarily the best approach. For example, you may need a container control that automatically lays out child controls according to different rules, perhaps adjusting them to accommodate the width of the largest control or shrinking them to fit the size of the container, to name just two possibilities. You could create a custom user control to encapsulate this type of functionality, but that wouldn't let you use the same layout logic in different containers (for example, forms, group boxes, and tabs). To provide a more generic, reusable system, you need to create a layout manager.A layout manager is a class that dictates the layout of child controls in a container. Ideally, a layout manager can be reused with any type of child control and applied to any type of container.
Note
If you've programmed with Java before, the idea of layout managers is nothing new. Some of the layout managers provided for Java containers include FlowLayout (similar to a word processor), BorderLayout (which divides the screen into five zones), CardLayout (like a stack of cards layered on top of each other), GridLayout (which allows one component per equal-sized cell), and GridBagLayout (which adds support for variable control sizes and location with a grid).Generally, a layout manager connects itself to the action by listening for layout events from the container control. It then iterates through all the items in the Controls collection, and arranges them accordingly. Depending on the layout manager, this may mean ignoring the Location property and even the Size property of each control. It could also involve inspecting other extended properties, as I discuss at the end of this section.
The SingleLineFlow Layout Manager
A good example of a simple layout provider is shown with the following SingleLineFlow example. It lays out one control per line, from top to bottom, and gives each control the width of the container. It's ideal for a property page display (as with a TabPage container control).
public class SingleLineFlow
{
private Control container;
private int margin;
public SingleLineFlow(Control parent, int margin)
{
this.container = parent;
this.margin = margin;
// Attach the event handler.
container.Layout += new LayoutEventHandler(UpdateLayout);
// Refresh the layout.
UpdateLayout(this, null);
}
public int Margin
{
get
{
return margin;
}
set
{
margin = value;}
}
}
// This is public so it can be triggered manually if needed.
public void UpdateLayout(object sender,
System.Windows.Forms.LayoutEventArgs e)
{
int y = 0;
foreach (Control ctrl in container.Controls)
{
y += Margin;
ctrl.Left = Margin;
ctrl.Top = y;
ctrl.Width = container.Width;
ctrl.Height = Margin;
}
}
}
The bulk of the work is performed in the UpdateLayout() method, which adjusts the position of the controls in the container. The client doesn't need to call this method manually. Instead, once the layout manager is connected to the correct container, it fires automatically as controls are added or removed. The UpdateLayout() method arranges controls with a fixed height and uses the width of the container. Many more alternatives are possible-for example, you could record the width of the largest contained control, and resize all the other controls and the container itself to match.The following form code shows how easy it is to use the layout provider. It adds several check box controls to a TabPage container when a form is loaded. Because a layout provider is being used, the client doesn't need to worry about details like the position or size of the child controls-they are organized automatically.
private void form1_Load(object sender, System.EventArgs e)
{
// Create and attach the layout manager.
SingleLineFlow layoutManager = new SingleLineFlow(tabPage1, 20);
// Add 10 sample checkboxes.
CheckBox chkbox;
for (int i = 1; i < 11; i++)
{
chkbox = new CheckBox();
chkbox.Text = "Setting " + i.ToString();
tabPage1.Controls.Add(chkbox);
}
}
Without the layout manager, all the check boxes would just be layered on top of each other with the default size and the coordinates (0, 0). Figure 11-15 shows the result with the SingleLineFlow layout manager.

Figure 11-15: The SingleLineFlow layout manager in action
You can use the same technique with controls that you add at design time. However, in this case the layout logic isn't performed until the layout manager class is created. That means that the controls won't be organized in the IDE view at design-time. Instead, they will be reorganized when the program begins and you attach the layout manager.You could extend this example layout manager so that it creates a tabular layout, or so that it provides a multicolumn single line flow layout. The only limits are time and imagination.
Control Layout Engines As Extender Providers
In the previous example, the control layout engine treats all controls equally. However, what happens if you need a more customizable layout that allows individual control settings to affect it? For example, the default layout provided by Windows Forms gives every control a Size and a Location property that is used to determine where the control is placed in the container. Is it possible for you to add other layout properties (for example, a Justification or Column property) to standard controls?The answer is yes-if you develop your layout manager using extender providers. The basic strategy is as follows:
Create an extender provider for the container control. This will also be the main layout manager class. You can use this extender provider to add properties that configure the layout for all controls (like MarginWidth, or NumberOfColumns).
Create an extender provider for all other controls. This can add additional properties that configure the position of the control as governed by the layout manager. For example, you might provide a LayoutListPriority property. The smaller the LayoutListPriority, the higher the control would be placed by the SingleLineFlow layout manager. Controls with equal LayoutListPriority values would be entered in the order they are found in the Controls collection.
These techniques aren't a conceptual leap, but they do require some lengthy trial-and-error coding, and they are a specialty item that won't be of interest to all application programmers. Microsoft provides an interesting article about how custom layout can be developed with extender providers on their MSDN Web site (look for it at http://msdn.microsoft.com/library/en-us/dndotnet/html/custlaywinforms.asp). Their approach may be more effort than it's worth for most programmers (which may explain the article's low reader ranking), but it could also form the nucleus for an advanced localization system for a specialized product.