I am looking for a way to define a WPF resource (for now, to be used as a static resource) that is accessible from everywhere within my application.
My resource is defined in this resource dictionary:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="flatButtonStyle" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" TargetType="{x:Type Button}">
<Setter Property="BorderThickness" Value="4"/>
</Style>
</ResourceDictionary>
The selected answer to this question indicates that I must merge that resource dictionary into my App.xaml
file (of a project called AppWideResources
):
<Application x:Class="AppWideResources.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/AppWideResources;component/CommonResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
At first, this seems to work; the button in this window is appropriately styled in a flat way with an extra-thick border:
<Window x:Class="AppWideResources.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AppWideResources" Height="300" Width="300"
>
<StackPanel>
<Button Style="{StaticResource flatButtonStyle}" Content="Test" HorizontalAlignment="Stretch"/>
</StackPanel>
</Window>
However, this stops working as soon as I use my shared resource in a control template:
My (extremely simplified, for the purpose of this question) control:
using System;
using System.Windows;
using System.Windows.Controls;
namespace AppWideResources
{
public class MyControl : Control
{
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
}
}
}
... and the corresponding Themes\Generic.xaml
file:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AppWideResources">
<Style TargetType="local:MyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyControl">
<StackPanel Orientation="Horizontal">
<Button Style="{StaticResource flatButtonStyle}" Content="1"/>
<Button Style="{StaticResource flatButtonStyle}" Content="2"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This control is then inserted into my window:
<Window x:Class="AppWideResources.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AppWideResources"
Title="AppWideResources" Height="300" Width="300"
>
<StackPanel>
<Button Style="{StaticResource flatButtonStyle}" Content="Test" HorizontalAlignment="Stretch"/>
<local:MyControl/>
</StackPanel>
</Window>
If I run this, a XamlParseException
is thrown, saying that the static resource flatButtonStyle
cannot be found.
The only workaround I have found is by explicitly merging the common resource dictionary into the local resource dictionary of the control template:
<ControlTemplate TargetType="local:MyControl">
<ControlTemplate.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/AppWideResources;component/CommonResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ControlTemplate.Resources>
<StackPanel Orientation="Horizontal">
<Button Style="{StaticResource flatButtonStyle}" Content="1"/>
<Button Style="{StaticResource flatButtonStyle}" Content="2"/>
</StackPanel>
</ControlTemplate>
However, not only is this quite verbose and error-prone with several lines of redundant code (my actual project contains quite a few templated controls, not just one), I am also concerned about wasting system resources by loading the static resources several times (once for every time CommonResources.xaml
is included). (Is that concern justified? Or does WPF use the same instances every time when loading a particular resource dictionary?)
So, the question is: What is the proper way to make a WPF resource accessible throughout the whole application, including control templates?
StaticResource will be resolved and assigned to the property during the loading of the XAML which occurs before the application is actually run. So, static resource has to be available at the time of loading
.
It works for normal button since resource look up continues till application resources and resolve it from there. But MyControl template still not loaded into app visual tree so it can't traverse upto application resources
.
There are two ways to achieve that:
Merge ResourceDictionary in Generic.xaml as well which you have already achieved.
Other way is to use DynamicResource which assigns an Expression object to the property during loading but does not actually lookup the resource until runtime when the Expression object is asked for the value. This defers looking up the resource until it is needed at runtime
.
Read it here - StaticResource v/s DynamicResource.
This will work without merging dictionaries in Generic.xaml:
<Button Style="{DynamicResource flatButtonStyle}" Content="1"/>
<Button Style="{DynamicResource flatButtonStyle}" Content="2"/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With