Chapter 14
Basic Desktop Programming

Wrox.com Code Downloads for this Chapter

You can find the wrox.com code downloads for this chapter at www.wrox.com/go/beginningvisualc#2015programming on the Download Code tab. The code is in the Chapter 14 download and individually named according to the names throughout the chapter.

The first part of this book has concerned itself with the ins and outs of C#, but now it is time to move away from the details of the programming language and into the world of the graphical user interface (GUI).

Over the past 10 years, Visual Studio has provided the Windows developers with a couple of choices for creating user interfaces: Windows Forms, which is a basic tool for creating applications that target classic Windows, and Windows Presentation Foundations (WPF), which provide a wider range of application types and attempts to solve a number of problems with Windows Forms. WPF is technically platform-independent, and some of its flexibility can be seen in the fact that a subset of WPF called Silverlight is used to create interactive web applications. In this and the next chapter you are going to learn how to use WPF to create Windows applications, and in Chapter 23 you will build on this knowledge when you create Universal Windows Apps.

At the heart of the development of most graphical Windows applications is the Window Designer. You create a user interface by dragging and dropping controls from a Toolbox to your window, placing them where you want them to appear when you run the application. With WPF this is only partly true, as the user interface is in fact written entirely in another language called Extensible Application Markup Language (XAML, pronounced zammel). Visual Studio allows you to do both and as you get more comfortable with WPF, you are likely going to combine dragging and dropping controls with writing raw XAML.

In this chapter, you work with the Visual Studio WPF designer to create a number of windows for the card game that you wrote in previous chapters. You learn to use some of the many controls that ship with Visual Studio that cover a wide range of functionality. Through the design capabilities of Visual Studio, developing user interfaces and handling user interaction is very straightforward — and fun! Presenting all of Visual Studio's controls is impossible within the scope of this book, so this chapter looks at some of the most commonly used controls, ranging from labels and text boxes to menu bars and layout panels.

XAML

XAML is a language that uses XML syntax and enables controls to be added to a user interface in a declarative, hierarchical way. That is to say, you can add controls in the form of XML elements, and specify control properties with XML attributes. You can also have controls that contain other controls, which is essential for both layout and functionality.

XAML is designed with today's powerful graphics cards in mind, and as such it enables you to use all the advanced capabilities that these graphics cards offer through DirectX. The following lists some of these capabilities:

  • Floating-point coordinates and vector graphics to provide layout that can be scaled, rotated, and otherwise transformed with no loss of quality
  • 2D and 3D capabilities for advanced rendering
  • Advanced font processing and rendering
  • Solid, gradient, and texture fills with optional transparency for UI objects
  • Animation storyboarding that can be used in all manner of situations, including user-­triggered events such as mouse clicks on buttons
  • Reusable resources that you can use to dynamically style controls

Separation of Concerns

One problem that exists with maintaining Windows applications that has been written over the years is that they very often mix the code that generates the user interface and the code that executes based on users' actions. This makes it difficult for multiple developers and designers to work on the same project. WPF solves this in two ways. First, by using XAML to describe the GUI rather than C#, the GUI becomes platform independent, and you can in fact render XAML without any code whatsoever. Second, this means that it feels natural to place the C# code in a different file than you place the GUI code. Visual Studio utilizes something called code-behind files, which are C# files that are dynamically linked to the XAML files.

Because the GUI is separated from the code, it is possible to create tailor-made applications for designing the GUI, and this is exactly what Microsoft has done. The design tool Blend for Visual Studio is the favored tool used by designers when creating GUIs for WPF. This tool can load the same projects as Visual Studio, but where Visual Studio targets the developer more than the designer, the opposite is true in Expression Blend. This means that on large projects with designers and developers, everyone can work together on the same project, using their preferred tool without fear of inadvertently influencing the others.

XAML in Action

As stated, XAML is XML, which means that as long as the files are fairly small, it is possible to see immediately what it is describing. Take a look at this small example and see if you can tell what it does:

<Window x:Class="Ch14Ex01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="Hello World" Height="350" Width="525">
    <Grid>
    <Button Content="Hello World"
            HorizontalAlignment="Left"
            Margin="220,151,0,0"
            VerticalAlignment="Top"
            Width="75"/>
  </Grid>
</Window>

The XAML in this example creates a window with a single button on it. Both the window and the button display the text "Hello World". XML allows you to place tags inside other tags as long as you close them properly. When an element in placed inside another in XAML, this element becomes the content of the enclosing element, meaning that the Button could also have been written like this:

    <Button HorizontalAlignment="Left"
            Margin="220,151,0,0"
            VerticalAlignment="Top"
            Width="75">
      Hello World
    </Button>

Here, the Content property of the Button has been removed and the text is now a child node of the Button control. Content can be just about anything in XAML, which is also demonstrated in this example: The Button element is the content of the Grid element, which is itself the content of the Window element.

Most, if not all, controls can have content, and there are very few limits to what you can do to change the appearance of the built-in controls. Chapter 15 explores this in more detail.

Namespaces

The Window element of the previous example is the root element of the XAML file. This element usually includes a number of namespace declarations. By default, the Visual Studio designer includes two namespaces that you should be aware of: http://schemas.microsoft.com/winfx/2006/xaml/presentation and http://schemas.microsoft.com/winfx/2006/xaml. The first one is the default namespace of WPF and declares a lot of controls that you are going to use to create user interfaces. The second one declares the XAML language itself. Namespaces don't have to be declared on the root tag, but doing so ensures that their content can be easily accessed throughout the XAML file, so there is rarely any need to move the declarations.

When you create a new window in Visual Studio, the presentation namespace is always declared as the default and the language namespace as xmlns:x. As seen with the Window, Button, and Grid tags, this ensures that you don't have to prefix the controls you add to the window, but the language elements you specify must be prefixed with an x.

The last namespace that you will see quite often is the system namespace: xmlns:sys="clr-namespace:System;assembly=mscorlib". This namespace allows you to use the built-in types of the .NET Framework in your XAML. By doing this, the markup you write can explicitly declare the types of elements you are creating. For example, it is possible to declare an array in markup and state that the members of the array are strings:

  <Window.Resources>
    <ResourceDictionary>
      <x:Array Type="sys:String" x:Key="localArray">
        <sys:String>"Benjamin Perkins"</sys:String>
        <sys:String>"Jacob Vibe Hammer"</sys:String>
        <sys:String>"Job D. Reid"</sys:String>
      </x:Array>
    </ResourceDictionary>
  </Window.Resources>

Code-Behind Files

Although XAML is a powerful way to declare user interfaces, it is not a programming language. Whenever you want to do more than presentation, you need C#. It is possible to embed C# code directly into XAML, but mixing code and markup is never recommended and you will not see it done in this book. What you will see quite a lot is the use of code-behind files. These files are normal C# files that have the same name as the XAML file, plus a .cs extension. Although you can call them whatever you like, it's best to stick to the naming convention. Visual Studio creates code-behind files automatically when you create a new window in your application, because it expects you to add code to the window. It also adds the x:Class property to the Window tag in the XAML:

  <Window x:Class="Ch14Ex01.MainWindow"

This tells the compiler that it can find the code for this window in, not a file, but the class Ch14Ex01.MainWindow . Because you can specify only the fully qualified class name, and not the assembly in which the class is found, it is not possible to put the code-behind file somewhere outside of the project in which the XAML is defined. Visual Studio puts the code-behind files in the same directory as the XAML files so you never have to worry about this while working in Visual Studio.

The Playground

Now you know enough about how WPF is constructed to start getting your hands dirty, so it's time to look at the editor. Start by creating a new WPF project by selecting File 1 New 1 Project. From the New Project dialog box, navigate to the Clasic Desktop node under Visual C# 1 Windows and select the project template WPF Application. To be able to reuse this example with the next examples, name the project Ch14Ex01.

Visual Studio now displays an empty window and a number of panels around it. The greater part of the screen is divided in two sections. The upper section, known as the Design View, displays a WYSIWYG (What You See Is What You Get) representation of the window you are designing and the lower section, known as the XAML View, displays a textual representation of the same window.

To the right of the Design View, you see the Solution Explorer that you have seen in previous projects and a Properties panel that displays information about the current selection in the Design and XAML Views. It is worth noting that the selection in the Properties panel, XAML View, and Design View are always in sync, so if you move the cursor in the XAML View you will see the selection change in the other two.

Collapsed to the left of the Design View are a number of panels, one of which is the Toolbox. This chapter shows you how to use many of the controls from the Toolbox panel to create dialog boxes for the card game, so expand it and pin it open by clicking the pin in the top-right corner. While you are at it, expand the Common WPF Controls node in the panel as well. You will be using most of the controls shown here in this chapter.

WPF Controls

Controls combine prepackaged code and a GUI that can be reused to create more complex applications. They can define how they draw themselves by default and a set of standard behaviors. Some controls, such as the Label, Button, and TextBox controls are easily recognizable and have been used in Windows applications for about 20 years. Others, such as Canvas and StackPanel, don't display anything and simply help you create the GUI.

Out-of-the-box controls look exactly as you would expect a control to look in a standard Windows application and use the current Windows Theme to draw themselves. All of this is highly customizable and with only a few clicks you can completely change how a control is displayed. This customization is done using properties that are defined on the controls. WPF uses normal properties that you have seen before and adds a new type of property called a dependency property. These are examined in detail in Chapter 15, but for now it is enough to know that many of the properties of WPF do more than just get and set a value; for one, they are able to notify observers of changes.

Besides defining how something looks on the screen, controls also define standard behavior, such as the ability to click on a button and select something in a list. You can change what happens when a user performs an action on a control by “handling” the events that the control defines. When and how you implement the event handler will vary from application to application and from control to control, but generally speaking you will always handle the Click event for a button; for a ListBox control, you often have to react when the user changes the selection and so the SelectionChanged event should be handled. On other controls, such as the Label or TextBlock controls, you will rarely implement any event.

You can add controls to a window in a number of ways, but the most common way is to drag and drop them from the Toolbox onto the Design View or the XAML View.

Properties

As mentioned, all controls have a number of properties that are used to manipulate the behavior of the control. Some of these are easy to understand such as height and width, whereas others are less obvious such as RenderTransform. All of them can be set using the Properties panel, directly in XAML, or by manipulating the control on the Design View.

Dependency Properties

For the most part, normal .NET properties are simple getters and setters, which is fine for most cases. However, when you are working with a dynamic user interface that can and should change when properties change, you have to write a lot of code in these get and set methods that will be repeated many times. A dependency property is a property that is registered with the WPF property system in such a way as to allow extended functionality. This extended functionality includes, but is not limited to, automatic property change notifications. Specifically, dependency properties have the following features:

  • You can use styles to change the values of dependency properties.
  • You can set the value of a dependency property by using resources or by data binding.
  • You can change dependency property values in an animation.
  • You can set dependency properties hierarchically in XAML — that is, a value for a dependency property that you set on a parent element can be used to set the default value for the same dependency property of its child elements.
  • You can configure notifications for property value changes using a well-defined coding pattern.
  • You can configure sets of related properties so that they all update in response to a change to one of them. This is known as coercion. The changed property is said to coerce the values of the other properties.
  • You can apply metadata to a dependency property to specify other behavior characteristics. For example, you might specify that if a given property changes, then it might be necessary to rearrange the user interface.

In practice, because of the way in which dependency properties are implemented, you might not notice much of a difference compared to ordinary properties. However, when you create your own controls, you will quickly find that a lot of functionality suddenly disappears when you use ordinary .NET properties.

Chapter 15 shows how you can implement new dependency properties.

Attached Properties

An attached property is a property that is made available to each child object of an instance of the class that defines the property. For example, as you will see later in this chapter, the Grid control that you used in the previous examples allows you to define columns and rows for ordering the child controls of the Grid. Each child control can then use the attached properties Column and Row to specify where it belongs in the grid:

<Grid HorizontalAlignment="Left" Height="167" VerticalAlignment="Top" Width="290">
  <Button Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top" Width="75" Grid.Column="0" Grid.Row="0"
Height="22"/>
…
  </Grid>

Here, the attached property is referred to using the name of the parent element, a period, and the name of the attached property.

In WPF, attached properties serve a variety of uses. You will see a lot of attached properties shortly, when you look at how to position controls in the “Control Layout” section. You will learn how container controls define attached properties that enable child controls to define, for example, which edges of the container to dock to.

Events

In Chapter 13, you learned what events are and how to use them. This section covers particular kinds of events — specifically, the events generated by WPF controls — and introduces routed events, which are usually associated with user actions. For example, when the user clicks a button, that button generates an event indicating what just happened to it. Handling the event is the means by which the programmer can provide some functionality for that button.

Many of the events you handle are common to most of the controls that you work with in this book. This includes events such as LostFocus and MouseEnter. This is because the events themselves are inherited from base classes such as Control or ContentControl. Other events such as the CalendarOpened event of the DatePicker are more specific and only found on specialized controls. Some of the most used events are listed in Table 14.1.

Table 14.1 Common Control Events

Event Description
Click Occurs when a control is clicked. In some cases, this event also occurs when a user presses the Enter key.
Drop Occurs when a drag-and-drop operation is completed — in other words, when an object has been dragged over the control, and the user releases the mouse button.
DragEnter Occurs when an object being dragged enters the bounds of the control.
DragLeave Occurs when an object being dragged leaves the bounds of the control.
DragOver Occurs when an object has been dragged over the control.
KeyDown Occurs when a key is pressed while the control has focus. This event always occurs before KeyPress and KeyUp.
KeyUp Occurs when a key is released while a control has focus. This event always occurs after KeyDown event.
GotFocus Occurs when a control receives focus. Do not use this event to perform validation of controls. Use Validating and Validated instead.
LostFocus Occurs when a control loses focus. Do not use this event to perform validation of controls. Use Validating and Validated instead.
MouseDoubleClick Occurs when a control is double-clicked.
MouseDown Occurs when the mouse pointer is over a control and a mouse button is pressed. This is not the same as a Click event because MouseDown occurs as soon as the button is pressed and before it is released.
MouseMove Occurs continually as the mouse travels over the control.
MouseUp Occurs when the mouse pointer is over a control and a mouse button is released.

You will see many of these events in the examples in this chapter.

Handling Events

There are two basic ways to add a handler for an event. One way is to use the Events list in the Properties window, shown in Figure 14.2, which is displayed when you click the lightning bolt button.

Screenshot of a properties window, displaying 17 data entry fields, such as name, click, contextmenuclosing, contextmenuopening, datacontextchanged, dragenter, and dragleave.

Figure 14.2

To add a handler for a particular event, either type the name of the event and press Return, or double-click to the right of the event name in the Events list. This causes the event to be added to the XAML tag. The method signature to handle the event is added to the C# code-behind file.

    <Button x:Name="rotatedButton" Content="2nd Button" Width="75"
            Height="22" FontWeight="Bold" Margin="218,138,224,159"
            RenderTransformOrigin="0.5,0.5"
            Click="rotatedButton_Click">
      …
    </Button>
private void rotatedButton_Click(object sender, RoutedEventArgs e)
    {
    }

You can also type the name of the event directly in XAML and add the name of the handler there. If you do this, you can right-click on the event and chose Navigate to Event Handler. This will add the event handler to the code-behind file.

Routed Events

WPF uses events that are called routed events. A standard .NET event is handled by the code that has explicitly subscribed to it and it is sent only to those subscribers. Routed events are different in that they can send the event to all controls in the hierarchy in which the control participates.

A routed event can travel up and down the hierarchy of the control on which the event occurred. So, if you right-click a button, the MouseRightButtonDown event will first be sent to the button itself, then to the parent of the control — in the case of the earlier example, the Grid control. If this doesn't handle it, then the event is finally sent to the window. If, on the other hand you don't want the event to travel further up the hierarchy, then you simply set the RoutedEventArgs property Handled to true, and no additional calls will be made at that point. When an event travels up the control hierarchy like this, it is called a bubbling event.

Routed events can also travel in the other direction, that is, from the root element to the control on which the action was performed. This is called a tunneling event and by convention all events like this are prefixed with the word Preview and always occur before their bubbling counterparts. An example of this is the PreviewMouseRightButtonDown event.

Finally, a routed event can behave exactly like a normal .NET event and only be sent to the control on which the action was made.

Routed Commands

Routed commands serve much the same purpose as events in that they cause some code to execute. Where Events are bound directly to a single element in the XAML and a handler in the code, Routed Commands are more sophisticated.

The key difference between events and commands is in their use. An event should be used whenever you have a piece of code that has to respond to a user action that happens in only one place in your application. An example of such an event could be when the user clicks OK in a window to save and close it. A command can be used when you have code that will be executed to respond to actions that happen in many locations. An example of this is when the content of an application is saved. There is often a menu with a Save command that can be selected, as well as a toolbar button for the same purpose. It is possible to use event handlers to do this, but it would mean implementing the same code in many locations — a command allows you to write the code just once.

When you create a command, you must also implement code that can respond to the question, “Should this code be available to the user at the moment?” This means that when a command is associated with a button, that button can ask the command if it can execute and set its state accordingly.

A command is much more complicated to implement than an event, so you are not going to see them in use until Chapter 15, where they will be used with menu items.

Control Types

As stated, WPF has a lot of controls to choose from. Two types of interest are the Content and Items controls. Content controls, such as the Button control, have a Content property that can be set to any other control. This means that you can determine how the control is displayed, but you can specify only a single control directly in the content. That being said, you can specify an Items control, which is a control that allows you to insert multiple controls as content. An example of an Items control is the Grid control. When you are creating user interfaces, you are continually combining these two control types.

In addition to Content and Items controls, there are a number of other types of controls that don't allow you to use other controls as their content. One example of this is the Image control, which is used to display an image. Changing that behavior defeats the purpose of the control.

Control Layout

So far in this chapter you have used the Grid element to lay out a few controls, primarily because that is the control supplied by default when you create a new WPF application. However, you haven't yet examined the full capabilities of this class, nor have you learned about the other layout containers that you can use to achieve alternative layouts. This section looks at control layout in more detail, as it is a fundamental concept of WPF.

All content layout controls derive from the abstract Panel class. This class simply defines a container that can contain a collection of objects that derive from UIElement. All WPF controls derive from UIElement. You cannot use the Panel class directly for control layout, but you can derive from it if you want to. Alternatively, you can use one of the following layout controls that derive from Panel:

  • Canvas — This control enables you to position child controls any way you see fit. It doesn't place any restrictions on child control positioning, but nor does it provide any assistance in positioning.
  • DockPanel — This control enables you to dock child controls against one of its four edges. The last child control fills the remaining space.
  • Grid — This control enables flexible positioning of child controls. You can divide the layout of this control into rows and columns, which enables you to align controls in a grid layout.
  • StackPanel — This control positions its child controls in a sequential horizontal or vertical layout.
  • WrapPanel — This control positions its child controls in a sequential horizontal or vertical layout as StackPanel, but rather than a single row or column of controls, this control wraps its children into multiple rows or columns according to the space available.

You'll look at how to use these controls in more detail shortly. First, however, there are a few basic concepts to understand:

  • How controls appear in stack order
  • How to use alignment, margins, and padding to position controls and their content
  • How to use the Border control

Stack Order

When a container control contains multiple child controls, they are drawn in a specific stack order. You might be familiar with this concept from drawing packages. The best way to think of stack order is to imagine that each control is contained in a plate of glass, and the container contains a stack of these plates of glass. The appearance of the container, therefore, is what you would see if you looked down from the top through these layers of glass. The controls contained by the container overlap, so what you see is determined by the order of the glass plates. If a control is higher up the stack, then it will be the control that you see in the overlap area. Controls lower down may be partially or completely hidden by controls above them.

This also affects hit testing when you click on a window with the mouse. The target control will always be the one that is uppermost in the stack when considering overlapping controls. The stack order of controls is determined by the order in which they appear in the list of children for a container. The first child in a container is placed on the lowest layer in the stack, and the last child on the topmost layer. The children between the first and last child are placed on increasingly higher layers. The stack order of controls has additional implications for some of the layout controls that you can use in WPF, as you will see shortly.

Alignment, Margins, Padding, and Dimensions

Earlier examples used the Margin, HorizontalAlignment, and VerticalAlignment properties to position controls in a Grid container, but without going into much detail about their use. You have also seen how you can use Height and Width to specify dimensions. These properties, along with Padding, which you haven't looked at yet, are useful for all of the layout controls (or most of them, as you will see), but in different ways. Different layout controls can also set default values for these properties. You'll see a lot of this by example in subsequent sections, but before doing that, it is worth covering the basics.

The two alignment properties, HorizontalAlignment and VerticalAlignment, determine how the control is aligned. HorizontalAlignment can be set to Left, Right, Center, or Stretch. Left and Right tend to position controls to the left or right edges of the container, Center positions controls in the middle, and Stretch changes the width of the control so that its edges reach to the sides of the container. VerticalAlignment is similar, and has the values Top, Bottom, Center, or Stretch.

Margin and Padding specify the space to leave blank around the edges of controls and inside the edges of controls, respectively. Earlier examples used Margin to position controls relative to the edges of a window. This worked because with HorizontalAlignment set to Left and VerticalAlignment set to Top, the control is positioned tight against the top-left corner, and Margin inserted a gap around the edge of the control. Padding is used similarly, but spaces out the content of a control from its edges. This is particularly useful for Border, as you will see in the next section. Both Padding and Margin can be specified in four parts (in the form leftAmount, topAmount, rightAmount, bottomAmount) or as a single value (a Thickness value).

Later, you will see how Height and Width are often controlled by other properties. For example, with HorizontalAlignment set to Stretch, the Width property of a control changes as the width of its container changes.

Border

The Border control is a very simple, and very useful, container control. It holds a single child, not multiple children like the more complicated controls you'll look at in a moment. This child will be sized to completely fill the Border control. This might not seem particularly useful, but remember that you can use the Margin and Padding properties to position the Border within its container, and the content of the Border within the edges of the Border. You can also set, for example, the Background property of a Border so that it is visible. You will see this control in action shortly.

Canvas

The Canvas control, as previously noted, provides complete freedom over control positioning. Another thing about Canvas is that the HorizontalAligment and VerticalAlignment properties used with a child element will have no effect whatsoever over the positioning of those elements.

You can use Margin to position elements in a Canvas as it was done in earlier examples, but a better way is to use the Canvas.Left, Canvas.Top, Canvas.Right, and Canvas.Bottom attached properties that the Canvas class exposes:

<Canvas…>
  <Button Canvas.Top="10" Canvas.Left="10"…>Button1</Button>
</Canvas>

The preceding code positions a Button so that its top edge is 10 pixels from the top edge of the Canvas, and its left edge is 10 pixels from the left edge of the Canvas. Note that the Top and Left properties take precedence over Bottom and Right. For example, if you specify both Top and Bottom, then the Bottom property is ignored.

Figure 14.3 shows two Rectangle controls positioned in a Canvas control, with the window resized to two sizes.

Image described by surrounding text.

Figure 14.3

One Rectangle is positioned relative to the top-left corner, and one is positioned relative to the bottom-right corner. As you resize the window, these relative positions are maintained. You can also see the importance of the stacking order of the Rectangle controls. The bottom-right Rectangle is higher up in the stacking order, so when they overlap this is the control that you see.

The code for this example is as follows (you can find it in the downloaded code at LayoutExamplesCanvas.xaml):

<Window x:Class="LayoutExamples.Canvas"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LayoutExamples"
        mc:Ignorable="d"
        Title="Canvas" Height="300" Width="300">
    <Canvas Background="AliceBlue">
        <Rectangle Canvas.Left="50" Canvas.Top="50" Height="40" Width="100"
    Stroke="Black" Fill="Chocolate"/>
        <Rectangle Canvas.Right="50" Canvas.Bottom="50" Height="40" Width="100"
    Stroke="Black" Fill="Bisque"/>
    </Canvas>
</Window>

DockPanel

The DockPanel control, as its name suggests, enables you to dock controls to one of its edges. This sort of layout should be familiar to you, even if you've never stopped to notice it before. It is how, for example, the Ribbon control in Word remains at the top of the Word window, or how the various windows in Visual Studio are positioned. In Visual Studio you can also change the docking of windows by dragging them around.

DockPanel has a single attached property that child controls can use to specify the edge to which controls dock: DockPanel.Dock. You can set this property to Left, Top, Right, or Bottom.

The stack order of controls in a DockPanel is extremely important, as every time you dock a control to an edge you also reduce the available space of subsequent child controls. For example, you might dock a toolbar to the top of a DockPanel and then a second toolbar to the left of the DockPanel. The first control would stretch across the entire top of the DockPanel display area, but the second control would only stretch from the bottom of the first toolbar to the bottom of the DockPanel along the left edge.

The last child control you specify will (usually) fill the area that remains after all the previous children have been positioned. (You can control this behavior, which is why this statement is qualified.)

When you position a control in a DockPanel, the area occupied by the control might be smaller than the area of the DockPanel that is reserved for the control. For example, if you dock a Button with a Width of 100, a Height of 50, and a HorizontalAlingment of Left to the top of a DockPanel, then there will be space to the right of the Button that isn't used by other docked children. In addition, if the Button control has a Margin of 20, then a total of 90 pixels at the top of the DockPanel will be reserved (the height of the control plus the top and bottom margins). You need to take this behavior into account when you use DockPanel for layout; otherwise, you can end up with unexpected results.

Figure 14.4 shows a sample DockPanel layout.

Screenshot of a Dockpanels window, displaying Dockpanel.Dock=”Top”, DockPanel.Dock=”Left”, Last control, and DockPanel.”Bottom”.

Figure 14.4

The code for this layout is as follows (you can find it in the downloadable code at LayoutExamplesDockPanels.xaml):

<Window x:Class="LayoutExamples.DockPanels"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LayoutExamples"
        mc:Ignorable="d"
        Title="DockPanels" Height="300" Width="300">
    <DockPanel Background="AliceBlue">
        <Border DockPanel.Dock="Top" Padding="10" Margin="5"
    Background="Aquamarine" Height="45">
            <Label>1) DockPanel.Dock="Top"</Label>
        </Border>
        <Border DockPanel.Dock="Top" Padding="10" Margin="5"
    Background="PaleVioletRed" Height="45" Width="200">
            <Label>2) DockPanel.Dock="Top"</Label>
        </Border>
        <Border DockPanel.Dock="Left" Padding="10" Margin="5"
    Background="Bisque" Width="200">
            <Label>3) DockPanel.Dock="Left"</Label>
        </Border>
        <Border DockPanel.Dock="Bottom" Padding="10" Margin="5"
    Background="Ivory" Width="200" HorizontalAlignment="Right">
            <Label>4) DockPanel.Dock="Bottom"</Label>
        </Border>
        <Border Padding="10" Margin="5" Background="BlueViolet">
            <Label Foreground="White">5) Last control</Label>
        </Border>
    </DockPanel>
</Window>

This code uses the Border control introduced earlier to clearly mark out the docked control regions in the example layout, along with Label controls to output simple informative text. To understand the layout, you must read it from top to bottom, looking at each control in turn:

  1. The first Border control is docked to the top of the DockPanel. The total area taken up in the DockPanel is the top 55 pixels (Height + 2 × Margin). Note that the Padding property does not affect this layout, as it is inside the edge of the Border, but this property does control the positioning of the embedded Label control. The Border control fills any available space along the edge it is docked to if not constrained by Height or Width properties, which is why it stretches across the DockPanel.
  2. The second Border control is also docked to the top of the DockPanel, and takes up another 55 pixels from the top of the display area. This Border control also includes a Width property, which causes the border to take up only a portion of the width of the DockPanel. It is positioned centrally, as the default value for HorizonalAlignment in a DockPanel is Center.
  3. The third Border control is docked to the left of the DockPanel and takes up 210 pixels of the left of the display.
  4. The fourth Border control is docked to the bottom of the DockPanel and takes up 30 pixels plus the height of the Label control it contains (whatever that is). This height is determined by the Margin, Padding, and contents of the Border control, as it is not specified explicitly. The Border control is locked to the bottom-right corner of the DockPanel, as it has a HorizontalAlignment of Right.
  5. The fifth and final Border control fills the remaining space.

Run this example and experiment with resizing content. Note that the further up the stacking order a control is, the more priority is given to its space. By shrinking the window, the fifth Border control can be completely obscured by controls further up the stacking order. Be careful when using DockPanel control layout to avoid this, perhaps by setting minimum dimensions for the window.

StackPanel

You can think of StackPanel as being a slimmed down version of DockPanel, where the edge to which child controls are docked is fixed for those controls. The other difference between these ­controls is that the last child control of a StackPanel doesn't fill the remaining space. However, controls will, by default, stretch to the edges of the StackPanel control.

The direction in which controls are stacked is determined by three properties. Orientation can be set to Horizontal or Vertical, and HorizontalAlignment and VerticalAlignment can be used to determine whether control stacks are positioned next to the top, bottom, left, or right edge of the StackPanel. You can even make the stacked controls stack at the center of the StackPanel using the Center value for the alignment property you use.

Figure 14.5 shows two StackPanel controls, each of which contains three buttons. The top StackPanel has its Orientation property set to Horizontal and the bottom one has Orientation set to Vertical.

Image described by surrounding text.

Figure 14.5

The code used here is as follows (you can find it in the downloaded code at LayoutExamplesStackPanels.xaml):

<Window x:Class="LayoutExamples.StackPanels"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LayoutExamples"
        mc:Ignorable="d"
        Title="StackPanels" Height="300" Width="300">
    <Grid>
        <StackPanel HorizontalAlignment="Left" Height="128" VerticalAlignment="Top"
 Width="284" Orientation="Horizontal">
            <Button Content="Button" Height="128" VerticalAlignment="Top"
                    Width="75"/>
            <Button Content="Button" Height="128" VerticalAlignment="Top"
                    Width="75"/>
            <Button Content="Button" Height="128" VerticalAlignment="Top"
                    Width="75"/>
        </StackPanel>
        <StackPanel HorizontalAlignment="Left" Height="128" VerticalAlignment="Top"
 Width="284" Margin="0,128,0,0" Orientation="Vertical">
            <Button Content="Button" HorizontalAlignment="Left" Width="284"/>
            <Button Content="Button" HorizontalAlignment="Left" Width="284"/>
            <Button Content="Button" HorizontalAlignment="Left" Width="284"/>
        </StackPanel>
    </Grid>
</Window>

WrapPanel

WrapPanel is essentially an extended version of StackPanel; controls that “don't fit” are moved to additional rows (or columns). Figure 14.6 shows a WrapPanel control containing multiple shapes, with the window resized to two sizes.

Image described by surrounding text.

Figure 14.6

The code to achieve this effect is shown here (you can find it in the downloaded code at LayoutExamplesWrapPanel.xaml):

<Window x:Class="LayoutExamples.WrapPanel"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LayoutExamples"
        mc:Ignorable="d"
        Title="WrapPanel" Height="92" Width="260">
    <WrapPanel Background="AliceBlue">
        <Rectangle Fill="#FF000000" Height="50" Width="50" Stroke="Black"
    RadiusX="10" RadiusY="10"/>
        <Rectangle Fill="#FF111111" Height="50" Width="50" Stroke="Black"
    RadiusX="10" RadiusY="10"/>
        <Rectangle Fill="#FF222222" Height="50" Width="50" Stroke="Black"
    RadiusX="10" RadiusY="10"/>
        <Rectangle Fill="#FFFFFFFF" Height="50" Width="50" Stroke="Black"
    RadiusX="10" RadiusY="10"/>
    </WrapPanel>
</Window>

WrapPanel controls are a great way to create a dynamic layout that enables users to control exactly how content should be viewed.

Grid

Grid controls can have multiple rows and columns that you can use to lay out child controls. You have used Grid controls several times already in this chapter, but in all cases you used a Grid with a single row and a single column. To add more rows and columns, you must use the RowDefinitions and ColumnDefinitions properties, which are collections of RowDefinition and ColumnDefinition objects, respectively, and are specified using property element syntax:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition/>
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition/>
  </Grid.ColumnDefinitions>
 …
</Grid>

This code defines a Grid control with two rows and two columns. Note that no extra information is required here; with this code, each row and column is dynamically resized automatically as the Grid control resizes. Each row will be a third of the height of the Grid, and each column will be half the width. You can display lines between cells in a Grid by setting the Grid.ShowGridlines property to true.

You can control the resizing with the Width, Height, MinWidth, MaxWidth, MinHeight, and MaxHeight properties. For example, setting the Width property of a column ensures that the column stays at that width. You can also set the Width property of a column to *, which means “fill the remaining space after calculating the width of all other columns.” This is actually the default. When you have multiple columns with a Width of *, then the remaining space is divided between them equally. The * value can also be used with the Height property of rows. The other possible value for Height and Width is Auto, which sizes the row or column according to its content. You can also use GridSplitter controls to enable users to customize the dimensions of rows and columns by clicking and dragging.

Child controls of a Grid control can use the attached Grid.Column and Grid.Row properties to specify which cell they are contained in. Both these properties default to 0, so if you omit them, then the child control is placed in the top-left cell. Child controls can also use Grid.ColumnSpan and Grid.RowSpan to be positioned over multiple cells in a table, where the upper-left cell is specified by Grid.Column and Grid.Row.

The Game Client

Now that you know the basics of what it means to work with WPF and Visual Studio, it is time to start working with the controls to create something useful. The remaining sections of this chapter and Chapter 15 are dedicated to writing a game client for the card game you have been developing over the previous chapters. You are going to use a lot of controls to write the game client, and you are even going to write one yourself.

In this chapter you are going to write the supporting dialog boxes of the game — this includes the About, Options, and New Game windows.

The About Window

An About window, or About box as it's sometimes called, is used to display information about the developer of the application and the application itself. Some About windows are quite complex, like the one found in Microsoft Office applications and Visual Studio, and display version and licensing information. By convention, the About window can be accessed from the Help menu where it is usually the last item on the list.

Figure 14.8 shows a screenshot of the finished dialog box that you are about to create.

Screenshot of an About window, displaying Karli Cards (c) copyright 2012 by Wrox Press and all readers.

Figure 14.8

Designing the User Interface

An About window is not something that the user is going to see very often. In fact, the reason that it is usually located on the Help menu is that it is very often only used when the user needs to find information about the version of the application or who to contact when something is wrong. But this also means that it is something the user has a specific purpose for visiting and if you include such a window in your application, you should treat it as important.

Whenever you are designing an application, you should strive to keep the look and feel as consistent as possible. This means that you should stick to a few select colors and use the same styling of controls everywhere in the application. In the case of Karli Cards, you are going to work with three main colors — red, black, and white.

If you look at Figure 14.8 you will see that the top-left corner of the window is occupied by a Wrox Press logo. You have not used images before, but adding a few select images to your applications can make the user interface look more professional.

The Image Control

Image is a very simple control that can be used to great effect. It allows you to display a single image and to resize this image as you see fit. The control exposes two properties, as shown in Table 14.2.

Table 14.2 Image Control

Property Description
Source Use this property to specify the location of the image. This can be a location on disk or somewhere on the web. As you will see in Chapter 15, it is also possible to create a static resource and use it as the source.
Stretch It's actually pretty rare to have an image that is exactly the right size for your purpose, and sometimes the size of the image must change as the application window is resized. You can use this property to control how the image behaves. There are four possibilities:
None — The image doesn't resize.
Fill — The image resizes to fill the entire space. This may contort the image.
Uniform — The image keeps its aspect ratio and doesn't fill the available space if this would change the aspect ratio.
UniformToFill — The image keeps its aspect ratio and fills the available space. If keeping the ratio means that some of the image is too large for the space available, the image is clipped to fit.

The Label Control

You have already seen this most simple of controls used in some of the previous examples. It displays simple text information to the user and in some cases relays information about shortcut keys. The control uses the Content property to display its text. The Label control displays text on a single line. If you prefix a letter with an underscore “_” character, the letter will become underlined and it will then be possible to access the control directly by using the prefixed letter and Alt. For example, _Name assigns the shortcut Alt+N to any control directly following the label.

The TextBlock Control

Like Label, this control displays simple text without any complicated formatting. Unlike the Label, the TextBlock control is capable of displaying multiple lines of text. It is not possible to format individual parts of the text.

The TextBlock displays the text even if it will not fit in the space granted to the control. The control itself does not provide any scrollbars in this case, but it can be wrapped in a handy view control when needed: the ScrollViewer.

The Button Control

Like the Label control, you have already seen quite a bit of the Button control. This control is used everywhere and is easily recognized on a user interface. Your users will expect that they can left-click it to perform an action — no more and no less. Altering this behavior will most likely lead to bad interface design and frustrated users.

By default, the button displays itself with a single short line of text or an image that describes what happens when you click on it.

The button does not contain any properties to display images or text, but you can use the Content property to display simple text or embed an Image control in the content to display an image. You can find this code in the downloaded code at Ch14Ex01ImageButton.xaml:

<Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="10" >
      <StackPanel Orientation="Horizontal">
        <Image Source=".ImagesDelete_black_32x32.png" Stretch="UniformToFill"
Width="16" Height="16"/>
        <TextBlock>Delete</TextBlock>
      </StackPanel>
    </Button>

Figure 14.9 shows the Delete button with text and an image.

Image described by surrounding text.

Figure 14.9

The Options Window

The next window you are going to create is the Options window. This window will allow the players to set a number of parameters that will alter the game play. It will also allow you to use some controls that you haven't used yet: the CheckBox, RadioButton, ComboBox, TextBox, and TabControl controls.

Figure 14.10 shows the window with the first tab selected. At first glance the window looks much like the About window, but there is a lot more to do on this window.

Screenshot of Options dialog box, displaying selected Game tab, with checkbox for Play against computer and data entry field for Number of players.

Figure 14.10

The TextBox Control

Previously in this chapter you used the Label and TextBlock controls. These controls are designed exclusively for displaying text to the user. The TextBox control allows the user to type text into the application. Although it can just display text as well, you should not use it for this purpose unless the user is allowed to edit the displayed text. If you decide that you want to display text using a textbox, be sure to set its IsEnabled property to false to prevent users from being able to edit it.

You control how the text is displayed and can be entered into the TextBox using a number of properties shown in Table 14.3.

Table 14.3 TextBox Properties

Property Description
Text The text currently displayed in the TextBox control.
IsEnabled When this is set to true, the user can edit the text in the TextBox. When it is false, the text is grayed out and the user cannot give focus to the control.
TextWrapping Sometimes you want the TextBox to display only a single line of text. In this case, you can set this property to NoWrap. This is the default. If you want your text to be displayed on multiple lines, you can set it to either Wrap or WrapWithOverflow. Wrap will cause the text that extends beyond the edge of the box to be moved to the line below. WrapWithOverflow will in some cases allow very long words to extend beyond the edge if no suitable breakpoint can be determined.
VerticalScrollBarVisibility If your TextBox allows the user to enter multiple lines of text, then the user can potentially type text that will disappear below the lower edge of the box. In that case, it's a good idea to display a scrollbar. Set this to Auto if you want the scrollbar to appear only if the text is too long to be displayed. Set it to Visible to always display it, and Hidden or Disabled to never display a scrollbar.
AcceptsReturn This property controls how text can be entered into the control. If you set this to false, which is the default, then the user can't break the line with a Return.

The CheckBox Control

CheckBoxes present the users with options that they can select or clear. You should use a CheckBox if you have want to present an option to the users that can be turned on or off, or want the users to answer yes or no to a question. For example, in the Options dialog box, you want the user to answer to decide whether they should play against the computer. To this end a CheckBox with the text “Play Against Computer” is used.

A CheckBox is designed to be used as a single entity that is unaffected by other CheckBoxes on the view. You will sometimes see CheckBoxes used in a way that links them together so that selecting one causes another to become cleared, but this is not the intended use for this control. If you want this functionality, you should use a RadioButton, described in the next section.

CheckBoxes can also display a third state, which is known as “indeterminate” and is supposed to indicate that the yes/no answer could not be answered. This state is commonly used when a CheckBox is used to show information about something else. For example, CheckBoxes are sometimes used to indicate whether all child nodes in a Tree View are selected. In this case, the CheckBox will be selected if all nodes are selected, cleared if none are, and indeterminate if some, but not all, are selected.

Table 14.4 lists the properties commonly used to control the CheckBox control.

Table 14.4 CheckBox Properties

Property Description
Content The CheckBox is a Content control and its display can therefore be heavily customized. Adding a text to the Content property yields the default view.
IsThreeState Used to indicate if the control can have two or three states. The default is false, meaning that only two possible values exist.
IsChecked This is either true or false. By default, setting it to true displays a checkmark. If IsThreeState is true, null is possible and indicates that the state is indeterminate.

The RadioButton Control

RadioButtons are used with other RadioButtons to allow users to choose between multiple options where only one can be selected at any time. You should use RadioButtons when you want the users to answer a question that has a very limited number of possible values. If there are more than four or five possible values, you should consider using a ListBox or a ComboBox instead. In the Options window you will create shortly, the user can choose the skill level of the computer player. There are three options: Dumb, Good, and Cheats. Only one should ever be selected at any given time.

When more than one RadioButton is displayed in the same view they will by default know about each other and as soon as any one of them is selected, all the others are cleared. If you have multiple unrelated RadioButtons on the same view, they can be grouped together to avoid controls clearing the values of unrelated controls.

You can control RadioButtons with the properties listed in Table 14.5.

Table 14.5 RadioButton Properties

Property Description
Content RadioButtons are Content controls and can therefore have their display modified. By default, you enter a text in the Content.
IsChecked This is either true or false. If IsThreeState is true, null is possible and indicates that the state is indeterminate.
GroupName The name of the group the control belongs to. By default this is empty and any RadioButtons without a GroupName is considered in the same group.

The ComboBox Control

Like the RadioButton and CheckBox controls, ComboBoxes allow users to select exactly one option. However, ComboBoxes are fundamentally different from the other two in two ways:

  • ComboBoxes display the possible choices in a drop-down list.
  • It is possible to allow the users to type new values.

ComboBoxes are commonly used to display long lists of values, such as country or state names, but they can be used for many purposes. In the Options dialog box, a ComboBox is used to display a list from which the user can choose the number of players. Although this could just as well have been done using RadioButtons, the use of a ComboBox saves space in the view.

A ComboBox can be changed to display itself with a TextBox at the top that allows the users to type any values that they feel are missing. One of the exercises of this chapter asks you to add a ComboBox to the Options dialog box from which the users can either type their name or select it from a list.

The two properties — IsReadOnly and IsEditable — are very important for the behavior of the control and work together to provide four possible ways for the user to select the value of the ComboBox using the keyboard (see Table 14.6):

Table 14.6 IsReadOnly and IsEditable Combinations

IsReadOnly is true IsReadOnly is false
IsEditable is true The TextBox is displayed but the control does not react to key presses. If a selection is made in the list, the text can be selected in the TextBox. The TextBox is displayed and the user can type anything she wishes. If something is typed that is in the list, it is selected. The control will display the best possible match as the user is typing.
IsEditable is false When IsEditable is false, IsReadOnly no longer has any effect because the TextBox is not displayed. When the control is selected, the user can select a value from the list by typing but it is not possible to type a value that isn't in the list.

A ComboBox is an Items control, which means that you can add multiple items to it. Table 14.7 shows additional properties for the ComboBox control.

Table 14.7 Other ComboBox Properties

ComboBox Property Description
Text The Text property represents the text displayed at the head of the ComboBox. It is either an element of the list or a new text typed by the user.
SelectedIndex Represents the index of the selected item in the list. If this is –1 then no selection is made. This is also the case if the user has typed something that was not in the list.
SelectedItem Represents the actual item of the list, not just the index or the text. If nothing is selected or the user has typed something new, this returns null.

The TabControl

The TabControl is radically different than the other controls presented this section. It is a layout control that is used to group controls on pages that can be selected by clicking on them.

Tab controls are used when you want to display a lot of information in a single window but don't want to clutter the view too much. In this case, you should divide the information into groups of related items and create a single page for each group. Generally speaking, you should never allow controls on one page to affect controls on another page. If you do so anyway, the user will not realize that something has changed on another page and will be confused when settings change behind her back.

By default each page is constructed of TabItems that, by default, are populated by a single Grid control, but you can change the Grid to any other control as you see fit. On each tab, you can lay out your UI and, by selecting the TabItems, you can change between the tabs. Each TabItem has a Header that can be used to display the tab itself. This can be used as a Content control, meaning that you can customize how the header is displayed so that it can be more than just a text.

Handling Events in the Options Window

The window looks fine at this point, and there are even a few things users can do with it, although nothing happens when a setting is changed. Users expect that the options they choose are stored and used by the application. You could do this by storing the values of the controls in the window, but this is not very flexible and mixes the data of the application with the GUI, which is not a good idea. Instead, you should create a class to hold the selections made by the users.

Data Binding

Data binding is a way of declaratively connecting controls with data. In the Options window, you handled the Checked event of the RadioButtons in order to set the value of the ComputerSkillLevel property in the GameOptions class. This works well, and you can use code and event handling to set all the values you have in a window, but very often it is better to bind the properties of your controls directly to the data.

A binding consists of four components:

  • The binding target, which specifies the object on which the binding is used
  • The target property, which specifies the property to set
  • The binding source, which specifies the object used by the binding
  • The source property, which specifies which property holds the data

You don't always set all of these elements explicitly; particularly the binding target is very often implicitly specified by the fact that you are setting a binding to a property on a control.

The binding source is always set in order to make a binding work, but it can be set in several ways. In the following sections and in Chapter 15, you are going to see several ways of binding data from sources.

The DataContext

A DataContext control defines a data source that can be used for data binding on all child elements of an element. You will often have a single instance of a class that holds most of the data that is used in a view. If this is the case you can set the DataContext of the window to the instance of that object, which makes you able to bind properties from that class in your view. This is demonstrated in the “Dynamic Binding to External Objects” section.

Binding to Local Objects

You can bind to any .NET object that has the data you need as long as the compiler can locate the object. If the object is found in the same context, that is the same XAML block, as the control using the object, you can specify the binding source by setting the ElementName property of the binding. Take a look at this changed ComboBox from the Options window:

<ComboBox HorizontalAlignment="Left" Margin="196,58,0,0" VerticalAlignment="Top"
Width="86" Name="numberOfPlayersComboBox" SelectedIndex="0"
IsEnabled="{Binding ElementName=playAgainstComputerCheck, Path=IsChecked}" >

Notice the IsEnabled property. Instead of specifying true or false, there is now lengthy text within a couple of curly brackets. This way of specifying property values is called markup extension syntax, and is a shorthand for specifying properties. The same could have been written like this:

            <ComboBox HorizontalAlignment="Left" Margin="196,58,0,0"
VerticalAlignment="Top" Width="86" Name="numberOfPlayersComboBox"
SelectedIndex="0" >
              <ComboBox.IsEnabled>
                <Binding ElementName="playAgainstComputerCheck"
Path="IsChecked"/>
              </ComboBox.IsEnabled>

Both examples set the binding source to the playAgainstComputerCheck CheckBox. The source property is specified in the Path to be the IsChecked property.

The binding target is set to the IsEnabled property. Both examples do this by the specifying the binding as the content of the property — they just do it using different syntax. Finally, the binding target is implicitly specified by the fact that the binding is done on the ComboBox.

The binding in this example causes the IsEnabled property of the ComboBox to be set or cleared depending on the value of the IsChecked property of the CheckBox. The result is that without any code, the ComboBox is enabled and disabled when the user changes the value of the CheckBox.

Static Binding to External Objects

It is possible to create object instances on the fly by specifying that a class is used as a resource in the XAML. This is done by adding a namespace to the XAML to allow the class to be located, and then declaring the class as a resource on an element in the XAML.

You can create resource references on parent elements of the object that you want to data bind.

Dynamic Binding to External Objects

Now you can bind to objects that are created on the fly as they are needed in order to provide some data. What if you already have an instantiated object that you want to use for data binding? In that case, you need to do a little plumbing in the code.

In the case of the Options window, you don't want the options to be cleared every time the window is opened, and you want the selections the user made to persist and be used in the rest of the application.

You can do this in code by setting the value of the DataContext property to the instance.

Starting a Game with the ListBox Control

You are now only one window short of having created all the supporting windows in the game. The last window before creating the game board is a window where the player can add new players and select the players who will be participating in a new game. This window will use a ListBox to display the names of the players.

ListBoxes and ComboBoxes can often be used for the same purpose, but where a ComboBox normally allows you to select only a single entry, ListBoxes often allows the user to select multiple items. Another key difference is that a ListBox will display its content in a list that is always expanded. This means that it takes up more real estate on the window, but it allows the user to see the options available right away.

Table 14.8 lists a few particularly interesting properties for the ListBox control.

Table 14.8 Interesting ListBox Properties

Property Description
SelectionMode This property controls how the user can select items from the list. There are three possible values: Single, which allows the user to select only one item, Multiple, which allows the user to select multiple items without holding down the Ctrl key, and Extended, which allows the user to select multiple consecutive items by holding down the Shift key, and non-consecutive items by holding down the Ctrl key.
SelectedItem Gets or sets the first selected item or null if nothing is selected. Even if multiple items are selected, only the first item is returned.
SelectedItems Gets a list containing the items that are currently selected.
SelectedIndex Works like SelectedItem, but returns the index instead of the item itself and –1 instead of null if nothing is selected.


EXERCISES

  1. 14.1 A TextBlock control can be used to display large amounts of text, but the control does not provide any way to scroll the text itself if the text extends beyond the viewport. By combining the TextBlock with another control, create a window that contains a TextBlock with a lot of text that can be scrolled and where the scrollbar appears only if the text extends beyond the viewport.

  2. 14.2 The Slider and Progress controls have a few things in common, such as a minimum, maximum, and current value. Using only data binding on the ProgressBar, create a window with a slider and a progress bar, where the Slider control controls the minimum, maximum, and current value of the progress bar.

  3. 14.3 Change the ProgressBar in the previous question to display itself diagonally from the ­bottom-left corner to the top-right corner of the window.

  4. 14.4 Create a new class with the name PersistentSlider and three properties: MinValue, MaxValue, and CurrentValue. The class must be able to participate in data binding and all the properties must be able to notify bound controls of changes.

    1. In the code-behind of the window you created in the two previous exercises, create a new field of type PersistentSlider and initialize it with some default values.
    2. In the constructor, bind the instance to the windows data source.
    3. Bind the slider's Minimum, Maximum, and Value properties to the data source.

    Answers to the exercises can be found in Appendix A.


image What You Learned in This Chapter

Key Concept Description
XAML XAML is a language that uses XML syntax and enables controls to be added to a user interface in a declarative, hierarchical way.
Data binding You can use data binding to connect properties of controls to the value of other controls. You can also define resources and use code defined in classes outside your views as a data source for both values of properties and as content for controls. DataContexts can be used to specify the binding source of existing object instances and thereby allow you to bind to instances that are created in other parts of your application.
Routed events Routed events are special events used in WPF. They come in two flavors: bubbling and tunneling. Bubbling events are first called on the control on which they are activated and then bobble up through the view tree to the root element. Tunneling events move the other way, from the root element to the control that was activated by the user. Both bubbling and tunneling can be stopped by setting the Handled property of the event arguments to true.
INotifyPropertyChanged The INotifyPropertyChanged interface is implemented by a class that will be used from a WPF view. When property setters of the class are called, they raise the event PropertyChanged with the name of the property that changed its value. Any control property that is bound to the property that raised the event will be notified of the change and can update itself accordingly.
ObservableCollections An ObservableCollection is a collection that, among others, implement the INotifyPropertyChanged interface. You use this specialized collection when you want to provide properties or values that are lists to a WPF view for data binding.
Content controls Content controls can contain a single control in their content. An example of such a control is Button. This control can be Grid or StackPanel; they allow you to create complex customizations.
Items controls Items controls can contain a list of controls in their content. An example of such a control is the ListBox. Each control in the list can be customized.
Layout controls You learned to use a number of controls that are used to help you create the view:
  1. Canvas allows for explicit positioning of controls but little else.
  2. StackPanel stacks controls horizontally or vertically.
  3. WrapPanel stacks controls and wraps them to the next line or column depending on the orientation of the panel.
  4. DockPanel allows you to dock controls to the edges of the control or fill the entire content.
  5. Grid allows you to define rows and columns and use these to position the controls.
UI controls UI controls display themselves on the view, often using the layout controls to guide their positions. These controls were used:
  1. Label controls display short text.
  2. TextBlock controls display text that can need multiple lines to display.
  3. TextBox controls allow the users to provide text input.
  4. Button controls allow the users to perform a single action.
  5. Image controls are used to display an image.
  6. CheckBoxes let the users answer yes/no questions such as “Play Against Computer?”
  7. RadioButtons let the users select exactly one from multiple options.
  8. ComboBoxes display a drop-down list of items from which the user can select a single item. The control can also display a TextBox, letting the user enter new options.
  9. ListBox controls display a list of items. Unlike the ComboBox the list is always expanded. The control allows for multiple items being selected.
  10. TabControls allows you to group controls on pages.
..................Content has been hidden....................

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