Learn what WPF resources are, how StaticResource and DynamicResource differ, how lookup works, and how to share colours and sizes across CoinJar with resource dictionaries.

Bishal Biswas
Self Learner
The problem resources solve
Look at a typical first WPF window. The same green appears on the header, the Save button and the total. The same margin, 12, appears twenty times. Now the designer says “make the green a bit darker”. You hunt through every file and hope you found them all.
A resource is a named object that you define once and reuse by name. It is like a contact in your phone: you store the number once, then you just tap “Mum” instead of typing digits every time. Change the number in one place and every call goes to the new one.
Almost anything can be a resource: a brush, a colour, a number, a margin, a style, a template, a converter, even plain text.
Defining and using a resource
Every element that inherits from FrameworkElement (windows, panels, controls) has a Resources property. It is a resource dictionary: a collection where each item has a key, set with x:Key.
<Window.Resources>
<SolidColorBrush x:Key="AccentBrush" Color="#2E7D32" />
</Window.Resources>
<StackPanel>
<TextBlock Text="CoinJar" Foreground="{StaticResource AccentBrush}" />
<Button Content="Save" Background="{StaticResource AccentBrush}" />
</StackPanel>
{StaticResource AccentBrush} is a markup extension (you met those in XAML Namespaces and Markup Extensions). It means “find the resource with this key and use it here”.
Resources that are not brushes
To store simple values such as numbers and text, map the System namespace to a prefix:
<Window xmlns:sys="clr-namespace:System;assembly=mscorlib" ...>
<Window.Resources>
<sys:Double x:Key="TitleFontSize">22</sys:Double>
<sys:String x:Key="AppName">CoinJar</sys:String>
<Thickness x:Key="CardPadding">16,12</Thickness>
<CornerRadius x:Key="CardCorner">8</CornerRadius>
<Color x:Key="AccentColor">#2E7D32</Color>
</Window.Resources>
</Window>
<TextBlock Text="{StaticResource AppName}" FontSize="{StaticResource TitleFontSize}" />
<Border Padding="{StaticResource CardPadding}" CornerRadius="{StaticResource CardCorner}" />
Note the difference between a Color (just a colour value) and a SolidColorBrush (something that can paint). Properties such as Background need a brush. You can build a brush from a colour resource:
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}" />
Brushes get their own lesson in Brushes, Shapes and Effects.
How WPF finds a resource
When WPF meets {StaticResource AccentBrush}, it searches outward, like asking for something at home before asking the neighbours:
1. The element's own Resources
2. Its parent's Resources, then the grandparent's ... up to the Window
3. Application.Resources (App.xaml)
4. The theme resources of the controls
5. System resources (Windows colours and fonts)
The first match wins. This gives you a useful rule:
- put resources used by one window in
Window.Resources - put resources used everywhere in
App.xaml - put a resource on a single panel to override a shared one just for that area
If no match is found at all, StaticResource throws an exception when the window loads, with a message like “Cannot find resource named ‘AccentBrush’”.
StaticResource versus DynamicResource
There are two ways to look up a resource.
StaticResource |
DynamicResource |
|
|---|---|---|
| When it looks | once, when the XAML loads | every time the resource changes |
| If the key is missing | exception | silently uses the default value |
| Must be defined earlier in the file | yes | no |
| Speed | a little faster | a little slower |
| Use for | things that never change | things that can change at run time, such as themes |
Here is the difference in action. Both text blocks start green:
<TextBlock Text="Static" Foreground="{StaticResource AccentBrush}" />
<TextBlock Text="Dynamic" Foreground="{DynamicResource AccentBrush}" />
Now replace the resource from code:
Resources["AccentBrush"] = new SolidColorBrush(Colors.DarkOrange);
Only the “Dynamic” text turns orange. The static one grabbed the old brush once and kept it. This is the whole trick behind switching between light and dark themes, which you will build in Light, Dark and Fluent Themes.
A good default: use StaticResource unless you know the value will be swapped while the app runs.
Order matters for StaticResource
StaticResource reads the XAML from top to bottom. This fails, because AccentColor is used before it is defined:
<Window.Resources>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}" />
<Color x:Key="AccentColor">#2E7D32</Color>
</Window.Resources>
Swap the two lines and it works. Define the basics (colours) first and the things built from them (brushes, styles) after.
Application resources in App.xaml
Resources in App.xaml are available to every window in the app:
<Application x:Class="CoinJar.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<SolidColorBrush x:Key="AccentBrush" Color="#2E7D32" />
</Application.Resources>
</Application>
This is fine for a few resources, but App.xaml quickly becomes a very long file. The fix is to split resources into separate files.
Resource dictionary files
A resource dictionary file is a XAML file whose root element is ResourceDictionary. In Visual Studio, right click a folder, choose Add, then Resource Dictionary. Or create the file by hand. Let us make Themes/Colors.xaml in CoinJar:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="AccentColor">#2E7D32</Color>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}" />
<SolidColorBrush x:Key="CardBrush" Color="#FFFFFF" />
<SolidColorBrush x:Key="PageBrush" Color="#F3F5F7" />
<SolidColorBrush x:Key="MutedTextBrush" Color="#6B7280" />
</ResourceDictionary>
The WPF project includes .xaml files automatically, so there is nothing to add to the .csproj.
Merging dictionaries
A file on its own does nothing. You merge it into another dictionary with MergedDictionaries:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Colors.xaml" />
<ResourceDictionary Source="Themes/Sizes.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Resources that belong to App.xaml itself go here -->
<SolidColorBrush x:Key="DangerBrush" Color="#C62828" />
</ResourceDictionary>
</Application.Resources>
Two rules to remember:
- Once you use
MergedDictionaries, you must write an explicitResourceDictionaryelement, and any extra resources go inside it, after the merged list. - If two merged files define the same key, the one merged last wins. Resources defined directly in the dictionary beat all the merged ones.
Pack URIs
The Source above is a relative path. WPF also understands pack URIs, a special address format for files compiled into an app. You need them to load a dictionary from another assembly, such as a shared class library made with dotnet new wpflib:
<ResourceDictionary Source="pack://application:,,,/CoinJar.Theme;component/Themes/Colors.xaml" />
Read it as: “in this application, in the assembly named CoinJar.Theme, the file Themes/Colors.xaml”. The ,,, is part of the format, not a typo.
Using resources from C#
Code behind can read and change resources too:
// Throws if the key is not found
var accent = (Brush)FindResource("AccentBrush");
// Returns null if the key is not found
var maybe = TryFindResource("AccentBrush") as Brush;
// App wide resources
Application.Current.Resources["AccentBrush"] = new SolidColorBrush(Colors.Teal);
FindResource uses the same outward search as XAML, starting from the element you call it on. Everything that uses DynamicResource updates at once when you replace a resource.
System resources
Windows has its own colours and fonts, and the user can change them (for example a high contrast setting for accessibility). WPF exposes them through SystemColors, SystemFonts and SystemParameters. Use the ...Key versions with DynamicResource so your app follows the user’s settings:
<TextBlock Text="Selected"
Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
You will come back to this in Accessibility and Localization.
Shared or not: x:Shared
By default a resource is created once and the same object is handed to everyone who asks for it. That is great for brushes. For something that can only have one parent, such as an Image or a Path element, sharing breaks things: the element can appear in only one place. Mark it with x:Shared="False" to get a fresh copy each time:
<Path x:Key="CoinIcon" x:Shared="False" Data="M 0,8 A 8,8 0 1 1 16,8 A 8,8 0 1 1 0,8" Fill="Gold" />
x:Shared only works in compiled XAML, which is what a normal WPF project uses.
Tips
- Name resources by purpose, not by look.
AccentBrushandDangerBrushsurvive a redesign.GreenBrushis wrong the day the accent becomes blue. - Keep one kind of thing per file.
Colors.xaml,Sizes.xaml,Styles.xaml. It is easy to find things. - Use the XAML designer’s error list. A missing
StaticResourceusually shows there before you run. - Converters are resources too. You have been doing this since Value Converters.
Quick recap
- A resource is a named, reusable object stored in a
Resourcesdictionary with anx:Key. - Lookup goes from the element up through its parents, the window,
App.xaml, then themes and system resources. StaticResourcelooks once and needs the resource defined earlier.DynamicResourcefollows changes and is the basis of theme switching.- Split resources into
ResourceDictionaryfiles and combine them withMergedDictionaries. The last merged key wins. - Pack URIs load dictionaries from other assemblies.
FindResource,TryFindResourceand theResourcesindexer work from C#.
<!-- Themes/Colors.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Colours first, brushes built from them after -->
<Color x:Key="AccentColor">#2E7D32</Color>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}" />
<SolidColorBrush x:Key="PageBrush" Color="#F3F5F7" />
<SolidColorBrush x:Key="CardBrush" Color="#FFFFFF" />
<SolidColorBrush x:Key="MutedTextBrush" Color="#6B7280" />
</ResourceDictionary>
<!-- Themes/Sizes.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:Double x:Key="TitleFontSize">22</sys:Double>
<Thickness x:Key="PagePadding">16</Thickness>
<Thickness x:Key="CardPadding">16,12</Thickness>
<CornerRadius x:Key="CardCorner">8</CornerRadius>
</ResourceDictionary>
<!-- App.xaml -->
<Application x:Class="CoinJar.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Colors.xaml" />
<ResourceDictionary Source="Themes/Sizes.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- App level resources go inside, after the merged list -->
<SolidColorBrush x:Key="DangerBrush" Color="#C62828" />
</ResourceDictionary>
</Application.Resources>
</Application>
<!-- MainWindow.xaml -->
<Window x:Class="CoinJar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="CoinJar" Height="320" Width="460"
Background="{StaticResource PageBrush}">
<Window.Resources>
<!-- Only this window uses it, so it lives here -->
<sys:String x:Key="Tagline">Every coin counts.</sys:String>
</Window.Resources>
<StackPanel Margin="{StaticResource PagePadding}">
<!-- DynamicResource follows changes to AccentBrush -->
<TextBlock Text="CoinJar" FontWeight="Bold"
FontSize="{StaticResource TitleFontSize}"
Foreground="{DynamicResource AccentBrush}" />
<TextBlock Text="{StaticResource Tagline}" Foreground="{StaticResource MutedTextBrush}" />
<Border Background="{StaticResource CardBrush}" Padding="{StaticResource CardPadding}"
CornerRadius="{StaticResource CardCorner}" Margin="0,16,0,0">
<StackPanel>
<TextBlock Text="Spent this month" Foreground="{StaticResource MutedTextBrush}" />
<TextBlock Text="$412.35" FontSize="28" Foreground="{DynamicResource AccentBrush}" />
<TextBlock Text="Over budget on Fun" Foreground="{StaticResource DangerBrush}" />
</StackPanel>
</Border>
<Button Content="Change accent" Margin="0,16,0,0" HorizontalAlignment="Left"
Padding="12,4" Click="ChangeAccent_Click" />
</StackPanel>
</Window>
// MainWindow.xaml.cs
using System.Windows;
using System.Windows.Media;
namespace CoinJar;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ChangeAccent_Click(object sender, RoutedEventArgs e)
{
// Replace the app wide brush. Only DynamicResource users update.
var current = (SolidColorBrush)FindResource("AccentBrush");
Application.Current.Resources["AccentBrush"] =
current.Color == Colors.DarkOrange
? new SolidColorBrush(Color.FromRgb(0x2E, 0x7D, 0x32))
: new SolidColorBrush(Colors.DarkOrange);
}
}
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready