Wednesday, July 15, 2009

[2009.07.15] WPF Centric XAML Markup Extensions [III/IV]

Resources Summary

A static resource provides a value for any XAML property attribute by looking up a reference to an already defined resource

Resources provide a clean and simple way to reuse commonly defined objects and values. FrameworkElement and FrameworkContentElement (in the PresentationFramework.dll assembly) follow separate inheritance paths, but they both define the Resource property. This means that any object that derives from either of these two top level classes will also expose this property. The entire Controls and Document tree derives from these two classes which implies that all WPF controls/elements will have a Resource property.

While you can define resources on a per element basis, it is more common to define them at the root level of the document or in the App.xaml file. This ensures maximum reachability, in that either window level or the entire application will have access to the resource definitions.

Resource is backed by the ResourceDictionary type which, under the covers is implemented as a Hashtable. This means that all resources are identified by unique keys and since we are dealing with a hashtable, the keys can be any Object. However, in WPF world, a String is commonly used as the key for a ResourceDictionary. However, non-string keys are used in certain areas, such as styling and data templating.

A resource is keyed using the XAML specific markup extension: x:Key attribute.

There are two kinds of resources in WPF and are used for different scenarios. In addition, the way in which they are resolved differ as well. These are:

  1. Static resource - resolved during loadtime
  2. Dynamic resource- resolved during runtime

Static Resources and the StaticResource Markup Extension

Static resources are resolved just prior to the Loaded event being fired for the element. This event is raised before the final rendering, but after the layout system has calculated all necessary values for rendering. Since binding may affect the render state of the element, it happens before the element is rendered to the screen. For instance, if the code is using a resource to set the background color of a button, then the binding of the button's Background property will have already been done before the button is visible to the user.

Static resource lookup occurs by recursively traversing the logical tree upwards of the application. Since all elements expose the Resource property, when a resource is referenced, the lookup starts at the element, by examining if it defines a resource dictionary. If not, it progresses to the parent of the element until it reaches the root of the application. Finally, it will check the application level file, App.xaml. If you reference a static resource in XAML that is not defined, you will get design time warnings and if you try to debug the application, you will get a XamlParseException.

[2009.07.15].03.static.resource.error
Fig.3 Design time support for undefined resources

Some reasons to use static resources:

  • some performance gain since static resources are not reevaluated based on runtime behaviors (ex: page reload)
  • setting a property that is not a DependencyObject or Freezable
  • creating a shared resource dictionary that may be compiled into a DLL
  • creating a theme where using static resource lookup is predictable whereas using dynamic resources leaves the reference unevaluated until runtime even though the references are known at load time
  • setting a large number of dependency properties which has built in caching so the property only needs to be evaluated at load time
  • can maintain separate instances of the resource using the x:Shared XAML specific markup extension

Now let's see how to use resource reference in markup.

  1: <Window x:Class="MarkupExtDemo.Window1"
  2:         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4:         Title="ME Demo" Width="400" Height="300">
  5:     <Window.Resources>
  6:         <!--define a root level brush resource-->
  7:         <SolidColorBrush x:Key="scb" Color="LightSteelBlue" />
  8:     </Window.Resources>
  9:     
 10:     <StackPanel>
 11: 
 12:         <Button Content="Button 1 : using Property-Element syntax" Margin="5" >
 13:             <!--define a local brush resource-->
 14:             <Button.Resources>
 15:                 <SolidColorBrush x:Key="scb" Color="AliceBlue" />
 16:             </Button.Resources>
 17:             <Button.Background>
 18:                 <StaticResource ResourceKey="scb" />
 19:             </Button.Background>
 20:         </Button>
 21: 
 22:         <Button Content="Button 2: {Binding Source={StaticResource scb}}" Margin="5"
 23:                 Background="{Binding Source={StaticResource scb}}" />
 24: 
 25:         <Button Content="Button 3: {Binding Source={StaticResource ResourceKey=scb}" Margin="5"
 26:                 Background="{Binding Source={StaticResource ResourceKey=scb}}" />
 27: 
 28:         <Button Content="Button 4: {StaticResource scb}" Margin="5"
 29:                 Background="{StaticResource scb}" />
 30:         <Button Content="Button 5: {StaticResource ResourceKey=scb}" Margin="5"
 31:                 Background="{StaticResource ResourceKey=scb}" />
 32:     </StackPanel>
 33: </Window>
Listing 1


[2009.07.15].04.static.resource.example
Fig. 4 StaticResource markup extension
This example illustrates the multitude of ways you can use to accomplish the same goal.

Button 1 uses the Property Element syntax of XAML and a locally defined resource within the Button element itself. Notice also there is a root level resource and both of these resources have the exact same key. As seen in the image, this works based on the lookup process that static resources follow, namely starting with the resource dictionary (if one exists) at the element level then working up the logical tree. Even though the keys are the same in both dictionaries, there is no conflict because the key is unique with respect to each dictionary. However, if you were to try and define another resource with the same key, say in the Window.Resources dictionary, the compiler will yell at you. This is because the dictionary is a hashtable which supports unique keys.

Note: Interestingly, I was using VS2010B1 to write up this example. However, I was actually getting an error when using the property-element syntax and the designer did not seem to take notice of my local resource reference. However, it worked fine in VS2008 SP1. Just wanted to point that out.

Button 2-3 uses the Binding markup extension and its Source attribute which required further markup to be fully resolved. Recall from the binding section, I said that you can use the binding markup most places that needs the WPF centric markup. However, it it not the most succinct method, but if you are looking for uniformity when using markup extension, this is the way to do it.

Button 2 uses the positional parameter based on the constructor of Binding while Button 3 uses the named parameter method (which I personally prefer).

Button 4-5 uses the more compact StaticResource markup extension to solve the same problem. It also indicates the use of positional and named parameters.

A concept that static resources does not support is forward resource reference. What this means is trying to reference a resource that is lexically defined after the object that is using the resource. The code below best illustrates this.

  1:         <Style>
  2:             <Setter Property="Control.Background" 
  3:                     Value="{StaticResource ResourceKey=scb2}" />
  4:         </Style>
  5: 
  6:         <SolidColorBrush x:Key="scb2" Color="Red" />
Listing 2
The style is trying to reference a resource that is defined after its own definition. This action is not supported but if for some reason this type of reference works, the application will still suffer a performance penalty when the hashtable for the ResourceDictionary is searched.


Dynamic Resources and the DynamicResource Markup Extension

A dynamic resource provides a value for any XAML property attribute by deferring that value to be a reference to a defined resource. So what happens to markup extensions using dynamic? Well an temporary expression is created and this is not evaluated until runtime when the resource is actually needed. At this point, the object represented by the resource is constructed and assigned to the property that was using the DynamicResource markup extension. As in the static resource case, dynamic resource references are stored in a ResourceDictionary where each entry has a unique key.

Dynamic resource lookup is a little bit more involved than its static brethren. It starts off by checking the element first where the resource is first referenced. If the element defines the resource locally, then that will be used. If not, lookup process will check if the if the element defines either a Style or a Template property and the resource dictionary in the Style and FrameworkTemplate are checked, respectively. The lookup then continues to the parent of the element and recurses all the way to the root element. The application level (App.xaml) will then be checked. If still nothing, the process will check the theme dictionary for the currently applied theme and then finally, it will check the System resources.

Refer to Fig. 3 above. As you can see, if you try to reference a static resource that is undefined, you will get design time support warnings. However, if you use a dynamic resource instead, you will not receive such warnings, because remember, the dynamic extension creates a temporary expression that is not resolved until runtime. Also the code in Listing 2 illustrating forward resource reference will actually work under the dynamic case again, due to the runtime lookup nature of this type of reference.

Another point to note is that even if you do dynamically reference a resource that is correctly defined, such as the SolidColorBrush in Listing 1, the DynamicResource extension still creates a temporary expression that is not resolved until runtime. Now if you have resources that can be resolved at loadtime rather than runtime, then there is no reason to do a dynamic bind to it. I brought this up because Microsoft Expression Blend (at least as of Blend 3 Mix Preview) always creates DynamicResource references automatically.

Some reasons to use dynamic resources:

  • value of the resource depends on runtime conditions
  • creating or referencing theme styles for a custom control
  • you may want to change the ResourceDictionary during the life time of the application
  • you need to use forward resource reference
  • you want an on-demand access to resources, not have everything loaded as in the static case
  • creating styles that depends on themes or other settings
  • need to reapply resources if the logical tree of the application changes

Now let's actually take the code from above, and just change all the static instances to dynamic.

When you do that, the application will compile and all is well, until you run it. You will get a XamlParseException as shown in Fig. 5.


[2009.07.15].05.dynamic.resource.error.01
Fig.5 DynamicResource parse error
What is going on here?


Well this is an indication of the limitation of using dynamic resources. Basically a dynamic resource reference can only be set on:

  • dependency properties
  • for a value within the Setter element of a Style
  • properties on a Freezable object

Therefore, during runtime, the resource lookup with fail when it tries to evaluate the expressions in the Source property of the Binding markup extension because it is not a DependencyProperty.

Therefore, Buttons 2-3 will fail while the others will pass the resource reference. Fig. 6 shows that you do have some design time support for incorrectly referenced dynamic resources, but you actually don't hit the wall until you run the application.


[2009.07.15].06.dynamic.resource.error.02
Fig.6 Design time error for dynamic resource reference

No comments: