WPF Simplified Part 9: Logical Resources

There are two types of resources in WPF – binary and logical. Binary resources are plain old .NET resources like bitmaps, fonts, and strings that can either be embedded inside the assembly or packaged in a resource file. We’ll discuss this in another post.

Logical resources, henceforth called resources are new to WPF. In XAML, resources are simply objects stored in an element’s Resource property so that they can be shared by child elements. Just about any object in WPF can be a resource, like a brush, a color, some text, some style etc.

Consider the simple XAML,

<Grid>
    <Button Height="100" Width="100" Content="Some Text" FontStyle="Italic" />
    <RadioButton Content="Some Text" FontStyle="Italic" />

</Grid>

Since we want both the child elements of Grid to have the same FontStyle we could define a FontStyle resource, put it in the Grid’s Resource property and point the FontStyles of the Button and RadioButton to the resource, like below,

<Grid>
     <Grid.Resources>
         <!-- Define a FontStyle resource -->
         <FontStyle x:Key="GridFontStyle">Italic</FontStyle>
     </Grid.Resources>

     <Button Height="100" Width="100" Content="Some Text" 
             FontStyle="{StaticResource GridFontStyle}" />
     <RadioButton Content="Some Text" FontStyle="{StaticResource GridFontStyle}" />     
 </Grid>

Doing this in code is simple enough,

<Grid x:Name="MyGrid">
    <Button x:Name="MyButton"  Height="100" Width="100" Content="Some Text" />
    <RadioButton x:Name="MyRadioButton" Content="Some Text" />    
</Grid>

// And in code...
this.MyGrid.Resources.Add("GridFontStyle", FontStyles.Italic);
this.MyButton.FontStyle = (FontStyle)this.MyGrid.FindResource("GridFontStyle");
this.MyRadioButton.FontStyle = (FontStyle)this.MyGrid.FindResource("GridFontStyle");

Every resource in the element’s Resource property must be unique, this is because internally WPF uses a ResourceDictionary to implement the resources. Thus, the x:Key that you specify in XAML, or equivalently the first parameter to Add, becomes the key in the dictionary for that resource. Note that although we have used a String object as the key any object can be used as a key. However, we don’t have to explicitly set the x:Key on all resources, for example in the case of Styles setting the TargetType is sufficient (for an example see here), and WPF implicitly creates an x:Key for the resource.

Resources are scoped hierarchically, like this,imageSo when we use FindResource to get a resource like in the above example, FindResource will start looking at the parent’s resources, then the parent’s parent’s resources etc., traversing the logical tree upward all the way to Window’s resources, then the entire Application’s resources and eventually the System’s resources! FindResource is quite a hard worker,

[In App.xaml]

<Application.Resources>
    <FontStyle x:Key="ApplicationFontStyle">Italic</FontStyle>
</Application.Resources>

[In Window1.xaml]

<Window.Resources>
    <FontStyle x:Key="WindowFontStyle">Italic</FontStyle>
</Window.Resources>
    
<Grid x:Name="MyGrid">
    <Button Height="100" Width="100" Content="Some Text" FontStyle="{StaticResource WindowFontStyle}" />
    <RadioButton Content="Some Text" FontStyle="{StaticResource ApplicationFontStyle}" />
</Grid>

We can use FrameworkElement.Resources to search the element’s resource dictionary directly, which will return null if the resource isn’t found.

// Looks up the Grid's ResourceDictionary directly using a key and returns null
this.MyButton.FontStyle = (FontStyle)this.MyGrid.Resources["WindowFontStyle"];

// Looks up the logical tree and finds the resource in Window's resources
this.MyButton.FontStyle = (FontStyle)this.MyGrid.FindResource("WindowFontStyle");

In addtion to using StaticResource in XAML, we could also use DynamicResource, the differences being,

                                                  StaticResource                                                DynamicResource
The reference must appear after the resource to which it refers. Forward references are allowed.
The resource is applied only once – the first time it is needed. The resource is reapplied every time the resource changes.
Can be used on regular .NET properties and almost anywhere. Can only be used to set dependency property values.
Resources are loaded when the Window or Page loads. Not loaded until the resource is actually used.
Code equivalent: FindReference Code equivalent: SetResourceReference

As an example, consider the XAML,

<Window.Resources>
    <RadioButton x:Key="MyRadioButton" x:Shared="False" Content="Some Text" />
</Window.Resources>
    
<Grid>
    <!-- The button’s background is set to a System resource -->
    <Button x:Name="MyButton" Height="100" Width="100" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" >

        <!-- The static resource can be used anywhere... --> 
        <StaticResource ResourceKey="MyRadioButton" />
    </Button>

    <!-- We can use the same resource here because we set x:Shared to False -->
    <StaticResource ResourceKey="MyRadioButton" />        
</Grid>

A few notes about the XAML above,

1. The button’s Background dependency property is set to a system resource. Setting the property like this,

    Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"

is interpreted as a dynamic reference to a resource named by the string SystemColors.ControlBrushKey. Setting x:Static means the text should be treated as the name of a static property not a string. The equivalent in code is,

    this.MyButton.SetResourceReference(Button.BackgroundProperty, SystemColors.ControlBrushKey);

2. The RadioButton resource is used to instantiate a radio button inside the button and also inside the grid. By default, when a resource is applied in multiple places, the same object instance is used, so a resource can only be applied once in an element tree because each resource application is the same instance. This can be overridden by setting x:Shared to false. Note that you’ll get a VisualStudio designer error if you’re trying to use x:Shared and instantiate a new copy of the resource, x:Shared will work fine in a compiled XAML.

image

Digg This
Advertisements

About soumya chattopadhyay
I live and work in Seattle, WA. I work with Microsoft technologies, and I'm especially interested in C#.

2 Responses to WPF Simplified Part 9: Logical Resources

  1. Pingback: WPF Simplified Series « I.Net

  2. Thanks for this site. Extremely educational entry.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: