Saturday, September 20, 2014

PRISM & WPF Resource Dictionaries

The web is a great spot to get information, getting something useful out of the information can be another matter. At work, I've been spending a large amount of time working on a PRISM based modular WPF application. One of the things on my to-do list has been to figure out how to "correctly" structure resource dictionaries in a modular application.

The longer I work with WPF, the more I value the concept of blend-ability. One of the issues I found with several of the approaches to managing resource dictionaries is that they are compiled at run time, and at design time they aren't available. It's quite annoying to never know what anything is going to look like until the application runs.

After digging around on the net, I realized there was some contradicting advice. For example, one camp believes in cramming every thing into one dictionary, in the name of performance. The other camp believes in using a new resource dictionary for every style. I decided to go with organization over performance, knowing that if performance was an issue things can be restructured.

By piecing together blog posts, MSDN articles, and stackoverflow questions I arrived at a system that is working well.

Resources structure:
  • I found this blog post which outlines a folder structure for resource files, and goes by the style per resource dictionary rule.  After adding a new resource dictionary file into the folder structure, a reference to that file is added to the a merged dictionary referred to as the ResourceLibrary.  The ResourceLibrary is then referenced by all the project modules.  
  • In each PRISM module, a local resource dictionary could be merge into that modules App.xaml file for further specific module styling.
Design Time Blendability: 
  • This SO post contains the key to a very simple solution to allow resources to be visible in Blend and VS at design time. Leave the App.xaml in each PRISM module. If you've already deleted it, add one back into the project. In the App.xaml, use a merged dictionary and a Pack Uri to reference the resource dictionary in your shared infrastructure project.
 <Application x:Class="KNE.Athena.ProjectModule.App"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
   <Application.Resources>  
     <ResourceDictionary>  
       <ResourceDictionary.MergedDictionaries>  
         <ResourceDictionary Source= "pack://application:,,,/KNE.Athena.Infrastructure;component/ResourceDictionaries/ResourceLibrary.xaml"/>  
       </ResourceDictionary.MergedDictionaries>  
       <Style TargetType="{x:Type Rectangle}"/>  
     </ResourceDictionary>  
   </Application.Resources>  
 </Application>  

Note the interesting line inside the merged dictionary  <Style TargetType="{x:Type Rectangle}"/> 
I can't actually say for certain that it is necessary anymore, as I seem to have gotten this to work with out it, however it is there to fix a Microsoft bug...

Other Notes - BasedOn Styles need merged dictionary, and performance concerns:

After reorganizing my styles into the structure described in section one,  I was surprised to find that styles that made use of the BasedOn property where not correctly inheriting the styles.  The answer for this was occurring was of course on stackoverflow. When the base style and derived style are not defined in the same .xaml file, one must first merge in the style from the other resource dictionary.   Below you can see my AddButton is based on Button, and at the top of the file I merge in the button style.

 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
   <ResourceDictionary.MergedDictionaries>  
     <ResourceDictionary Source="../BaseControlStyles/ButtonStyle.xaml"/>  
   </ResourceDictionary.MergedDictionaries>  
   <Style x:Key="AddButtonStyle" BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">  
     <Setter Property="Content" Value="+" />  
     <Setter Property="Width" Value="25"/>  
     <Setter Property="Height" Value="25"/>  
     <Setter Property="ToolTip" Value="Add Item"/>  
   </Style>  
 </ResourceDictionary>  

Last, be aware that performance issues could arise.  Apperently in WPF, each time a control references a resource dictionary, a new instance of the resource dictionary is created. Repeatedly parsing many lines of xaml can have a negative performance impact.  This is another thing that I'm not sure if Microsoft has addressed, this blog post is several years old.  The fix outlined in this blog post is simple, but I have not yet found a need to implement it in my application.

1 comment:

  1. Thank you. This was exactly what I was looking for. The rest of the blog looks great as well.

    ReplyDelete