Creating the Application Basics
I want the example application to show two lists of data: a list of teams on the left that users can choose from, and the players for a selected team on the right. There are more players assigned to each team than there are teams, so I would the list of players should to take up more room than the list of teams, but users should to be able to resize both areas.
WPF provides a great way to create the basic mechanics for such a UI. When you adds a new window object using either Blend or Visual Studio, the created default includes a Grid layout element. Grids are very useful, but are also very confusingly named. Most developers think of "grids" as containers for lists of data organized in columns and rows. In WPF, a grid has nothing to do with displayed data; instead, a Grid is a layout element—a way of organizing other controls or elements in a window (or any other container).
|Figure 1. Manipulating Child Items: The yellow outline around the layout grid in the object tree indicates that it is selected for manipulation of child elements or adding columns.|
By default, a Grid has one row and one column, meaning the entire window is a single cell, which behaves very much like a Windows Forms window. You can drop controls into the cell, and then place and size them. Grids truly begin to add value when you add more cells. In the example application, the left and right sides of the window are independent areas. Creating a second column in the Grid provides a way to treat both sides of the window almost as if they were independent windows. You can (for instance) put controls into the "team" side of the window and arrange them within their cell. If that cell changes size for whatever reason (perhaps because the user resizes the area, or maybe because the window changed in size, or perhaps because you decide later to add another cell), you have to worry only about keeping the controls aligned and sized properly within that cell. This simplifies the overall setup of the interface. For this reason, Grid elements should be your default choice for layout in WPF.
Expression Blend makes it easy to add grid columns and rows visually. Simply select the grid in the object tree by double-clicking in it (the grid is a child of the window and appears as such in the object tree), which highlights it in yellow. It is important that the grid isn't just selected, but it is selected for child element editing, which is what the yellow outline indicates (see Figure 1
). If you've only selected an element (single click), you can manipulate the object's properties, but to add child elements such as columns, rows, or even other controls such as buttons, you need to select the grid with a double click.
After selecting the grid in this way, the visual design view shows "bars" above the top end left hand edge of the grid. You can move the mouse over that bar to get an orange "ghost" indicator for a new grid row or column (see Figure 2
). Click where you would like the new column to appear.
|Figure 2. Adding Rows and Columns: You add a new grid cell using the mouse and the visual designer.||
|Figure 3. Changing Column Properties: You can change column definition properties through the Properties pane.||
For this example choose to add the new column at a location about one-fourth the width of the window. After adding a column, you can change the width by dragging the column at the very top. You can also change column widths (or row height) through the Properties pane (see Figure 3
). To do that, make sure you've selected the grid in the object hierarchy and click on the ellipsis (
) button next to the ColumnDefinitions
property in the Layout
category in the Properties pane.
|Author's Note: You may need to extend the Layout category to see the property.
Note that by default, the width of the column will be set to something like "0.266 Star." WPF lets you define the width of columns in either pixels or stars (*
). Pixel widths are absolute width definitions based on logical pixels (one logical pixel is 1/96th of an inch, which maps to one pixel on a standard monitor, but may map to something else on high-res monitors). The star definition is similar to percentages. For example, you could set the width of column one to 25*
and column two to 75*
to achieve a 1:3
ratio. However, unlike percentages, star definitions do not have to add up to 100 percent—you could achieve the same spread by setting the first column width to 1*
and the second column to 3*
. This often simplifies the overall setup.
If you were to add an additional column on the right whose width is equal to the left column, you could simply add one more column with a 1*
width definition, rather than having to reset the percentages of all columns. This feature is especially useful in complex scenarios.
To make the two sides resizable, you'll use a GridSplitter. These objects automatically dock to the sides of a cell and provide a resize/slider grip for users. When a user moves the slider, the grid cells automatically resize. You could simply drop a GridSplitter into the second cell of the grid and dock it to the left edge of that cell. Unfortunately, the splitter has a certain width (typically about 3 pixels) that you have to subtract from the available area in the second cell, making the cell layout significantly more complex, especially if you want to change the splitter's width or remove it entirely. A more manageable approach is to add an additional and very narrow cell between the two existing cells and use that exclusively for the splitter.
To do so, add a grid cell before the second one and set its width to three pixels. Then, choose a GridSplitter from the Toolbox and place it inside the second cell. Set the splitter margins to zero on all four sides. Be sure you set it so it will stretch both horizontally and vertically. Also, you should set the Height
properties to Auto
(you can reset that value to Auto
by clicking the tiny white square to the left of the property value, which brings up a menu). This ensures that the splitter uses up the entire column area. Creating a special column just for the splitter greatly simplifies grid splitter management, so you should always place GridSplitters in their own columns or rows.
Note that you don't need to change the width definition of the two previously existing columns. The definition for this example simply means that you have a column three pixels wide, regardless of the overall width of the grid. The other two columns will use up the remaining width at a 1:3 ratio. This demonstrates how much easier the star system is than a percentage-based system. What is the percentage of a 3-pixel wide column? There is no right answer to this question because that depends on the overall width of the grid (and the window that hosts the grid). This was one of the classical problems of percentage-based definitions (as used by HTML for instance) that has been solved elegantly with the star system.
You can also inspect the XAML for the UI so far by clicking on the XAML tab. The following snippet shows the grid portion only: