Chapter 20. WPF and Silverlight

WPF and Silverlight are related technologies for building user interfaces in .NET. Although they are aimed at two significantly different scenarios, they share so many concepts and features that it makes sense to discuss both of them at the same time—almost everything in this chapter applies to both WPF and Silverlight.

As its name suggests, the Windows Presentation Foundation (WPF) is for building interactive applications that run on Windows. WPF applications typically run as standalone applications, requiring an installation step to get them onto the target machine, as they may need prerequisites to be installed first. (WPF is .NET-based, so it requires the .NET Framework to be installed.) This means they are deployed like old-school Windows desktop applications. However, WPF makes it easy for applications to exploit the graphical potential of modern computers in a way that is extremely hard to achieve with more traditional Windows UI technologies. WPF applications don’t have to look old-school.

Silverlight is for web applications, or more specifically, so-called Rich Internet Applications (RIAs). It does not depend on the full .NET Framework—it is a browser plug-in that provides a self-contained, lightweight, cross-platform version of the framework. The whole Silverlight runtime is around a 5 MB download, whereas the full .NET Framework is far more than 200 MB[53]—and Silverlight installs in seconds rather than minutes. Once the plug-in is installed, Silverlight content downloads as part of a web page, just like AJAX and Flash content, with no installation step for new applications. (Like with Flash-based Adobe AIR applications, it’s also possible for a Silverlight application to run out-of-browser once it has been downloaded, if the user consents.) But because Silverlight contains a form of the .NET Framework, you get to write client-side code in C#, which can run in all of the popular web browsers, on both Windows and Mac OS X.

Note

At the time of this writing, Microsoft does not produce a Silverlight plug-in for Linux. However, an open source project called Moonlight offers a Linux-compatible version of Silverlight. This is based on the Mono project, an open source version of C# and the .NET Framework that can run on various non-Microsoft systems, including Linux.

Microsoft has provided some assistance to the Moonlight project to help its developers achieve compatibility with the Microsoft Silverlight plug-in. However, be aware that the Moonlight plug-in has historically lagged behind Microsoft’s—as we write this, Moonlight’s current official release is two major version numbers behind Microsoft’s. If you need to support Linux desktop machines with a Silverlight-based web application, this lag will limit the features you can use.

Despite the very different environments in which WPF and Silverlight applications run, they have a great deal in common. Both use a markup language called Xaml to define the layout and structure of user interfaces. Their APIs are sufficiently similar that it is possible to write a single codebase that can be compiled for either WPF or Silverlight. There are critical concepts, such as data binding and templating, which you need to understand to be productive in either system.

It’s not accurate to say that Silverlight is a subset of WPF. However, this doesn’t stop people from saying it; even Microsoft sometimes makes this claim. It’s strictly untrue: WPF has many features that Silverlight does not and Silverlight has a few features that WPF does not, so neither is a subset of the other. But even if you allow a slightly woolly interpretation of the word subset, it’s a misleading way to describe it. Even where both Silverlight and WPF offer equivalent features they don’t always work in the same way. A few minutes with a decompilation tool such as Reflector or ILDASM makes it abundantly clear that WPF and Silverlight are quite different beasts on the inside. So if you are contemplating building a single application that works both in the browser as a Silverlight application and on the desktop as a WPF application, it’s important to understand the point in the following warning.

Warning

While it is possible to write a single codebase that can run as both WPF and Silverlight code, this doesn’t happen automatically. Silverlight code is likely to need some modification before it will run correctly in WPF. If you have existing WPF code, significant chunks of it may need rewriting before it will run in Silverlight.

Codebases that run on both WPF and Silverlight tend to use conditional compilation—they use the C# preprocessor’s #if, #else, and #endif directives to incorporate two different versions of the code in a single source file in the places where differences are required. Consequently, development and testing must be performed on Silverlight and WPF side by side throughout the development process.

In practice, it’s not common to need to write a single body of code that runs in both environments. It might be useful if you’re writing a reusable user interface component that you plan to use in multiple different applications, but any single application is likely to pick just one platform—either WPF or Silverlight—depending on how and where you need to deploy it.

In this chapter, the examples will use Silverlight, but WPF equivalents would be very similar. We will call out areas in which a WPF version would look different. We will start by looking at one of the most important features, which is common to both WPF and Silverlight.

Xaml and Code Behind

Xaml is an XML-based markup language that can be used to construct a user interface. Xaml is a former acronym—it used to be short for eXtensible Application Markup Language, but as so often happens, for obscure marketing reasons it officially no longer stands for anything. And to be fair, most acronyms are reverse-engineered—the usual process is to look through the list of unused and pronounceable (it’s pronounced “Zammel,” by the way) three- and four-letter combinations, trying to think of things that the available letters might plausibly stand for.

Since etymology can’t tell us anything useful about what Xaml is, let’s look at an example. As always, following the examples yourself in Visual Studio is highly encouraged. To do that, you’ll need to create a new Silverlight project. There’s a separate section under Visual C# in the New Project dialog for Silverlight projects, and you should choose the Silverlight Application template. (Or if you prefer, you can find the WPF Application template in the Windows section underneath Visual C#, although if you choose that, the details will look slightly different from the examples in this chapter.)

When you create a new Silverlight project, Visual Studio will ask you if you’d like it to create a new web project to host the Silverlight application. (If you add a Silverlight project to a solution that already contains a web project, it will also offer to associate the Silverlight application with that web project.) Silverlight applications run from the web browser (initially, at least), so you’ll need a web page simply to run your code. It’s not strictly necessary to create a whole web application, because if you choose not to, Visual Studio will just generate a web page automatically when you debug or run the project, but in general, Silverlight projects are an element of a web application, so you’d normally want both kinds of projects in your solution. Let it create one for now.

Note

If you were building a WPF application, you wouldn’t have an associated web project, because WPF is for standalone Windows desktop applications.

Once Visual Studio has created the project, it shows a file called MainPage.xaml. This is a Xaml file defining the appearance and layout of your user interface. Initially, it contains just a couple of elements: a <UserControl> at the root (or a <Window> in a WPF project), and a <Grid> inside this. We’ll add a couple of elements to the user interface so that there’s something to interact with. Example 20-1 shows the Xaml you get by default with a new Silverlight project, along with two new elements: a Button and a TextBlock; the additional content is shown in bold.

Example 20-1. Creating a UI with Xaml

<UserControl
    x:Class="SimpleSilverlight.MainPage"
    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"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
    d:DesignHeight="300" d:DesignWidth="400">

    >

    <Grid x:Name="LayoutRoot" Background="White">

        <Button
            x:Name="myButton"
            HorizontalAlignment="Center" VerticalAlignment="Top"
            FontSize="20"
            Content="Click me!"
            />

        <TextBlock
            x:Name="messageText"
            Text="Message will appear here"
            TextWrapping="Wrap"
            TextAlignment="Center"
            FontSize="30" FontWeight="Bold"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            />

    </Grid>
</UserControl>

Note

Visual Studio presents Xaml in a split view. At the top it shows how it looks, and at the bottom it shows the Xaml source. You can either edit the source directly or drag items around on the design view at the top, adding new items from the Toolbox. As you make changes in one view the other view updates automatically.

If you run the application by pressing F5, Visual Studio will show the Silverlight application in a web page, as you can see in Figure 20-1.

Silverlight application in a web browser

Figure 20-1. Silverlight application in a web browser

Warning

You will see the Silverlight application only if you run the correct page from the web application. Visual Studio will usually launch the right one if you create a brand-new web application at the same time as your Silverlight application. But be aware that if you add other pages to your web application, Visual Studio might pick one of those when you debug and you might not see your Silverlight UI. You can tell Visual Studio to always use the same file in the web project by right-clicking on it in the Solution Explorer and selecting Set as Start Page. (Visual Studio creates two test pages for your Silverlight code—an .aspx and an .html file, both of which will be named by appending TestPage to your Silverlight project’s name. Either works; it offers both so that you can choose between a dynamic ASP.NET page and static HTML to host your Silverlight UI.)

This simple Silverlight example contains a button, but if you click it, nothing will happen because we have not defined any behavior. Xaml files in WPF and Silverlight are usually paired with a so-called code behind file, a C# (or VB.NET, or whatever language you’re using) file that contains code associated with the Xaml file, and we can use this to make the button do something.

The easiest way to add a click handler for the button to your code behind is from the Xaml file. You can just double-click the button on the design view and it will add a click handler. In fact, most user interface elements offer a wide range of events, so you might want a bit more control. You could select the item on the design surface and then go to the Properties panel—it has an Events tab that lists all the available events, and you can double-click on any of these to add a handler. Or if you prefer typing, you can add a handler from the Xaml source editor view. If you go to the Button element and start adding a new Click attribute, you’ll find that when you type the opening quote for the attribute value an IntelliSense pop up appears showing the text “<New Event Handler>”. If you press the Tab or Enter key, Visual Studio will fill in the attribute value with myButton_Click.

No matter which way you add an event, Visual Studio populates the attribute by taking the first part from the element’s name, as specified with the x:Name attribute, and adding the event name on the end:

<Button
    x:Name="myButton"
    HorizontalAlignment="Center" VerticalAlignment="Top"
    FontSize="20"
    Content="Click me!"
    Click="myButton_Click"
    />

It doesn’t just edit the Xaml—it also adds a method with this name to the code behind file. You can go to the code behind by pressing F7, or you can find it in the Solution Explorer—if you expand a Xaml file node, you’ll see a .xaml.cs file inside it, and that’s the code behind. Example 20-2 shows the click handler, along with some additional code in bold. (You’re not obligated to use this naming convention for handlers, by the way. You could rename it after Visual Studio creates the handler, as long as you change both the Xaml and the code behind.)

Example 20-2. Click handler in the code behind

private void myButton_Click(object sender, RoutedEventArgs e)
{
    messageText.Text = "Hello, world!";
}

Because the Xaml refers to this handler method in the Button element’s Click attribute, the method will run anytime the button is clicked. The one line of code we added here refers to the TextBlock element. If you look at the Xaml, you’ll see that the element’s x:Name attribute has a value of messageText, and this lets us use this name in the code behind to refer to that element. Example 20-2 sets the Text property, which, as you’ve no doubt guessed, causes the TextBlock to show the specified text when the button is clicked.

Note

Just to be clear, this is happening on the client side. The Silverlight plug-in downloads your application and then renders the UI as defined by your Xaml. It hosts your code behind (and any other code in your Silverlight project) inside the web browser process, and calls the specified event handlers without needing to communicate any further with the web server. Silverlight applications can communicate back with the web server after being loaded, but this click-handling interaction does not involve the server at all, unlike clicking a button on a normal web form.

The Xaml in Example 20-1 and the C# in Example 20-2 both set the Text of the TextBlock. The Xaml does this using standard XML’s attribute syntax, while the C# code does it using normal C# property syntax. This highlights an important feature of Xaml: elements typically correspond to objects, and attributes correspond either to properties or to events.

Xaml and Objects

Although Xaml is the usual mechanism for defining the user interface of WPF and Silverlight applications, it’s not strictly necessary. You could remove the bold code in Example 20-1 that adds the Button and TextBlock to the Xaml, and instead modify the class definition and constructor in the code behind, as Example 20-3 shows.

Example 20-3. Creating UI elements in code

public partial class MainPage : UserControl
{
    private Button myButton;
    private TextBlock messageText;

    public MainPage()
    {
        InitializeComponent();

        myButton = new Button
        {
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Top,
            FontSize = 20,
            Content = "Click me!"
        };
        myButton.Click += myButton_Click;

        messageText = new TextBlock
        {
            Text = "Message will appear here",
            TextWrapping = TextWrapping.Wrap,
            TextAlignment = TextAlignment.Center,
            FontSize = 30,
            FontWeight = FontWeights.Bold,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center
        };

        LayoutRoot.Children.Add(myButton);
        LayoutRoot.Children.Add(messageText);
    }
...

Each element that had an x:Name attribute has been replaced here with a field in the class, and we initialize that field in the constructor. This example uses the C# object initializer syntax to set the property values to emphasize the structural similarity between this code and the Xaml it replaces, but normal property setter syntax works too, of course.

XML attribute values are just text, whereas in C# we have to provide values of the correct type—enumeration entries, numbers, or strings as appropriate. The Xaml compiler works out how to turn text into something of the appropriate type. (It uses the .NET Framework class library’s TypeConverter system to do this.) Also, as you will recall C# uses a different syntax to attach event handlers than the one for setting properties—we’ve used the += syntax here—whereas Xaml uses attribute syntax for both properties and event handlers.

This code has the same effect as Xaml. Xaml is really just a language for creating objects, setting their properties, and attaching event handlers, so for the most part it doesn’t really matter whether you use C# or Xaml to create your user interface. This raises the question of why we have Xaml at all, when C# seems to work perfectly well. The main reason Xaml exists is to make it possible to create user interfaces in tools other than a text editor. For example, Microsoft offers a program called Expression Blend, part of its Expression family of design-oriented programs. Blend is a tool for creating WPF and Silverlight user interfaces, and it works mostly in Xaml.

This separation is more than just a convenience for people wanting to write design tools. It’s useful to both developers and designers. It enforces some separation, making it possible for designers to work on the visual design of an application, without needing tools that can edit C# source files. In fact, successful collaboration between developers and designers takes a bit more than this—the separation of Xaml and code behind is not in itself sufficient, because it’s still fairly easy for designers and developers to trip over one another. If a developer writes code behind that relies on certain elements with particular x:Name attributes being present in the Xaml, but the designer decides to delete those elements because they’re ugly and then creates new replacements but forgets to give them the same names, we’re obviously going to see problems. In practice, a smooth developer/designer workflow goes a bit deeper than this, and relies on other WPF and Silverlight features, most notably templates, which we’ll be getting to later. But Xaml is an important part of the solution.

Note

The x:Name attribute is optional. In fact, most Xaml elements tend not to be named—you only name the elements that you need to be able to access from the code behind. This makes the Xaml less cluttered, and if you are working with designers, it makes it easier for them to know which elements are structurally important, and which ones they can rework for design purposes.

The equivalence between elements and objects suggests that Xaml doesn’t necessarily have to be used just for the user interface. The Xaml syntax can be used to create .NET objects of almost any kind. As long as a type has a default constructor and can be configured through its properties with suitable type converters, it’s possible to use it from Xaml—it’s technically possible to create a Windows Forms UI in Xaml, for example. However, Xaml tends to be cumbersome if you use it for types that weren’t designed with Xaml in mind, so in practice, it’s a much better fit for WPF, Silverlight, and also the Workflow Foundation, all of which are meant to be used from Xaml, than it is for other parts of the .NET Framework.

Given that you have a choice between Xaml and C#, which should you use? Xaml is often easier because you can use tools such as Visual Studio’s Xaml designer (or even Expression Blend) to edit the appearance and layout—this can take much less effort than tweaking code repeatedly until the outcome looks the way you want. Obviously, if developers and designers are involved, Xaml is preferable by far, because it enables designers to tweak and refine the appearance without needing to involve developers for every change. But even for a UI being created entirely by developers, an interactive design surface is a much more efficient way to create a layout than code. This doesn’t mean you should go out of your way to avoid creating elements in code, however, particularly if code looks like the most straightforward solution to a problem. Use whichever approach is more convenient for the task at hand.

Now that we’ve seen that Xaml is really just a way of creating objects, what types of objects do Silverlight and WPF offer?

Elements and Controls

Some of the types used to construct a user interface are interactive elements with a distinctive behavior of their own, such as buttons, checkboxes, and listboxes. Although you need code to connect these elements to your application, they have some built-in interactive behavior: buttons light up when the mouse cursor moves over them and look pushed in when clicked; listboxes allow items to be selected; and so on. Other elements are more primitive. There are graphical shape elements and text elements, which are visible to the user but which don’t have an intrinsic behavior—if you want them to do more than simply be visible you need to write code to make that happen. And some elements don’t even appear directly; for example, there are layout elements that are often not visible themselves, as their job is to decide where other elements go.

You can tell what type of element you’re dealing with by looking at the corresponding .NET type’s base class. Most UI elements ultimately derive from FrameworkElement, but this class has some more specialized subtypes. Panel is the base class of layout elements. Shape is the base class of elements involving 2D graphical shapes. Control is the base class of elements that have some intrinsic interactive behavior of their own.

Note

This means that not all UI elements are controls. In fact, the majority of UI elements are not controls. Having said that, the term control is often used loosely—many authors, and even some parts of Microsoft’s documentation, use the term control to describe any UI element, including ones that don’t in fact derive from Control. To further confuse the issue there’s a System.Windows.Controls namespace, in which not all of the types derive from Control.

We believe this is confusing, so in this book, we will use the term control only when talking about types that derive from Control. When we’re discussing features that apply to all UI objects that derive from FrameworkElement (which includes all controls) we will use the more general term element. But be aware that you will come across other, more confusing conventions on the Web and in other books.

Before we get to the controls, we’ll look at how elements are positioned and sized—interactive elements are not much use if you can’t choose where they appear.

Layout Panels

Panel is the abstract base class of user interface elements that control the layout of other elements. You choose a particular concrete derived type to determine which layout mechanism to use. Silverlight version 3 offers three[54] panel types: Grid, StackPanel, and Canvas. WPF provides these and a few more, as we’ll see shortly.

Grid is the most powerful panel, which is why Visual Studio provides you with one by default in a new UI. As the name suggests, it carves up the available space into rows and columns, and then positions child elements into the resultant grid cells. By default, a Grid has a single row and a single column, making just one big cell, but you can add more. Example 20-4 shows how to do this.

Note

This example uses a Xaml feature called a property element—the <Grid.ColumnDefinitions> element does not represent a child object to be added to the grid, but instead indicates that we want to set the Grid object’s ColumnDefinitions property. The <ColumnDefinition> elements it contains are added to the collection in that property, whereas the <Button> elements are added to the collection in the Children property of the Grid.

Example 20-4. Grid with rows and columns

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="2*" />
        <RowDefinition />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Button Grid.Column="0" Grid.Row="0"
        Content="(0, 0)" />
    <Button Grid.Column="1" Grid.Row="0"
        Content="(1, 0)" />
    <Button Grid.Column="2" Grid.Row="0"
        Content="(2, 0)" />

    <Button Grid.Column="0" Grid.Row="1"
        Grid.ColumnSpan="3"
        Content="Row 1, 3 columns wide" />

    <Button Grid.Column="0" Grid.Row="2"
        Grid.ColumnSpan="3"
        Content="Row 2, 3 columns wide" />

    <Button Grid.Column="1" Grid.Row="3"
        FontSize="50"
        Content="(3, 1)" />

</Grid>

Figure 20-2 shows how this looks. The four rows are fairly clear—each button belongs to just one row. The columns are less obvious—you can see all three clearly in the first row, because there’s one button in each, but the next two rows contain just one button each, spanning all three rows. And the final row contains a single button in the second column.

Grid children

Figure 20-2. Grid children

The Grid knows which columns and rows elements belong to, and how many they span, because each button in Example 20-4 has properties that control this. The Grid.Column and Grid.Row properties do what their names suggest, while the Grid.ColumnSpan and Grid.RowSpan properties determine how many grid cells the element occupies. The column and row default to 0, while the spans default to 1.

Note

These properties use another special Xaml feature called attached properties. An attached property is one defined by a different type (e.g., Grid) than the object it is applied to (e.g., Button). The attached properties in Example 20-4 are attributes, but you can also set attached properties with the property element syntax shown earlier—for example, if a <Grid> element could contain a <ToolTipService.ToolTip> element, to set the attachable ToolTip property defined by the ToolTipService class.

While Silverlight, WPF, and Xaml support the idea that properties don’t necessarily have to be defined by the object on which they are set, C# has no syntax for this. So classes that define attachable properties also define get and set methods to enable those properties to be used from code. For example, the Grid class offers SetColumn, SetRow, and so on.

The rows and columns in Figure 20-2 are different sizes. This is because of the settings on the <RowDefinition> and <ColumnDefinition> elements. The first column’s Width has been set to Auto, so it takes its size from the widest child in that column. In this case, only one child belongs exclusively to that column, so the column is exactly wide enough to hold it. The other two columns are at their default width, the value 1*, which causes them to share the remaining space equally. The rows use similar features, except the first row has a fixed height of 30, so it ignores the size of the content and makes every element 30 pixels high. The final row is Auto sized, and since its content has a large font size, it ends up being fairly tall. And the middle two rows use so-called star sizing, so as with the second and third columns, they end up sharing the space left over. However, since they have different star size values—1* and 2*—they get different amounts of space. The 2* row gets to be twice the height of the 1* row. Note that the ratios are all that matter with star sizing—changing 1* and 2* to 10* and 20* would not change the outcome in this example, because 20* is still twice as large as 10*.

So as you can see, a grid can use fixed sizes, it can base sizes on the content at hand, or it can divide the available space proportionally. This makes it a pretty flexible layout mechanism. You can build dock-style layouts where elements are aligned to the top, bottom, left, or right of the available space through the use of Auto sized rows and columns, and by making elements span all the available rows when docking to the left or right, or all the columns when docking to the top or the bottom. You can also stack elements horizontally or vertically by using multiple rows or columns with Auto sizes. And as we’ll see, it’s even possible to exercise precise control over the size and position of elements within the grid. One slight problem is that your Xaml can get a little verbose when using grids. So there are some simpler panel types.

StackPanel arranges children in a vertical or horizontal stack. Example 20-5 shows a StackPanel with its Orientation set explicitly to Vertical. You can doubtless guess how to make a horizontal stack. (In fact, vertical stacks are the default, so you could leave the orientation out from Example 20-5 without changing its behavior.)

Example 20-5. Vertical StackPanel

<StackPanel Orientation="Vertical">
    <Button Content="Buttons" FontSize="30" />
    <Button Content="in" />
    <Button Content="a" />
    <Button Content="stack" />
</StackPanel>

Figure 20-3 shows the result. Notice that in the direction of stacking—vertical in this example—the behavior is similar to the Auto height grid rows, in that each row has been made tall enough to accommodate the content. In the other direction, the elements have been stretched to fill the available space, although as we’ll see shortly, you can change that.

Vertical StackPanel

Figure 20-3. Vertical StackPanel

The Canvas panel takes an even simpler approach: it doesn’t have a layout strategy, and it simply puts elements where you tell it to. As Example 20-6 shows, just as Grid offers attachable properties to specify which grid cells elements occupy, Canvas defines attachable Left and Top properties that specify where the elements should appear.

Example 20-6. Explicit positioning with Canvas

<Canvas>
    <Button Content="Buttons" FontSize="30" />
    <Button Canvas.Left="20" Canvas.Top="40"
            Content="on" />
    <Button Canvas.Left="80" Canvas.Top="40"
            Content="a" />
    <Button Canvas.Left="60" Canvas.Top="100"
            Content="Canvas" />
</Canvas>

As Figure 20-4 shows, the exact positioning possible with a Canvas has let us position elements so that they overlap. (This figure includes some of the browser chrome to illustrate that positions are relative to the top-left corner of the Canvas.) Notice that the Canvas sizes children based on how much space they require—similar to the Auto rows and columns, but in this case the buttons are sized to content in both dimensions. Unless you specify explicit widths and heights, a Canvas will attempt to give each child exactly as much space as it requires.

Buttons on a Canvas

Figure 20-4. Buttons on a Canvas

Silverlight and WPF have extensible layout systems, so you can derive your own types from Panel or use libraries that offer other panels. For example, Microsoft offers the Silverlight Toolkit, a free library you can download in source or binary form from http://silverlight.codeplex.com/, which defines various controls, panels, and other useful components. This includes two panels, both based on panels that are built into WPF. There’s WrapPanel, which lays out its children in much the same way that text is word-wrapped in web browsers and word processors—items are arranged from left to right until all the space is used up, at which point the panel starts on a new line. And there’s also DockPanel, which lets you arrange elements by stacking them up against the left, right, top, or bottom of the panel. (DockPanel doesn’t do anything Grid can’t do, but it can be slightly simpler to use.)

Layout in WPF and Silverlight is not just about panels. Panels define the strategy by which elements are allocated a layout slot—the area on-screen in which they must fit themselves. But properties are available on all elements—regardless of the panel in use—that can influence both how big the layout slot is and what the element does with the space it is offered.

General-purpose layout properties

All elements have common properties that influence layout. There are Width and Height properties that let you specify an explicit size, rather than basing the size on the content or the available space. This is important for elements that don’t otherwise have an intrinsic size. Textual content has a natural size, but some graphical elements such as Ellipse and Rectangle don’t. If you were to create an Ellipse without setting the height and put it in a vertical StackPanel it would vanish, because the StackPanel asks it to calculate the minimum amount of space it requires, and if you have not specified any constraints, that’ll be zero. So elements with no intrinsic size usually have an explicit Width and Height, or you might use MinWidth and MinHeight to ensure that they never vanish entirely, but are able to expand to fill whatever space is available—some layouts will end up with more space than needed if the user resizes a window, so it can be useful to have a layout that adapts. MaxWidth and MaxHeight let you specify upper limits on just how far elements will expand.

The various width and height properties are useful when an element is being asked to determine its own size, such as in Auto sized grid cells. But sometimes an element’s layout slot size is imposed on it—for example, if your Silverlight user interface is configured to fill the entire browser window, the user is in charge of how big it is. This is sometimes referred to as constrained layout—this describes situations where the layout system has to make things fit a predetermined space, rather than trying to work out how much space is required. Most user interfaces contain a mixture of constrained and unconstrained layout—the top level of the UI is usually constrained by window size, but you might have individual elements such as text blocks or buttons that have to be large enough to display their content.

Note

When elements that have no intrinsic size are put in a constrained layout, they will fill the space available if you don’t set the width and height. For example, if you put an Ellipse as the only element of the root Grid layout element, and you don’t set any of the width or height properties, it will fill the whole Silverlight application UI.

You can even get a mixture of constrained and unconstrained layouts on one element. In Figure 20-3, we saw a vertical stack of elements, and vertically, each one’s size was based on its content—since the elements are free to size themselves it means we have unconstrained layout vertically. But the elements are all the same width regardless of content, indicating that constrained layout was in use horizontally. Stack panels always work this way—children are unconstrained in the direction of stacking, but are constrained to have the same sized layout slots in the other direction.

When an element has more space than it needs due to constrained layout, additional properties that determine what the element does with the excess space come into play. The HorizontalAlignment attribute lets you position the element within its slot. Example 20-7 shows a modified version of Example 20-5, specifying each of the four HorizontalAlignment options.

Example 20-7. Horizontal alignment

<StackPanel Orientation="Vertical">
    <Button Content="Buttons" FontSize="30"
            HorizontalAlignment="Left" />
    <Button Content="in"
            HorizontalAlignment="Right" />
    <Button Content="a"
            HorizontalAlignment="Stretch" />
    <Button Content="stack"
            HorizontalAlignment="Center" />
</StackPanel>

Figure 20-5 shows the results. As before, each child has been given a layout slot that fills the whole width of the StackPanel, but all except the third row have been sized to content, and have then positioned themselves within their slot based on the HorizontalAlignment property. The third button still fills the whole of its row because its alignment is Stretch. That’s the default, which is why elements fill their whole layout slot unless you specify an alignment. VerticalAlignment works in much the same way, offering Top, Bottom, Center, and Stretch.

Horizontal alignment

Figure 20-5. Horizontal alignment

Note

The alignment properties do something only when the layout slot is larger than the element requires. When an element has been given a slot exactly as large as it asked for in either the horizontal or vertical dimension, the corresponding alignment property does nothing. So setting VerticalAlignment on the child of a vertical StackPanel does nothing—the layout slot is already exactly as tall as the element requires, so the element is simultaneously at the top, the bottom, and the center of the slot.

Another very important ubiquitous layout property is Margin—this lets you specify the amount of space you’d like between the edge of an element and the boundary of its layout slot. In unconstrained layout, a margin will cause an element to be given a larger slot than it would otherwise have had, while in constrained layout, it causes an element to fill less of the slot than it otherwise would have. Example 20-8 illustrates this within a vertical StackPanel—since this uses constrained horizontal layout and unconstrained vertical layout for its children, we’ll see both effects.

Example 20-8. Buttons with Margin properties

<StackPanel Orientation="Vertical">
    <Button Content="Buttons" FontSize="30" />
    <Button Content="in" Margin="10" />
    <Button Content="a" Margin="20" />
    <Button Content="stack" Margin="30" />
</StackPanel>

In Figure 20-6, the first button fills the entire width because it has no margin. But each successive button gets narrower, because each has a larger margin than the last. Since the width is constrained, the layout system needs to make the buttons narrower to provide the specified margin between the element’s edges and its layout slot. But since the children here are unconstrained vertically, the margin has no effect on their vertical size, and instead ends up adding increasing amounts of space between each element—in the unconstrained case, Margin makes the slot larger.

Buttons with margins

Figure 20-6. Buttons with margins

Example 20-8 specifies the margins as single numbers, denoting a uniform margin on all four sides, but you can be more precise. You can provide two numbers, setting the horizontal and vertical margins. Or you can provide four numbers, indicating the left, top, right, and bottom[55] margins independently. This enables precise positioning of elements within a Grid—it turns out that you don’t have to use a Canvas to specify the position of an element. If you align an element to the left and the top, the first two numbers in a margin effectively determine its position within the containing grid cell, just as the attachable Canvas.Left and Canvas.Top properties work for children of a Canvas. The interactive design surfaces in Visual Studio and Blend use this to let you drag elements around on a grid and place them exactly where you want. It appears to be a completely free form of layout, but if you inspect what these programs do to the Xaml as you move elements around, they simply set the alignment properties appropriately and adjust the margins.

All of the layout features we’ve looked at so far take a rigidly rectangular approach—everything is either strictly horizontal or strictly vertical. In fact, WPF and Silverlight are a bit more flexible than that, thanks to their support for transforms.

Transforms

You can apply a transform to any element, modifying its size, position, and orientation, or even skewing it. (If you’re familiar with the coordinate geometry features found in most modern graphics system, you’ll recognize these as being the usual two-dimensional affine transformations possible with a 2×3 matrix.[56]) Example 20-9 shows another variation on our StackPanel example, with transforms applied to the children.

Example 20-9. Transforms

<StackPanel Orientation="Vertical">
    <Button Content="Buttons" FontSize="30">
        <Button.RenderTransform>
            <ScaleTransform ScaleX="1.5" ScaleY="0.5" />
        </Button.RenderTransform>
    </Button>
    <Button Content="in">
        <Button.RenderTransform>
            <RotateTransform Angle="30" />
        </Button.RenderTransform>
    </Button>
    <Button Content="a">
        <Button.RenderTransform>
            <SkewTransform AngleX="30" />
        </Button.RenderTransform>
    </Button>
    <Button Content="stack">
        <Button.RenderTransform>
            <TranslateTransform Y="-50" />
        </Button.RenderTransform>
    </Button>
</StackPanel>

As Figure 20-7 shows, the RenderTransform property Example 20-9 uses can mess up the layout. The transform is applied after the layout calculations are complete, so the ScaleTransform on the first button has had the effect of making it too large to fit—the default HorizontalAlignment of Stretch is in effect here, so the button has been made exactly as wide as the containing StackPanel, and then has been scaled to be 1.5 times wider and 0.5 times higher, causing it to be cropped horizontally. Likewise, the elements that have been rotated and skewed have had corners cut off. WPF offers a LayoutTransform property that takes the transform into account before performing layout, which can avoid these problems, but Silverlight does not—you would need to tweak the layout to get things to fit.

Transformed buttons

Figure 20-7. Transformed buttons

Note

A transform applies not just to the target element, but also to all that element’s children. For example, if you apply a RotateTransform to a panel, the panel’s contents will rotate.

This support for rotation, scaling, and shearing reveals that WPF and Silverlight are designed to support more graphically interesting user interface styles than traditional, rigidly rectilinear Windows user interfaces. So this seems like a good time to look at some of the graphical elements.

Graphical Elements

WPF and Silverlight support several kinds of graphical elements. The shape elements provide scalable vector-oriented two-dimensional shapes. There are also various ways to incorporate bitmap images. Video is supported through the media element. And WPF and Silverlight both provide some support for 3D graphics, although they take rather different approaches.

Shapes

Shape is the base class of various two-dimensional shapes. It’s an abstract class, and it defines common properties such as Fill and Stroke to control how the interior and outline of shapes are painted. Some of the derived classes are self-explanatory—it doesn’t take much imagination to work out what Ellipse, Rectangle, and Line do. Polyline, Polygon, and Path require a little more explanation.

Polyline lets you define a shape as a series of straight lines—you simply provide a list of coordinate pairs defining each point the shape’s outline passes through. Polygon does the same thing, but closes off the shape—it automatically joins the final point with the first one. However, you rarely use either of these, because Path lets you do all this and more. (Expression Blend never creates Polyline or Polygon elements—even if you create a shape whose outline is made up entirely of straight edges, it still makes a Path. And most Xaml export tools from programs such as Adobe Illustrator do the same. So in practice, Path is the one you’ll come across. The other two exist because they are slightly simpler to work with from code.)

Path lets you define a shape with any mixture of straight and curved segments in its outline. Example 20-10 shows a Path made up entirely of straight edges.

Example 20-10. Path with straight edges

<Path Fill="Red" Stroke="Black"
      StrokeThickness="5"
      Data="M50,0 L100,50 50,100 0,50 z"
      />

The Data property defines the shape. It consists of a series of commands and coordinates. The letters indicate the command—the initial M means Move to the specified position, (50, 0) in this case. The L means draw a Line to the next coordinate. And since this example has three coordinate pairs after the L, even though L requires only one, that means we repeat the command—so that’s three straight line segments passing through the coordinates (100, 50), (50, 100), and (0, 50). Each segment starts where the previous one left off. Finally, the z indicates that we’d like to make this a closed shape, so it will join that final point back up with the first one to form the diamond shape you see in Figure 20-8. This shape is filled in and given a thick outline, thanks to the Fill, Stroke, and StrokeThickness properties, which are available on any shape element.

Note

The shape defined by the Data describes the center of the line drawn for the outline. This means that making the StrokeThickness larger effectively increases the size of the shape—a thicker outline will encroach into the interior of the shape, but will also expand outward by the same amount. That means that the Path in Example 20-10 has a bounding box slightly larger than that implied by the coordinates in the Data. The first line segment starts at (50, 0), which is at the very top of the shape, but the stroke thickness means that the peak of the shape actually appears a bit higher. (The peak is at approximately (50, −3.54). The angle of this particular stroke means that the top corner is above the specified point by half the stroke thickness multiplied by √2.) So if you put this path at the very top left of the UI its top and left corners will be slightly cropped.

Path with straight edges

Figure 20-8. Path with straight edges

Path offers more complex commands for drawing curved shapes. Example 20-11 shows a shape with straight line segments and a single cubic Bezier curve segment, indicated with the C command.

Example 20-11. Path with Bezier curve and straight edges

<Path Fill="Red" Stroke="Black"
      StrokeThickness="5"
      Data="M50,0 L100,50 C125,74 75,125 50,100 L0,50 z"
      />

Cubic Bezier curves require four points to define them. So the C command demands three pairs of coordinates. (The first point is wherever the previous command finished, so it requires three more to bring the total to four.) Therefore, in this case, the three pairs of numbers that follow the C do not constitute three repeated commands as they did with L. You can repeat the C command; you just need to add three pairs for each segment. Figure 20-9 shows the shape defined by Example 20-11.

Path with a mixture of straight and curved edges

Figure 20-9. Path with a mixture of straight and curved edges

These examples have used simple named colors for the Fill and Stroke, but you can get more advanced. You can specify hexadecimal RGB colors using a #, as you would with HTML—for example, Fill="#FF8800" indicates a shade of orange, by mixing full-strength red (FF) with slightly more than half-strength green (88) and no blue (00). You can extend this to eight digits to define partially transparent colors—for example, Fill="8000FFFF" specifies an alpha (transparency) of 80 (semitransparent), 0 red, and full-strength green and blue, to define a semitransparent shade of turquoise.

You can also create more complex brushes. Linear and radial gradient brushes are available. Example 20-12 sets the fill of a shape to a radial gradient brush, and sets its stroke to be a linear gradient brush.

Example 20-12. Gradient brushes for fill and stroke

<Path StrokeThickness="10"
      Data="M50,0 L100,50 C125,74 75,125 50,100 L0,50 z"
      >
  <Path.Fill>
    <RadialGradientBrush>
      <GradientStop Offset="0" Color="Blue" />
      <GradientStop Offset="1" Color="White" />
    </RadialGradientBrush>
  </Path.Fill>

  <Path.Stroke>
    <LinearGradientBrush StartPoint="0,0"
                         EndPoint="0,1">
      <GradientStop Offset="0" Color="Black" />
      <GradientStop Offset="0.5" Color="White" />
      <GradientStop Offset="1" Color="Black" />
    </LinearGradientBrush>

  </Path.Stroke>
</Path>

As you can see in Figure 20-10, these brushes change color across the shape. The radial brush starts from a point in the middle (or some other point—there are properties to control the exact settings) and spreads out to an elliptical boundary. The linear gradient brush simply changes colors between the specified start and end points. Notice that you can run through as many different colors as you like with the GradientStop elements.

Gradient brushes

Figure 20-10. Gradient brushes

You can even create a bitmap-based brush with which to paint shapes, so let’s look at bitmap handling next.

Images

The shape elements are great for graphics that can be built out of geometric elements. Skilled designers can produce remarkably realistic-looking imagery with these sorts of primitives using tools such as Adobe Illustrator. However, some kinds of pictures do not lend themselves to this sort of construction—photographs, for example. You might be able to draw a stylized rendition of a photograph, but if you just want to incorporate a photographic image directly into an application, bitmaps are the way to go.

Bitmaps are pixel-oriented rather than vector-based. (From a tool perspective, it’s like the distinction between Adobe Photoshop and Adobe Illustrator.) Bitmaps do not scale as well—if you enlarge a bitmap, eventually you just see the individual pixels, leading to an appearance that is either jagged or fuzzy, depending on the way in which the bitmap is enlarged. Shapes don’t have that problem; because shapes are geometrically defined, WPF or Silverlight can render them perfectly crisply no matter how large you make them. So there’s a trade-off here—bitmaps can offer a much more photorealistic impression than vector art, but they don’t adapt so well to changes in size. That’s why graphics systems need to support both.

The simplest way to use a bitmap is with the <Image> element. You can point its Source property at any URL that contains a bitmap. Example 20-13 uses a miscellaneous image from one of the authors’ blogs. WPF or Silverlight will download and display the image at runtime. (The image may not appear in the design view, though.)

Example 20-13. Image element with HTTP URL

<Image Source="http://www.interact-sw.co.uk/images/WpfMidpointGradient.png"
       Stretch="None" />

The Stretch property indicates how to size the image. The value None says that we want the image to be rendered at its natural size. The Image element’s default behavior is to resize the bitmap so that it fills the layout slot, but that’s not always appropriate. This particular image happens to be a screenshot, and those tend to go a bit blurry if you resize them, so disabling stretching is a good idea here. Resizing is less problematic for photographs, though, so the default behavior of stretching to fit is useful there.

The Image class is a user interface element, deriving from FrameworkElement like any other. But there’s also ImageBrush—this derives from a different class, Brush, in common with the gradient brushes we saw earlier. You can use an ImageBrush to paint a shape. Example 20-14 uses the same image to provide the Fill of a Path. (Again, you may find that the image appears only at runtime, not at design time.)

Example 20-14. Painting a shape with an ImageBrush

<Path StrokeThickness="3" Stroke="Black"
      Data="M50,0 L100,50 C125,74 75,125 50,100 L0,50 z"
      >
  <Path.Fill>
    <ImageBrush
       ImageSource="http://www.interact-sw.co.uk/images/WpfMidpointGradient.png"
       />
  </Path.Fill>

</Path>

You don’t have to download images with HTTP. You can compile an image into a WPF or Silverlight application as a resource—simply adding a JPEG or PNG to the project in Visual Studio will do that. Or with WPF you can point an Image or ImageBrush at a file on disk.

Note

Silverlight supports only JPEG and PNG bitmaps—to keep the Silverlight plug-in download small, Microsoft chose a minimal set of formats, and these two cover most bases. JPEG provides efficient compression for photographic and photorealistic images, but does a bad job with screenshots and doesn’t support transparency. Conversely, PNG can reproduce screenshots perfectly and supports transparency, but compresses photographic images inefficiently.

WPF supports a much wider range of image types, including TIFF, BMP, and GIF. Moreover, it’s built on top of the extensible Windows Imaging Components (WIC) mechanism, so the set of supported formats is not closed. Some digital camera vendors provide WIC drivers for their native raw image formats, so if you have those installed, WPF can display those images directly.

Still images may not be enough for your application. You might want to incorporate movies.

Media

WPF and Silverlight offer the MediaElement, which can render videos. It can also be used to play audio files. In use, it’s almost identical to the Image element; you just point it at a video file rather than a bitmap.

Silverlight offers a VideoBrush that lets you create a brush from a video, in the same way that ImageBrush lets you create a brush from a bitmap. Slightly surprisingly, WPF does not offer this type—this is a good example of how Silverlight is not a subset of WPF. It’s possible to paint things with video in WPF, though; you just do it using something called a VisualBrush. VisualBrush is far more powerful than VideoBrush—it lets you take any UI element (even one that has children, like a panel) and turn it into a brush. So you can wrap a MediaElement in a VisualBrush to create the same effect; Silverlight doesn’t have VisualBrush, which is why it provides the more specialized VideoBrush.

Speaking of moving images, you can also apply movement to other elements in a user interface.

Animation

WPF and Silverlight allow any element to be animated—most properties that have an impact on the appearance of the UI can be modified over time. Of course, you could achieve that yourself by setting up a timer, and modifying properties of UI elements each time the timer fires. But you can let the animation system do that work for you. A complete description of animation would fill a chapter, but Example 20-15 shows a typical example.

Example 20-15. An animation

<UserControl.Resources>
    <Storyboard x:Key="ellipseAnimation">
        <DoubleAnimation
                     From="50" To="100"
                     AutoReverse="True" RepeatBehavior="Forever"
                     Storyboard.TargetName="animatedEllipse"
                     Storyboard.TargetProperty="Width" />
    </Storyboard>
</UserControl.Resources>

Animations are separate objects from the things they animate, and typically live in a Resources section—all elements have a Resources property which is a handy place to put useful objects. It’s just a dictionary—a name/value collection—a specialized dictionary similar to those of the kind described in Chapter 9. This particular example would appear as a child of the UserControl at the root of the user interface.

While this is a simple example, it illustrates all the important points. The whole thing is contained in a Storyboard—this is a collection of animations. Animations are always defined in storyboards, as this enables you to target multiple properties, or perhaps orchestrate a sequence of different animations over time. This example is simple and contains just a single animation, but we’re still required to put it in a Storyboard.

The animation itself has a From and a To value specifying the range of values the property will span during the animation—these are numbers because this is a DoubleAnimation (as in the System.Double floating-point type); if this were a ColorAnimation you’d see color values in there instead. The AutoReverse and RepeatBehavior properties here indicate that this animation runs back and forth indefinitely. And the final two properties indicate the element and property to be animated. So somewhere in the Xaml we’d expect to find an element with the name indicated, for example:

<Ellipse x:Name="animatedEllipse"
         Fill="Blue" />

Something needs to kick the animation off. In the code behind, you’d extract the animation from the resources and start it like this:

Storyboard anim = (Storyboard) Resources["ellipseAnimation"];
anim.Begin();

There are other ways to start animations. WPF supports triggers, which let you place instructions in Xaml that certain animations should be run when specific things happen. So you could tie an animation to the raising of a MouseEnter event, for example, or run an animation when the value of a property changes. You can do something similar in Silverlight using behaviors, which make it easy to define a variety of UI responses (such as running animations) with Expression Blend. Both WPF and Silverlight also support automatic running of animations in control templates, as we’ll see later.

3D graphics

WPF has basic support for 3D graphics, but that’s a topic that would take a whole chapter to cover in itself, so we won’t be getting into that in this book. Silverlight doesn’t have WPF’s 3D features, but it does have some very limited support for 3D in the form of special transforms. Besides the RenderTransform we saw earlier, you can set an element’s Projection property to make it look like it has been rotated in 3D, including perspective effects you can’t get with a 2D affine transform. This falls short of the full 3D models you can create in WPF, but provides the bare bones required to build up 3D aspects to the user interface.

Layout and graphical services are necessary to render things on-screen, but most applications require something a little more high-level—standard elements the user can interact with. So WPF and Silverlight provide controls.

Controls

Silverlight and WPF offer a range of controls, similar to many of the common controls you find in typical Windows applications. For example, there are buttons—CheckBox and RadioButton for selection, Button for a basic pushbutton, and HyperlinkButton for when you want to make your button look like a hyperlink. There’s also RepeatButton, which looks like a normal button but repeatedly raises click events for as long as you hold the button down.

For the most part, these work in a very straightforward fashion—you already saw how to handle the Click event, in Example 20-2 and Example 20-3. And as you’d expect, the two selection buttons offer events called Checked and Unchecked to notify you when they’re toggled, and an IsChecked property to represent the state. However, there is one potentially surprising feature that buttons inherit from their ContentControl base class.

Content controls

Many controls have some sort of caption—buttons usually contain text; tab pages have a header label. You might expect these controls to offer a property of type string to hold that caption, but if you look at the Content property of a Button or the Header of a TabItem, you’ll see that these properties are of type object. You can put text in there, but you don’t have to. Example 20-16 shows an alternative.

Example 20-16. Button with Ellipse as content

<Button>
  <Button.Content>
    <Ellipse Fill="Green" Width="100" Height="50" />
  </Button.Content>
</Button>

In fact, you don’t need to write that <Button.Content> property element—the base ContentControl class is marked with a [ContentProperty("Content")] attribute, which tells the Xaml compiler to treat elements that appear inside the element as the value of the Content property. So Example 20-16 is equivalent to this:

<Button>
  <Ellipse Fill="Green" Width="100" Height="50" />
</Button>

This creates a button with a green ellipse as its content. Or you can get more ambitious and put a panel in there:

<Button>
  <StackPanel Orientation="Horizontal">
    <Ellipse Fill="Green" Width="100" Height="50" />
    <TextBlock Text="Click me!" FontSize="45" />
    <Ellipse Fill="Green" Width="100" Height="50" />
  </StackPanel>
</Button>

Figure 20-11 shows the results. Content controls let you go completely crazy—there’s nothing stopping you from putting buttons inside buttons inside tab controls inside listbox items inside more buttons. Just because you can doesn’t mean you should, of course—this would be a terrible design for a user interface. The point is that you’re free to put any kind of content in a content control.

Button with mixed content

Figure 20-11. Button with mixed content

Some controls can contain multiple pieces of content. For example, a TabItem has a Content property which holds the main body of the tab page, and also a Header property for the tab caption. Both properties accept any kind of content. And then the items controls take this a step further.

Items controls

ItemsControl is the base class of controls that display multiple items, such as ListBox, ComboBox, and TreeView. If you add children to these controls, each child can be an arbitrary piece of content, much like a button’s content but with as many children as you like. Example 20-17 adds various elements to a ListBox.

Example 20-17. ListBox with mixed content

<ListBox>

    <StackPanel Orientation="Horizontal">
        <Ellipse Fill="Green" Width="100" Height="50" />
        <TextBlock Text="Text and graphics" FontSize="45" />
        <Ellipse Fill="Green" Width="100" Height="50" />
    </StackPanel>

    <Button Content="Button" />

    <TextBox Text="Editable" />

</ListBox>

Figure 20-12 shows the results. As well as showing the content we provided, the ListBox provides the usual visual responses to mouse input—the item underneath the mouse has a slightly darker background than the item below to indicate that it can be selected. The item at the bottom is darker still because it is currently selected. These highlights come from the item container—all items controls generate an item container for each child. A ListBox will generate ListBoxItem containers; TreeView generates TreeViewItem objects, and so on.

ListBox with mixed content

Figure 20-12. ListBox with mixed content

Sometimes it’s useful to bring your own container, because you may need to do more than populate it with a single piece of content. For example, when building a tree view, you don’t just need to set the node caption; you may also want to add child nodes. Example 20-18 explicitly creates TreeViewItem containers to define a tree structure.

Example 20-18. Explicit TreeViewItem containers

<ctl:TreeView>
    <ctl:TreeViewItem>
        <ctl:TreeViewItem.Header>
            <StackPanel Orientation="Horizontal">
                <Ellipse Fill="Green" Width="100" Height="50" />
                <TextBlock Text="Content" FontSize="45" />
                <Ellipse Fill="Green" Width="100" Height="50" />
            </StackPanel>
        </ctl:TreeViewItem.Header>

        <ctl:TreeViewItem Header="Child A" />
        <ctl:TreeViewItem Header="Child B" />
    </ctl:TreeViewItem>


    <ctl:TreeViewItem>
        <ctl:TreeViewItem.Header>
            <Button Content="Button" />
        </ctl:TreeViewItem.Header>

        <ctl:TreeViewItem Header="Child 1" />
        <ctl:TreeViewItem Header="Child 2" />
        <ctl:TreeViewItem>
            <ctl:TreeViewItem.Header>
                <Button Content="Child 3" />
            </ctl:TreeViewItem.Header>
        </ctl:TreeViewItem>
    </ctl:TreeViewItem>

    <ctl:TreeViewItem>
        <ctl:TreeViewItem.Header>
            <TextBox Text="Editable" />
        </ctl:TreeViewItem.Header>
    </ctl:TreeViewItem>

</ctl:TreeView>

Notice the unusual ctl: prefix—see the sidebar on the next page for an explanation.

As you can see from Figure 20-13, each Header property value has ended up as the label for a single node in the tree. The parent-child relationship of the nodes is determined by the nesting of the TreeViewItem elements in the Xaml.

TreeView with content

Figure 20-13. TreeView with content

While you can add elements directly to items controls like this, it’s often easier and more flexible to use data binding, so we’ll be coming back to items controls later.

Because this chapter is just an introduction to Silverlight and WPF, we won’t go through all the available controls in detail. There are simple data entry controls such as TextBox, AutoCompleteBox, Slider, and DatePicker. There are more comprehensive data-oriented controls such as DataGrid and DataPager. There are also utility controls such as the draggable Thumb and GridSplitter. But there’s one more kind of control we need to look at: user controls.

User Controls

A user control is, as the name suggests, a user-defined control. In Silverlight, you’ll have at least one of these—your whole user interface is one big user control, as you can see from the <UserControl> element at the root of your main page’s Xaml. But you can create more. User controls are a useful way to manage complexity.

A problem that crops up a lot in big WPF and Silverlight projects—particularly the first such project any team works on—is the 10,000-line Xaml file. Visual Studio creates one Xaml file for your user interface, and the path of least resistance is to put everything in there. As you add graphical resources, templates, data sources, animations, styles, and all the other things you can put in Xaml, it can grow very large surprisingly quickly. And there’s a related problem of having the entire application’s functionality in the one code behind file. Such programs are not maintainable, so you need to split things up.

Instead of creating one big Xaml file, it’s usually best to try to have as little as possible in your main page. It should typically do nothing more than define the overall layout, saying where each piece of the UI belongs. And then each part can go into its own user control. A user control is simply a Xaml file with some code behind. And since Xaml files with code behind always compile into classes, you can use them from other Xaml files—remember that Xaml is just a way to create objects. Example 20-19 shows the Xaml for an application’s main UI that uses this approach.

Example 20-19. Main UI containing nothing but user controls

<UserControl
    x:Class="SlUcExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:SlUcExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <app:SearchBarView      Grid.Column="0" Grid.ColumnSpan="2" />
        <app:ProductListView    Grid.Column="0" Grid.Row="1" />
        <app:ProductDetailsView Grid.Column="1" Grid.Row="1" />

    </Grid>
</UserControl>

Notice that this example defines an XML namespace prefix, app, and tells the Xaml compiler that this refers to types in the SlUcExample namespace—the default project namespace for this particular example. This time we don’t need the assembly= part because the user controls are defined as part of this project, not in a separate DLL. This prefix then refers to three user controls which would be defined elsewhere in the project.

Defining the user controls themselves is simple. You can add them as new items to your project in Visual Studio, and it will create a Xaml file with a corresponding code behind file, which you edit in exactly the same way as the main UI.

Note

As you can see in Example 20-19, we chose names that end in View for all the user controls. This is not mandatory, but it helps distinguish user control classes, which define appearance and superficial interactive behavior, from the other types that define the core behavior of your application. This distinction isn’t useful if you plan to put everything into the code behind, of course, but we presume you have more refined software design sensibilities than that, and will want to ensure that each class in your application has a single, well-defined, reasonably narrow responsibility.

User controls can contain any other controls and elements, so you can use elements built into Silverlight as well as any control libraries you may have acquired. So user controls have a lot of flexibility. However, you don’t necessarily have to build a user control anytime you want some custom UI—the scope for customization of built-in controls is greater than you might think, thanks to control templates.

Control Templates

As you already saw, controls are elements that have interactive behavior of some kind—buttons are clickable; you can type into text boxes; you can scroll through the items in a listbox and select them. What may not be obvious is that most controls only provide behavior. Controls do not define their own appearance.

This may appear to be a ludicrous claim. After all, if you add a Button to your user interface, you can see it. In fact, the appearance comes from a separate entity called a template. Controls have a default template, which is why something appears when you create a control, but this separation of appearance from behavior is important because you are free to replace the default template with your own. This lets you change the appearance of a control completely, without losing any of the behavior.

The behavior of controls is often surprisingly subtle and complex. You might think that a button is a pretty simple sort of thing, and that you could create your own equivalent by handling the MouseLeftButtonDown event on a shape. And while that would give you a clickable element, there’s a lot missing. For example, there’s the way buttons push down and pop back up. They should respond to keyboard input as well as mouse input. They should be visible to accessibility tools so that users with visual or coordination issues can use your application. And a button is about as simple as it gets. If you’ve ever used a Flash application with, say, a scroll bar that just didn’t feel like it was working properly you’re already familiar with the hazards of trying to recreate basic controls from scratch. Fortunately, control templates mean you don’t have to.

Note

Only controls have templates. So while types such as Button and TextBox have them, more primitive types such as shapes and TextBlock—UI elements that don’t have any intrinsic behavior—don’t. This shouldn’t be too surprising; an Ellipse element’s only job is to look like an Ellipse, so what would it mean for it to have a template? (And what element would you use inside the template to define the appearance? Another Ellipse? Where would it get its appearance from?)

The Control base class defines a Template property. To customize the appearance of a control, you simply set this property. As Example 20-20 shows, the property expects a ControlTemplate object, and then inside this, you can put any element you like to define the appearance. (You could, of course, use a panel if you wanted to build up a complex appearance with multiple elements.)

Example 20-20. Button with custom template

<Button Content="OK" FontSize="20">
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border
                Background="LightBlue"
                BorderThickness="3"
                BorderBrush="Black"
                CornerRadius="10">

                <ContentPresenter
                    Margin="20"
                    Content="{TemplateBinding Content}"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    />

            </Border>
        </ControlTemplate>
    </Button.Template>
</Button>

Figure 20-14 shows the results. It’s rather static—it doesn’t offer a visual response to mouse activity yet, but we’ll fix that later. But it will still raise the Click event when clicked, so it’s functional, if rather dull. Notice that we’ve set the Content property of the button, and this content—the text “OK”—has appeared as you’d hope. That doesn’t happen automatically; our template needs to say where the content should appear, and that’s the purpose of the ContentPresenter in Example 20-20. Templates for content controls need one of these placeholders for the Content property to do anything. And if you’re defining a template for a control that can hold multiple pieces of content—the Content and Header of a TabItem, for example—you need to provide a ContentPresenter for each.

Button with custom template

Figure 20-14. Button with custom template

How does Silverlight (or WPF) know which placeholder corresponds to which property? Look at the Content property of the ContentPresenter in Example 20-20—its value has an unusual syntax. The attribute value is enclosed in braces, which indicates that we’re not setting a literal value—in this case the TemplateBinding text signifies that we want to connect this particular property in this element in the template to a corresponding property of this template’s control. So {TemplateBinding Content} connects this ContentPresenter to our Button element’s Content property, while {TemplateBinding Header} would connect it to the Header property in a control that had such a property.

In fact, it’s common to use many template bindings. Example 20-20 hardcodes a lot of features of the appearance into the template, but it’s possible to reuse templates on several different controls, at which point you might want to retain the flexibility to change things such as the background color, border thickness, and so on, without needing to define a new template every time. Example 20-21 looks the same as Figure 20-14, but instead of hardcoding everything into the template it picks up more of the control’s properties using template bindings.

Example 20-21. Template with less hardcoding

<Button Content="OK" Background="LightBlue">
  <Button.Template>
    <ControlTemplate TargetType="Button">
      <Border
          Background="{TemplateBinding Background}"
          BorderThickness="{TemplateBinding BorderThickness}"
          BorderBrush="{TemplateBinding BorderBrush}"
          CornerRadius="10">

        <ContentPresenter
            Margin="{TemplateBinding Padding}"
            Content="{TemplateBinding Content}"
            ContentTemplate="{TemplateBinding ContentTemplate}"
            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
            VerticalAlignment="{TemplateBinding HorizontalContentAlignment}"
            />

      </Border>
    </ControlTemplate>
  </Button.Template>
</Button>

This template is now looking like a candidate for reuse—we might want to apply this to lots of different buttons. The usual way to do this is to wrap it in a style.

Styles

A style is an object that defines a set of property values for a particular type of element. Since elements’ appearances are defined entirely by their properties—Template is a property, remember—this means a style can define as much of a control’s appearance as you like. It could be as simple as just setting some basic properties such as FontFamily and Background, or it could go as far as defining a template along with property values for every property that affects appearance. Example 20-22 sits between these two extremes—it puts the template from Example 20-21 into a style, along with settings for a few other properties.

Example 20-22. Button style

<UserControl.Resources>
  <Style x:Key="buttonStyle" TargetType="Button">
    <Setter Property="Background" Value="LightBlue" />
    <Setter Property="BorderBrush" Value="DarkBlue" />
    <Setter Property="BorderThickness" Value="3" />
    <Setter Property="FontSize" Value="20" />

    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Button">
          <Border
           Background="{TemplateBinding Background}"
           BorderThickness="{TemplateBinding BorderThickness}"
           BorderBrush="{TemplateBinding BorderBrush}"
           CornerRadius="10">

           <ContentPresenter
            Margin="{TemplateBinding Padding}"
            Content="{TemplateBinding Content}"
            ContentTemplate="{TemplateBinding ContentTemplate}"
            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
            VerticalAlignment="{TemplateBinding HorizontalContentAlignment}"
            />

          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>

  </Style>
</UserControl.Resources>

Notice that the style is inside a Resources section—remember that all elements have a Resources property, which is a dictionary that can hold useful objects such as styles. We can then apply the style to an element like so:

<Button Content="OK" Style="{StaticResource buttonStyle}" />

This will pick up all the properties from the style. Again notice the use of braces in the attribute value—this signifies that we’re using a markup extension, which is a type that works out at runtime how to set the property’s real value. We already saw the TemplateBinding markup extension, and now we’re using StaticResource, which looks up an entry in a resource dictionary.

Note

Unlike the Template property, which is available only on controls, the Style property is defined by FrameworkElement, so it’s available on all kinds of elements.

By the way, an element that uses a style is free to override any of the properties the style sets, as shown in Example 20-23.

Example 20-23. Overriding a style property

<Button Content="OK" Style="{StaticResource buttonStyle}"
        Background="Yellow" />

Properties set directly on the element override properties from the style. This is why it’s important to use TemplateBinding in templates. The style in Example 20-22 sets a default Background color of LightBlue, and the template then picks that up with a TemplateBinding, which means that when Example 20-23 sets the background to yellow, the control template picks up the new color—that wouldn’t have happened if the light blue background had been baked directly into the template. So the combination of styles, templates, and template bindings makes it possible to create a complete look for a control while retaining the flexibility to change individual aspects of that look on a control-by-control basis.

There’s one problem with our button style: it’s rather static. It doesn’t offer any visible response to mouse input. Most controls light up when the mouse cursor moves over them if they are able to respond to input, and the fact that our control doesn’t is likely to make users think either that the application has crashed or that the button is merely decorative. We need to fix this.

The Visual State Manager

A control template can include a set of instructions describing how its appearance should change as the control changes its state. These are added with an attachable property called VisualStateGroups, defined by the VisualStateManager class.[57] Example 20-24 shows a modified version of the template that adds this attachable property.

Example 20-24. Control template with visual state transitions

<ControlTemplate TargetType="Button">
    <Border x:Name="background"
      Background="{TemplateBinding Background}"
      BorderThickness="{TemplateBinding BorderThickness}"
      BorderBrush="{TemplateBinding BorderBrush}"
      CornerRadius="10">

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="background"
                            Storyboard.TargetProperty="(Border.Background).
                                                (SolidColorBrush.Color)"
                            To="Red" Duration="0:0:0.5" />
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Normal">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="background"
                            Storyboard.TargetProperty="(Border.Background).
                                                (SolidColorBrush.Color)"
                            Duration="0:0:0.5" />
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>

        </VisualStateManager.VisualStateGroups>

        <ContentPresenter
            Margin="{TemplateBinding Padding}"
            Content="{TemplateBinding Content}"
            ContentTemplate="{TemplateBinding ContentTemplate}"
            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
            VerticalAlignment="{TemplateBinding HorizontalContentAlignment}"
            />

    </Border>
</ControlTemplate>

The VisualStateGroups property contains one or more VisualStateGroup elements—the groups you can add in here are determined by the control. Button defines two groups: CommonStates and FocusStates. Each group defines some aspect of the control’s state that can vary independently of the other groups. For example, FocusStates defines a Focused and an Unfocused state based on whether the button has the keyboard focus. The CommonStates group defines Normal, MouseOver, Pressed, and Disabled states—the control can be in only one of those four states at any time, but whether it’s focused is independent of whether the mouse cursor is over it, hence the use of different groups. (The groups aren’t wholly independent—a disabled button cannot acquire the focus, for example. But you see multiple state groups anytime there’s at least some degree of independence.)

Example 20-24 defines behaviors for when the button enters the MouseOver state and the Normal state, with a VisualState for each. These define the animations to run when the state is entered. In this example, both animations target the Border element’s Background. The first animation fades the background to red when the mouse enters, and the second animates it back to its original color when the state returns to normal. (The absence of a To property on the second animation causes the property to animate back to its base value.)

Note

Visual state transitions typically end up being very verbose—the only way to modify properties is with animations, even if you want the changes to happen instantaneously, so even a simple change requires a lot of markup. And you will typically want to provide transitions for all of the states. In practice, you would normally create them interactively in Expression Blend, which will add all the necessary Xaml for you.

So far, everything we’ve looked at has been strictly about the visible bits, but any real application needs to connect the frontend up to real data. To help with this, WPF and Silverlight offer data binding.

Data Binding

Data binding lets you connect properties of any .NET object to properties of user interface elements. The syntax looks pretty similar to template binding. Example 20-25 shows a simple form with a couple of text entry fields, both using data binding to hook up to a source object.

Example 20-25. Data entry with data binding

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <TextBlock VerticalAlignment="Center" Text="Name:" />
    <TextBox Grid.Column="1"
             Text="{Binding Path=Name}" />

    <TextBlock VerticalAlignment="Center" Grid.Row="1" Text="Age:" />
    <TextBox Grid.Column="1" Grid.Row="1"
             Text="{Binding Path=Age}" />
</Grid>

Just as template bindings refer to properties on the target control, so these data binding expressions refer to properties on some source object. Data sources don’t need to be anything special—Example 20-26 shows an extremely simple class that will work just fine as the data source for Example 20-25.

Example 20-26. A very simple data source

public class Person
{
    public string Name { get; set; }
    public double Age { get; set; }
}

The code behind can create an instance of this type, and then make it available to the binding expressions in our user interface by putting it in the DataContext property as Example 20-27 shows.

Example 20-27. Setting up a data source

public partial class MainPage : UserControl
{
    private Person source = new Person { Name = "Ian", Age = 36 };

    public MainPage()
    {
        InitializeComponent();

        this.DataContext = source;
    }
}

As you can see from Figure 20-15, the UI displays the two properties from the source object thanks to data binding. This may not seem any more convenient than just writing code to set the Text properties of the two TextBox elements directly, but data binding can do a little more than that. When the user types new values into the text boxes, the source Person object’s properties get updated with those new values. If we were to modify the Person class to implement the INotifyPropertyChanged interface—a common way to provide notification events anytime a property changes—data binding would detect changes in the data source and update the UI automatically.

Arguably the most important benefit of this kind of data binding is that it provides an opportunity to separate your application logic from your UI code. Notice that our Person class doesn’t need to know anything about the user interface, and yet the data it holds is connected to the UI. It’s much easier to write unit tests for classes that don’t require a user interface simply to run.

Data-bound text boxes

Figure 20-15. Data-bound text boxes

A classic rookie mistake with WPF and Silverlight is to write code that relies too much on UI elements—an example would be to use TextBox elements as the place you store your data. That might seem like a simplifying step—why add a class to remember the Name and Age when the UI can remember them for us? But that would mean any code that needed to access that data would have to read it out of the UI elements. This causes two problems: first, it makes it hard to change anything in the user interface without breaking the whole program, and second, it makes it impossible to test any individual part of the program in isolation. So while the separation illustrated with this example may seem excessive, for any nontrivial application it turns out to be very useful to keep the UI code completely separate from the rest of the code, hooking things together only via data binding. This tends to lead to code that is much easier to maintain than programs where a lot of the code deals directly with user interface elements.

Example 20-25 uses just a couple of ad hoc binding expressions in a user interface, but there’s a slightly more structured and very powerful data binding feature you can use with item controls: data templates.

Data Templates

Just as a control’s appearance is defined by a control template, you can create a data template to define the appearance of a particular data type. Look at the user interface in Figure 20-16—it shows a pair of listboxes, in a typical master/details scenario.

The ListBox on the left looks fairly ordinary—it lists product categories, showing each one as simple text. You might think this works by fetching the list of categories and then iterating over them with a loop that creates a ListBoxItem for each one. In fact, it’s much simpler than that. Example 20-28 shows the Xaml for the ListBox on the left.

Note

This application is using the Adventure Works sample database introduced in Chapter 14, which the hosting web application is making available to the Silverlight client with a combination of the WCF Data Services mechanism described in the same chapter, and some of the networking features described in Chapter 13. The precise details of the server code are not directly relevant to this chapter, but you can get the code by downloading the samples from this book’s web page: http://oreilly.com/catalog/9780596159832/.

Lists with data templates

Figure 20-16. Lists with data templates

Example 20-28. ListBox displaying simple text

<ListBox x:Name="categoryList" DisplayMemberPath="DisplayName"
         SelectionChanged="categoryList_SelectionChanged">

Example 20-29 shows the code that puts the categories into it.

Example 20-29. Providing items for a listbox

categoryList.ItemsSource = categoryViewModels;

Obviously, we left out some code—that categoryViewModels variable, which contains a list of objects each representing a category, had to come from somewhere. But right now we’re focusing on how the data gets hooked up to the UI, not where it came from, so to avoid distracting you with details irrelevant to this chapter’s topic, we’re just showing the code that deals with the UI aspects. And as you can see, it’s really very simple. ListBox derives from ItemsControl, from which it inherits an ItemsSource property, and you can assign any collection into ItemsSource. The control will iterate through the collection for you, generating an item container (a ListBoxItem in this case) for every object.

The Xaml sets the DisplayMemberPath attribute to DisplayName—this determines which property on the source object the ListBoxItem reads to work out what text to display for the object. And that’s why the lefthand list displays the category names. But clearly the list on the righthand side of Figure 20-16 is much more interesting. It shows all the products for the currently selected category, but it’s not just displaying text—it’s showing an image for each product. The product list is updated when we select a category, and Example 20-30 shows the code that handles the SelectionChanged event of the category ListBox, which was hooked up in Example 20-28.

Example 20-30. Loading the selected category’s products

private void categoryList_SelectionChanged(object sender,
                     SelectionChangedEventArgs e)
{
    CategoryViewModel currentCategory =
        categoryList.SelectedItem as CategoryViewModel;
    if (currentCategory == null)
    {
        productList.ItemsSource = null;
    }
    else
    {
        productList.ItemsSource = currentCategory.Products;
    }
}

This has some code to deal with the fact that we sometimes get a SelectionChanged event to notify us that nothing at all is selected. But the interesting code here looks much the same as before—once again we’re just setting the ItemsSource of a ListBox (the one on the right this time) to a collection of objects, the products in the selected category.

Example 20-30 sets the ItemsSource in much the same way as Example 20-29, but the two listboxes—on the left and right of Figure 20-16—look very different. That’s because the Xaml for the second listbox is different:

<ListBox
    x:Name="productList"
    Grid.Column="1">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />

                </Grid.ColumnDefinitions>

                <TextBlock Text="{Binding Path=DisplayName}" />
                <Image Grid.Column="1" Source="{Binding Path=Thumbnail}" />
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Instead of using DisplayMemberPath to specify what text to display, this sets the ItemTemplate, which does for an items control’s data items roughly what a control’s Template property does for the whole control—it defines the appearance. For each item in the ItemsSource, an instance of that DataTemplate will be created, with its DataContext set to the source item in question. So those two Binding expressions will pick up the Text property for the TextBlock and the Thumbnail property for the Image from the data source object for the product.

Note

The fact that our source object provides a Thumbnail property is a good example of why we need a view model class that’s distinct from the model. The underlying model may well offer the bitmap—indeed, in this example, there is a model object (not shown, but available for download) with a property containing the raw binary data for the bitmap. And while WPF can automatically convert a byte array to the ImageSource type the Image element requires, Silverlight cannot, and it becomes the job of the view model to transform the data into a suitable data type. So although the view model has no dependencies on the view code itself, it provides data tailored specifically for the view, even to the point of offering properties with types specific to WPF or Silverlight.

There is a connection between data templates and content controls: any content control is able to load a data template. (In fact, the heart of the mechanism is the ContentPresenter type that appears in any content control’s template, as you saw in Example 20-20. This is the element that knows how to load a data template.) The reason items controls are able to instantiate a data template for each item is that the item containers (ListBoxItem, TreeViewItem, etc.) are content controls. So you can use data templates in all sorts of places—for the content of buttons, the headers and contents of tab controls, the labels on tree views, and so on. Just as items controls offer an ItemTemplate property, you’ll find similar ContentTemplate and HeaderTemplate properties that also accept data templates.

Summary

In this chapter, we discussed how you can build the structure of a user interface with Xaml, and how the associated code behind file can handle events and provide the UI elements with the information they need to perform their work. You saw some of the more important control types, and in particular, you looked at the content controls that can contain anything you like as content. You also saw how to connect your application’s data to the screen with data binding.



[53] It’s not usually necessary to download the entire .NET Framework—an online installer can determine which bits are required for the target machine. Even so, a full Silverlight download ends up being about one-fifth the size of the smallest possible download required for the full framework.

[54] If you look in the documentation you’ll see that more than three types derive from Panel. However, the others are in the System.Windows.Controls.Primitives namespace, signifying that they are not meant for general use. These are specialized panels designed only to be used inside specific controls.

[55] Yes, that is a different order than CSS. Silverlight and WPF follow the coordinate geometry convention of specifying pairs of coordinates as horizontal and then vertical measures—x before y. Hence left, then top, followed likewise by right, then bottom.

[56] Strictly speaking, it’s a 3×3 matrix, but the final column is fixed to contain (0, 0, 1).

[57] This class was originally unique to Silverlight. It was added later to WPF in .NET 4. WPF has an older mechanism called triggers that can also be used to get the same results. Triggers are more complex, but are also more powerful. Silverlight does not currently offer them.

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

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