Chapter 23. Resources, styles, and control templates

Chapter 22 described the powerful animation features available in Silverlight. These features are useful for creating entertaining illustrations and for adding a degree of richness to your application. This richness can also be applied to controls, as you'll see in this chapter.

Throughout this chapter, you'll see how to apply rich styles to your application. These styles are similar to the CSS features you may have seen in the HTML world. In general, a Style declaration will be part of a resource, so we'll cover resources first. From there, you'll learn how to manage resources in dictionaries before moving on to creating rich visual Styles themselves. We'll cover both explicit and implicit styles. Then you'll see how to expand on the definition of a Style to define a ControlTemplate, enabling you to redefine the visual structure of a Control. Once we've covered that subject, you'll learn how to use the VisualStateManager to deliver engaging visual states and transition animations within your Control elements.

23.1 Being resourceful

In general, it's a good idea to create reusable items whenever possible. This practice makes your application more maintainable. In fact, creating reusable components is a common idea within object-oriented languages such as C#. But, sometimes, you may have items that represent nonexecutable pieces of data—for instance, an image, a media file, or some XAML. These types of content generally fall into one of three categories: declarative resources, loose resources, and bundled resources. We'll cover all three of these categories in this section, beginning with declarative resources, which are fairly different compared to the other two.

23.1.1 Declarative resources

Declarative resources are items intended to be shared across multiple elements in your project. They can be any object you want to share. For instance, a resource can be used to define a DataTemplate or Storyboard, as hinted at earlier in this book. Resources are also a vital part of the styling and templating features discussed later in this chapter. Before you see those features, let's examine the basic syntax and usage of a declarative resource (see listing 23.1).

Example 23.1. The basic syntax and usage of a resource

The basic syntax and usage of a resource

This listing shows a basic declarative resource scoped to a StackPanel in the form of a LinearGradientBrush The basic syntax and usage of a resource. This GradientBrush is used by both TextBlock elements defined in this listing The basic syntax and usage of a resource. This shared approach is possible because the resource is within the same scope as the two TextBlock elements. Within this section, you'll learn about resource scoping in further detail. In addition, you'll see how to use declarative resources at design time. This task will demonstrate to the meaning behind the x:Key attribute and StaticResource items shown in listing 23.1. Finally, this section will end with a discussion of using declarative resources at runtime.

DEFINING DECLARATIVE RESOURCES

Resources must be defined within an appropriately named collection called Resources. This collection is a ResourceDictionary, a specialized dictionary containing resources identified by implicit or explicit keys. These resources and their associated keys can be defined at both design time and runtime. Both approaches are shown in listing 23.2.

Example 23.2. SolidColorBrush defined as a resource at design time and runtime

XAML:

<StackPanel x:Name="myStackPanel">
  <StackPanel.Resources>
    <SolidColorBrush x:Key="theSolidColorBrush" Color="Green" />
  </StackPanel.Resources>
</StackPanel>

C#:

SolidColorBrush brush = new SolidColorBrush();
brush.Color = Colors.Green;
myStackPanel.Resources.Add("theSolidColorBrush", brush);

This example shows how to define a resource at design time and runtime. Both approaches require you to specify two items. The first item is the key, which in this case is theSolidColorBrush. The other is the resource itself, which in this case is a SolidColorBrush.

The key of a resource is a string that uniquely identifies it. At runtime, this identifier is set when you add a resource to a ResourceDictionary. Because the ResourceDictionary class implements the IDictionary interface, you can add a resource using the Add method. The first parameter of this method represents a key. The ResourceDictionary implements the IDictionary interface, so you should use the x:Key attribute to identify a resource at design time. Keys are only necessary when you want to explicitly use a resource, as shown in the examples so far. When we get into styles, you'll see how an implicit key can be useful as well. Once you've selected a key, you may move on to the details of the resource.

The resource itself is the reason for this section. This item can be virtually any object that Silverlight allows to be shared. For example, controls and visuals can't be shared. In general, you'll most likely use Storyboard, Style, and Template items as resources with the occasional low-level int or double type and maybe even instances of your own classes for data. Regardless of the type of resource, the item can only be used within its respective scope.

ALL ABOUT SCOPE

So far, we've seen resources placed into various on-page elements. That's great for nonstyle resources such as references to data objects and whatnot. When used with styles, though, it's similar to placing CSS styles directly into your HTML page—it works, but it's not really a best practice. In most cases, your resources are going to be placed in a central location, such as in App.xaml.

Resources defined in App.xaml are available to the entire application. The syntax is the same as defining resources locally. After a resource has been defined, either locally or through App.xaml, it can be referenced at design time through the StaticResource markup extension:

<TextBlock x:Name="myTextBlock" Text="Hello, World"
  Foreground="{StaticResource theSolidColorBrush}" />

The StaticResource extension expects a single value that must match a key from an in-scope ResourceDictionary. The resource must also be defined syntactically before it's referenced. Because of this requirement, listing 23.3 won't work.

Example 23.3. How not to be seen

XAML:

<StackPanel x:Name="myStackPanel">
  <TextBlock x:Name="myTextBlock" Text="Hello, World"
    Foreground="{StaticResource theSolidColorBrush}" />
  <StackPanel.Resources>
    <SolidColorBrush x:Key="theSolidColorBrush" Color="Green" />
  </StackPanel.Resources>
</StackPanel>

This listing shows an invalid use of a resource; the resource is used before it's defined. If you attempt to run this example, it'll throw an XamlParseException. The order in which entries appear is especially important once you start working with independent resource dictionaries and have to merge them in the correct order.

MERGING RESOURCE DICTIONARIES

Though App.xaml may ultimately be the aggregation point for your resources, a common strategy is to place resources into individual resource dictionary files. The files are typically groups of related resources or entire application themes. Those resource dictionary files are then compiled into the application (build action of Page, Custom Tool set to MSBuild:Compile) and merged in from App.xaml or into the dictionaries of individual pages.

For example, listings 23.4 through 23.7 show how to merge two different resource files into your application using App.xaml as the aggregation point but also referencing from within the dictionaries. Listing 23.4 shows the first resource file, StandardColors.xaml.

Example 23.4. Merging resource dictionaries—colors dictionary

StandardColors.xaml:

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <SolidColorBrush x:Key="TextColor" Color="#FF303030" />

  <SolidColorBrush x:Key="HeadlineTextColor" Color="Black" />

  <LinearGradientBrush x:Key="PageBackgroundColor"
                       StartPoint="0,0"
                       EndPoint="0,1">
     <GradientStop Offset="0" Color="#FFFFFFFF" />
     <GradientStop Offset="1" Color="#FFD0D0D0" />
  </LinearGradientBrush>
</ResourceDictionary>

The StandardColors.xaml resource dictionary is our base dictionary. It has no dependencies on others, but is used in several other places. This dictionary defines three Brush resources, two of which are simple SolidColorBrush instances; the third is a LinearGradientBrush.

The StandardColors.xaml resource dictionary is used by the ControlStyles.xaml resource dictionary shown in listing 23.5.

Example 23.5. Merging resource dictionaries—control styles dictionary

Merging resource dictionaries—control styles dictionary

The standard colors dictionary is merged into the control styles dictionary Merging resource dictionaries—control styles dictionary. This is required because the control styles dictionary uses resources defined in the color dictionary. As you'll see in the next listing, dependencies can't be chained; they don't ripple "upward" and must be explicitly defined in each XAML file. Listing 23.6 shows App.xaml where the dictionaries are made available to the whole application.

Example 23.6. Merging resource dictionaries—App.xaml

Merging resource dictionaries—App.xaml

Listing 23.6 shows App.xaml. Both StandardColors.xaml Merging resource dictionaries—App.xamland ControlStyles.xaml Merging resource dictionaries—App.xamlare merged into App.xaml to make their included resources available to the rest of the application. As I mentioned earlier, you can't merge resources and expect the dependencies to flow through. For example, StandardColors.xaml is merged into ControlStyles.xaml. That's not sufficient to make the resources in StandardColors. xaml available outside on the control styles; they're in a private dictionary. To expose them to the rest of the application, they're all merged into the applicationwide dictionary in the Application object (see listing 23.7).

Example 23.7. Merging resource dictionaries—main page

Merging resource dictionaries—main page

Finally, we get to our main page. MainPage.xaml is using a resource Merging resource dictionaries—main pagedefined in StandardColors.xaml. It's also using a control style Merging resource dictionaries—main pagedefined in ControlStyles.xaml. Finally, the TextBlock Merging resource dictionaries—main pageis using an implicit style defined in ControlStyles.xaml.

If you're coming from a CSS background, the way the resources are nested may seem odd to you because each goes into a discrete dictionary instead of a global sheet. In the next section, we'll cover how to access those discrete dictionaries, whether they're at an element level or application level.

USING DECLARATIVE RESOURCES AT RUNTIME

Referencing resources at design time is useful for setting up the initial state of an application. As an application runs, you may need to work with those resources dynamically. To help you accomplish this feat, Silverlight enables you to search for, insert, edit, and remove resources at runtime.

Searching for a resource at runtime involves referencing the Resources property, which is a ResourceDictionary available on every FrameworkElement and Application. Because of this, you can readily search for a declarative resource by using its key. If the resource isn't found, null will be returned; if the resource is found, its object representation will be returned. Because the return value may be an object, you may need to cast the value to another type, as shown in this example:

var brush =
   myStackPanel.Resources["theSolidColorBrush"] as SolidColorBrush;
if (brush != null)
   brush.Color = Colors.Blue;

This code retrieves the SolidColorBrush defined as a resource in listing 23.2. Once it's retrieved, this Brush is changed from Green to Blue. This small but interesting change occurs at runtime. When this code is executed, the TextBlock in listing 23.2 changes to Blue without any additional code because the Silverlight system automatically listens for those changes. But, it doesn't necessarily listen for when resources are removed.

Accessing resources in elements uses the same syntax for any given element. To access resources defined in the application, it's slightly different:

var brush =
   Application.Current.Resources["theSolidColorBrush"] as SolidColorBrush;
if (brush != null)
   brush.Color = Colors.Blue;

Rather than specifying an element, you need to specify Application.Current. Once you move beyond that, the syntax is the same. This will also pick up any merged-in resources, so there's no special step required to navigate down into any other dictionaries. (Note that in XAML, the difference is abstracted away by the StaticResource extension.)

Resources may be removed at runtime through the Remove method. This method takes a string that represents the key of the resource to delete. Once it's deleted, this resource can't be used. If the resource was applied to any items in your Silverlight application, the Resources attributes will still be in use; if you remove a resource, you may want to manually update any elements using the declarative resource.

Declarative resources are those defined within your Silverlight application. These resources can be created at either design time or runtime. In addition, declarative resources can be added, edited, and removed at runtime through the readily available Resources property. In addition to declarative resources, Silverlight has another type of resource known as loose resources.

23.1.2 Accessing loose resources

In addition to using resources defined within your XAML, Silverlight enables you to access loose resources. A loose resource is an external entity, which may represent something such as an image hosted on some server on the Internet or some publicly visible JSON data. Regardless of the type of content, Silverlight provides the ability to access loose resources. To demonstrate accessing a loose resource, imagine an ASP.NET web application with the structure shown in figure 23.1.

Figure 23.1 shows the structure of a basic ASP.NET web application. This web application has one web page named Default.aspx. Assume that this web page hosts the Silverlight application defined within the MySilverlightApplication. xap file, which is nestled within the ClientBin directory. This will become important in a moment. Also note the four image files that are part of this web application structure: image01.png, image02.png, image03.png, and image04.png. These images represent the loose resources that we'll use throughout this section.

A sample web site project structure. Note the four .png files.

Figure 23.1. A sample web site project structure. Note the four .png files.

You'll learn two different ways to access loose resources. The first approach involves referencing loose resources whose location is relative to the Silverlight application. The second approach involves using an absolute Uri.

REFERENCING RELATIVE LOOSE RESOURCES

Silverlight allows you to access loose resources relative to the site of origin—the location where the requesting Silverlight application resides. In many cases, your Silverlight application will be stored within a subdirectory. For instance, in figure 23.1, the Silverlight application (MySilverlightApplication.xap) is stored within the ClientBin directory, so this directory can be considered the site of origin. If you want to access image01.png in figure 23.1, you could use the Source shown here:

<Image x:Name="myImage" Source="image01.png" />

This accesses a resource in the same directory as MySilverlightApplication.xap. This directory represents the site of origin. If you change the Source property to reference /image01.png, you'd get the same result because the site of origin represents the root directory when a relative URI is used. This syntax will still allow you to reference loose resources in subdirectories. For instance, you could reference image02.png in figure 23.1 using the Source in this example:

<Image x:Name="myImage" Source="directory/image02.png" />

This markup shows how to reference a loose resource in a subdirectory, demonstrating that you can use subdirectories with relative references. If you reference a .xap file on a remote server, all your references will be relative to that remote reference. This is important because you can't use a relative URI to access loose resources in directories that are ancestors to the site of origin. This restriction is a security measure to help ensure that preexisting loose resources can't be used unless you explicitly allow it. To allow this use, you must expose them through the cross-domain policy file mentioned in chapter 14 and use an absolute Uri.

RETRIEVING LOOSE RESOURCES WITH AN ABSOLUTE URI

Silverlight gives you the flexibility to access loose resources via an absolute Uri. This gives you the flexibility to access resources from anywhere across the Internet as long as the target server allows it in its cross-domain policy file. This requirement is also necessary if you want to access a resource located up the directory tree from your .xap file. For instance, if the Silverlight web site structure in figure 23.1 is hosted at http://www.silverlightinaction.com, you could access image03.png by using the Source shown here:

<Image x:Name=
   "myImage" Source="http://www.silverlightinaction.com/image03.png" />

This example shows how to access a loose resource via an absolute Uri. This Uri points at the location of the resource, and this location will be loaded as a loose resource. There's also a way to bundle resources along with your Silverlight application.

23.1.3 Bundled resources

The third kind of resource used in Silverlight is referred to as a bundled resource. A bundled resource is an item included in the .xap file of a Silverlight application. The term "bundled resource" is a made-up expression used solely for the sake of communication. Bundled resources give you a way to include resources specific to a Silverlight application.

Throughout this section, you'll learn about the two types of bundled resources that can be used in Silverlight. The first is known as a content file—a file that's added to the .xap file and deployed alongside a Silverlight application. The other type of resource is known as an embedded file, which represents an item that gets embedded into a Silverlight assembly. This kind of resource can be useful for helping to hide your valuable resources.

USING CONTENT FILES

A content file is one that's added to a .xap file and deployed alongside a Silverlight application within the .xap. If you define an image as a content file, that image will be included within the resulting .xap file when the Silverlight application is built. In fact, any file with a build action of Content will be bundled into the resulting .xap file. Figure 23.2 shows a .png and .xml file being bundled into a .xap file as content files.

Defining a file as a content file. When it's compiled, the content file is added to a .xap file.

Figure 23.2. Defining a file as a content file. When it's compiled, the content file is added to a .xap file.

Figure 23.2 shows two files that have been added and marked as content files. The first, xmlData.xml, is a basic XML file. Once the project is built, this file ends up in the root of the .xap file structure. The second, image.png, belongs to a nested directory called images. When the project is built, this relative directory structure is carried over to the final .xap file, which can be accessed using a relative path reference. All content files can be referenced by providing a path relative to the application assembly. This approach can be used at design time, as shown in this markup:

<Image x:Name="myImage" Source="/images/image.png" />

This example shows the syntax used to reference a content file at design time. The leading forward slash (/) informs Silverlight to begin looking at the same level as the application assembly. This approach is the recommended way to include content with a Silverlight application because it makes things more easily accessible. Sometimes, you may come across somebody who does things the old-school way—the content files will be embedded within the Silverlight assembly. You'll now see how to access these embedded files.

USING EMBEDDED FILES

An embedded file is a file embedded within a Silverlight assembly, which may be either an application or a library. Either way, an embedded file becomes a part of an assembly by changing the build action to Resource. This file can be retrieved at design time or runtime by providing a special URL structure.

Embedded resources are accessible through a URL that has three parts. The first part names the assembly to which the resource belongs. The second piece is a special keyword called Component that declares a resource as being retrieved. The final part is a relative URL that maps to the location of the resource within the assembly. These three items come together to form a URL template that looks like the following:

[AssemblyName];component/[RelativePath]

This template can be used at design time or runtime. The design-time implementation relies on the element type to convert the resource. At runtime, you must manually convert the resource. First, you retrieve the embedded resource from the assembly through the Application class, as shown in this line of code:

StreamResourceInfo resource = Application.GetResourceStream(
  new Uri("SilverlightApp1;component/embedded.png", UriKind.Relative));

This example shows how to retrieve the resource from the assembly. This resource is represented as a StreamResourceInfo, which is part of the System.Windows. Resources namespace. This class instance must be converted to the type appropriate for your situation. As we mentioned earlier, you shouldn't come across this scenario very often. When it comes to content files, you'll probably come across a loose resource. In XAML, you'll most likely use declarative resources. This approach is especially true if you're giving your elements Style.

23.2 Giving your elements style

As you saw in section 23.1, resources are the nonexecutable parts of your application. These parts are useful for creating items that can be reused multiple times. In addition to being reused, resources can also be shared by multiple elements. These two characteristics make resources a natural fit for styling.

Styling is a way to consistently share the same property values across multiple elements. To see why this is a good idea, imagine needing to create a typical forms-based application. This application must use TextBox elements that have a bold 9 pt Verdana font for input. In addition, you want to give the TextBox elements a subtle gradient background to make them more appealing. Without styles, you may decide to implement these visual enhancements as shown in listing 23.8.

Example 23.8. Brute-force approach to applying common properties

Brute-force approach to applying common properties
Brute-force approach to applying common properties

Listing 23.8 shows the brute-force approach to defining the visual properties of multiple elements. It defines two TextBox elements with the same values for the Height, Width, FontFamily, FontSize, and FontWeight properties. In addition, the same complex LinearGradientBrush definition is used for the Background of both TextBox elements. Unfortunately, this approach isn't scalable. For instance, if you need to change the font of the TextBox items, you'd have to make the change to each item, but you can overcome this minor inconvenience using a Style.

A Style is a way to share the same property values across multiple elements. Throughout this section, you'll learn how to create and use a Style. This approach will help you avoid the maintenance nightmare shown in that last listing. You'll first see how to define the visual properties of a control through a Style. From there, you'll learn how to share a Style definition across multiple elements.

23.2.1 Defining the look

To define the look of an element using a Style, you simply set the Style property. This instruction may sound redundant, but the property name is the same as the type name. The Style property is available on every FrameworkElement, so virtually every control in the Silverlight framework can be styled. You can do this by taking advantage of the Style class's Setters collection.

The Setters collection stores the entire definition of a Style. This definition is made up of individual property/value pairs similar to those seen in CSS within the HTML world. In Silverlight, each combination is defined within a Setter element, which lets you assign a value to a single visual property. Interestingly, this approach can be used to set both simple and complex property values.

SETTING SIMPLE PROPERTY VALUES

A simple property is a property that can be set at design time with a primitively typed value. A primitively typed value is something like an int or string value. These kinds of values can be used at design time to set the values for properties such as FontSize and FontFamily. Listing 23.9 shows how to use five simple properties as part of a Style.

Example 23.9. A basic style definition

A basic style definition

This example shows how to define a Style A basic style definition that uses five simple properties, each of which is defined within a Setter element. These elements are automatically added to the Style object's Setters collection. More importantly, each of the items in this collection is defined by two publicly visible attributes.

The two attributes that define a Setter are called Property and Value. The Property attribute determines which property the Value will be used with. The Property must be a DependencyProperty, but the Value property can be set to any object. Because of this fact, the Setter element is flexible, making it a natural fit for simple properties. In addition, it's also a natural fit for complex properties.

SETTING COMPLEX PROPERTY VALUES

A complex property is a property whose value is a general-purpose object. In general, these kinds of properties have nested properties that must be set. For example, a LinearGradientBrush could be considered a complex property value because it has the additional stops broken out using property element syntax. Now, imagine trying to use this LinearGradientBrush as part of a Style. You must use an approach similar to that shown in listing 23.10.

Example 23.10. A complex property in a style definition

A complex property in a style definition

Listing 23.10 shows how to define a more complex property as part of a Style A complex property in a style definition. There aren't any new elements shown here, but it does show you how to break out the Setter.Value into a nested property itself. This approach gives you the flexibility to use something as complex as a LinearGradientBrush.

The approaches shown in both of these examples haven't solved the problem of scalability; they've just shown the syntax of a Style used inside of an element instead of explicit properties. To solve to problem of scalability, you must understand how to target your Style definitions so they may be reused.

23.2.2 Explicitly keyed style definitions

In CSS, one way to define styles is to include two parts. One part represents the name of the style; the other part is the name of the HTML tag the style is applicable to. An explicit Style in Silverlight also uses these two parts.

The first part of a Style uniquely identifies a Style definition. As shown in the previous two examples, this part is optional. It becomes a requirement only if you create a Style as a resource and want to refer to it explicitly. If you choose this approach, you must specify the x:Key attribute to uniquely identify the Style, and you must specify the other part—the TargetType.

The TargetType property signals which System.Type a Style is applicable to. This property doesn't need to be set if you define a Style within an element, as shown in the previous examples. If you define a Style as a resource, you must set this property as shown in listing 23.11.

Example 23.11. Defining the TargetType of a Style

Defining the TargetType of a Style

Listing 23.11 shows a Style defined as a resource. The Style in this case is configured to be used with TextBox elements as set through the TargetType property Defining the TargetType of a Style. If you were to attempt to use this Style with an element of another type, an error would occur. It can be used with any types that might derive from TextBox.

CSS supports implicit styles, where you simply specify the type (a DIV, for example) and CSS applies it to all DIV elements within a certain scope. Silverlight supports a similar type of implicit styling.

23.2.3 Implicit style definitions

In the previous example, we saw how a style may be reused by applying it to each control. In a large application with many controls, this procedure can be both tedious and error prone. Silverlight 4 introduced implicit styles.

Implicit styles look exactly like their explicit cousins, except they omit the key. That's it. If you define a style with a TargetType and omit the key, the TargetType becomes the key and the style becomes implicit.

Listing 23.12 shows the previous example but converted to an implicit style.

Example 23.12. Defining the TargetType of a Style

Defining the TargetType of a Style

Just as we saw in the previous example, this listing defines a style Defining the TargetType of a Style that targets the TextBox type. But we've omitted the x:Key, so this is an implicit style. Note the TextBox controls at the bottom of the listing: they now pick up the style without requiring any StaticResource setting. This is a huge timesaver for applications of any real complexity.

Styling, both explicit and implicit, is a powerful way to define the user interface standards for your application. Design professionals (or integrators, depending on how your team is set up) spend a good bit of their time defining styles, just as they would with HTML/CSS. They can work with them directly in XAML as we have here or use Expression Blend to make the process easier.

Once you've mastered styling, you're ready to take a step beyond setting simple properties and into working with the control templates themselves.

23.3 Creating templates

The styling features shown in section 23.2 are a welcome addition to the Silverlight world. These items allow you to quickly create a consistent look throughout an application. This look can be shared across the application by defining the styles as resources. But, occasionally, the styling options can be somewhat limiting. To overcome these limitations, you can use a template.

A template empowers you to redefine the entire visual representation of an element, giving you the flexibility to make any Control look the way you want it to look. When doing this, you don't sacrifice the behavior of the Control. You could create a Button that looks and feels like an octagon and still reacts to the Click event. Over the course of this section, you'll experience the full power of a template by building a control template. You'll also see how to elegantly create a reusable template.

23.3.1 Building a control template

When you build a control template, it'll ultimately be used with a Control. Every Control in Silverlight exposes a property called Template. This property is a ControlTemplate that lets you take complete control over what a Control looks like. In a sense, when you set this property, you're resetting the control's appearance, giving you a clean slate to work with. From there, you can make a Control look like whatever you want it to look like. For instance, listing 23.13 changes the look of a Button to make it look more like a sphere.

Example 23.13. Changing the look of a Button through a Template

Changing the look of a Button through a Template
Changing the look of a Button through a Template

This example shows a basic ControlTemplate Changing the look of a Button through a Template. This ControlTemplate is associated with a Button through its Template property. Notably, if you were to define a ControlTemplate as a resource, you'd associate the template with a specific type through the TargetType property. This property behaves the same way as it did with the Style class. Interestingly, when a template is used with a ContentControl, the Content property doesn't behave the same way.

Over the course of this section, you'll learn how to display content within a ControlTemplate. This content will generally be placed inside a Panel or Border because a ControlTemplate can have only one root element. This root element can then be used to house the contents of a ContentControl or an ItemsControl. You'll also see how to customize the way in which the Items of an ItemsControl are arranged. But first, you'll see how to use properties that are part of the target control in your templates.

CONSIDERING CONTROL PROPERTIES

Ultimately, the purpose of a ControlTemplate is to define the appearance of a Control. This Control may have properties set that should be used within your template. For instance, you may want to use the Background or FontFamily property values of a Control in your ControlTemplate. In these types of situations, you should use a TemplateBinding.

A TemplateBinding is a special type of data binding used within a ControlTemplate. This data binding uses the Control to which the ControlTemplate is applied as its data source. The data source is identified as a specific property within that Control. This property is referenced by name when you create a TemplateBinding. An example of such a reference is shown in listing 23.14.

Example 23.14. Using a TemplateBinding for the target element's properties

Using a TemplateBinding for the target element's properties
Using a TemplateBinding for the target element's properties

This example shows the basic syntax of a TemplateBinding Using a TemplateBinding for the target element's properties. This syntax mimics the data-binding syntax explained in chapter 11. In this case, the binding causes the Height and Width property values of the Button to be used by the Ellipse in the ControlTemplate. These property values are simple in comparison to what the value of the Content property could be, though. If you're going to display the Content of a ContentControl in a ControlTemplate, you may want to consider using another approach.

DISPLAYING THE CONTENT

You may have noticed that the Content of the Button elements in the past two listings hasn't been shown because, when you define a ControlTemplate, you must tell Silverlight where to place that Content. To help you do this task, Silverlight provides two FrameworkElement instances: ContentPresenter and ItemsPresenter.

The ContentPresenter class empowers you to specify where the Content of a ContentControl should be shown. It may be easiest to think of this element as a placeholder for some piece of Content. Beyond that, the syntax of a ContentPresenter is the element itself, as shown in listing 23.15.

Example 23.15. Using a ContentPresenter to display content

Using a ContentPresenter to display content

This example shows the general usage of a ContentPresenter. As you can see, this object is a placeholder designed to be used inside a ControlTemplate. This element is generally limited to ContentControl scenarios and isn't usually used in ItemsControl situations. For these scenarios, you may want to consider an ItemsPresenter such as the one shown in listing 23.16.

Example 23.16. Using an ItemsPresenter to display the Items of an ItemsControl

Using an ItemsPresenter to display the Items of an ItemsControl

This example shows a ListBox with a ControlTemplate applied to it. The Items of that ListBox are positioned according to the ItemsPresenter Using an ItemsPresenter to display the Items of an ItemsControl. This element is important because it determines where the Items will be positioned in a ControlTemplate, but the ItemsPresenter doesn't determine how the Items will be arranged. That's the role of the ItemsPanel.

CONTROLLING ITEM ARRANGEMENT

The Items of an ItemsControl control are arranged according to the ItemsPanel property. This property is a special kind of template that defines the Panel that will be used to lay out the Items. By default, this property is set to use a StackPanel with a Vertical Orientation. In reality, you're free to use any of the Panel elements discussed in chapter 3. You could use a Horizontal StackPanel, as shown in listing 23.17.

Example 23.17. Declaring the ItemsPanel to arrange the Items of an ItemsControl

Declaring the ItemsPanel to arrange the Items of an ItemsControl
Declaring the ItemsPanel to arrange the Items of an ItemsControl

This example uses a Horizontal StackPanel as the ItemsPanel Declaring the ItemsPanel to arrange the Items of an ItemsControlto arrange the Items in the ListBox horizontally. It's highly likely that you'll only use a StackPanel as an ItemsPanel. Although you can technically use another Panel element, the other options require actual code. This code will be based entirely on your particular situation, so we won't cover that topic in this section.

The ControlTemplate class enables you to redefine the way a Control looks. This new definition can use the target Control property values through a TemplateBinding. When it comes to displaying the Content of a ContentControl, you should use a ContentPresenter within a ControlTemplate. If this ControlTemplate is associated with an ItemsControl, you may need to use an ItemsPresenter to show the Items. These Items can be rendered in new ways thanks to the ItemsPanel property.

Once you've settled on a ControlTemplate, you may want to use it across multiple controls. Thankfully, Silverlight makes it easy to create reusable templates.

23.3.2 Creating reusable templates

Creating individual templates can be useful when you want to give individualized attention to your controls; templates can also be useful for creating a truly unique yet consistent user experience. To help make it easier to deliver a consistent user experience, Silverlight allows you to define a ControlTemplate as part of a Style (see listing 23.18).

Example 23.18. Using a ControlTemplate within a Style

Using a ControlTemplate within a Style

This example shows a ControlTemplate defined within a Style Using a ControlTemplate within a Style. Though this is a simple template, you can also define a ControlTemplate as complex as you need it to be. You can even go as far as defining a ControlTemplate that considers visual states.

23.4 Dealing with visual states

Templates give you the flexibility to completely dictate what a Control looks like, but the template explanation given in section 23.3 is only useful for defining the default look of a Control. This default look represents the Control's normal state. In reality, most controls have multiple states. For instance, a Button can be in a pressed or disabled state. To enable you to manage what a Control looks like in such a state, Silverlight provides something known as the VisualStateManager.

The VisualStateManager is an element that manages the states and the transitioning between states. This element belongs to the System.Windows namespace. Because the VisualStateManager is in this namespace, it's ready to be utilized in your Control definitions. Before you can fully utilize the VisualStateManager, you must gain an understanding of components involved in state and transition management. Once you understand these components, you can leverage the power of the VisualStateManager itself. As you'll see in the conclusion of this section, this power can be wielded across multiple elements with the help of a Style.

23.4.1 Understanding the components

The VisualStateManager relies on a variety of components to do its job. These components make up something referred to as the parts and states model. This model is designed to separate a Control element's appearance from its behavior, ensuring that you can customize the visual pieces of a Control without having to change its underlying logic. To enable this feat, the parts and states model relies on three components: states, transitions, and parts.

STATES

A state is used to reflect a particular aspect of a control. For instance, the Button has one state that defines what it looks like by default. If a user moves the mouse over this Button, it'll enter another state. If the Button is pressed, it'll change to yet another state. These three states are shown in table 23.1.

Table 23.1. Several states of a Button. Each state has a slightly different visual appearance.

Normal

MouseOver

Pressed

Several states of a Button. Each state has a slightly different visual appearance.

Several states of a Button. Each state has a slightly different visual appearance.

Several states of a Button. Each state has a slightly different visual appearance.

This figure shows three of the states exposed by the Button class. In reality, the Button class has many more states. These states are exposed to the VisualStateManager with the help of the TemplateVisualStateAttribute. This attribute can be used by a Control to identify the states a Control can be in. In addition, because a Control can simultaneously be in multiple states, the TemplateVisualStateAttribute exposes the group that a state belongs to. The states and groups available on the Button class are listed in table 23.2.

Table 23.2. The states and groups of the Button class

State

GroupName

Disabled

CommonStates

MouseOver

CommonStates

Normal

CommonStates

Pressed

CommonStates

Focused

FocusStates

Unfocused

FocusStates

Each state is identified by a Name property, which is part of the TemplateVisualStateAttribute. This property is complemented by another called GroupName, which determines the grouping for the state. The reason for this property is to logically group together visual states. This is necessary because a Control can be in multiple states at the same time. For instance, a Button can simultaneously be in a Focused state as well as a Pressed state because the Pressed state is in a different group than the Focused state. Perhaps a better example is a CheckBox being in a Checked state while also being in a Disabled state. Either way, the main thing to understand is that groups are exclusive— a Control can be in multiple states as long as those states belong to different groups. States that are part of the same group have the ability to transition between one another.

TRANSITIONS

A transition defines the way a Control looks as it changes from one state to another. This change is represented as a Storyboard, so you're free to implement a smooth shift between two states. You can even do this at a fine granular level because of the inclusion of parts.

PARTS

A part represents a specific element within a ControlTemplate. A part is generally used when some underlying logic may need to change an area of a ControlTemplate. For instance, the thumb on a Slider will change any time a user clicks the track. This event will cause some underlying logic to move the position of the thumb. Both the thumb and track are defined as parts, as shown in figure 23.3.

The required parts of a Slider are the thumb and the track. The actual appearance of the slider isn't important as long as it has the parts that form the UI contract.

Figure 23.3. The required parts of a Slider are the thumb and the track. The actual appearance of the slider isn't important as long as it has the parts that form the UI contract.

This figure shows the two main parts of a Slider, which has more parts. These parts are defined by the TemplatePartAttribute, which enables you to specify the name and type of a UIElement that represents a part within a Control. This attribute is used to transmit data about the element that represents the part within the parts and states model. Now that this model has been explained, let's look at how to leverage it with the VisualStateManager.

23.4.2 Leveraging the VisualStateManager

The VisualStateManager is used by a ControlTemplate to manage the change between states. This change can be used to generate two different kinds of effects. The first is known as a state-based effect, which can be useful for doing something such as creating an enlarged Button if a user moves the mouse over it. The other type is known as a transitioning effect, which is useful for creating a fluid interface for controls that may change between states of the same group. Both kinds of effects will be covered in this section.

CREATING STATE-BASED EFFECTS

A state-based effect is a transition that occurs at the moment a Control enters a VisualState. When a Control enters this state, the Storyboard associated with the VisualState begins. This Storyboard is defined as part of a ControlTemplate. The Storyboard can be useful for creating a glowing effect or a ballooning effect (see listing 23.19).

Example 23.19. Creating a Button that enlarges when a user hovers over it

Creating a Button that enlarges when a user hovers over it
Creating a Button that enlarges when a user hovers over it

This listing defines an effect that occurs when a user triggers the MouseOver VisualState for the Button Creating a Button that enlarges when a user hovers over it. All of the items in this example have been described in the previous chapters. You should note three main things. First, the VisualStateGroups element tells the ControlTemplate that some custom Storyboard is going to be used for a state. Second, this state belongs to a predefined group, which is described by the VisualStateGroup element. Third, the VisualState items associated with this group are detailed inside the element. This approach is useful for creating effects when a Control enters a state. But the effect created in listing 23.19 would be better defined as a VisualStateTransition.

DEFINE TRANSITIONING EFFECTS

In addition to state-based transitions, the VisualStateManager enables you to define a transition between states. You can trigger this transition in code by calling the GoToState method or by using a DataStateBehavior or GoToStateAction behavior. We'll use GoToState here because it's the most useful to control authors.

To define a transition in XAML, you must use an element called VisualStateTransition, which allows you to associate a Storyboard with a change between two states. The beginning state is identified by a string property named From. The state being transitioned to is specified by a string property called To. Listing 23.20 defines a transition that changes the Button in the previous listing back to a Normal state.

Example 23.20. Creating a Button that transitions when a user hovers or leaves it

Creating a Button that transitions when a user hovers or leaves it
Creating a Button that transitions when a user hovers or leaves it

This listing shows the definitions of two VisualTransition elements. The first VisualTransition scales the Button up as it changes from the Normal VisualState to the MouseOver VisualState Creating a Button that transitions when a user hovers or leaves it. The second VisualTransition scales the Button down as it goes from the MouseOver VisualState back to the Normal VisualState Creating a Button that transitions when a user hovers or leaves it. These two transitions are necessary because, otherwise, the Button would be stuck looking like it did in a MouseOver state. There are two other interesting tidbits in this example.

First, you'll also notice the addition of the two VisualState definitions Creating a Button that transitions when a user hovers or leaves it. These are necessary to keep the transition animations in place. Without these definitions, the transition animations would be lost. The other interesting piece in this example is the use of the VisualStateGroup element Creating a Button that transitions when a user hovers or leaves it. You can only create transitions between states that belong to the same group because, as we stated earlier, a Control can be in multiple states as long as those states belong to different groups. Creating transitions between states empowers you to create deeper and richer controls, so it's only natural to want to share these effects with multiple Control instances.

23.5 Sharing your visual states

Because the visual states you create with the VisualTransition and VisualState elements are part of a ControlTemplate, you can define them as part of a Style. For the sake of completeness, listing 23.21 shows the transitions from the previous example defined as part of a Style.

Example 23.21. Creating a button that enlarges when hovered over

Creating a button that enlarges when hovered over

This listing shows how the previously defined Button ControlTemplate can be included in a Style. This ControlTemplate uses the VisualState and VisualTransition elements from listing 23.20. This example puts everything from this chapter together. The main thing to note is that you can leverage the VisualStateManager within a Style declaration. This is an exciting news because there can be a lot of XAML involved in creating effects for the various states and transitions of a Control. These states and transitions are part of something known as the parts and states model, which is supported by Microsoft Expression Blend. Because of this convenience, you're empowered to create some of the richest controls available on the Internet.

23.6 Summary

Resources in Silverlight come in many flavors. The ones most associated with the word resource are those we put in XAML, inside resource dictionaries. Those dictionaries can be parts of controls in app.xaml or in separate files merged into existing resource dictionaries. Resources defined this way can be just about anything from data source or view model classes, to styles, to brush and color definitions.

The most common uses of XAML resources are styles and control templates. Styles provide a way to factor out the common property settings for controls and store them in a location where they can be shared among many instances. Building upon styles are control templates. Control templates go a step beyond what you can do with the public properties affected with styles; they let you completely change the visual representation of a control including its visual states and transitions.

Another common type of resource is a file resource, accessed as a loose file, embedded into the compiled DLL or copied into the zipped-up .xap. You'll use these whenever you want a file to be delivered with the application itself.

Understanding resources, styles, templates, and visual states are all prerequisites to building your own controls. We've covered user controls previously. In chapter 24 we'll take on building truly lookless controls using everything we've learned so far.

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

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