Chapter 2. Understanding the Basics of WPF

In This Chapter

  • Laying out applications

  • Using layout panels

  • Working with the grid

  • Implementing display-only, input, and list-based controls

As Chapter 1 explains, WPF brings not only a dramatic shift to the look and feel of Windows applications but also changes the manner of development. The days of dragging and dropping controls from the toolbox onto a form are long gone. Even though it is still possible to drag and drop in WPF, you will find yourself better off and much happier if you work in XAML directly.

What was once difficult is now relatively simple. For example, in traditional Windows applications, when the user changes the size of the form, the controls typically stay huddled in their corner and a large area of empty canvas is displayed. The only cure for this was a lot of custom code or expensive third-party controls. WPF brings the concept of flow layout from the Web into the Windows world.

In the GDI/GDI+ world of WinForms, modifying a control's style or building complex looks was a Herculean feat. WPF has completely redefined the control paradigm, giving you, the developer, the freedom to make a control do unimaginable tasks — including playing a video on a button face. However, keep in mind that just because you can do something doesn't mean you should!

In this chapter, we work with WPF's layout process to control the layout of your application and introduce you to the various WPF controls.

Using WPF to Lay Out Your Application

Traditional Windows Forms development deals in absolutes. Position and size for controls are decided at design time and are based on the resolution of the developer's machine. When applications are deployed to users, the form that looked great on the developer's machine could now look very different (and possibly be downright unusable) because of hardware resolution differences.

Instead of depending on screen resolution, WPF measures UI Elements in Device Independent Units (DIUs) that are based on the system DPI. This enables a consistent look between many different hardware configurations.

WPF layout is based on relative values and is adjusted at runtime. When you place controls in a layout container (see the next section), the rendering engine considers the height and width only as "suggested" values. Location is defined in relation to other controls or the container. Actual rendering is a two-step process that starts with measuring all controls (and querying them for their preferred dimensions) and then arranging them accordingly. If controls could speak, the conversation might go something like this:

  • Layout Engine: "Control, how much space would you like to have?"

  • <This is the Measure Stage>

  • Control: "I would like 50 DIUs for height, 100 DIUs for width, and a margin of 3 DIUs in the containing Grid cell."

  • Layout Engine asks all other controls and layout containers.

  • Layout Engine: "Sorry, you can have only 40 DIUs for height, but I can grant the rest of your requests."

  • <This is the Arrange Stage>

Arranging Elements with Layout Panels

Designing a Window begins with a Layout control, or Panel. Panels are different than Content controls in that they can hold multiple items, and depending on the Panel, a significant amount of plumbing is taken care of for you.

Panels control how UIElements relate to each other and to their containing UIElement and do not dictate absolute positioning. Most application Windows require some combination of Panels to achieve the required user interface, so it's important to understand them all. WPF ships with six core Panels:

  • Stack Panel

  • Wrap Panel

  • Dock Panel

  • Canvas

  • Uniform Grid

  • Grid

The Stack Panel

Stack Panels place UIElements in — wait for it — stacks. Items are placed in either a vertical pile (the default), like a stack of DVDs, or a horizontal arrangement, like books on a shelf. It is important to understand that the order items appear in the XAML is the order they appear in the Panel — the first UIElement in the XAML appears at the top (vertical) or on the far left (horizontal). Figures 2-1 and 2-2 show the same set of buttons in both orientations. Listing 2-1 contains the XAML for the Vertical Stack Panel shown in Figure 2-1.

Note

The code for these samples can be found at csharpfordummies.net.

Vertical Stack Panel.

Figure 2-1. Vertical Stack Panel.

Example 2-1. Vertical Stack Panel XAML

<StackPanel Name="pnlStack" Grid.Row="0"
   Orientation="Vertical">
    <Button Content="A Button"/>
    <Button Content="Another Button"/>
    <TextBlock Text="This is a text block"/>
    <Button Content="Short"/>
    <Button Content="Really Long Button Label"/>
</StackPanel>
Horizontal Stack Panel.

Figure 2-2. Horizontal Stack Panel.

Remember the conversation between the rendering Engine and the Control at the beginning of this chapter? The horizontal layout illustrates the clipping that can take place when the sum of the preferred sizes of controls in a container is larger than the container can hold.

Orientation (as well as all other properties) can be changed at runtime, as illustrated by Listing 2-2. The Button at the bottom of the Window changes the orientation, the button label, and the Window Title in the click event. Chapter 4 shows a better way of coding Button click events.

Example 2-2. Changing Stack Panel Orientation in Code

private void cmdOrientation_Click(object sender,
   RoutedEventArgs e)
{
    Button button = sender as Button;
    if (button.Content.ToString() == "Set Vertical")
    {
        pnlStack.Orientation = Orientation.Vertical;
        button.Content = "Set Horizontal";
        Title = "Stack Panel - Vertical";
    }
    else
    {
        pnlStack.Orientation = Orientation.Horizontal;
        button.Content = "Set Vertical";
        Title = "Stack Panel - Horizontal";
    }
}

The Wrap Panel

The Wrap Panel automatically wraps overflow content onto the next line(s). This is different than how a typical toolbar works, where overflow items are hidden when there isn't enough real estate to show them. Figures 2-3 and 2-4 show the same content controls from the Stack Panel (a mixture of buttons and a text block) in a Wrap Panel. The first Window has enough room to show all the UIElements, and the second shows the wrapping of elements because of a lack of room. The XAML for the Wrap Panel sample is in Listing 2-3.

Wrap Panel (wide form).

Figure 2-3. Wrap Panel (wide form).

Wrap Panel (narrow form).

Figure 2-4. Wrap Panel (narrow form).

Note that even with the Wrap Panel, if the container can't hold the widest item (the last button in the example), some clipping will take place.

Example 2-3. Wrap Panel XAML

<WrapPanel>
    <Button Content="A Button"/>
    <Button Content="Another Button"/>
    <TextBlock Text="This is a text block"/>
    <Button Content="Short"/>
    <Button Content="Really Long Button Label"/>
</WrapPanel>

The Dock Panel

The Dock Panel uses attached properties (introduced in Chapter 1 of this minibook) to "dock" child UIElements. (See Figure 2-5 and Listing 2-4.) An important thing to remember is that child elements are docked in XAML order, which means if you have two items assigned to the left side (through DockPanel.Dock="left"), the first UIElement as it appears in the XAML gets the far left wall of the Panel, followed by the next item.

The Dock Panel also has a setting called LastChildFill. If this is true, the last element in XAML will fill the remaining real estate. Elements (prior to the last XAML element) that do not have a Dock setting specified will default to DockPanel.Dock="Left".

Dock Panel.

Figure 2-5. Dock Panel.

Example 2-4. Dock Panel XAML

<DockPanel LastChildFill="True">
    <Button DockPanel.Dock="Left" Content="Far Left"/>
    <Button DockPanel.Dock="Left" Content="Near Left"/>
    <Button DockPanel.Dock="Top" Content="Top"/>
    <Button DockPanel.Dock="Bottom" Content="Bottom"/>
    <Button Content="Fill"/>
    <Button Content="Fill More"/>
</DockPanel>

Canvas

The Canvas is a bit of an anomaly in WPF, since it doesn't use flow layout, but goes back to fix position layout rendering. "What?!" you say. "I thought flow layout was the way of the future!"

Well, it is . . . most of the time. In some cases, part of your application needs to be laid out the "old way." A graphical application used to design floor plans is a perfect example.

Items are placed (or drawn) on the canvas relative to any side, and layering is handled through z-order. (See Figure 2-6 and Listing 2-5.)

Canvas sample.

Figure 2-6. Canvas sample.

Example 2-5. Canvas XAML

<Canvas>
    <Rectangle Canvas.Left="40" Canvas.Top="40"
   Height="53" Name="rectangle1" Stroke="Black" Width="96"
   Fill="#FFE22323" />
    <Ellipse Canvas.Left="28" Canvas.Top="142"
   Height="80" Name="ellipse1" Stroke="Black" Width="161"
   Fill="#FF0000FA" />
    <Ellipse Canvas.Left="96" Canvas.Top="14" Height="108"
   Name="ellipse2" Stroke="Black" Width="78" Fill="#FFE5D620"
   />
</Canvas>

The Uniform Grid

The Uniform Grid divides the layout area into equally sized cells. (See Figure 2-7 and Listing 2-6.) The number of Rows and Columns are defined in the UniformGrid XAML tag. As discussed in Chapter 1, cell contents are positioned using the Grid.Row and Grid.Column attached properties. Note that the Rows and Columns are zero-based.

The Uniform Grid is not as versatile as the Grid (see the next section), but if you need a very quick checkerboard pattern, it can be an effective Panel. In order to highlight the borders of the cells, I've added Borders. For more on Borders, see the section "Exploring Common XAML Controls," later in this chapter.

Uniform Grid.

Figure 2-7. Uniform Grid.

Example 2-6. Uniform Grid XAML

<UniformGrid Rows="2" Columns="2">
    <Border Grid.Row="0" Grid.Column="0" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="0,0"/>
    </Border>
    <Border Grid.Row="0" Grid.Column="1" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="0,1"/>
</Border>
    <Border Grid.Row="1" Grid.Column="0" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="1,0"/>
    </Border>
    <Border Grid.Row="1" Grid.Column="1" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="1,1"/>
    </Border>
</UniformGrid>

The Grid

Chapter 1 introduced the Grid, which is the most common starting point to screen design. The Grid is in fact the default panel in a Window when you add a new WPF Window to your project.

The Grid divides the layout area with rows (RowDefinitions) and columns (ColumnDefinitions). The difference between the Grid and the Uniform Grid is that the Grid allows for sizing of the cells by defining RowDefinition Height and ColumnDefinition Width.

Definitions for Rows and Columns are specified with the Grid.RowDefinitions and Grid.ColumnDefinitions tags (see Listing 2-7).

Example 2-7. XAML Grid RowDefinitions and ColumnDefinitions

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <RowDefinition Height="3*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
</Grid>

Sizing Rows and Columns

There are three GridUnitTypes used for defining Heights and Widths:

  • Pixel: Fixed size in Device Independent Units.

    You define a fixed height or width based on DIUs by specifying a number in the definition. This goes against the Flow Layout grain, but there are certainly valid reasons to do this, such as when a graphic image on a Window doesn't scale well (up or down) and needs to be a fixed size. Fixed sizing should be used with caution, as it can limit the effectiveness of the user interface. If the content is dynamic or needs to be localized, the controls could clip the content or wind up leaving a lot of wasted space.

  • Auto: Size is based on the preferred size of the contents.

    The Auto definition allows the Row or Column to determine how large (or small) it can be based on its content. This is decided during the measure stage of the layout process.

  • Star: Size uses all remaining space.

    The Star tells the rendering engine, "Give me all you've got! I'll take it all!" Each star defined gets an equal portion of what's left after all other sizing options have been computed. To achieve proportional sizing, multipliers can be added. For example, in Figure 2-8 (and in Listing 2-8), the first row uses 40 percent (⅖) of the available space and the second row uses the remaining 60 percent (⅗).

    Basic Grid with proportional (*) row heights.

    Figure 2-8. Basic Grid with proportional (*) row heights.

Example 2-8. Basic Grid XAML

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <RowDefinition Height="3*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Border Grid.Row="0" Grid.Column="0" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="0,0"/>
    </Border>
    <Border Grid.Row="0" Grid.Column="1" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="0,1"/>
    </Border>
<Border Grid.Row="1" Grid.Column="0" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="1,0"/>
    </Border>
    <Border Grid.Row="1" Grid.Column="1" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="1,1"/>
    </Border>
</Grid>

RowSpan and ColumnSpan

Similar to HTML tables, content in a Grid can span rows or columns by using the Grid.RowSpan and Grid.ColumnSpan attached properties. Figure 2-9 (and Listing 2-9) shows a Grid layout with the Border controls spanning both columns in the first row and spanning the next two rows in the first column.

Grid with row and column spans.

Figure 2-9. Grid with row and column spans.

Example 2-9. Column and Row Span XAML

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <RowDefinition Height="3*" />
        <RowDefinition Height="5*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Border Grid.Row="0" Grid.Column="0" Grid.
   ColumnSpan="2"  BorderBrush="Black" BorderThickness="1"
   HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <TextBlock Text="0,0 - 0,1"/>
    </Border>
    <Border Grid.Row="1" Grid.Column="0" Grid.
   RowSpan="2" BorderBrush="Black" BorderThickness="1"
   HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <TextBlock Text="1,0 - 2,1"/>
</Border>
    <Border Grid.Row="1" Grid.Column="1" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="1,1"/>
    </Border>
    <Border Grid.Row="2" Grid.Column="1" BorderBrush="Black"
   BorderThickness="1" HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch">
        <TextBlock Text="2,1"/>
    </Border>
</Grid>

Horizontal and vertical alignment within parent container's layout slot

You align an element within a container's layout slot by setting the VerticalAlignment and HorizontalAlignment properties (see the Border element in Listing 2-9 for an example). Horizontal settings are Center, Left, Right, and Stretch. Vertical options are Center, Top, Bottom, and Stretch. Stretch specifies the element to fill all available space. Explicit sizing of elements overrides the Stretch setting.

Content alignment within Content Controls

The same options can be used for setting the alignment of the Content within a Content Control by using the HorizontalContentAlignment and VerticalContentAlignment properties in the control.

Margin versus padding

Margins create space around a UIElement and its parent container. Margin values start with the left and rotate clockwise (which is different than CSS, just to keep you on your toes). You can also use some abbreviations. Setting the value as one number makes a uniform margin; setting the value to two numbers (comma separated) sets the left and right margins to the first number and the top and bottom margins to the second.

<Button Margin="2,4,2,4" Content="Push Me" /> <!--L,T,R,B-->
<Button Margin="2,4" Content="Push Me" /> <!--LR,TB-->
<Button Margin="2" Content="Push Me" /> <!--LTRB-->

Shared size groups

Most complex Windows require multiple Panels to achieve the desire User Experience. This can introduce erratic Windows if size of the content in one Grid is different than that of the other. Figure 2-10 illustrates the problem.

Fortunately, there is a simple solution. By setting the Grid.IsSharedSizeScope attached property on the parent Grid, all the child Grids can define Rows and Columns that subscribe to a SharedSizeGroup, and the rendering engine will ensure that they are sized correctly. Figure 2-11 illustrates the effects of setting these properties. The abbreviated XAML is shown in Listing 2-10.

Example 2-10. Shared Size Groups

<Grid Grid.IsSharedSizeScope="False">
    <Grid Grid.Row="1" Grid.Column="0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"
   SharedSizeGroup="Header" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
    </Grid>
    <Grid Grid.Row="3" Grid.Column="0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"
   SharedSizeGroup="Header"/>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
    </Grid>
</Grid>
Multiple Grids without shared sizing.

Figure 2-10. Multiple Grids without shared sizing.

Multiple Grids with shared sizing.

Figure 2-11. Multiple Grids with shared sizing.

Putting it all together with a simple data entry form

For complex data entry forms, the DataGrid is most appropriate (for more information, see the section "Exploring Common XAML Controls," later in this chapter). The data entry form in this example uses multiple Grids to achieve the desired look. The text boxes are contained in columns with Star sizing so they will grow and shrink with the form. Also notice how the buttons stay in the same relative position as the form size changes.

Listing 2-11 shows the XAML required to build the Window shown in Figures 2-12 and 2-13.

Example 2-11. Simple Data Entry Form XAML

<Grid Background="FloralWhite">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="10"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Label  Grid.Row="0" Grid.Column="0" Grid.RowSpan="2"
    Content="Name"
             HorizontalAlignment="Stretch" HorizontalContentAli
    gnment="Center">
        <Label.LayoutTransform>
            <RotateTransform Angle="−90"/>
        </Label.LayoutTransform>
    </Label>
    <Label Grid.Row="0" Grid.Column="1" Content="First:"
            HorizontalAlignment="Stretch" HorizontalContentAl
    ignment="Right"/>
     <TextBox Grid.Row="0" Grid.Column="2"
    HorizontalAlignment="Stretch" />
     <Label Grid.Row="1" Grid.Column="1" Content="Last:"
             HorizontalAlignment="Stretch" HorizontalContentAl
    ignment="Right"/>
     <TextBox Grid.Row="1" Grid.Column="2"
    HorizontalAlignment="Stretch" />
     <Label Grid.Row="2" Grid.Column="1" Content="Address:"
             HorizontalAlignment="Stretch" HorizontalContentAli
    gnment="Right"/>
<TextBox Grid.Row="2" Grid.Column="2"
    HorizontalAlignment="Stretch" />
     <Label Grid.Row="3" Grid.Column="1" Content="City:"
             HorizontalAlignment="Stretch" HorizontalContentAl
    ignment="Right"/>
     <TextBox Grid.Row="3" Grid.Column="2"
    HorizontalAlignment="Stretch" />
     <Button Grid.Row="3" Grid.Column="3" Content="Lookup"
    Margin="3,0,3,0"/>
     <Label Grid.Row="4" Grid.Column="1" Content="State:"
             HorizontalAlignment="Stretch" HorizontalContentAli
    gnment="Right"/>
     <Grid Grid.Row="4" Grid.Column="2">
         <Grid.RowDefinitions>
             <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="2*"/>
         </Grid.ColumnDefinitions>
         <TextBox Grid.Row="0" Grid.Column="0"
    HorizontalAlignment="Stretch" />
         <Label Grid.Row="0" Grid.Column="1" Content="Zip:"
    HorizontalAlignment="Right" />
         <TextBox Grid.Row="0" Grid.Column="2"
    HorizontalAlignment="Stretch" />
     </Grid>
     <Grid Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="3">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Button Grid.Row="0" Grid.Column="1" Content="Save"
   Margin="3,0"/>
        <Button Grid.Row="0" Grid.Column="2" Content="Close"
   Margin="3,0"/>
    </Grid>
</Grid>

And yes, that's a lot of XAML! One of the many great things about WPF is the flexibility to be able to create just about any look and feel you can dream up. But, sometimes (well, most of the time) it will take a lot of angle brackets.

Simple data entry form.

Figure 2-12. Simple data entry form.

Simple data entry form widened.

Figure 2-13. Simple data entry form widened.

In addition to using XAML for layout, all the examples shown can be done exclusively in code. "Shenanigans!" you say? No, it's true. Even Row/Column sizing.

Fixed sizing is specified by assigning a number to the Width property of the ColumnDefinition or RowDefinition. Assigning Auto or * is more complicated, since the Width property is of type GridLength. (See Listing 2-12.)

Example 2-12. Setting Auto and Star Sizing in Code

//Set to Auto sizing
column1 = new ColumnDefinition();
column1.Width = new GridLength(1, GridUnitType.Auto);
//Set to Star sizing
column2 = new ColumnDefinition();
column2.Width = new GridLength(1, GridUnitType.Star);

Note

For the full example (and many more), refer to the MSDN Documentation found here: http://msdn.microsoft.com/en-us/library/system.windows.gridunittype.aspx

Panels of honorable mention

In addition to the Panels already discussed, there are four additional specialized Layout Panels. As they are extremely specialized, I won't cover them in any great detail:

  • TabPanel: Handles the layout of items on a TabControl.

  • ToolbarPanel: Handles the layout of items in a Toolbar.

  • ToolbarOverflowPanel: Handles the layout of the controls that overflow from a Toolbar.

  • VirtualizingStackPanel: Used for large amounts of data binding scenarios. Renders only the visible items in the data collection.

  • InkCanvas: Canvas panel that accepts digital ink input. Used for scenarios like collection of signatures with Tablet PCs.

Exploring Common XAML Controls

A significant number of controls ship out of the box with Visual Studio 2010 (and more and more vendor-supplied controls are available for purchase). This section covers the more commonly used controls. I prefer to divide the available controls into three categories:

  • Display-only controls

  • Basic input controls

  • List- based controls

All the controls in this section are bindable to data (see Chapter 3 in this minibook) and modifiable through code.

Display only controls

Four main controls focus on displaying information to the user:

  • Image: The Image control display images (of type .bmp, .gif, .ico, .jpg, .png, .wdp, and .tiff). To preserve the image's aspect ratio, set the Width or Height, but not both. Additionally, the DecodePixelWidth should be set to the same size as the Width. This will cause the rendering engine to scale the image appropriately, potentially saving a significant amount of memory.

    Listing 2-13 shows the XAML to load an image that shows a color wheel. Only the Width and DecodePixelWidth are set. The resulting Window is shown in Figure 2-14.

    Example 2-13. Image Control

    <Image Grid.Row="0" Grid.Column="0" Width="256" >
        <Image.Source>
            <BitmapImage DecodePixelWidth="256" UriSource="/
       Images/1460_PaintPalette_256x256.png"/>
        </Image.Source>
    </Image>
    Image control.

    Figure 2-14. Image control.

  • TextBlock and Label: Both the TextBlock and the Label controls are designed to provide text or other content to the user with a few distinctions. The TextBlock control is designed to be the light-weight "little brother" to the Label, deriving directly from UIElement.

    The Label control provides access modifier capability and also derives from ContentControl, which opens up additional possibilities. Placing an underscore (_) before a letter enables the access modifiers. To provide an underscore in the Label, use a double underscore. In XAML, since it is essentially XML, the underscore is used because an ampersand would break the XAML. The Target attribute specifies the control to receive focus when the access modifier is keyed. You have to use a Binding expression, which is covered in Chapter 3.

    Both the TextBlock and Label controls are illustrated in Listing 2-14 and Figure 2-15.

    Example 2-14. TextBlock and Label

    <TextBlock Grid.Row="0" Grid.Column="0" Margin="5,0"
       HorizontalAlignment="Right" Text="Text_Block:"/>
    <TextBox Grid.Row="0" Grid.Column="1" Margin="5,0"
       HorizontalAlignment="Stretch"/>
    <Label Grid.Row="1" Grid.Column="0" Margin="5,0"
       HorizontalAlignment="Stretch"
           HorizontalContentAlignment="Right" Content="_Label__
       Content:" Target="{Binding ElementName=SampleTextBox}"/>
    <TextBox Name="SampleTextBox" Grid.Row="1" Grid.Column="1"
       Margin="5,0" HorizontalAlignment="Stretch" />

    In the sample, the "L" in the Label content is the access modifier and the double underscore adds an underscore character to the rendered output.

    TextBlock and Label controls.

    Figure 2-15. TextBlock and Label controls.

  • ProgressBar: The final display-only control is the Progress Bar. Although technically a descendant of the RangeBase class, it does not enable user input like the Slider (see the next section). Figure 2-16 shows a progress bar sample. To have the bar in perpetual motion, set the IsIndeterminate property to True (although in Visual Studio 2010 Beta 2, this is not functioning properly — I'm sure it will be fixed by the final release of Visual Studio 2010 and .NET 4).

    Progress Bar at 50 percent.

    Figure 2-16. Progress Bar at 50 percent.

Basic input controls

The workhorses of line of business applications are the basic input controls. You will find some of these on every Window you create, and they are very straightforward. Figure 2-17 shows all these controls on a single Window. Here are the basic input controls:

  • TextBox and PasswordBox: The TextBox and PasswordBox both allow for the input of standard text into the Window. The PasswordBox obfuscates the characters typed (using either the default system password character or a developer-specified character) and is used for collection of sensitive information. The TextBox exposes its contents through the Text property, the PasswordBox through the Password property.

    <TextBox Text="Some Text"/>
    
    <PasswordBox PasswordChar="X" Password="Some Text"/>
  • CheckBox: Check boxes represent Boolean values through the IsChecked property. The IsChecked property is nullable, which provides for three-state display (True, False, Unknown).

    <CheckBox IsChecked="True" Content="True"/>
    <CheckBox IsChecked="False" Content="False"/>
    <CheckBox IsChecked="{x:Null}" Content="Null"/>
  • RadioButton: Radio buttons allow for a single selection within a range of choices. The choices are determined by the GroupName property. After one of the radio buttons is selected, the group can only be entirely unselected programmatically.

    <RadioButton GroupName="RBSample" IsChecked="True" Content="Red"/>
    <RadioButton GroupName="RBSample" Content="White"/>
    <RadioButton GroupName="RBSample" Content="Blue"/>
  • Slider: The slider control is a ranged input control. Similar to the ProgressBar, the control takes Minimum, Maximum, and Interval values. Additionally, you can specify to show Ticks, the location, and the Tick frequency. Ticks? Those are the value lines that show on sliders.

    <Slider Interval="1" Minimum="1" Maximum="10" IsSnapToTickEnabled="True"
        TickPlacement="BottomRight" TickFrequency="1"/>
  • DatePicker: New in Visual Studio 2010, the DatePicker control provides a concise method for getting (or displaying) date information by combining a TextBox with a Calendar control. Included in the many options is the capability to select multiple dates for a range of dates.

    <DatePicker />
  • Calendar: Also new in Visual Studio 2010 is the Calendar Control. The difference between the Calendar and the DatePicker is the Calendar control is always in full display mode whereas the DatePicker's default look is similar to a text box.

    <Calendar />
  • Button: Okay, you caught me. The Button control doesn't really fit in with the other controls in this section, because it's more of an action control. Buttons respond to a user's click. The following code shows the Button implemented with an event handler in the Window's code-behind file. In Chapter 4, you find out how to use Commands to gain much more control of Buttons, but for now clicking the button merely displays "Hello World."

    <Button Content="Click Me" Click="Button_Click"/>
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Hello World");
    }
All the basic input controls.

Figure 2-17. All the basic input controls.

List-based controls

The list-based controls (also referred to as Item controls) add an incredible amount of flexibility. As I discuss in Chapter 1, the list based controls no longer have to rely on data tricks or other magic to make the content meaningful to the user but can be templated to show greater details about the Items contained.

Data binding is covered in great detail in Chapter 3, but the controls don't do anything unless you have something to show. The ComboBox, ListBox, and DataGrid control samples will be bound to a class that represents a Person. IList<Person>. See the Person class:

public class Person
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

Here are the list based controls:

  • ComboBox and ListBox: The ListBox and the ComboBox in the sample below use a DataTemplate to create the display for the contained Items. The main difference between the two controls is that the ComboBox displays a single item with a drop-down selector (see Figure 2-18) and the ListBox shows the entire list of items up to the allowed space and scrolls the rest (see Figure 2-19). The ComboBox can be set up to enable selecting items that are NOT in the list, as well as editing the items in the list.

    Both the ComboBox and ListBox shown in Figures 2-18 and 2-19 use the exact same XAML between the DataTemplate tags, so only the ComboBox XAML is shown in Listing 2-15.

    Example 2-15. ComboBox XAML

    <ComboBox Margin="5,0" HorizontalAlignment="Stretch" Hori
       zontalContentAlignment="Stretch" ItemsSource="{Binding
       Path=People}">
      <ComboBox.ItemTemplate>
        <DataTemplate>
          <Border HorizontalAlignment="Stretch"
       BorderBrush="AliceBlue" BorderThickness="1">
            <Grid HorizontalAlignment="Stretch">
              <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
              </Grid.RowDefinitions>
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
              </Grid.ColumnDefinitions>
              <TextBlock Grid.Row="0" Grid.Column="0" Grid.
       ColumnSpan="3" Margin="5,0" Text="{Binding Name}"/>
              <TextBlock Grid.Row="1" Grid.Column="0" Grid.
       ColumnSpan="3" Margin="5,0" Text="{Binding Address}"/>
              <TextBlock Grid.Row="2" Grid.Column="0"
       Margin="5,0" Text="{Binding City}"/>
              <TextBlock Grid.Row="2" Grid.Column="1"
       Margin="5,0" Text="{Binding State}"/>
              <TextBlock Grid.Row="2" Grid.Column="2"
       Margin="5,0" Text="{Binding Zip}"/>
           </Grid>
          </Border>
        </DataTemplate>
      </ComboBox.ItemTemplate>
    </ComboBox>
    The ComboBox.

    Figure 2-18. The ComboBox.

    The ListBox.

    Figure 2-19. The ListBox.

  • TreeView: The TreeView is a hierarchical ItemsControl much like Windows Explorer. The nodes (or branches) can be expanded or contracted, giving a nice user interface into any multilevel data. (See Figure 2-20.)

    The sample shown in Listing 2-16 (taken from MSDN) uses hard-coded data, but with a simple hierarchical template tree views can be bound just like any other control.

    Example 2-16. The TreeView XAML

    <TreeView Name="myTreeViewEvent" >
        <TreeViewItem Header="Employee1" IsSelected="True">
            <TreeViewItem Header="Jesper Aaberg"/>
            <TreeViewItem Header="Employee Number">
                <TreeViewItem Header="12345"/>
            </TreeViewItem>
            <TreeViewItem Header="Work Days">
                <TreeViewItem Header="Monday"/>
                <TreeViewItem Header="Tuesday"/>
                <TreeViewItem Header="Thursday"/>
            </TreeViewItem>
        </TreeViewItem>
        <TreeViewItem Header="Employee2">
            <TreeViewItem Header="Dominik Paiha"/>
            <TreeViewItem Header="Employee Number">
                <TreeViewItem Header="98765"/>
            </TreeViewItem>
            <TreeViewItem Header="Work Days">
                <TreeViewItem Header="Tuesday"/>
                <TreeViewItem Header="Wednesday"/>
                <TreeViewItem Header="Friday"/>
            </TreeViewItem>
        </TreeViewItem>
    </TreeView>
    The TreeView.

    Figure 2-20. The TreeView.

  • DataGrid: Also new in the .NET 4 (along with the DatePicker and Calendar controls) is the DataGrid. (See Figure 2-21.) Conspicuously absent from the earlier versions of WPF, this control was part of the WPF Toolkit, an out-of-band release available from www.codeplex.com/wpf (still a great resource for WPF information).

    The DataGrid has five base columns:

    • DataGridTextColumn: For Text

    • DataGridCheckBoxColumn: For Boolean

    • DataGridComboBoxColumn: For ListItems

    • DataGridHyperlinkColumn: For displaying Links

    • DataGridTemplateColumn: For designing custom columns

    The DataGrid can be set to AutoGenerate the columns based on the data it is bound to (as in the sample). It then uses reflection to determine the best column type based on the data.

    <DataGrid HorizontalAlignment="Stretch"
       VerticalAlignment="Stretch"
       AutoGenerateColumns="True" ItemsSource="{Binding
       People}">
    </DataGrid>
    A simple Data Grid.

    Figure 2-21. A simple Data Grid.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.102.142