WPF is very dynamic and allows us to use resources in many different ways. We can use Static or Dynamic. We can place all our resources in App.xaml or use merged dictionaries each time.
You can see all the approaches and what I think is best practice in my WPF Merged Dictionary problems and solutions post.
If you’re new to WPF resources, I suggest reading my Explicit, Implicit and Default styles in WPF post before diving into this one.
This benchmark is supposed to show, in numbers, how any approach affects performance.
Here’s what I want answered with this benchmark:
- Is splitting resources in App.xaml to many Merged Dictionaries file vs. one long App.xaml file means longer application startup time?
- Is loading all resources in App.xaml once better than loading the resources every time with merged dictionary?
- Is DynamicResource instead of StaticResource an issue? How big of an issue?’
Before answering any of those, there’s an important thing to understand with WPF resources. And that is when a resource instance is actually created.
When is an instance of the resource created?
A resource instance is not created at declaration. It’s created at first use. To prove that, I created this simple TrueVisibilityConverter :
class TrueVisibilityConverter : IValueConverter
{
public TrueVisibilityConverter()
{
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return false;
}
}
And declared it in App.xaml:
<Application x:Class="WPFXamlResourceBenchmark.App" ...>
<Application.Resources>
<local:TrueVisibilityConverter x:Key="TrueVisibilityConverter"/>
</Application.Resources>
</Application>
When placing a breakpoint in the Converter’s constructor and launching the application, the breakpoint isn’t hit. It was hit only When adding usage of it in MainWindow.cs.
<Window x:Class="WPFXamlResourceBenchmark.MainWindow" ...>
<Grid Visibility="{Binding
Converter={StaticResource TrueVisibilityConverter}}"/>
</Window>
Now that we got that out of the way, let’s answer some questions and see some numbers.
Question 1: Is splitting resources in App.xaml to many Merged Dictionaries file vs. one long App.xaml file means longer application startup time?
As we just saw, declaring resources in App.xaml doesn’t do much unless we use those resources. So for a correct benchmark we need to use a bunch of resources in MainWindow.
Benchmark 1:
- Create a lot of different resources in App.xaml (I used ControlTemplates showing icon drawings)
- Use the resources In MainWindow to make sure they are created.
- Measure load time from App constructor until MainWindow was loaded
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
App.stopwatch.Stop();
File.AppendAllText(file, "\r\n" + App.stopwatch.ElapsedMilliseconds);
};
Benchmark 2:
- Create a lot of resource dictionary files and place the resources from the previous benchmark in the new files
- In App.xaml, merge all the new dictionaries:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
<ResourceDictionary Source="Dictionary2.xaml"/>
<ResourceDictionary Source="Dictionary3.xaml"/>
<ResourceDictionary Source="Dictionary4.xaml"/>
<ResourceDictionary Source="Dictionary5.xaml"/>
<ResourceDictionary Source="Dictionary6.xaml"/>
<ResourceDictionary Source="Dictionary7.xaml"/>
<ResourceDictionary Source="Dictionary8.xaml"/>
<ResourceDictionary Source="Dictionary9.xaml"/>
<ResourceDictionary Source="Dictionary10.xaml"/>
<ResourceDictionary Source="Dictionary11.xaml"/>
<ResourceDictionary Source="Dictionary12.xaml"/>
<ResourceDictionary Source="Dictionary13.xaml"/>
<ResourceDictionary Source="Dictionary14.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Results:
Question 2: Is loading all resources in App.xaml once better than loading the resources every time with merged dictionary?
It makes sense that loading resources in App.xaml once should be faster, since with merged dictionaries a new instance of the resource is created every time. The question is, how significant is this to application’s life cycle?
WPF has to do a lot of things behind the scenes – Measure, Render, create Control classes, etc. It’s quite possible loading resources is insignificant.
Benchmark 1 – Everything in App.xaml:
- Created two simple resources in App.xaml: A converter and a ControlTemplate with an icon
<Application x:Class="WPFXamlResourceBenchmark.App" ...>
<Application.Resources>
<local:TrueVisibilityConverter x:Key="TrueVisibilityConverter"/>
<ControlTemplate x:Key="Icon1">
<Viewbox>
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="appbar_3d_collada" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="44" Height="30.3735" Canvas.Left="15" Canvas.Top="21.6194" Stretch="Fill" Fill="#FF000000" Data="F1 M 39.2598,21.6194C 47.9001,21.6194 55.3802,24.406 59,28.4646L 59,33.4834C 56.3537,29.575 49.2267,26.7756 40.85,26.7756C 30.2185,26.7756 21.6,31.285 21.6,36.8475C 21.6,40.4514 25.2176,43.6131 30.6564,45.3929C 22.7477,43.5121 17.2,39.1167 17.2,33.9944C 17.2,27.1599 27.0765,21.6194 39.2598,21.6194 Z M 35.8402,51.9929C 27.1999,51.9929 19.7198,49.2063 16.1,45.1478L 15,40.129C 17.6463,44.0373 25.8733,46.8367 34.25,46.8367C 44.8815,46.8367 53.5,42.3274 53.5,36.7648C 53.5,33.161 49.8824,29.9992 44.4436,28.2194C 52.3523,30.1002 57.9,34.4956 57.9,39.6179C 57.9,46.4525 48.0235,51.9929 35.8402,51.9929 Z "/>
</Canvas>
</Viewbox>
</ControlTemplate>
2. Create a simple UserControl that uses these resource
<UserControl x:Class="WPFXamlResourceBenchmark.MySquare">
<ContentControl Template="{DynamicResource Icon1}"
Visibility="{Binding Converter={StaticResource TrueVisibilityConverter}}"/>
</UserControl>
3. On the click of a button, add 500 of these babies to MainWindow
private void OnAddMySquare(object sender, RoutedEventArgs e)
{
stopwatch.Start();
for (int i = 0; i < 500; i++)
{
MyStackPanel.Children.Add(new MySquare());
}
}
4. Measure the loading time of the last MySquare and print it
public partial class MySquare : UserControl
{
volatile static int index = 0;
public MySquare()
{
InitializeComponent();
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
index++;
if (index == 500)
{
var sw = MainWindow.stopwatch;
sw.Stop();
File.AppendAllText(file, "\r\n" + sw.ElapsedMilliseconds);
}
}
Like all benchmarks here, I ran this about 10 times and got average time.
Benchmark 2 – Use Merged Dictionaries:
- Instead of having the resources in App.xaml, create a new Resource Dictionary – Dictionary.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" >
<local:TrueVisibilityConverter x:Key="TrueVisibilityConverter"/>
<ControlTemplate x:Key="Icon1">
<Viewbox>
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="appbar_3d_collada" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="44" Height="30.3735" Canvas.Left="15" Canvas.Top="21.6194" Stretch="Fill" Fill="#FF000000" Data="F1 M 39.2598,21.6194C 47.9001,21.6194 55.3802,24.406 59,28.4646L 59,33.4834C 56.3537,29.575 49.2267,26.7756 40.85,26.7756C 30.2185,26.7756 21.6,31.285 21.6,36.8475C 21.6,40.4514 25.2176,43.6131 30.6564,45.3929C 22.7477,43.5121 17.2,39.1167 17.2,33.9944C 17.2,27.1599 27.0765,21.6194 39.2598,21.6194 Z M 35.8402,51.9929C 27.1999,51.9929 19.7198,49.2063 16.1,45.1478L 15,40.129C 17.6463,44.0373 25.8733,46.8367 34.25,46.8367C 44.8815,46.8367 53.5,42.3274 53.5,36.7648C 53.5,33.161 49.8824,29.9992 44.4436,28.2194C 52.3523,30.1002 57.9,34.4956 57.9,39.6179C 57.9,46.4525 48.0235,51.9929 35.8402,51.9929 Z "/>
</Canvas>
</Viewbox>
</ControlTemplate>
</ResourceDictionary>
2. Now, add this resource dictionary in MySquare.xaml
<UserControl x:Class="WPFXamlResourceBenchmark.MySquare"...>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<ContentControl Template="{StaticResource Icon1}"
Visibility="{Binding Converter={StaticResource TrueVisibilityConverter}}"
/>
</UserControl>
3. Create and add MySquare to StackPanel 500 times and measure the time span (same as in previous benchmark)
The results:
Question 3: Is DynamicResource instead of StaticResource an issue? How big of an issue?
I used here the same benchmark as before, and ran it twice. MySquare.xaml now looked like this:
<UserControl x:Class="WPFXamlResourceBenchmark.MySquare" ...>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary-Exp2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<ContentControl Template="{DynamicResource Icon1}"
Visibility="{Binding Converter={StaticResource TrueVisibilityConverter}}"/>
</UserControl>
Note that the ContentControl’s Template is now with DynamicResource.
The results:
It’s reasonable to assume that when we have a big application, a new screen / control has enough resources to be equivalent to the 500 resources we are using here. So using DynamicResource will actually add 50 milliseconds to its load time. 50 milliseconds is not that much noticeable for user experience, but when added up, it can make some difference.
Another thing to consider is Memory
When we declare a resource in application scope in App.xaml, the instance is created at first use and never garbage collected. It stays in memory forever for further use.
On the other hand, when declaring a resource in some Control’s resources, or in the Control using Merged Dictionaries, the resource will be garbage collected when the control is unloaded.
So if you have a memory-heavy resource that is rarely used in the application, it makes sense not to declare it in App.xaml.
Summary
I think understanding resources when working on a large application is critical. Resources can seriously affect your application’s load time and performance.
We also saw that there isn’t always a right and wrong answer here. I’d say declare everything in App.xaml and use it as StaticResource, except that there is a memory factor to consider.
So here are some rule of thumb conclusions:
- Prefer StaticResource to DynamicResource
- If you’re creating a Control a lot of times, declare the resources in App.xaml for re-usability. Otherwise, new instances will be created each time which will impact performance.
- If you’re using a resource rarely and it’s memory-heavy, prefer to user merged dictionaries instead of declaring it in application scope (App.xaml)
- In a small application and even medium sized, using DynamicResource and merged dictionaries inappropriately probably won’t make a noticeable difference.
However, these small and medium applications tend to grow into large monsters and it will be much harder to refactor a large monster.
Have a great day everyone.
And don’t forget to share, like and comment if you liked the post!