Resources, styles, templates and themes

Resource lookup order, implicit and explicit styles, control and data templates, triggers, and merged theme dictionaries that keep a design consistent.

Resource lookup order

<Window.Resources>
  <SolidColorBrush x:Key="Accent">#2F6FED</SolidColorBrush>
  <sys:Double x:Key="Gap" xmlns:sys="clr-namespace:System;assembly=System.Runtime">12</sys:Double>
</Window.Resources>

<StackPanel>
  <TextBlock Text="Total" Foreground="{StaticResource Accent}" />
  <Border Background="{DynamicResource PanelBackground}" />
</StackPanel>
  1. The element's own Resources.
  2. Each ancestor's Resources, walking up the logical tree.
  3. Application.Current.Resources.
  4. The system and theme dictionaries built into WPF.
  5. The default value on the property itself.
ReferenceResolvedCostUse when
StaticResourceAt load time, onceCheapestThe resource never changes after load
DynamicResourceOn every accessA lookup per requestThe resource may be swapped at runtime, e.g. a theme
x:StaticA compile-time constantFreeA fixed number or string, not a brush

Styles and triggers

<Style x:Key="PrimaryButton" TargetType="Button">
  <Setter Property="Padding" Value="16,8" />
  <Setter Property="FontWeight" Value="SemiBold" />
  <Setter Property="Background" Value="{StaticResource Accent}" />
  <Setter Property="Foreground" Value="White" />
  <Style.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
      <Setter Property="Opacity" Value="0.9" />
    </Trigger>
    <Trigger Property="IsEnabled" Value="False">
      <Setter Property="Opacity" Value="0.5" />
    </Trigger>
    <DataTrigger Binding="{Binding IsBusy}" Value="True">
      <Setter Property="IsEnabled" Value="False" />
    </DataTrigger>
  </Style.Triggers>
</Style>
  • Omit x:Key and the style becomes implicit: it applies to every control of that type in the resource's scope.
  • A style can only set properties; a template defines structure. Setting Template inside a style is the normal way to restyle a control completely.
  • Triggers in a style are evaluated in order and the last matching setter wins for a given property - order matters when several can fire.
  • Use BasedOn to extend an existing style instead of copying every setter.

ControlTemplate and DataTemplate

<!-- control template: replaces the structure of the button itself -->
<ControlTemplate x:Key="RoundButton" TargetType="Button">
  <Border x:Name="Chrome"
          CornerRadius="6"
          Background="{TemplateBinding Background}"
          Padding="{TemplateBinding Padding}">
    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
  </Border>
  <ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
      <Setter TargetName="Chrome" Property="Background" Value="#1F5ED0" />
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

<!-- data template: how one item of a collection is rendered -->
<DataTemplate x:Key="OrderRow" DataType="{x:Type models:Order}">
  <StackPanel Orientation="Horizontal">
    <TextBlock Text="{Binding Id}" Width="80" />
    <TextBlock Text="{Binding Customer}" />
  </StackPanel>
</DataTemplate>
<!-- theme dictionaries loaded at startup -->
<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="pack://application:,,,/Themes/Colors.Light.xaml" />
  <ResourceDictionary Source="pack://application:,,,/Themes/Controls.xaml" />
</ResourceDictionary.MergedDictionaries>

Keep raw values - colours, sizes, corner radii - in one dictionary, and reference them from styles and templates. A theme swap is then a single dictionary replacement, which is only possible if nothing downstream hard-codes a colour.

💡
Use TemplateBinding inside a ControlTemplate when the value comes from the templated control's own properties. Use {Binding RelativeSource={RelativeSource TemplatedParent}, Path=...} when you need a converter or a two-way mode - TemplateBinding supports neither.

FAQ

Why is my DynamicResource not updating?
A dictionary swap replaces the object, but a bound value only re-reads on the next lookup. Reassign the merged dictionary in the intended order and make sure the property was set with DynamicResource, not StaticResource.
Where should implicit styles live?
Not in App.xaml unless you truly mean application-wide. An implicit style in a module's resource dictionary keeps the redesign local instead of leaking into every window.

Custom controls and user controls Items controls, collections and virtualization

Last refreshed 2026-09-18.