After controls are positioned, sized, and z-ordered the way you like, you might think that’s all you need to do. That is, until a user resizes your form. For example, suppose users want to enter a long string into a text box. They may attempt to widen your form, as shown in Figure 4.8.
Figure 4.8 All Controls Anchored Top, Left
Users aren’t likely to be happy with this less-than-professional resizing; ideally, the text box should expand as the form does.
Anchoring
Preserving the distance between the edge of a control and the adjacent edge of a control’s container is a technique known as anchoring. By default, all controls are anchored to the top, left edges of their containers. We’re accustomed to Windows moving child controls to keep this anchoring intact as the container’s left or top edge changes. However, Windows does
only so much; it doesn’t resize controls to anchor them to other edges. Fortunately, Windows Forms does so without any coding required on your part.
For example, you can change the edges to which a control is anchored by changing the Anchor property to any bitwise combination of the values in the AnchorStyles enumeration:
namespace System.Windows.Forms { enum AnchorStyles {
None = 0,
Top = 1, // default Bottom = 2,
Left = 4, // default Right = 8,
} }
Getting our text box to resize as the form is resized, we change the Anchor property to include the right edge as well as the left and the top edges. Using the Properties window, you even get the fancy drop-down editor shown in Figure 4.9.
When we set the text box from Figure 4.8 to be anchored to the top, left, and right, the Windows Forms Designer generates the following (elided) code:
// AnchoringForm.designer.cs partial class AnchoringForm {
...
void InitializeComponent() { ...
this.anchoredTextBox.Anchor = AnchorStyles.Top |
Figure 4.9 Setting the Anchor Property in the Properties Window
AnchorStyles.Left | AnchorStyles.Right;
...
} }
This code makes sure that the text box resizes so that its right edge always resides the same distance from the right edge of the host form’s client area, as illustrated in Figure 4.10.
Figure 4.10 Automatic Control Resizing with the Anchor Property
Even though the default for Windows Forms controls is to anchor to the top and left edges of a form, anchoring does not have to include either of those edges. For example, it’s common to anchor a modal dialog’s OK and Cancel buttons to only the bottom and right edges; in this way, these buttons stay at the bottom-right corner as the dialog is resized but aren’t resized themselves. A control is resized if the user has selected two opposing edges.
If neither of the opposing edges is selected, neither left nor right, then the control is not resized in that dimension but instead maintains the same proportion of space between the opposing edges. The middle square in Figures 4.11 and 4.12 shows this behavior as well as several other anchoring combinations.
Figure 4.11 Anchoring Settings Before Widening
So far, we’ve concentrated on what happens when a form increases in size. However, you may need to pay special attention when a form’s size decreases. For example, consider what happens when the form in Figure 4.11 is made smaller, resulting in the form shown in Figure 4.13.
Figure 4.12 Anchoring Settings After Widening
Figure 4.13 Anchor Settings After Narrowing
The way the controls are anchored causes controls to overlap as the edges they are anchored to come close together. You can resolve this by setting your form to a suitable min-imum size using its Minmin-imumSize property.
Docking
As powerful as anchoring is, it doesn’t do everything by itself. For example, if you wanted to build a text editor, you’d probably like to have a menu, a tool strip, and a status strip hugging the edges with a text box that takes up the rest of the client area not occupied by the other controls. Anchoring would be tricky in this case, because some controls need more or less space depending on the run-time environment they find themselves in.
Because anchoring depends on keeping a control a fixed number of pixels away from a form’s edge, we’d have to do some programming at run-time to figure out, for example, how high the status strip was and then set that as the distance to anchor the text box away from the edge. Instead, it is far easier to tell the form that the text box should simply take whatever space remains in the client area. For that, we have docking.
Docking is a way to identify the edge that we want a control to “stick” itself to. For exam-ple, Figure 4.14 shows a form with three controls, all docked. The menu strip is docked to the top edge, the status strip is docked to the bottom edge, and the text box is docked to fill the rest.
You configure docking behavior in the Properties window (shown in Figure 4.15) by set-ting a control’s Dock property, which is one of the DockStyle enumeration values:
namespace System.Windows.Forms { enum DockStyle {
None = 0, // default Top = 1,
Bottom = 2, Left = 3, Right = 4, Fill = 5, }
}
Figure 4.14 A Docking Example
Docking can also be affected by z-order and needs special consideration.
Docking and Z-Order
As a form resizes, the docking settings keep the controls along their designated edges (or the rest of the space, as determined by the Fill DockStyle). It’s even possible to have mul-tiple controls docked to the same edge, as shown in Figure 4.16.
Figure 4.15 Setting the Dock Property in the Properties Window
Figure 4.16 Two Status Strips Docked to the Bottom Edge
Although I don’t recommend docking two status strips to the same edge, it’s certainly possible. Docking is done in reverse z-order priority. In other words, for statusStrip1 to be closest to the bottom edge, it must be further down in the z-order than statusStrip2. The fol-lowing Add calls give statusStrip1 edge priority over statusStrip2:
// DockingForm.designer.cs partial class DockingForm {
...
void InitializeComponent() { ...
this.Controls.Add(this.textBox1); // z-order 0 this.Controls.Add(this.menuStrip1); // z-order 1 this.Controls.Add(this.statusStrip2); // z-order 2 this.Controls.Add(this.statusStrip1); // z-order 3 ...
} }
Given the drag-and-drop Windows Forms Designer model, which inserts each new con-trol with a z-order of 0, it makes sense that docking priority is the reverse of z-order. How-ever, as you add new controls on the form and need to adjust the z-order, you may find a conflict between controls along a certain edge and those set to fill. In that case, the fill con-trol needs to have the lowest edge priority on the form, or else it will dock all the way to an edge that is set to be used by another control. Figure 4.17 shows an example.
Figure 4.17 TextBox Whose DockStyle.Fill Has Higher Docking Priority Than a StatusStrip
Notice that the text in the bottom part of the text box is cut off by the status strip along the bottom edge. This indicates that the status strip has a lower docking priority than the text box. However, docking priority isn’t set directly in the Designer. Instead, you set the z-order. In our example, right-clicking on the text box in the Designer and choosing Bring To Front pushes the text box to the top of the z-order but to the bottom of the docking pri-ority, letting the status strip own the bottom edge and removing it from the client area that the text box is allowed to fill, as Figure 4.18 illustrates.
Whenever you see a visual anomaly like this on your form, you can usually resolve the problem by bringing to the front the control whose Dock property is set to DockStyle.Fill.
Alternatively, you can use Document Outline from the View | Other Windows menu.
DockStyle.Fill has special significance in Windows Forms because there are several con-trols that should naturally fill their containers as soon as they are dropped onto a form. It’s common for these controls, including Panel, to implement smart tags, with an option to tog-gle a control’s Dock property between DockStyle.Fill and the previous DockStyle value, as shown in Figure 4.19.
Figure 4.18 TextBox Whose DockStyle.Fill Has Lower Docking Priority Than a StatusStrip
Figure 4.19 Setting the Dock Property via a Smart Tag
The set of controls that actually provides the “Dock in parent container” smart tag is somewhat smaller than you might expect. For example, DataGridView and Panel offer this, but GroupBox, ListBox, and TabControl don’t.1
Some controls, such as MenuStrip, ToolStrip, and StatusStrip, take it one step further by automatically docking to the most logical edge when dropped onto a form; MenuStrip
1 Those .NET Framework controls that do support this feature are augmented with the Docking attribute, which is covered in Chapter 12: Design Time Integration: Designers and Smart Tags.
n n
w e
and ToolStrip dock to the top, and StatusStrip docks to the bottom. The default dock set-tings can be changed as necessary, although they suffice for most scenarios.
Docking with Tool Strip Controls
After most controls are docked to a particular edge at design time, they remain there after compilation until the UI needs an update or an application becomes obsolete. In this respect, tool strip controls are special because they allow users to dynamically redock them at run-time. For example, Figure 4.20 shows a ToolStrip control being dragged from a form’s top edge to the same form’s bottom edge by the user at run-time.
Figure 4.20 Dragging a ToolStrip Control at Run-Time
Rather than force you to handle the complexities of dynamically juggling the Dock property values of all controls to cope with run-time tool strip dragging, System.
Windows.Forms comes with the ToolStripContainer control. Its purpose is to simplify design-time tool strip composition and enable run-time tool strip dragging independently of the docked arrangement of other controls on a form.
ToolStripContainer
When you drag a ToolStripContainer onto your form, the first thing you should do is set its Dock property to DockStyle.Fill, as shown in Figure 4.21.
Figure 4.21 Form Filled with a ToolStripContainer (with the top ToolStripPanel visible)
n n
e e
w w
By its nature, ToolStripContainer should be dock-filled to cover the entire surface area of a form, because it provides special docking semantics to the form itself, independent of controls hosted on the form. However, ToolStripContainer provides a special area for those controls known as the content panel. The content panel is abstracted as the ToolStripContent-Panel, which derives from Panel. ToolStripContainer exposes the content panel through its ContentPanel property.
ToolStripContainer provides four special areas to host the tool strip controls, one for each edge of the ToolStripContainer. Each of these is a ToolStripPanel hosted by ToolStrip-Container and exposed via four properties: TopToolStripPanel, BottomToolStripPanel, Left-ToolStripPanel, and RightToolStripPanel.2 By default, all tool strip panels are visible, although you can hide or show each by setting one of the four following properties as appropriate: TopToolStripPanelVisible, BottomToolStripPanelVisible, LeftToolStripPanel-Visible, and RightToolStripPanelVisible. Alternatively, you can use the ToolStripContainer’s Properties window or smart tag, as shown in Figure 4.22.
2 You can also use ToolStripPanels individually by adding them to the Toolbox. Click Choose Items | .NET Framework Components | ToolStripPanel. From there, you can drag them onto your form and treat them like any other control. This is especially useful if you don’t need full ToolStripContainer support.
Figure 4.22 Configuring Which Edges That Tool Strip Controls Can Be Dragged to at Run-Time
Users can drag tool strip controls across any tool strip panels that remain visible after your configurations.
Each tool strip panel needs to be expanded before you can drop tool strip controls onto it by clicking its expand or collapse button, shown in Figure 4.21. By default, the top tool strip panel is expanded so that you can drag a tool strip control right onto it without man-ually expanding it.
Figure 4.23 shows a form that uses a ToolStripContainer hosting a MenuStrip and Tool-Strip control in its top tool strip panel, and a StatusTool-Strip in its bottom tool strip panel. It sup-ports tool strip dragging across only the top and bottom tool strip panels.
As you can see, tool strip panels can host multiple tool strips, stacking them either hor-izontally or vertically to match the tool strip panel in which they’re hosted. Also, one or more tool strip controls can be hosted in the same row; they do not have to be positioned flush against another tool strip control in the same row, or flush against the adjacent form edge. The Windows Forms Designer allows you to drag and position tool strips within these constraints, storing the final design-time position of each using the Location property.
Run-Time Tool Strip Support
At run-time, a tool strip can be dragged from one tool strip panel to another, as long as it has a drag grip, as determined by a tool strip’s GripStyle property. GripStyle can be one of two ToolStripGripStyle enumeration values: Visible or Hidden. By default, ToolStrip.GripStyle is set to ToolStripGripStyle.Visible, whereas MenuStrip.GripStyle and StatusStrip.GripStyle are set to ToolStripGripStyle.Hidden.
Additionally, you can allow users to drag tool strip items around their host tool strip.
You enable this by setting a tool strip’s AllowItemReorder property to true. At run-time, users drag a tool strip item by pressing the Alt key and dragging the item using the left mouse button.
Of course, when users can move tool strips and tool strip items around, they’ll want them to appear in the same position they left them in the previous application session when they start a new session. Consequently, you need to store pertinent tool strip details such as size, location, visibility, and order of tool strip items at the end of a session, refreshing them at the beginning of the next. This support is dependent on the settings system and is discussed in Chapter 15: Settings.
ToolStrip Overflow
There is one side effect you should consider when allowing users to drag tool strips around a form: Tool strips might end up being aligned to edges that are too small to display them in
Figure 4.23 A ToolStripContainer with Three Tool Strip Controls and Dragging Supported Between Top and Bottom Panels
n n
w e
w
their entirety. Some tool strip items may overflow the edge of a form and their functionality become hidden. The solution is to give users a visual cue that more tool strip items are avail-able and then provide access to them.
Such support is native to all tool strip controls via a special drop-down menu, called a chevron, shown on a ToolStrip in Figure 4.24.
Figure 4.24 ToolStrip Control with Overflowing Tool Strip Items
Whether a tool strip supports overflow is determined by its Boolean CanOverflow prop-erty, which is set to true by default.
But that’s only half the story; hosted tool strip items are also responsible for instructing their containing tool strip how they should be treated if visual overflow is enabled. To do this, each tool strip item exposes an Overflow property, which stores one of the following values of the ToolStripItemOverflow enumeration:
namespace System.Windows.Forms { enum ToolStripItemOverflow {
Never = 0 // Never overflow
Always = 1, // Always overflow, even if enough space to show AsNeeded = 2, // Overflow space runs out (default)
} }
For ToolStrip, CanOverflow defaults to true, and the Overflow property for most tool strip items defaults to ToolStripItemOverflow.AsNeeded. As a result, most of your tool strip resizing needs are handled out of the box, at both design-time and run-time.
MenuStrip exposes CanOverflow, although it’s false by default and can’t be set from the various Designer windows such as the Properties window. However, you can programmat-ically set it to true, and you need to update the Overflow property for any hosted tool strip menu items because their default Overflow value is Never.
StatusStrip has the same design as MenuStrip with regard to its CanOverflow property, so if you need it to overflow, you must do so programmatically. You also need to change its layout style to StackWithOverflow (discussed later), because the default is Table, which doesn’t support overflowing. Fortunately, the various tool strip items you can host on a
StatusStrip have their Overflow properties set to AsNeeded, so you don’t need to recon-figure them.
Tool strip item alignment also plays a role in overflow behavior by determining which tool strip items overflow before others. Tool strip alignment is governed by the Alignment property, which is implemented by tool strip items to allow you to specify that they glue themselves to the left or right edge of a tool strip control, as shown in Figure 4.25.
Figure 4.25 Tool Strip Items with Left and Right Alignment
For a tool strip whose items are all left-aligned, the right-most item is the first to over-flow, followed by its siblings in right-to-left order as their host tool strip’s width decreases.
When a combination of left- and right-aligned tool strip items coexists on a tool strip, the set of right-aligned items overflows starting with the leftmost tool strip item, and then each tool strip item overflows in left-to-right order. When all right-aligned items have over-flowed, the set of left-aligned items overflows in right-to-left order.
ToolStrip Layout Styles
Sometimes, it is preferable to show all tool strip items irrespective of the size of a host form.
One common example is the menu strip, which typically increases in height to make room for tool strip items that would otherwise become hidden as the width decreases. Figure 4.26 illustrates the default behavior of MenuStrip.
Figure 4.26 Collapsing MenuStrip Control
As the form’s width decreases, the MenuStrip hides its items while providing an over-flow chevron to access them. A tool strip’s behavior is determined by its LayoutStyle prop-erty, whose value comes from ToolStripLayoutStyle:
namespace System.Windows.Forms { enum ToolStripLayoutStyle {
StackWithOverflow = 0, // MenuStrip and ToolStrip default HorizontalStackWithOverflow = 1, // Items placed horizontally,
// with overflow
VerticalStackWithOverflow = 2, // Items laid out vertically, // with overflow
Flow = 3, // Items wrap horizontally, in either RTL or LTR order Table = 4 // Items arranged in rows and columns
// (StatusStrip default) }
}
By default, MenuStrip and ToolStrip controls have a ToolStripLayoutStyle of StackWith-Overflow, whose effect is to automatically switch between HorizontalStackWithOverflow and VerticalStackWithOverflow as either MenuStrip or ToolStrip is dragged to a horizon-tal or vertical edge, respectively. If you want your MenuStrip’s or ToolStrip’s items to always be laid out either horizontally or vertically, irrespective of the orientation of the edge to which they are aligned, you can explicitly choose HorizontalStackWithOverflow or Ver-ticalStackWithOverflow as required. All three of these options automatically set a MenuS-trip or ToolSMenuS-trip to overflow its items as necessary.
While a MenuStrip might be set to horizontally stack with overflow by default, but it doesn’t actually provide overflow behavior (by showing the overflow chevron). Instead, items become hidden as a form’s width decreases, which is fine because the default
While a MenuStrip might be set to horizontally stack with overflow by default, but it doesn’t actually provide overflow behavior (by showing the overflow chevron). Instead, items become hidden as a form’s width decreases, which is fine because the default