Windows Presentation Foundation

Hello, WPF

WPF from Scratch
Navigation Applications
Content Model
Layout
Controls
Data Binding
Dependency Properties
Resources
Styles and Control Templates
Graphics
Application Deployment
Where Are We?

Layout

Introduction
Layout Basics
DockPanel
StackPanel
Grid
Canvas
Viewbox
Text Layout
Common Layout Properties
When Content Doesn't Fit
Custom Layout
Where Are We?

Controls

Introduction
What Are Controls?
Handling Input
Built-In Controls
Where Are We?

Data Binding

Introduction
Without Data Binding
Data Binding
Binding to List Data
Data Sources
Master-Detail Binding
Where Are We?

Styles and Control Templates

Introduction
Without Styles
Inline Styles
Named Styles
Element-Typed Styles
Data Templates and Styles
Triggers
Control Templates
Where Are We?

Resources

Introduction
Creating and Using Resources
Resources and Styles
Binary Resources
Global Applications
Where Are We?

Graphics

Introduction
Graphics Fundamentals
Shapes
Brushes and Pens
Transformations
Visual-Layer Programming
Video and 3-D
Where Are We?

Animation

Animation Fundamentals
Timelines
Storyboards
Key Frame Animations
Creating Animations Procedurally
Where Are We?

Custom Controls

Introduction
Custom Control Basics
Choosing a Base Class
Custom Functionality
Templates
Default Visuals
Where Are We?

ClickOnce Deployment

A Brief History of Windows Deployment
ClickOnce: Local Install
The Pieces of ClickOnce
Publish Properties
Deploying Updates
ClickOnce: Express Applications
Choosing Local Install versus Express
Signing ClickOnce Applications
Programming for ClickOnce
Security Considerations
Where Are We?

Creating and Using Resources

6.1. Creating and Using Resources

The term resource has a very broad meaningany object can be a resource. A brush or a color used in various parts of a user interface could be a resource. Snippets of graphics or text can be resources . There is nothing special an object has to do to qualify as a resource. The resource-handling infrastructure is entirely dedicated to making it possible to get hold of the resource you require, and it doesn't care what the resource is. It simply provides a mechanism for identifying and locating objects.

At the heart of resource management is the ResourceDictionary class. This is a fairly simple collection class. It behaves much like an ordinary Hashtable; it allows objects to be associated with keys, and provides an indexer that lets you retrieve those objects using these keys. So you could, in principle, use the ResourceDictionary like a Hashtable, as Example 6-1 shows.

Example 6-1. ResourceDictionary programming
ResourceDictionary myDictionary = new ResourceDictionary( );
myDictionary.Add("Foo", "Bar");
myDictionary.Add("HW", "Hello, world");

Console.WriteLine(myDictionary["Foo"]);
Console.WriteLine(myDictionary["HW"]);

In practice, you will not often create your own ResourceDictionary like this. Instead, you will normally use ones provided by WPF. For example, the FrameworkElement base class, from which most user-interface elements derive, provides a resource dictionary in its Resources property. Moreover, this dictionary can be populated from markup, as Example 6-2 shows.

Example 6-2. Populating a ResourceDictionary from XAML
<?Mapping XmlNamespace="urn:System" ClrNamespace="System" Assembly="mscorlib" ?>

<Window x:Class="ResourcePlay.Window1" Text="ResourcePlay"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    xmlns:s="urn:System">


    <Window.Resources>
        <SolidColorBrush x:Key="Foo" Color="Green" />

        <s:String x:Key="HW">Hello, world</s:String>
    </Window.Resources>

    <Grid Name="myGrid">
    </Grid>
</Window>


The x:Key attribute specifies the key to be used for the resource in the dictionary. In principle, you can use anything as a key, but in practice, strings are the most common choice, although distinct object instances stored in public properties are used for system resources.

When you use XAML to populate a resource dictionary, WPF may choose to defer the creation of the resources. Under certain circumstances, it may choose to leave sections of the tree in their serialized form (known as BAML), and only expand this into real objects on demand. This can significantly improve the startup time for a user interface in cases where not all of the objects are needed as soon as the UI appears. For the most part, this optimization will not have any direct effect on your code's behavior other than speeding it up. However, if there is something wrong with your markup, this deferred creation can cause the resulting errors to emerge later than you might have expected.


Example 6-3 shows code retrieving the resources defined in Example 6-2.

Example 6-3. Retrieving resources from an element's ResourceDictionary
Brush b = (Brush) this.Resources["Foo"];
String s = (String) this.Resources["HW"];

Notice that this code accesses the ResourceDictionary using this.Resources. This is all very well for the code-behind for the markup that defined the resources. However it is not always this convenient to get hold of the right dictionary. What if we want to define resources accessible to all windows in the application? It would be both tedious and inefficient to copy the same resources into every window in the application. And what if we want a custom control to pick up resources specified by its parent window, rather than baking them into the control? To solve these problems, and to make it easy to achieve consistency across your user interface, FrameworkElement extends the basic ResourceDictionary facilities with a hierarchical resource scope .

6.1.1. Resource Scope

As well as offering a ResourceDictionary for every element that wants one, FrameworkElement also provides a FindResource method to retrieve resources. Example 6-4 shows the use of this method to retrieve the same resources as Example 6-3.

Example 6-4. Using FindResource
Brush b = (Brush) this.FindResource("Foo");
String s = (String) this.FindResource("HW");

This may seem rather pointless: why does this FindResource method exist when we could just use the dictionary's indexer as we did in Example 6-3? The reason is that FindResource doesn't give up if the resource is not in the specified element's resource dictionary. It will search elsewhere.

The code in Example 6-5 uses the myGrid element from Example 6-2 instead of this. The Grid does not have any resources, so this code will set the b1 variable to null. However, because b2 is set using FindResources instead of the resource dictionary indexer, WPF will consider all of the resources in scope, not just those directly set on the Grid. It will start at the Grid element, but will then examine the parent, the parent's parent, and so on, all the way to the root element. (In this case, the parent happens to be the root element, so this is a short search. But in general, it will search as many elements as it needs to.) The result is that the b2 variable is set to the same Brush object as was retrieved in Example 6-3 and Example 6-4.

Example 6-5. FrameworkElement.Resources versus FindResource
// Returns null
Brush b1 = (Brush) myGrid.Resources["Foo"];

// Returns SolidColorBrush from Window.Resources
Brush b2 = (Brush) myGrid.FindResource("Foo");

It doesn't stop here. If FindResource gets all the way to the root of the UI without finding the specified resource, it will then look in the application. Not only do all framework elements have a Resources property, so does the Application object. Example 6-6 shows how to define application-scope resources in markup. (If you are using the normal Visual Studio 2005 Avalon project template, you would put this in the MyApp.xaml file.)

Example 6-6. Resources at application scope
<Application x:Class="ResourcePlay.MyApp"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    StartingUp="AppStartingUp"
    >
    <Application.Resources>
        <LinearGradientBrush x:Key="shady" StartPoint="0,0" EndPoint="1,1">
            <LinearGradientBrush.GradientStops>
                <GradientStop Offset="0" Color="Red"/>

                <GradientStop Offset="1" Color="Black"/>
            </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
    </Application.Resources>
</Application>

The application scope is handy for anything used throughout your application. For example, if you use styles or control templates, you would typically put these in the application resources, to ensure that you get a consistent look across all the windows in your application.

Resource searching doesn't stop at the application level. If a resource is not present in the UI tree or the application, FindResource will finally consult the system scope, which contains resources that represent system-wide settings, such as the configured color for selected items or the scrollbar width.

Figure 6-1 shows a typical hierarchy of resource sources. Several applications are running, each application may have several windows, and each window has a tree consisting of multiple elements. If FindResource is called on the element labeled "1" in the figure, it will first look in that element's resource dictionary. If that fails, it will keep working its way up the hierarchy through the numbered items in order, until it reaches the system resources.

Figure 6-1. Resource hierarchy


WPF uses the system scope to define brushes, fonts, and metrics that the user can configure at a system-wide level. The keys for these are provided as static properties of the SystemColors, SystemFonts, and SystemParameters classes, respectively. (Between them, these classes define over 400 resources, so they are not listed hereconsult the SDK documentation for each class to see the complete set.) Example 6-7 uses the system scope to retrieve a brush for the currently configured tool tip background color. (See Chapter 7 for more information on brushes.)

Example 6-7. Retrieving a system scope resource
Brush toolTipBackground = (Brush) myGrid.FindResource(SystemColors.InfoBrushKey);

These system-resource classes use objects rather than strings as resource keys. This avoids the risk of naming collisionsbecause system resources are always identified by a specific object, there will never be any ambiguity between system resources and your own named resources.


The system-resource classes also define static properties that let you retrieve the relevant object directly rather than having to go via the resource system. For example, SystemColors defines an InfoBrush property, which returns the same value that FindResource returns when passed SystemColors.InfoBrushKey. So rather than writing the code in Example 6-7, we could have written the code in Example 6-8.

Example 6-8. Retrieving a system resource through its corresponding property
Brush toolTipBackground = SystemColors.InfoBrush;

When writing code, these properties are likely to be simpler to use than the resource system. However, using the resource-key properties offers three advantages. First, if you want to let the user change your application's color scheme away from the system-wide default, you can override these system settings by putting resources into the application scope. Example 6-9 shows an application resource section that defines a new application-wide value for the InfoBrushKey resource.

Example 6-9. Application overriding system colors
// (Hypothetical function for retrieving settings)
Color col = GetColorFromUserSettings( );

Application.Current.Resources[SystemColors.InfoBrushKey] =
   new SolidColorBrush(Colors.Red);

This replacement value would be returned in Example 6-7, but not in Example 6-8. This is because in Example 6-8, SystemColors has no way of knowing what scope you would like to use, so it always goes straight to the system scope.

The second advantage offered by resource keys is that they provide a straightforward way of using system-defined resources from markup. Third, you can make your application respond automatically to changes in system resources. Both of these last two benefits come from using resource references.

6.1.2. Resource References

So far, we have seen how to retrieve the current value of a named resource in code. Since we usually use resource values to set element properties, we will now look at how to set an element's property to the value of a resource. This may seem like a ridiculously trivial step. You might expect it to look like Example 6-10.

Example 6-10. How not to use a system resource value
this.Background = (Brush) this.FindResource(SystemColors.ControlBrushKey);

This will work up to a pointit will successfully set the Background property to a brush that paints with whatever the currently selected color for control backgrounds is at the moment when this line of code runs. However, if the user changes this setting using the Display Properties control panel applet, this Background property will not be updated automatically. The code in Example 6-10 effectively takes a snapshot of the resource value.

The code in Example 6-11 does not suffer from this problem. Instead of taking a snapshot, it associates the Background property with the resource.

Example 6-11. Self-updating system resource reference
this.SetResourceReference(Window.BackgroundProperty, SystemColors.ControlBrushKey);

Unlike Example 6-10, if the system resource value changes, the property will automatically receive the new value. The practical upshot of this is that if the user changes the color scheme using the Display Properties applet, Example 6-11 will ensure that your user interface is updated automatically.

WPF defines markup extensions that are the XAML equivalent of the code in the previous two examples. (See Appendix A for more information on markup extensions.) These are the StaticResource and DynamicResource extensions. If you are using a system resource, or any other resource that might change at runtime, choose DynamicResource. If you know the resource will never change, use StaticResource, which takes a snapshot, avoiding the cost associated with tracking the resource for changes. (The cost is small, but you may as well avoid it for resources that never change.) Example 6-12 shows the use of both resource reference types.

Example 6-12. Using resources from markup
<Window x:Class="ResourcePlay.Window1" Text="ResourcePlay"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005">

    <Window.Resources>
        <SolidColorBrush x:Key="Foo" Color="LightGreen" />
    </Window.Resources>

    <Grid Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
        <TextBlock FontSize="36" Width="200" Height="200"
                   Background="{StaticResource Foo}">Hello!</TextBlock>
    </Grid>
</Window>


The top-level Window defines a brush as a resource. The TextBlock uses this for its Background property via a StaticResource reference. This has a similar effect as the code in Example 6-10. It takes a snapshot and is appropriate for resources that will not change while the application runs.

The grid's Background has been set to the system "control" color. (This is typically battleship greythe color often used as the background for dialogs.) Since this is a user-configurable color, and could therefore change at runtime, we've used a DynamicResource, which has the same effect as the call to SetResourceReference in Example 6-11.

The syntax here is a little more complex than for the StaticResource. This complexity is not because we are using DynamicResource. It is because the resource we wish to use is identified by an object, returned by the static SystemColors.ControlBrushKey property. We could have tried this:

    <!-- This will not work as intended -->
    <Grid Background="{DynamicResource SystemColors.ControlBrushKey}">

This is syntactically correct, but doesn't do what we want. It will be interpreted as a dynamic reference to a resource named by the string SystemColors.ControlBrushKey. However, there is no such resource, so the background will not be set. In order to retrieve the real resource key (the object returned by the ControlBrushKey static property) we have to use the x:Static markup extension as Example 6-12 doesthis tells the XAML compiler that the text should be treated as the name of a static property, not as a string.

6.1.3. Reusing Drawings

It is often useful to put drawings and shapes into resources. There are two main reasons for doing so. One is that drawings can be quite complex, and putting them inline as part of the main markup for a user interface can make the XAML hard to read. By putting drawings into the resources section, the overall UI structure can be clearer. The other main reason is to enable reuseyou may want to use the same graphic in multiple places.

There are many different ways in which you can represent shapes and drawings, which are shown in Chapter 7. All of them can be used as resources, although there are some restrictions with using certain types. In particular, if you use any element that derives from FrameworkElement as a resource, you can only reference it once. The reason for this restriction is that FrameworkElement is the basis of the user-interface tree. An element knows what its parent is and what children it has, so it is not possible for it to be in more than one place in the tree. (When you use a resource, you are not using a copy of the object, you are using the object itself.)

So although you make an Ellipse a resource, or even a whole drawing in a Canvas, you should do this only if you intend to use the drawing exactly once, because Ellipse and Canvas both derive from FrameworkElement. This is sometimes a useful thing to doyou may want to turn a graphic into a resource just to move it out of the main part of your markup so as to reduce clutter. In this case, the single-use restriction isn't a big problem. Example 6-13 uses this to define an Ellipse resource called shape. It also shows how to use the resource.

Example 6-13. Using a FrameworkElement resource
<Window.Resources>
    <Ellipse x:Key="shape" Fill="Blue" Width="100" Height="80" />
</Window.Resources>

...

<StackPanel>
    <Button>Foo</Button>

    <StaticResource ResourceKey="shape" />
    <Button>Bar</Button>
</StackPanel>

The StaticResource element here will be replaced at runtime with the resource it names. The result will look like Figure 6-2.

Figure 6-2. Reference to element resource


The Drawing classes, such as a GeometryDrawing or a DrawingGroup, are better candidates for storing drawings as resources. Since Drawing does not derive from FrameworkElement, you are free to use one instance in as many places as you like. DrawingGroup lets you put as many shapes and images into a single drawing as needed, and the various other types derived from Drawing provide access to all of WPF's graphics facilities. See Chapter 7 for more details.

Example 6-14 shows how to define and use a drawing resourcea Drawing is typically used in conjunction with a DrawingBrush. Figure 6-3 shows the result.

Example 6-14. Using a Drawing resource
<Window.Resources>

    <GeometryDrawing x:Key="drawing" Brush="Green">
        <GeometryDrawing.Geometry>
            <EllipseGeometry RadiusX="200" RadiusY="10" />
        </GeometryDrawing.Geometry>
    </GeometryDrawing>
</Window.Resources>

...

<Rectangle Width="250" Height="50">
    <Rectangle.Fill>
        <DrawingBrush Drawing="{StaticResource drawing}" />
    </Rectangle.Fill>
</Rectangle>

Figure 6-3. Reference to Drawing resource


Alternatively, you could just define a DrawingBrush resource. This moves some of the complexity into the Resources section, making the markup considerably simpler at the point at which you use the resource, as Example 6-15 shows. The results are the same as the previous example (Figure 6-3), but the markup that uses the resource is just one line long instead of five.

Example 6-15. Using a DrawingBrush resource
<Window.Resources>
    <DrawingBrush x:Key="dbrush" Drawing="{StaticResource drawing}" />
    <GeometryDrawing x:Key="drawing" Brush="Green">
        <GeometryDrawing.Geometry>
            <EllipseGeometry RadiusX="200" RadiusY="10" />

        </GeometryDrawing.Geometry>
    </GeometryDrawing>
</Window.Resources>

...

<Rectangle Width="250" Height="50" Fill="{StaticResource dbrush}" />

If you want the same shape to crop up in multiple drawings, you might want to drop down a level, and use individual geometry objects as resources. These can then be referred to from within drawings. Example 6-16 shows the use of a DrawingBrush with a GeometryDrawing that uses a Geometry resource. (Since this is yet another ellipse, we won't waste your time with another pictureit'll look much like Figure 6-3, only in cyan.)

Example 6-16. Using a Geometry resource
<Window.Resources>
    <EllipseGeometry x:Key="geom" RadiusX="200" RadiusY="30" />
</Window.Resources>

...

<Rectangle Width="250" Height="50">

    <Rectangle.Fill>

        <DrawingBrush>
            <DrawingBrush.Drawing>
                <GeometryDrawing Brush="Cyan" Geometry="{StaticResource geom}" />
            </DrawingBrush.Drawing>
        </DrawingBrush>

    </Rectangle.Fill>
</Rectangle>

In this particular example, the use of resources may seem a little extremeit would probably have been less effort just to create a new geometry from scratch. However, some geometries, such as PathGeometry, can become quite complex, in which case this kind of reuse makes more sense.

While drawings and geometries are powerful, reusable, and lightweight, they have one disadvantage. Because they are not proper framework elements, they cannot take advantage of WPF's layout system. You can scale them using the brush scaling features described in Chapter 7, but you cannot make them adapt their layout intelligently using the panels described in Chapter 2, because panels are all framework elements. So you might think that you have to choose between the ability to use framework element features such as layout and the ability to be reuse the resource. However, you can get the best of both worlds by using a ControlTemplate.

Example 6-17 shows markup that uses a ControlTemplate resource. This uses the same Ellipse element multiple times, something we could not do by making the Ellipse element itself a resource. As you can see in Figure 6-4, each Ellipse has been positioned and sized individually by the Grid. Templates are discussed in more detail in Chapter 5.

Example 6-17. Reusing arbitrary markup with templates
<Window.Resources>
    <ControlTemplate x:Key="shapeTemplate">
        <Ellipse Fill="Blue" Margin="3" />
    </ControlTemplate>
</Window.Resources>

...

<Grid Width="300" Height="150">
    <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>

        <ColumnDefinition Width="100" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <Control Template="{StaticResource shapeTemplate}"
             Grid.Row="0" Grid.Column="0" />
    <Control Template="{StaticResource shapeTemplate}"
             Grid.Row="0" Grid.Column="1" />
    <Control Template="{StaticResource shapeTemplate}"
             Grid.Row="1" Grid.Column="0" />

    <Control Template="{StaticResource shapeTemplate}"
             Grid.Row="1" Grid.Column="1" />
</StackPanel>

Figure 6-4. Reusing markup with templates


Control templates offer a good way of reusing arbitrary markup, but if you don't need to use FrameworkElement-based types in your drawing, using the more lightweight DrawingBrush class is more efficient. And if you are creating lots of drawings all containing similar shapes, you can even go as far as sharing individual geometry objects as resources. All of these drawing mechanisms are described in more detail in Chapter 7.


©2008 FAQ - WPF Labs - Discuss - Terms of Use - Privacy Policy - About WPF
- Interview Questions - Sharepoint Articles - Interview Questions Resource Library - All about LINQ - MS Knowledgebase Articles - Electronics and Hardware discussions