Content
Introduction
In the two parts of this series of articles we made extensive use of Resource
s to define our Style
s without actually paying much attention to them, except where it was needed in the discussion at hand. I will (try to) correct this inbalance in this article.
Mind however that I will be talking exclusively about resources defined in ResourceDictionaries
.
You can find the other parts of this series here:
- WPF Custom Visualization Part 1 of some: Styling
- WPF Custom Visualization Part 2 of some: Styling with Triggers
- WPF Custom Visualization Intermezzo: Resources (this article)
- WPF Custom Visualization Intermezzo 2: Binding
- WPF Custom Visualization Part 3 of N: Templating
- WPF Custom Visualization Part 4 of N: Addorners
What are Resources?
Resources are a means provided by the WPF framework to make something available from a central point. This results in three things:
- First, we must define something. This can be any instatiatable class in .NET. Until now (in the previous articles) we have been using
Styles
exclusively, but we could instiate just about any class defined in .NET.
- Second, we need to make it available for use. This means we must somehow be able to refer to it. This is typically done using a
ResourceKey
.
- Finally, we make it available from a central point. In the previous article, this was typically done by defining it in the
Resource
section of a control in XAML
Basic Resources in WPF: instantiating and referencing objects.
Concepts
A lot of what has been said about how to define Style
s, how to reference them, their scope, etc... is valid for Resource
s in general: after all, the Style
s we defined are simply Resource
s:
As mentioned above, there three components to using Resource
s:
- Defining the object as a resource
- Making the object referenceable
- Using the resource
How to do it?
To define an object as a resource, we must define an instance of it in the Resources
section of the scope ini which we want to use it. To make it referencable we must provide a Key
and to reference it we use the StaticResource
markup extension or the DynamicResource
markup extension. (Don't mind this last one too much for now, further I will provide more explanation on the difference bewteen the two.)
A most basic example is the following:
We have a class with definition:
public class MyCustomClass
{
public string StringProperty { get; set; }
public int IntegerPoperty { get; set; }
public override string ToString()
{
return "This MyCustomClass instance has a value for StringProperty of [" + StringProperty + "] and a value of IntegerProperty of [" + IntegerPoperty + "]";
}
}
And we use it as a Resource
:
<GroupBox.Resources>
-->
<local:MyCustomClass x:Key="groupBoxLevelInstance" StringProperty="GroupBoxResource" IntegerPoperty="10" />
</GroupBox.Resources>
-->
<Button Grid.Row="0" Content="{StaticResource groupBoxLevelInstance}"/>
Above definitions result in following visuals:
<img height="74" src="992230/BasicResourceExampleV2.png" width="398" />
Don't be fooled however by the fact that this results in a visual representation: that is simply because we reference the resource in the Content
property of the button, but a resource does NOT have to result in anything visual.
As Style
s had a scope in which they where visible, so do Resource
s in general. Their scope is hierarchically determined by the container in which they are defined, just as was the case with Style
s:
<Grid>
<StackPanel Grid.Row="1">
<StackPanel.Resources>
<local:MyCustomClass x:Key="stackpanelLevelInstance" StringProperty="StackpanelResource" IntegerPoperty="10" />
</StackPanel.Resources>
<Button Content="{StaticResource stackpanelLevelInstance}"/>
</StackPanel>
-->
-->
</Grid>
A Resource
is not available in a sibling scope of the element on which it is defined.
In fact, the whole discussion on scoping as in the first article is still valid here:
The scope is hierarchically determined by the container in which the style is defined. This results in following hierarchy:
- Application
- Window
- (Parent) Control
- Control
This hierarchy is defined by "containment", meaning that a dialog opened by a click on a button inside another window does NOT see the resources defined in this last window
The application contains all windows it displays. A Window with a button opening another Window does NOT contain the opened Window: any resources defining in the opening Window will not be available to the opened Window.
Making Resources available in WPF: the key is the Key
Concepts
The most basic way of making Resource
s available in an aplication is through the x:Key
directive and providing a string. However, Resource
s made available through this type of key are only referencable in the application itself, after all, how is WPF to know what external library to check? If you want a Resource
in a library project to be available in an application you must use the ComponentResourceKey
.
There are some other specialized ResourceKey
derived classes available like TemplateKey
and its derivatives DataTemplateKey
and ItemContainerTemplateKey
, but these are only valid when adding specific types of objects to the Resource
s. I will discuss these in a next article.
You may remember from our Style
article that we where able to specify a TargetType
on our style resource and in doing so didn't need to provide a Key. Turns out this is possible because the Style
class defines the DictionaryKeyProperty
attribute.
How to do it?
We've seen the basic use using a string in the above samples, so let's fast forward to the ComponentResourceKey
:
In a WPF library project we defined two classes:
namespace Resources.ResourceLib
{
public class DummyClass
{
}
}
namespace Resources.ResourceLib
{
public class OtherDummyClass
{
}
}
As you can see, these classes implement absolutely no functionality: they will eventually function as a kind of markers in the ComponentResourceKey
In the same project, we define a Theme/Generic.xaml with following content:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Resources.ResourceLib">
-->
<SolidColorBrush x:Key="{ComponentResourceKey {x:Type local:DummyClass}, ResourceId=myComponentLibBrush}" Color="Red"/>
-->
<SolidColorBrush x:Key="{ComponentResourceKey {x:Type local:OtherDummyClass}, ResourceId=myComponentLibBrush}" Color="Green"/>
-->
<SolidColorBrush x:Key="myLibBrush" Color="Red"/>
</ResourceDictionary>
Notice how we use our two defined classes in the ComponentResourceKey
. They enable WPF to find the assembly in which to look for the resources: WPF will look in the assembly in which the classes are defined. Also note how we are using two ComponentResourceKey
s with the same ResourceId
To use the above defined resources, we have following XAML:
<GroupBox Header="Resources from external libraries">
<GroupBox.Resources>
<SolidColorBrush x:Key="redColor" Color="Red"/>
</GroupBox.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Backgroundcolor from this application"
Background="{StaticResource redColor}"/>
<Button Grid.Row="1" Content="Backgroundcolor from external library using DummyClass type"
Background="{StaticResource {ComponentResourceKey TypeInTargetAssembly={x:Type rlib:DummyClass}, ResourceId=MyComponentLibBrush}}"/>
<Button Grid.Row="2" Content="Backgroundcolor from external library using OtherDummyClass type"
Background="{StaticResource {ComponentResourceKey TypeInTargetAssembly={x:Type rlib:OtherDummyClass}, ResourceId=MyComponentLibBrush}}"/>
-->
<Button Grid.Row="3" Content="Backgroundcolor from external library using regular key"
Background="{StaticResource myLibBrush}"/>
<Button Grid.Row="4" Content="Backgroundcolor from external library using regular key"
Background="{StaticResource {ComponentResourceKey TypeInTargetAssembly={x:Type rlib:DummyClass}, ResourceId=myLibBrush}}"/>
</Grid>
</GroupBox>
Above definitions result in following visuals:
<img height="112" src="992230/ComponentResourceKeyExampleV2.png" width="398" />
What is going on here? The first button is a common in-application resource reference and because the resource is defined in the resource section of a parent scope this will succeed. After all, this is what we have been doing until now. Next are two ComponentResourceKey
s defined in another library. These also succeed without any problem. Notice that, allthough we use the same ResourceId
twice, because we use a different TypeInTargetAssembly
WPF handles these as two different keys! The last two will compile but fail on execution with a message similar to following: "Cannot find resource named 'myLibBrush'. Resource names are case sensitive." The first one was to be expected: WPF has no way of determining in what library to search. The second may come a bit as a surprise: after all we are telling WPF where to look by providing a TypeInTargetAssembly
. But WPF looks for a key of an identical class, thus you must also define the key in de library as a ComponentResourceKey
!
There is one more catch here: in preparing for this article I created an ordinary .NET library project (NOT a WPF control library), put in the classes, defined the Generic.xaml, referenced it from my demo application and ... noticed this didn't work! What was happening here? Will, apparently this Theme/Generic.xaml stuff doesn't "just work": there is no free lunch.
What makes WPF look for this file is following attribute definitions in the AssemblyInfo.cs file:
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly )]
This also means that anything defined outside the Theme/Generic.xaml file is NOT available, even when using the ComponentResourceKey
:
Let's say we have a file Dictionary1.xaml in the library project:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Resources.ResourceLib">
-->
<SolidColorBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:DummyClass}, ResourceId=UnreferencableComponentLibBrush}" Color="Red"/>
</ResourceDictionary>
If we use the ComponentResourceKey
we will get a similar runtime exception as above:
<GroupBox Header="Resources from external libraries">
<Button Grid.Row="5" Content="Backgroundcolor from external library using ComponentResourceKey only in the client" Background="{StaticResource {ComponentResourceKey TypeInTargetAssembly={x:Type rlib:DummyClass}, ResourceId=UnreferencableComponentLibBrush}}"/>
</GroupBox>
You may remember from the Styles article that it sometimes wasn't necessary to specify a Key: if you specified a TargetType
on the Style
, then you didn't need the x:Key
. How is this possible? Turns out there is a DictionaryKeyProperty
which accomplishes this.
If you use a .NET decompiler and look at the Style
class, you'll see following code: (truncated for brevety)
namespace System.Windows
{
[ContentProperty("Setters")]
[DictionaryKeyProperty("TargetType")]
[Localizability(LocalizationCategory.Ignore)]
public class Style : DispatcherObject, INameScope, IAddChild, ISealable, IHaveResources, IQueryAmbient
{
}
}
As you can see, the class definition has an attribute DictionaryKeyProperty
which gives the property that can be used as a Key
in a dictionary like the resources are. Now, you might look at what other types this attribute is applied to and think you will be able to use the same technique there. Unfortunately, there is more to this then just this attribute. To make a long story short: as this guy on StackOverflow found out, this is only possible for following types:
Following types have the attribute defined, but do not support this syntax:
Referencing Resources in WPF: your options are...
Concepts
When referencing Resources
in XAML you'll mostly see the StaticResource
markup extension used. It will suffice in most scenarios, but every now and then it will not work. For example, if you want to reference a Resource
not allready known at the time of its use then this kind of referencing will not work. For those cases you can use the DynamicResource
markup extension.
How to do it?
We allready saw how to use the StaticResource
markup extension in the Basic Resources in WPF section. But what exactly does this mean?
Let's try a few things.
A first case in which the StaticResource
does not function and we have to replace it by a DynamicResource
reference is in the case of a forward reference. The following example is a bit artificial, but let's say that for some reason the Resource
definition follows the referencing of the resource, like in:
<GroupBox Name="DynamicDemos" Header="Dynamic Resource examples">
<GroupBox.Resources>
<local:MyCustomClass x:Key="BoundAsStaticResource" StringProperty="BoundAsStaticResource" IntegerPoperty="1" />
<local:MyCustomClass x:Key="BoundAsDynamicResource" StringProperty="BoundAsDynamicResource" IntegerPoperty="1" />
</GroupBox.Resources>
-->
<GroupBox Grid.Row="5" Header="Late defined resources">
<Grid>
<Button Content="{StaticResource LateDefinedResource}" />
<Button Content="{DynamicResource LateDefinedResource}" />
</Grid>
<GroupBox.Resources>
<local:MyCustomClass x:Key="LateDefinedResource" StringProperty="LateDefinedResource" IntegerPoperty="1" />
</GroupBox.Resources>
</GroupBox>
</GroupBox>
If you try to execute this example, it will fail with an exception stating "Provide value on 'System.Windows.StaticResourceExtension' threw an exception" with an InnerException of "Cannot find resource named 'LateDefinedResource'. Resource names are case sensitive.". If you put the Button
with the StaticResource
reference in comment (like in the sample code) or move the GroupBox.Resources
section before this Button
it will succeed.
The problem is of course that the Resource
is not yet known when the Button
is instantiated
Somethinig similar happens when you use a ResourceKey in a custom control, while the ResourceKey is not yet defined:
<UserControl x:Class="Resources.UserControlDynResourceDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Button with dynamic bound backgroundcolor" Background="{DynamicResource redColor}" />
<Button Grid.Row="1" Content="Button with static bound backgroundcolor" Background="{StaticResource redColor}" />
</Grid>
</UserControl>
<GroupBox Name="DynamicDemos" Header="Dynamic Resource examples">
<GroupBox.Resources>
<SolidColorBrush x:Key="redColor" Color="Red"/>
</GroupBox.Resources>
-->
<GroupBox Grid.Row="6" Header="Custom control with (then) undefined resourcekey">
<local:UserControlDynResourceDemo />
</GroupBox>
</GroupBox>
It will fail with the similar exception "Provide value on 'System.Windows.StaticResourceExtension' threw an exception" with an InnerException of "Cannot find resource named 'redColor'. Resource names are case sensitive.". If you put the Button
with the StaticResource
reference in comment (like in the sample code) it will succeed.
Another use case for the DynamicResource
markup extension is when the Resource
itself can change during the lifetime of the application
<GroupBox Name="DynamicDemos" Header="Dynamic Resource examples">
<GroupBox.Resources>
<local:MyCustomClass x:Key="BoundAsStaticResource" StringProperty="BoundAsStaticResource" IntegerPoperty="1" />
<local:MyCustomClass x:Key="BoundAsDynamicResource" StringProperty="BoundAsDynamicResource" IntegerPoperty="1" />
<SolidColorBrush x:Key="redColor" Color="Red"/>
</GroupBox.Resources>
-->
<Button Grid.Row="0" Content="{StaticResource BoundAsStaticResource}"/>
<Button Grid.Row="1" Content="Change the instance of the resource" Click="DynDemoButtonChangeInstanceStat_Click" />
<Button Grid.Row="2" Content="{DynamicResource BoundAsDynamicResource}"/>
<Button Grid.Row="3" Content="Change the instance of the resource" Click="DynDemoButtonChangeInstanceDyn_Click" />
<Button Grid.Row="4" Content="Change a property of the resource" Click="DynDemoButtonChangeProperty_Click" />
</GroupBox>
If we change the Resource
which is bound through the StaticResource
markup extension, then nothing happens: because we used this markup extension we can not change the value anymore.
private void DynDemoButtonChangeInstanceStat_Click(object sender, RoutedEventArgs e)
{
DynamicDemos.Resources["BoundAsStaticResource"] = new MyCustomClass() { StringProperty = "BoundAsStaticResourceV2", IntegerPoperty = 1 };
}
If we change the Resource
which is bound through the DynamicResource
markup extension, then the visuals do change to reflect the new Resource
instance:
private void DynDemoButtonChangeInstanceDyn_Click(object sender, RoutedEventArgs e)
{
DynamicDemos.Resources["BoundAsDynamicResource"] = new MyCustomClass() { StringProperty = "BoundAsDynamicResourceV2", IntegerPoperty = 1 };
}
This effectively acts like the Resource
is applied through a Binding
. If you are familiar with Binding
s, you may wonder what would happen if we change just a single property of our class, which does implement INotifyPropertyChanged
:
private void DynDemoButtonChangeProperty_Click(object sender, RoutedEventArgs e)
{
var currentInstance = DynamicDemos.Resources["BoundAsDynamicResource"] as MyCustomClass;
currentInstance.StringProperty = "BoundAsDynamicResourceChangePropertyV3";
}
As you can see, nothing is happening: only when you change the full Resource
instance does the reference get updated.
All the above definitions will result in following visuals:
<img height="236" src="992230/ResourceChangeInCodeAnimatedV2.gif" width="404" />
Re-using Resources in WPF
Concepts
Allthough a ResourceDictionary
is an enabler of re-use, you may be willing to re-use the ResourceDictionary
itself also. For this we have the MergedDictionaries
property of a ResourceDictionary
.
By using the MergedDictionaries
property we can reference another xaml file defining a ResourceDictionary
. I will show you the exact syntax in the following section, but think about this first please: referencing it from somewhere is one thing, but where does the target of the reference reside? Turns out you have several options here:
- Compiled in the project (either application or library) itself
- Compiled in another project (which then will typically be a library project)
- On your harddisk in the application folder or a subfolder of it
How to do it?
The most basic case is when you have a xaml resourcefile compiled in your application, which you want to share on multiple forms:
We have a file "SomeResourceDictionary.xaml" in our project with following content:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="redInSomeResourceDictionary" >Red</SolidColorBrush>
<SolidColorBrush x:Key="yellowInSomeResourceDictionary" >Yellow</SolidColorBrush>
</ResourceDictionary>
Mind the words "compiled in" above: this means that the xaml file is embedded into your binaries (typically the application exe or the library dll). Therefore we must also specify the Build Action in the files project properties:
<img height="248" src="992230/XamlFileBuildActionPageV2.PNG" width="406" />
If we want to use the resources defined in this file, then we use following:
<GroupBox Header="Mergedresourcedictionary">
<GroupBox.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SomeResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</GroupBox.Resources>
<Button Grid.Row="1" Content="Background color comming from merged resource in the GroupBox" Background="{StaticResource yellowInSomeResourceDictionary}" />
</GroupBox>
Above definitions result in following visuals:
<img height="34" src="992230/BasicMergedDictionariesExampleV2.PNG" width="394" />
You may not have noticed form the above, but we're actually using a relative URI here. This means the xaml file is being searched for in the same folder as the xaml file referencing it. Let's apply this to a window defined in a subfolder in our project.
Suppose we have following structure in our project:
- ProjectFolder
- ControlFolder
- OtherWindow.xaml
- SomeResourceDictionary.xaml
- SomeResourceDictionary.xaml
Let me draw your attention to the fact that we have two equally named files here in different folders. Now, for the content of these files:
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="redInSomeResourceDictionary" >Red</SolidColorBrush>
<SolidColorBrush x:Key="yellowInSomeResourceDictionary" >Yellow</SolidColorBrush>
</ResourceDictionary>
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="redInSomeResourceDictionary" >Blue</SolidColorBrush>
</ResourceDictionary>
<Window x:Class="Resources.ControlFolder.OtherWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="OtherWindow" Height="300" Width="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<StackPanel.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SomeResourceDictionary.xaml" />
<ResourceDictionary Source="/ResourceFolder/Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</StackPanel.Resources>
<Button Content="Button with Backround from entry in Absolute SomeResourceDictionary.xaml" Background="{StaticResource redInSomeResourceDictionary}" />
<Button Content="Button with Backround from entry in Absolute ResourceFolder/Dictionary1.xaml" Background="{StaticResource magentaInResourceFolderDictionary1}" />
</StackPanel>
<StackPanel Grid.Row="1">
<StackPanel.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SomeResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</StackPanel.Resources>
<Button Content="Button with Backround from entry in Relative SomeResourceDictionary.xaml" Background="{StaticResource redInSomeResourceDictionary}" />
</StackPanel>
</Grid>
</Window>
The above definitions will result in following visuals:
<img height="108" src="992230/MergedDictionariesAbsAndRelReferenceExampleV2.PNG" width="452" />
There is a caveat here when applying this in a library's Themes/Generic.xaml file. Apparently it is not possible to use these abbreviated relative or absolute URI's in the Generic.xaml of a library project. There we have to use what is called a Pack URI. Which takes us to the following paragraph:
It is also possible to reference XAML files compiled in other assemblies using what is called a Pack URI. A complete description of the Pack URI scheme is beyond the scope of this article, however there are some good resources on the internet.
We have a file "Dictionary1.xaml" in our library project Resources.XamlResourceLib with following content:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="blueInXamlLibResourceDictionary" >Blue</SolidColorBrush>
<SolidColorBrush x:Key="yellowInXamlLibResourceDictionary" >Yellow</SolidColorBrush>
</ResourceDictionary>
If we want to use the resources defined in this file, then we use following:
<GroupBox Header="Mergedresourcedictionary">
<GroupBox.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SomeResourceDictionary.xaml"/>
<ResourceDictionary Source="pack://application:,,,/Resources.XamlResourceLib;component/Dictionary1.xaml"/>
<ResourceDictionary Source="ExternalDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="redColor" Color="Red"/>
</ResourceDictionary>
</GroupBox.Resources>
<Button Grid.Row="2" Content="Background color comming from merged resource with library source in the GroupBox" Background="{StaticResource blueInXamlLibResourceDictionary}" />
<Button Grid.Row="3" Content="Background color comming from merged resource with external source in the GroupBox" Background="{StaticResource externalBrush}" />
</GroupBox>
The above definitions will result in following visuals:
<img height="53" src="992230/MergedDictionariesPackUriReferenceExampleV2.PNG" width="482" />
As you can see the syntax is very similar to what we used for referencing a XAML file in the application itself. The only things which differs is the string used for referencing the external XAML file. And this is thanks to this special Pack URI style string. There is one more thing to notice here: if you look in the AssemblyInfo.cs file in the project you will notice I commented out the ThemeInfo code and the referencing still works!
This makes the biggest difference with the ComponentResourceKey
which also allows referencing resources in library projects: if you use the MergedDictionaries
in combination with a Pack URI, you reference the XAML file, where when using the ComponentResourceKey
you reference the resource itself. Notice how with a ComponentResourceKey
we didn't have to include anything: simply using the ComponentResourceKey
was sufficient to access the resource in the library project. Notice also how with using ComponentResourceKey
we had to define the resource in the Theme/Generic.xaml file. Anything defined outside this file is not accessible as demonstrated above.
Now, suppose you want to be able to change some resources, like colors, without the need to recompile your application. Because until now the Build Action of our ResourceDictionary
xaml files was set to Page
, the file got compiled into our binaries. But if we change the Build Action to Content
, then the xaml file will not get compiled into our binaries but we must copy it ourselfs next to the binary or set the "Copy to Output Directory" property of the file to one of the following:
- Copy always
- Copy if newer
<img height="238" src="992230/XamlFileBuildActionContentV2.PNG" width="293" />
How to apply this in practice you can see in the above example with the ExternalDictionary.xaml file and the second button.
But all this merging comes with a new problem: what happens if we merge dictionaries containing equally named (or key-ed if you like) resources? Let's find out.
We have the following XAML resourcefiles:
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="commonKeyDictionary" >Red</SolidColorBrush>
<SolidColorBrush x:Key="uniqueKeyIn1Dictionary" >Yellow</SolidColorBrush>
</ResourceDictionary>
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="commonKeyDictionary" >Blue</SolidColorBrush>
<SolidColorBrush x:Key="uniqueKeyIn2Dictionary" >Green</SolidColorBrush>
</ResourceDictionary>
As you can see, we have in each file an equally named entry: x:Key="commonKeyDictionary"
First, what happens if we merge two dicionaries into a third dictionary, and the first two have overlapping Key
s?
<GroupBox Header="Mergedresourcedictionary: Equally named keys and Ordering: Case 1">
-->
<GroupBox.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="EqNamedKeyDictionary1.xaml"/>
<ResourceDictionary Source="EqNamedKeyDictionary2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</GroupBox.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Button with Background from equally named keys" Background="{StaticResource commonKeyDictionary}" />
<Button Grid.Row="1" Content="Button with Background from unique key 1" Background="{StaticResource uniqueKeyIn1Dictionary}" />
<Button Grid.Row="2" Content="Button with Background from unique key 2" Background="{StaticResource uniqueKeyIn2Dictionary}" />
</Grid>
</GroupBox>
The above definitions will result in following visuals:
<img height="89" src="992230/MergedDictionariesMergeOrdering1V2.PNG" width="397" />
Next, what happens if we merge a dicionary into another dictionary, and they have overlapping Key
s?
If we merge first and then define the new resource into the dictionary, following happens:
<GroupBox Header="Mergedresourcedictionary: Equally named keys and Ordering: Case 2">
-->
<GroupBox.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="EqNamedKeyDictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="commonKeyDictionary" Color="Blue"/>
</ResourceDictionary>
</GroupBox.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Button with Background from equally named keys" Background="{StaticResource commonKeyDictionary}" />
<Button Grid.Row="1" Content="Button with Background from unique key 1" Background="{StaticResource uniqueKeyIn1Dictionary}" />
</Grid>
</GroupBox>
The above definitions will result in following visuals:
<img height="67" src="992230/MergedDictionariesMergeOrdering2V2.PNG" width="397" />
But if we first define the resource in the dictionary and then merge another dictionary, following happens:
<GroupBox Header="Mergedresourcedictionary: Equally named keys and Ordering: Case 3">
-->
<GroupBox.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="commonKeyDictionary" Color="Blue"/>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="EqNamedKeyDictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</GroupBox.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Button with Background from equally named keys" Background="{StaticResource commonKeyDictionary}" />
<Button Grid.Row="1" Content="Button with Background from unique key 1" Background="{StaticResource uniqueKeyIn1Dictionary}" />
</Grid>
</GroupBox>
The above definitions will result in following visuals:
<img height="64" src="992230/MergedDictionariesMergeOrdering3V2.PNG" width="397" />
This is exactly the same as above: so it looks like no mather in what order you define the merging, it is always applied first en thus overridden by any following equally named/key-ed values!
Conclusion
That's it for resources. But this isn't everything: most of the above should be seen in the light of Styles. Next will be a second intermezzo on Bindings.
Version history
- Version 1.0: Initial version
- Version 1.1: Following changes:
- Added reference to other articles the series