IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

Default Templates in WPF

Wednesday 14 February, 2007, 06:15 PM

Someone recently asked me how WPF controls get their default templates. If you create a built-in control such as Button, its Template property will automatically be set to a ControlTemplate that defines the appropriate appearance for that control. Most controls look different in the various Windows themes, so the exact template you get depends on which theme is running.

The question is: where does WPF get the default template from?

Property Metadata

The first obvious place to look is the property metadata. Template, like most properties of WPF controls, is a dependency property. This means it has metadata associated with it which, amongst other things, can define a default value. We can retrieve this like so:

Control.TemplateProperty.GetMetadata(typeof(Button)).DefaultValue

This retrieves the default value that the Template property will have on an instance of Button. The reason we pass a Type into GetMetadata is that WPF lets a property have different metadata for different types. This allows a property to have different default values in different contexts. For example, the default value for the Focusable property is False for UIElement, but Control overrides this to True. (And UserControl overrides it back to False again.)

But this doesn't help us. It turns out that the default value for the Template property on a Button is null, as it is for other controls. So how does the property come to have a value by default?

Styles

The Template property gets its value from WPF's style system. The Template property is not special: lots of properties pick up their values this way. For example, if you are running the Windows Vista Aero theme, the Button has its Background, Foreground, BorderBrush, BorderThickness, FocusVisualStyle, HorizontalContentAlignment, VerticalContentAlignment, Padding, and Template properties all set by the style system. Although the Template may seem special because it gets to define the visuals for a control, it's still just another property.

There's one problem with this explanation. If you look at an ordinary WPF Button at runtime, its Style property will usually be null. How can the Template (and all these other properties) come from a style if Style is null?

Explicit Style vs. Theme Style

WPF elements can have two styles associated with them. The Style property can hold an 'explicit' style. (This is null by default, but can be set in a variety of ways.) But a control will also have a so-called theme style. And only the explicit style is directly visible, there is evidence of the presence of the theme style. For example, look at the BaseValueSource enumeration, which enumerates all the possible places that a dependency property's value may have come from. It includes both Style and DefaultStyle values, the latter indicating that the value came from the default style for the current theme. WPF returns a member of this enumeration when we ask it where it got the value for a particular property:

DependencyPropertyHelper.GetValueSource(myButton,
    Button.TemplateProperty).BaseValueSource

If myButton refers to a normal Button here, this will evaluate to DefaultStyle, indicating that the Template property came from the theme style. So we now know exactly how the Template property acquired its value...up to a point. Of course the next question is: where does this DefaultStyle come from? But first a quick digression.

Why Two Styles?

You might be wondering why elements need two styles. Well imagine if they only had one. Think what would happen with the following:

<Grid>
  <Grid.Resources>
    <Style TargetType="{x:Type Button}">
      <Setter Property="HorizontalAlignment"
              Value="Center" />
      <Setter Property="VerticalAlignment"
              Value="Center" />
    </Style>
  </Grid.Resources>

  <Button Content="Click me" />
</Grid>

The Button in this example has an explicit style. It doesn't look all that explicit, since the Style property has not been set directly. However, we've put a Style with a TargetType of Button's type object, and this has the effect of applying the Style to any Button inside that Grid.

Note: what I've written here contradicts what the SDK currently says. It says that an explicit style is one that is "referenced by non-type Key" which suggests that this doesn't count as an explicit style. However, if we use the DependencyPropertyHelper to ask where the Button in this example got its HorizontalAlignment and VerticalAlignment properties from, it says: Style, which the documentation says means the value "is from a style setter of an explicit style."

So experimentation indicates that an 'explicit style' is one that is not the default style from the theme. I think the documentation for "Dependency Property Value Precedence" is simply misleading right now. In fact a simpler way of looking at it is this: an explicit style is the one in the Style property. Given the XAML above, if you read the Button's Style property, you'll see that it actually contains a reference to the Style defined in the Grid's resources.

Anyway, having cleared up that this is indeed an explicit style, why does this distinction matter? Suppose elements had just one style: what would happen to all the other properties normally set by the style in this case? Remember that Background, Foreground, BorderBrush, BorderThickness, FocusVisualStyle, HorizontalContentAlignment, VerticalContentAlignment, Padding, and Template all get their value from the theme style by default. If the theme style were simply replaced with the custom style shown above, all of these properties would revert to their unstyled defaults. And since the default value for Template is null, this means the control would lose its appearance!

You could solve this by setting a custom style's BasedOn property to refer to the original style. But that would be tedious, seeing as how every single style would need to do that. So instead, WPF just remembers the original theme style even when an explicit style is supplied. Setters in the explicit style take precedence over the theme style. But if the explicit style does not supply a setter for a property that the theme style does, the theme style gets to supply that property's value. This is why a control doesn't vanish the moment you supply a custom style: the default theme style is still in place, and will still supply the Template and various other properties if your custom style doesn't supply different values.

Where Does The Theme Style Come From?

We still don't really have a satisfactory answer to the original question. We now know that by default, a control's Template comes from the theme style. But where does the theme style come from? WPF defines a property to hold it. However, it's not public. This doesn't stop us from taking a look at it using a technique that YOU SHOULD NOT USE IN PRODUCTION CODE:

Style ts = typeof(Button).GetProperty("ThemeStyle",
   BindingFlags.NonPublic | BindingFlags.Instance).GetValue(myButton,
   null) as Style;

By all means do that to satisfy you're curiosity, but please don't do that in live code, because there's no guarantee that property will be there in future versions of WPF. In any case, we don't need to do that. As long as you've not defined a custom default style at the application scope, the following code will find the system style:

Style ts = Application.Current.FindResource(typeof(Button));

You can verify for yourself that this returns the very same object instance. But why would it do that? Well, when you call FindResource on Application (or a FrameworkElement for that matter) it will walk its way up the resource tree, and eventually hit the system scope. The system scope is where the default theme styles are defined. (And in fact, WPF goes straight to the system scope when locating the default theme style. However, there's no public API for doing that, which is why the code shown here starts at the application scope instead; it still ends up at the system scope.)

Where Does The System Scope Come From?

The answer is still not looking especially concrete. We know that the Template property typically gets its default value from the theme style, and that the theme style comes from the system scope resources. But where do those come from? If you've ever written a custom control, then you'll know, because the way custom controls add theme-specific resources to the system scope turns out to be the same as the way WPF adds its own theme-specific resource to the same scope.

It works like this. Style resources are identified by a Type object. And when WPF looks for a style in the system scope, it looks at the identifying Type object's Assembly property. It examines that assembly for a ThemeInfo custom attribute. If that attribute is present, WPF examines its ThemeInfoDictionaryLocation property. If this is set to None, then it knows no theme-specific resources are present for this type. Otherwise, if it is set to SourceAssembly, it knows that the assembly that defines the type also contains theme-specific resources. If it is set to ExternalAssembly, it knows that theme-specific resources are available in a separate assembly whose name is formed by adding the theme name to the base assembly name.

Let's follow that through with Button. The Button type is defined in the PresentationFramework assembly. This assembly has the ThemeInfo attribute applied, and the ThemeInfoDictionaryLocation is set to ExternalAssembly. This means that if we're running the Aero theme, WPF should look for an assembly called PresentationFramework.Aero for Aero-specific resources. (You can see this very assembly in your GAC.)

Having worked out which assembly it's going to look in, it will then look for a specific named resource. The resource name always starts "/themes/" and then is based on the theme name, including the colour scheme. For example, Aero resources would be in "/themes/Aero.NormalColor.xaml". Windows 2000-style resources would be in "/themes/Classic.xaml" because there will only ever be one colour scheme for that theme. Luna is available in various colour, so "/themes/Luna.Metallic.xaml" would contain the silver resources, while "/themes/Luna.Homestead.xaml" contains olive, while "/themes/Luna.NormalColor.xaml" contains the normal tellytubby blue resources.

You can load up these resource dictionaries for yourself if you want. You need to build a relative pack URI that incorporates the component name and resource name. Here's one for WPF's Aero resources:

"/PresentationFramework.Aero, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35;component/themes/Aero.NormalColor.xaml"

You then wrap this in a relative Uri object and pass it to Application.LoadComponent. It will return you a ResourceDictionary containing all the Aero-specific system-scope resources. Here's a function that does most of the work:

public static ResourceDictionary LoadThemeDictionary(Type t,
    string themeName, string colorScheme)
{
    Assembly controlAssembly = t.Assembly;
    AssemblyName themeAssemblyName = controlAssembly.GetName();

    object[] attrs = controlAssembly.GetCustomAttributes(
        typeof(ThemeInfoAttribute), false);
    if (attrs.Length > 0)
    {
        ThemeInfoAttribute ti = (ThemeInfoAttribute) attrs[0];

        if (ti.ThemeDictionaryLocation == 
                             ResourceDictionaryLocation.None)
        {
            // There are no theme-specific resources.
            return null;
        }

        if (ti.ThemeDictionaryLocation ==
                ResourceDictionaryLocation.ExternalAssembly)
        {
            themeAssemblyName.Name += "." + themeName;
        }
    }

    string relativePackUriForResources = "/" +
        themeAssemblyName.FullName +
        ";component/themes/" +
        themeName + "." +
        colorScheme + ".xaml";

    Uri resourceLocater = new System.Uri(
        relativePackUriForResources,  System.UriKind.Relative);
    return Application.LoadComponent(resourceLocater)
               as ResourceDictionary;
}

This code isn't quite the whole story, because it doesn't do fallback to generic resources when theme-specific ones are not present. A "/themes/generic.xaml" can be present to use when theme-specific resources cannot be found. However, it does finally get us to the answer to: where does the template come from?

We can call this method passing in typeof(Button), "Aero", and "NormalColor", and we will be returned a ResourceDictionary. This dictionary will contain a Style for the key typeof(Button). This style actually contains very little, but its BasedOn property refers to a Style for ButtonBase, and that is what supplies a setter that determines the button's default value for the Template property.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk