Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding Generic.xaml, theme resource dictionaries, and default styles for WPF custom controls

Tech 1

Default styles for WPF controls are discovered through a theme-aware resource lookup. When a control is instantiated, WPF searches for a Style keyed by the control’s DefaultStyleKey (by default, the control’s Type) in the Themes folder of the control’s assembly.

  • If a theme-specific dictionary exists for the current Windows theme (e.g., Aero.NormalColor.xaml, Luna.NormalColor.xaml), WPF checks it first.
  • If the style is not found there, WPF falls back to Generic.xaml in the same Themes folder.
  • This mechanism applies to controsl (types deriving from System.Windows.Controls.Control). It does not apply to user controls that are composed in markup.

The practical outcome for a control library is that consumers do not need to merge your resource dictionaries manually. If your library ships Generic.xaml (and optional theme dictionaries), custom controls will automatically pick up their default styles.

Defining DefaultStyleKey for a custom control

A custom control must advertise how WPF should look up its default style by setting DefaultStyleKey. The common pattern is to override metadata in the static constructor.

C#

using System.Windows;
using System.Windows.Controls;

namespace Acme.Controls
{
    public class DialControl : Control
    {
        static DialControl()
        {
            // Instruct the style lookup to use this control’s type as the key
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(DialControl),
                new FrameworkPropertyMetadata(typeof(DialControl)));
        }
    }
}

Place the default Style in Themes/Generic.xaml with either an implicit style (no x:Key) whose TargetType matches the control, or a style keyed to the control’s type. Using TargetType with out x:Key is the typical approach.

XAML (Themes/Generic.xaml)

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Acme.Controls">

    <!-- Default look for DialControl -->
    <Style TargetType="{x:Type local:DialControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:DialControl}">
                    <Grid>
                        <Ellipse x:Name="PART_Rim" Fill="LightGray" Stroke="DarkGray" StrokeThickness="2"/>
                        <Line x:Name="PART_Hand" X1="0.5" Y1="0.5" X2="0.9" Y2="0.5"
                              Stretch="Fill" Stroke="Black" StrokeThickness="2"/>
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

The Themes folder must reside at the project root, and the XAML files inside must have Build Action set to Page.

Theme-specific resource dictionaries

If the control’s default appearance should vary by Windows themme, add dictionaries named after the theme in the same Themes folder. WPF chooses the file matching the current theme before falling back to Generic.xaml.

Common names include:

  • Aero.NormalColor.xaml (Vista/Win7 Aero)
  • Luna.NormalColor.xaml (Windows XP default)
  • Classic.xaml (Windows Classic)

Example overriding just a brush for Aero:

XAML (Themes/Aero.NormalColor.xaml)

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Acme.Controls">

    <Style TargetType="{x:Type local:DialControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:DialControl}">
                    <Grid>
                        <Ellipse Fill="#FFEFF6FF" Stroke="#FF8BAAD9" StrokeThickness="2"/>
                        <Line X1="0.5" Y1="0.5" X2="0.9" Y2="0.5" Stretch="Fill"
                              Stroke="#FF3A5FCD" StrokeThickness="2"/>
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

ThemeInfo attribute and resource lookup scope

The assembly-level ThemeInfo attribute tells WPF where to search for theme dictionaries and the generic dictionary. A common configuration for a control library is:

C# (Properties/AssemblyInfo.cs)

using System.Windows;

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None,            // no theme-specific dictionaries in this assembly
    ResourceDictionaryLocation.SourceAssembly   // Generic.xaml lives here
)]

Alternate configurations are possible. For example, to place theme-specific dictionaries in external satellite assemblies and keep Generic.xaml in the main library:

[assembly: ThemeInfo(
    ResourceDictionaryLocation.ExternalAssembly, // look for <AssemblyName>.<Theme>.dll
    ResourceDictionaryLocation.SourceAssembly
)]

When using ExternalAssembly for theme dictionaries, the satellite assemblies must follow the naming convention:

  • <YourAssembly>.Aero.dll, <YourAssembly>.Luna.dll, etc.

Each satellite contains a Themes folder with the appropriate theme XAML.

Changing the default style of an existing control by deriving

To alter a built-in control’s default look without touching app-level resources, derive from the control and override DefaultStyleKey metadata. You are responsible for supplying a complete ControlTemplate for the new derived type.

C#

using System.Windows;
using System.Windows.Controls;

namespace Acme.Controls
{
    public class FlatButton : Button
    {
        static FlatButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(FlatButton),
                new FrameworkPropertyMetadata(typeof(FlatButton)));
        }
    }
}

XAML (Themes/Generic.xaml)

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Acme.Controls">

    <Style TargetType="{x:Type local:FlatButton}">
        <Setter Property="Background" Value="#FF2D2D30"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:FlatButton}">
                    <Border Background="{TemplateBinding Background}" CornerRadius="2">
                        <ContentPresenter
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Margin="8,4"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Lookup summary

  • WPF searches theme-specific dictionary in Themes/<ThemeName>.xaml of the control’s assembly (or satellite), keyed by DefaultStyleKey.
  • If missing, WPF falls back to Themes/Generic.xaml in the location specified by ThemeInfo.
  • For custom controls, define DefaultStyleKey via OverrideMetadata and ship the default Style in Generic.xaml.
  • Consumers of the control library do not need too merge these dictionaries explicitly; the lookup is automatic.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.