WPF: Adventures in Theming (Part 2)

WPF: Adventures in Theming (Part 2)

No Comments

For this part I wanted to talk about the style lookup system, things to be aware of and some problems with WPF’s “theming” capability in general. Here are the basics of how static resource and dynamic resources function; this is straight from MSDN.

StaticResource Lookup Process

  1. The lookup process checks for the requested key within the resource dictionary defined by the element that sets the property.
  2. The lookup process then traverses the logical tree upward, to the parent element and its resource dictionary. This continues until the root element is reached.
  3. Next, application resources are checked. Application resources are those resources within the resource dictionary that is defined by the Application object for your WPF application.

Source [MSDN: Resource Overview]

DynamicResource Lookup Process

  1. The lookup process checks for the requested key within the resource dictionary defined by the element that sets the property.
    • If the element defines a Style property, the Resources dictionary within the Style is checked.
    • If the element defines a Template property, the Resources dictionary within the FrameworkTemplate is checked.
  2. The lookup process then traverses the logical tree upward, to the parent element and its resource dictionary. This continues until the root element is reached.
  3. Next, application resources are checked. Application resources are those resources within the resource dictionary that is defined by the Application object for your WPF application.
  4. Theme resource dictionary is checked, for the currently active theme. If the theme changes at runtime, the value is reevaluated.
  5. System resources are checked.

Source [MSDN: Resource Overview]

Style Lookup

Unfortunately knowing the above isn’t quite enough when it comes to styles. You remember in [Part 1] that if you have a custom style you have to add

BasedOn=”{StaticResource {x:Type _______}}”

to the custom style for it to be able to find your “theme” style.

I don’t know about the rest of you, but I thought this was really bizarre. I’m trying to restyle my application and I may or may not have control over everything in the application. If my application is pluggable or I’m using a 3rd party library there’s no guarantee I have access to everything that needs to be styled. There’s also just the sheer annoyance and it’s just one more thing that is easy to forget and easy to miss in a large application.

Now, if you don’t specify a BasedOn, a BasedOn style is still being used. What is happening is that when you don’t specify a specific BasedOn style, the theme dictionary is immediately used. The application dictionary is never checked :(

Sadly this portion of the WPF API is not extendable so there’s no provided way to inject my own real theme. But here’s how it works; first it gets the assembly containing the control to be styled. Then WPF checks to see what you defined for the ThemeInfoAttribute, ex

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None,
    ResourceDictionaryLocation.SourceAssembly)]

If the theme dictionary look-up location is set to ResourceDictionaryLocation.ExternalAssembly, then WPF attempts to load an assembly matching this pattern, [Control Assembly Name].[Theme Name]. For example, PresentationFramework.Aero. If it can’t find an assembly matching the current theme then it falls back to the generic resource dictionary. As would be the case if you were running the Zune theme on a media center version of XP.

Once it finds the assembly that is needed, it finds the resource dictionary matching the current theme, (eg. themes/aero.normalcolor.xaml). Then as styles and resources are requested the internal class System.Windows.SystemResources caches this information for faster lookups in the future. This is the style that is used as the base style for any custom style you write for a control where no BasedOn style is specified.

Window and UserControl

Window and UserControl styles is another pitfall users wanting to re-theme their application should be aware of. When you create a new Window in WPF, you’re not creating an instance of Window, you’re creating an instance of Window1 (or whatever your window class is) so it wont find your style for {x:Type Window} in your Application’s resource dictionary. The only solution for this is to explicitly set the Style in your Window1.xaml, by doing something like Style=”{DynamicResource {x:Type Window}}” in the XAML definition of the control. The problem with this is that you need to have access to the code for any UserControl or Window.

This is another location where when a style has not been specified the theme style is used. Even though there’s no record anywhere of a style for Window1, the theme dictionary is still eventually looked at for what the default style key for a derived window class should use. This lookup however, never hits the Application dictionary, so your style never gets hit. This is why in custom controls we override the default style key and set a new key so that it hits your generic dictionary for your control, which is the eventual fallback of the theme style look-up process.

TextBox

Another pitfall that is easy to overlook is the context menu of a TextBox. If you’ve created a new style for the ContextMenu in WPF you’re 99% of the way there to having a customized right click menu in your application. However, the TextBox ContextMenu will remain unchanged.

So when you redefine the style for your TextBox. Make sure you define a new ContextMenu. It doesn’t have to be special, in fact, I would do this anyway, just so that the ContextMenu is closer to the default one in Windows Forms; which has options like Select All and Delete.

Thoughts

Perhaps a future version of WPF will allow a user defined theme to be set so that you have control over where ALL style lookups eventually attempt to resolve.

Tags:

Archives

Back to Top