The XAML Resources in WPF and Silverlight can be a powerful tool but are often misunderstood and can cause lots of problems if not managed properly. The first point of confusion with Resources is that the term “resources” already has so many other meanings, including:
- Project items compiled as Resource
- Linked and embedded resources (resx files)
- Hardware resources, and specifically graphics in WPF
So first to clarify what we’re talking about here: XAML Resources are any .NET object that is declared in a ResourceDictionary. This is generally done in XAML but objects can also be added to a ResourceDictionary in code. ResourceDictionaries can be found on any FrameworkElement derived object (and also on Application) in the form of the Resources DependencyProperty.
Resources are typically accessed with the StaticResource or DynamicResource MarkupExtensions in XAML. I won’t get too much into the details of these except for a few key points. DynamicResource will update if the referenced Resource changes which can be useful but generally just causes extra overhead that can create performance and memory problems. StaticResource requires that the referenced Resource exist in the resource tree at load time. More on that later. StaticResource can cause compile-time errors for missing Resources while Dynamic will just use a default value until the Resource becomes available. StaticResource should always be your default choice unless you have a specific need for the behavior of Dynamic (Blend always uses Dynamic so be prepared to make lots of switches in what it generates).
Next, what is the Resource tree? WPF UI is inherently hierarchical. Each UI is made of a logical tree that is then used to render a visual tree. This means that each element in your UI has a clear parent structure with a single path up the hierarchy to the root Window element (or NavigationWindow, Page…) and then an Application object at the very top. The Resources property of each parent element is made available to every child element so that elements can access any Resource that is declared anywhere in its parent hierarchy. This makes it possible to share Resources between elements in specific areas while not requiring the memory and performance penalty of declaring them application-wide.
To get the maximum reuse benefits while not overly impacting performance, resources need to be carefully managed and organized. In addition to resources from the parent hierarchy, the MergedDictionary feature allows you to extract any Resource to a separate ResourceDictionary file that can then be imported to specific elements that need it. This can allow you to share XAML between elements at a lower level but it can also cause situations where multiple instances of a ResourceDictionary are created by elements under a common parent, consuming more memory and initialization time than if the ResourceDictionary were merged once into the parent.
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/Resources/Brushes.xaml"/> <ResourceDictionary Source="/MyCompany.MyApplication;component/Resources/Styles.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources>
Once you’ve created and structured your Resources you can get to the important part: actually using them. The most compelling reason to use a Resource is reuse but even in cases where a resource is used once they can still provide value by simplifying your XAML’s structure. Declaring things like templates and brushes as resources rather than inline can turn a deep hierarchy into a single line of XAML and can also introduce a descriptive name for a sub-section of XAML, making it more readable (it’s no coincidence that this sounds like refactoring methods). There are also cases (ItemsControl ItemTemplates in particular) where declaring things in-line can actually break them but simply moving them to resources will fix the problem.
The primary method of accessing a Resource is by Key. Keys are specified by the x:Key attribute on the element that is declared as a Resource. Keys are a required attribute for most types of resources and will cause compiler errors if missing. Static and DynamicResource references rely on Keys to find resources.
The other method of accessing resources instead relies on a type that is associated with a Style (TargetType) or DataTemplate (DataType). When no x:Key is specified on one of these types they are assigned an implicit key that will cause them to be applied to any object of the specified type in whose Resource tree they exist. This can be very powerful but also cause problems that are very hard to locate. For example, if someone creates a Style with TargetType of Button that sets Width to 100 and puts it in App.xaml, suddenly all Buttons application-wide will be fixed at 100 unless they have some other explicit setting for Width. Now someone trying to figure out why their Button text is getting cropped needs to figure out where that fixed width is coming from but gets no clues from the settings on the Button itself. It’s pretty easy to go check Window.Resources or App.xaml but now imagine the Style is located in one of 10 ResourceDictionary files that are merged into App.xaml. Implicit key resources should be used very carefully and be declared only in well known locations to avoid this sort of problem.
The first thing you should do when tracking down an issue with a named (explicit Key) Resource is to make sure the reference is using StaticResource rather than DynamicResource. This can often turn a mysterious run-time behavior into a compiler error, or in some cases a run-time exception, telling you immediately that the Resource doesn’t exist in the current Resource tree. If this happens it can most often be fixed simply by merging in a ResourceDictionary or just moving the Resource declaration itself.
If you find yourself getting into problems with name collisions (i.e. 10 different people created “ButtonStyle1” in Blend and spread them all over the app) it can help to actually diagram out the resource tree for the element in question (on paper or in your head) to find out which one is really being used. It starts at the top with App.xaml, then the Window or Page, then local parent controls’ resources, then the element itself. Inside each of these sections include any MergedDictionaries and anything in turn merged into those ResourceDictionaries. Once you can see the whole tree, whatever is declared last takes precedence: this goes for inside files as well as down the tree.
So if ButtonStyle1 is declared in Common.xaml (A) that is the first file merged into App.xaml but also as the last local Resource in App.xaml (B) and again in CustomStyles.xaml (C) merged into Window1.xaml then A and B are going to be ignored and C will be used. If you then rename C to ButtonStyle2, A will be ignored and B will be used. Using Visual Studio’s Find in Files is usually the quickest way to find name collisions or just track down lost resources (use x:Key=”MyResourceKey” to find declarations).