Managing logical resources
Logical resources may be of various types, such as brushes, geometries, styles, and templates. Placing all those resources in a single file such as App.xaml
hinders maintainability. A better approach would be to separate resources of different types (or based on some other criteria) to their own files. Still, they must be referenced somehow from within a common file such as App.xaml
so they are recognized. This recipe shows how to do just that.
Getting ready
Make sure Visual Studio is up and running.
How to do it...
We'll create an application that separates its resources across multiple files for convenience and manageability:
- Create a new WPF Application named
CH02.ManagingResources
. - We want to create a separate file that would hold (for example) brush resources. Right-click on the Project node in Solution explorer and select Add | ResourceDictionary…:
- In the Name box, type
Brushes.xaml
and click on Add. - A XAML editor is opened with a
ResourceDictionary
as a rootelementVisual
studio shows no design surface, because aResourceDictionary
is just a collection of any typed objects, not elements. Let's add one fancyBrush
:<LinearGradientBrush EndPoint="1,0" x:Key="brush1"> <GradientStop Color="Yellow" Offset="0" /> <GradientStop Color="Orange" Offset=".7" /> <GradientStop Color="DarkRed" Offset="1" /> </LinearGradientBrush>
- Open
MainWindow.xaml
. Add anEllipse
within theGrid
and set itsFill
to be the fancy brush:<Ellipse Fill="{StaticResource brush1}" />
Notice the designer shows nothing. We want
Brushes.xaml
to be somehow part of the logical resource search. - Open
App.xaml
. We need to merge external resource dictionaries into the main application dictionary. Add the following inside the<Application.Resources>
tag:<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Brushes.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
- Now open
MainWindow.xaml
. The designer should show the brush used correctly (if it doesn't, build the project to refresh the designer). Running the application should show the following:
How it works...
A ResourceDictionary
can incorporate other resource dictionaries using its MergedDictionaries
property (a collection). This means a ResourceDictionary
can reference as many resource dictionaries as desired and can have its own resources as well.
The Source
property must point to the location of the ResourceDictionary
. If that location is within a subfolder, that subfolder must be included. For example, if our Brushes.xaml
was under a logical folder named Resources, merging that into App.xaml
would look like as follows:
<ResourceDictionary Source="Resources/Brushes.xaml" />
There's more...
This idea can be also used to reference logical resources stored in other referenced assemblies. The Source
property would have to be based on the pack URI syntax. Suppose that Brushes.xaml
was placed in a class library within a Resources folder. The main application could merge it into another ResourceDictionary
as follows:
<ResourceDictionary Source="/MyClassLibrary;component/Resources/Brushes.xaml" />
Merging different resource dictionaries may cause an issue: two or more resources with the same keys that originate from different merged dictionaries. This is not an error and does not throw an exception. Instead, the selected object is the one from the last resource dictionary added (which has a resource with that key). Furthermore, if a resource in the current resource dictionary has the same key as the any of the resources in its merged dictionaries – it always wins out. Here's an example:
<ResourceDictionary> <SolidColorBrush Color="Blue" x:Key="brush1" /> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Resources/Brushes2.xaml" /> <ResourceDictionary Source="Resources/Brushes.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
With this markup, the resource named brush1
is a blue SolidColorBrush
because it appears in the ResourceDictionary
itself. This "overrides" any resources named brush1
in the merged dictionaries. If this blue brush did not exist, brush1
would be looked up in Brushes.xaml
first, as this is the last entry in the merged dictionaries collection.