Lesson 2 explained how to add controls to a form and arrange them nicely. Using those techniques, you can create forms like the one shown in Figure 3.1. (Although we haven't covered the code behind that program's form yet.)
That form looks okay in Figure 3.1, but what if the user enlarges the form as shown in Figure 3.2? Pretty lame, huh? Although the form is bigger, the areas that contain data are not.
The URL for the book selected in Figure 3.2 is too long to fit within the GroupBox
, so it is truncated even though the form has extra wasted space on the right. The ListBox
isn't big enough to display all of its items even though there's wasted space at the bottom. It would be nice if the controls rearranged themselves to use the available space and display the entire URL and more list items.
Figure 3.3 shows another problem with this form. If the user shrinks the form, the TextBox
es and URL LinkLabel
are chopped off, the Year Label
and TextBox
are chopped in half vertically, the ListBox
doesn't fit, and the cover picture is completely missing.
The program would look nicer if the controls were shrunk so you could at least see their edges. Some of the values still wouldn't fit, but at least the form wouldn't look so amateurish. You could even make the form refuse to shrink so it's too short to display the Year controls.
This lesson explains some simple ways you can make controls rearrange themselves to take advantage of whatever space is available, and how to give the form minimum and maximum sizes so the user can't resize it until it's completely useless.
Forms (and in fact all controls) have MinimumSize
and MaximumSize
properties that you can use to restrict the form's size. Simply set these properties to a width and height (or set their Width
and Height
sub-properties) and the form does the rest.
For example, to prevent the user from making the form shown in Figure 3.3 too small, you can set the form's MinimumSize
property to 663,
233
.
The MinimumSize
property prevents the user from making a form too small but it doesn't solve the problem shown in Figure 3.2. When the user resizes a form, it would be nice if the controls changed their sizes to match.
The Anchor
property lets a Windows Forms control resize itself when its container resizes. This property can take one or more of the values Top
, Bottom
, Left
, and Right
, in any combination. These values indicate that the control's edge should remain the same distance from the corresponding edge of its container.
For example, initially a control's Anchor
property is set to Top, Left
so it remains the same distance from its container's top and left edges. If you resize the form, the control doesn't move.
For a more interesting example, suppose you place a TextBox
on a form, set its Multiline
property to True
, arrange it so its edges are 12 pixels from the edges of the form, and set its Anchor
property to Top, Bottom, Left, Right
. Then when you resize the form, the TextBox
resizes itself so all of its edges remain 12 pixels from the form's corresponding edges.
To set the Anchor
property at design time, you can type a value like Top, Left, Right
into the Properties window or you can use the Properties window's Anchor
editor.
To use the editor, click the Anchor
property in the Properties window. Then click the dropdown arrow to the right to make the editor shown in Figure 3.4 appear. Click the skinny rectangles to select or deselect the anchors that you want to use. (In Figure 3.4 the top, bottom, and right anchors are selected.) When you're finished, press Enter to accept your changes or Esc to cancel them.
Using the Anchor
property, you can solve the problem shown in Figure 3.2. Table 3.1 gives the Anchor
property values used by the controls to let them take advantage of the form's available space.
Control | Anchor Property |
booksListBox |
Top, Bottom, Left |
detailsGroupBox |
Top, Bottom, Left, Right |
titleTextBox |
Top, Left, Right |
authorTextBox |
Top, Left, Right |
isbnTextBox |
Top, Left, Right |
urlLinkLabel |
Top, Left, Right |
coverPictureBox |
Top, Bottom, Right |
Now when the form resizes:
ListBox
stretches vertically to match the form's height.GroupBox
stretches vertically and horizontally to use as much of the form's width and height as possible.TextBox
es and LinkLabel
stretch horizontally to be as wide as possible while still fitting inside the GroupBox
.PictureBox
moves with the GroupBox
's right edge so it leaves as much room as possible to the left for the TextBox
es and LinkLabel
. It also stretches vertically as much as possible while fitting inside the GroupBox
.Figure 3.5 shows the result. Now the ListBox
is big enough to show all of its items and the LinkLabel
is big enough to show the entire URL.
Note that the TextBox
es and LinkLabel
do not stretch horizontally when the form resizes; they stretch when the GroupBox
that contains them resizes. In this example, when the form stretches, the GroupBox
stretches, and when the GroupBox
stretches, the TextBox
es and LinkLabel
stretch, so the result is the same.
The Anchor
property can handle most of your arranging needs, but some combinations of Anchor
values are so common that C# provides another property to let you handle these situations more easily: Dock
. The Dock
property lets you tell a control to attach itself to one of the edges of its container.
For example, a menu typically stretches across the top of a form. You could provide that behavior by setting the menu's Anchor
property to Top, Left, Right
, but setting Dock
to Top
is even easier.
The Dock
property can take one of six values. Left
, Right
, Top
, and Bottom
attach the control to the corresponding edge of its container. Fill
makes the control take up any space left over after any other controls' Dock
properties have had their way. None
detaches the control so its Anchor
property can take over.
The Dock
property processes positioning requests in a first-come-first-served order based on the controls' stacking order on the form. In other words, it positions the first control that it draws first. The second control positions itself in whatever space is left over. Then the third control positions itself in the remaining space, and so on.
Normally the stacking order is determined by the order in which you add controls to the form, but you can change the order by right-clicking a control and selecting Bring to Front or Send to Back. However, if you're working with a complicated set of Dock
properties and the stacking order gets messed up, it's often easier to delete all of the controls and start over from scratch.
Figure 3.6 shows a form holding five docked Label
s (with AutoSize
= False
). The numbers in the controls' Text
properties give the order in which they were created, which is also their stacking order.
The following list explains how the form's space was divvied up among the Labels
:
Label
has Dock = Top
, so it took the full width of the top part of the form.Label
has Dock = Left
, so it took the left edge of the remaining area (after the first Label
was positioned).Label
has Dock = Right
, so it took the right edge of the remaining area.Label
has Dock = Bottom
, so it took the bottom edge of the remaining area.Label
has Dock = Fill
, so it filled all of the remaining area.Visual Studio provides several controls that arrange the child controls that they contain in different ways. For example, the WPF Grid
control can arrange controls in rows and columns.
The following sections summarize layout containers for Windows Forms and WPF applications.
Windows Forms applications use only a few layout controls. Often most of a form's controls are placed directly on the form.
The following list summarizes the most useful Windows Forms layout controls.
Form
—The form itself is a layout container that lets you arrange controls by setting their Top
, Left
, Width
, Height
, Anchor
, and Dock
properties.FlowLayoutPanel
—Arranges controls left to right, right to left, top to bottom, or bottom to top, wrapping to new rows or columns if necessary.Panel
—Lets you arrange controls much as a form does by setting their Top
, Left
, Width
, Height
, Anchor
, and Dock
properties.TableLayoutPanel
—Lets you arrange controls in rows and columns. Set a control's RowSpan
and ColumnSpan
properties to let it span multiple rows or columns.Those few controls let you arrange controls very flexibly.
WPF applications use a different control arrangement philosophy than the one used by Windows Forms applications. In a Windows Forms application, a control's Anchor
and Dock
properties arrange that control as needed. In contrast, a WPF application typically uses containers to arrange controls.
For example, in a Windows Forms application, you might make a collection of Label
s and TextBox
es and line them up neatly in two columns and five rows. In contrast, in a WPF application you might create a Grid
control with two columns and five rows. You would then place Label
s and TextBox
es inside the Grid
's rows and columns. When the Grid
's rows and columns resize, the controls they contain resize.
The following list summarizes some of the most useful WPF container controls:
Canvas
—A simple control that lets you specify a control's X and Y positions.DockPanel
—Lets you dock child controls to the left, right, top, and bottom edges. If the LastChildFill
property is True
, the last child fills the remaining area.Grid
—Lets you arrange controls in rows and columns.StackPanel
—Arranges child controls vertically in a column or horizontally in a row.WrapPanel
—Arranges child controls vertically or horizontally much as a StackPanel
does except it wraps to a new column or row if necessary.For example, to make a layout similar to the one shown in Figure 3.5, you might use a Grid
that defines two columns. The left column could hold a vertical StackPanel
containing a Label
and the ListBox
. The right column could hold a GroupBox
containing a second Grid
that uses rows and columns to arrange the Label
s, TextBox
es, and Image
.
After you define the StackPanel
s, Grid
s, and other containers, you can add Label
s, TextBox
es, and other content controls to them.
Set a control's Margin
property to make it resize with its container. For example, Margin="10,7,10,0"
keeps a control's left, top, right, and bottom distances 10, 7, 10, and 0 pixels from its container's corresponding edges.
Set a control's Width
and Height
properties to give it a fixed size.
When you resize controls in the Window editor, you can click the symbols by the edges of a control's container to lock the sides of the control to the container's sides.
Arranging controls in this way can take a lot of work. Sometimes it's easier to just type XAML code in the Code Editor instead of using the interactive Window editor. However, the result is usually quite flexible and allows the controls to resize when the window resizes.
In this Try It, you get to practice using the Anchor
and Dock
properties by building the application shown in Figure 3.8.
When the window resizes, the TextBox
es and LinkLabel
stretch horizontally, and the PictureBox
stretches vertically. Notice in Figure 3.8 that the cover image is rather tall and thin. When the PictureBox
grows taller, it can display a larger version of the cover image. The control displays the image as large as possible without distorting it.
Note that the program you build won't actually do anything except sit there looking pretty and resizing controls when the form resizes. The techniques you need to make it respond to list selections are covered in later lessons.
In this lesson, you:
MenuStrip
, a StatusStrip
, and a Panel
. Use Dock
properties to make these three controls stay in their proper positions.Panel
.Anchor
property to make the ListBox
stretch vertically when the form resizes.Anchor
properties to make the TextBox
es and LinkLabel
stretch horizontally when the form resizes.Anchor
properties to make the PictureBox
resize vertically when the form resizes.TextBox
es and LinkLabel
stretch with the GroupBox
that contains them, not with the form itself. If you don't make the GroupBox
stretch, the controls it contains won't either.MenuStrip
to the form, select it, click the Type Here box that appears, and type &File. (The ampersand makes the “F” underlined.) Making the menu do something useful is covered in Lesson 5, so don't worry about that right now.StatusStrip
to the form and select it. Click the little dropdown arrow on the StatusStrip
and select StatusLabel
. Click the new StatusLabel
and use the Properties window to set its Text
to This is a StatusStrip.ListBox
, add a picture to the PictureBox
, and add text to the other controls, but don't worry about making the program take any actions.MenuStrip
, a StatusStrip
, and a Panel
. Use Dock
properties to make these three controls stay in their proper positions.
Size
and MinimumSize
properties to 726, 286
.MenuStrip
to the form. (Notice that by default the MenuStrip
has Dock = Top
.) Use the MenuStrip
hint from the “Hints” section of this lesson to create the empty File menu.StatusStrip
to the form. (Notice that by default the StatusStrip
has Dock = Bottom
.) Use the StatusStrip
hint from the “Hints” section of this lesson to create the This is a StatusStrip label.Panel
to the form. Set its Dock
property to Fill
. Set its BackColor
property to light green.Panel
.
LinkLabel
's AutoSize
property to False
and make it the same size as the TextBox
es.Text
values in the TextBox
es and LinkLabel
so you have something to look at. Enter enough items in the ListBox
so they won't all fit when the form has its initial size.PictureBox
's SizeMode
property to Zoom
. Place a relatively tall, thin image in its Image
property.Anchor
property to make the ListBox
stretch vertically when the form resizes.
ListBox
's Anchor
property to Top, Bottom, Left
.Anchor
properties to make the TextBox
es and LinkLabel
stretch horizontally when the form resizes.
GroupBox
's Anchor
property to Top, Bottom, Left, Right
.TextBox
es' and the LinkLabel
's Anchor
properties to Top, Left, Right
.Anchor
properties to make the PictureBox
resize vertically when the form resizes.
PictureBox
's Anchor
property to Top, Bottom, Left
.Run the program and see what happens when you resize the form.
TextBox
es resize horizontally when the form resizes. Use the OK and Cancel buttons as the form's accept and cancel buttons, and attach them to the form's lower-right corner.
Grid
with a DockPanel
. Add a Menu
(docked to the top), a StatusBar
(docked to the bottom), and a Grid
(filling the rest of the DockPanel
).StatusBarItem
inside the StatusBar
. Inside the StatusBarItem
add a Label
.Grid
two columns. In the left column, place a Label
and a ListBox
. Set the ListBox
's Height = Auto
and use its Items
property editor to add several ListBoxItem
s. Then use the XAML editor to add Label
s to the ListBoxItem
s. For example, one of the ListBoxItem
s might look like this:
<ListBoxItem>
<Label Content="Beginning Database Design Solutions"/>
</ListBoxItem>
GroupBox
to the Grid
's right column. A GroupBox
can have only a single child control. Make it a Grid
and give it the rows and columns you need to display the Image
, Label
s, and TextBox
es.A final tip: often it's easier to make one control just the way you want it and then copy and paste it in the XAML Code Editor. Don't forget to change the new control's name so you don't have two controls with the same name.
MenuStrip
and StatusStrip
with appropriate (default) Dock
values. Add a RichTextBox
control and set its Dock
property to Fill
. (That's all for now. In later lessons you'll add features to this program.)SplitContainer
control displays two areas separated by a splitter. The user can drag the splitter to divide the available space between the two areas. Make a program similar to the one shown in Figure 3.10. Feel free to use a different picture and information. Make the PictureBox
display its image as large as possible without distortion. Set the bottom TextBox
's MultiLine
property to True
and make it stretch vertically and horizontally as the form resizes. Make the other TextBox
es stretch horizontally. Set the SplitContainer
's Panel1MinSize
and Panel2MinSize
properties to 100
.
Grid
control. Then add a GridSplitter
to one of its rows or columns. The user can drag the GridSplitter
to resize the rows or columns on either side of it.
For this program, make a Grid
with three columns that have widths 1*, 5, and 2*. Place an Image
in the left column, a GridSplitter
in the middle column, and another Grid
in the right column.
Then make the GridSplitter
's XAML code look like this:
<GridSplitter Grid.Column="1" Margin="0,0,0,0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
Place the appropriate Label
s and TextBox
es in the Grid
on the right. Use the “Interesting Facts” Label
's Margin
and VerticalContentAlignment
properties to make its text stay centered in its area when you resize the form.
ListBox
es should be as large as possible and the three columns should divide the form evenly. (Hint: Use a TableLayoutPanel
.)
Expander
. It displays a header and an expander arrow. When the user clicks the arrow, the Expander
expands to display a child control, which is normally a Grid
.
Make a WPF program similar to the one shown in Figure 3.12. In that figure, the Expander
for Jupiter is expanded and the Expander
s for the other planets are collapsed. (Hints: The Window
in Figure 3.12 contains a StackPanel
that contains Expander
s holding Grid
s. First create the StackPanel
. Next make the Expander
and child controls for Mercury. Then copy and paste that Expander
in the XAML editor to make the Expander
s for the other planets.)