Why StaticResource Changes Are Not Reflected in WPF and How to Fix It

StaticResource resolves at XAML load time, so replacing the resource at runtime leaves existing references unchanged. DynamicResource follows such replacements.

Overview

WPF provides two resource reference mechanisms: StaticResource and DynamicResource.
When a resource referenced with StaticResource is replaced with another object in code at runtime and the UI does not update, the root cause is the difference in when each mechanism resolves its value.
This article explains the internal behavior of both mechanisms and provides criteria for choosing between them.


Prerequisites / Environment

The figure and table in this article come from displaying a screen in the environment above that references the same resource through both StaticResource and DynamicResource, then swapping the resource at run time.
The following points were confirmed in that environment:


Problem

Replacing an entry in a ResourceDictionary at runtime does not always cause the associated control to update its appearance.

For example, the following XAML defines a SolidColorBrush as a StaticResource, and the code-behind replaces it at runtime.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Resources>
        <SolidColorBrush x:Key="ThemeColor" Color="SkyBlue" />
    </Window.Resources>

    <Button Background="{StaticResource ThemeColor}" Content="Button" />
</Window>
// Attempt to change the color at runtime
Resources["ThemeColor"] = new SolidColorBrush(Colors.OrangeRed);

Despite the assignment, the button background remains SkyBlue.

Two buttons referencing the same brush key. The one using StaticResource stays sky blue while the one using DynamicResource turns orange after the resource is replaced.
Two buttons referencing the same ThemeColor, captured right after the resource was replaced with OrangeRed at runtime. The StaticResource side keeps the SkyBlue value it resolved at load time.

Cause / Background

The critical difference between StaticResource and DynamicResource lies in when the resource value is resolved.

How StaticResource Works

StaticResource resolves the resource exactly once, at the moment the XAML is parsed (loaded).
The resolved value is written directly to the property, after which no reference between the property and the resource dictionary is retained.
Consequently, replacing the dictionary entry with another object afterward is invisible to the property.
What the property received, however, is the brush or other object itself. Changing the Color of the same brush, when it is not frozen, does change what the StaticResource side shows (the last two rows of the table below).

Because XAML is parsed top-to-bottom, a resource referenced via StaticResource must be defined before its reference site in the markup.
Violating this order raises a XamlParseException at load time.

How DynamicResource Works

DynamicResource does not resolve the value immediately.
Instead, it records the association between the property and the resource key.
Whenever the corresponding entry in the resource dictionary changes at runtime, WPF detects the change and re-evaluates the property, updating the UI automatically.

Aspect StaticResource DynamicResource
Resolution timing XAML load time (once only) Load time + re-evaluated on every change
Replacing the entry at run time Not reflected Reflected immediately
Changing the same brush (not frozen) at run time Reflected Reflected
Definition order Must be defined before the reference Order is unrestricted
Performance High (no change monitoring) Lower (change listener overhead)

The difference can be confirmed by replacing the resource at run time and reading the property back.

configuration Border.Background
StaticResource, before swap #FFFFFFFF
StaticResource, after swap #FFFFFFFF
DynamicResource, before swap #FFFFFFFF
DynamicResource, after swap #FFFF0000
StaticResource, brush.Color changed #FFFF0000 (brush was frozen: False)
DynamicResource, brush.Color changed #FFFF0000 (brush was frozen: False)

Measured on .NET 10 / Windows 11 by reading Border.Background after replacing the entry behind the same key PanelBrush with a red brush at run time, and after changing the same brush’s Color to red without replacing the entry. Nothing else differs between the rows except how the resource is referenced and changed.

Both hold the same value before the swap. The two diverge only afterwards, where the StaticResource side stays white.


Solution

To reflect runtime resource changes in the UI, replace StaticResource with DynamicResource at the affected binding site.


Implementation

Dynamic Theme Toggle with DynamicResource

The following example switches the button background color on each button click by replacing the brush in the resource dictionary.
Because DynamicResource is used, the assignment propagates to the Background property immediately.

<Window x:Class="SampleApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Resources>
        <SolidColorBrush x:Key="ThemeColor" Color="SkyBlue" />
    </Window.Resources>

    <StackPanel>
        <Button Background="{DynamicResource ThemeColor}" Content="Target Button" />
        <Button Content="Toggle Theme" Click="OnThemeToggleClick" />
    </StackPanel>
</Window>
private bool _isDark = false;

private void OnThemeToggleClick(object sender, RoutedEventArgs e)
{
    _isDark = !_isDark;
    Resources["ThemeColor"] = _isDark
        ? new SolidColorBrush(Colors.DarkSlateGray)
        : new SolidColorBrush(Colors.SkyBlue);
}

The assignment to Resources["ThemeColor"] is reflected on the Background property without any additional refresh call.

Full Theme Switch via MergedDictionaries

For application-wide light/dark theme switching, a common pattern is to separate each theme into its own ResourceDictionary file and swap the active dictionary at runtime.

Theme file (Themes/Light.xaml):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush x:Key="Background" Color="White" />
    <SolidColorBrush x:Key="Foreground" Color="Black" />
</ResourceDictionary>

Theme file (Themes/Dark.xaml):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush x:Key="Background" Color="#1E1E1E" />
    <SolidColorBrush x:Key="Foreground" Color="White" />
</ResourceDictionary>

Theme switching code:

private void SwitchTheme(string themeName)
{
    var uri = new Uri($"Themes/{themeName}.xaml", UriKind.Relative);
    var dict = new ResourceDictionary { Source = uri };

    Application.Current.Resources.MergedDictionaries.Clear();
    Application.Current.Resources.MergedDictionaries.Add(dict);
}

All controls that reference theme resources via DynamicResource update automatically when MergedDictionaries is replaced.
Controls using StaticResource for the same keys will not update.


Notes


Alternatives / Comparison

Approach Pros Cons Best suited for
Replace StaticResource with DynamicResource Simple change; updates are immediate Adds listener overhead per binding Theme color toggles, OS color integration
Swap MergedDictionaries at runtime Centralized theme management Requires splitting resources into separate files Application-wide light/dark mode switching
INotifyPropertyChanged + data binding UI updates are driven by ViewModel state Does not use the resource dictionary system Data-driven display changes, not style-level theming

Summary

StaticResource fixes the property value at XAML load time; replacing a resource dictionary entry after that point has no effect on the property.
When the value must change at runtime, DynamicResource is required.

Selection criteria:

The recommended approach is to default to StaticResource and apply DynamicResource only where runtime changes are explicitly required.
This strategy balances maintainability and rendering performance.