Load Resource Dictionaries at Runtime in WPF
One of the really cool features of WPF is the ability to style controls. Styles in WPF can be grouped together into Resource Dictionaries. These resource dictionaries can then be loaded dynamically at runtime with just a small amount of Visual Basic or C# code. In Figure 1 you see an example WPF form that allows you to type in a XAML file name that contains a resource dictionary. The styles within this resource dictionary are applied to a Grid control. In this case, it is just a simple gradient fill, but the same concept will apply even if you had styles for all controls in your application.
Figure 1: Loading resources on the fly allows you to change styles at runtime
The Resource Dictionaries
A resource dictionary is nothing more than a collection of styles grouped together under a <ResourceDictionary> xaml tag. Each style can be keyed, or have just a TargetType attribute. The samples in this article are very simple and just have one Style that applies to a Grid control. The style will set the Background property of a Grid to a LinearGradientBrush of a certain set of colors. Below is the Green.xaml file.
<ResourceDictionary
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Grid"
x:Key="DynamicGridBrush">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Offset="0"
Color="LightBlue" />
<GradientStop Offset="0.65"
Color="LightGreen" />
<GradientStop Offset="1"
Color="White" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Next, is the Blue.xaml resource dictionary.
<ResourceDictionary
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Grid"
x:Key="DynamicGridBrush">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Offset="0"
Color="Blue" />
<GradientStop Offset="0.65"
Color="LightBlue" />
<GradientStop Offset="1"
Color="AliceBlue" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Now that you have the resource dictionaries defined you will now want to switch between them dynamically at runtime.
Setting the Style
In the form shown in Figure 1 you see a grid with a linear gradient. This is accomplished using the following code:
<Grid Style="{DynamicResource DynamicGridBrush}"
Height="180"
Width="250" />
Notice the use of the {DynamicResource DynamicGridBrush}. This code allows your XAML code to compile even if it can’t find the “DynamicGridBrush” at compile-time. The DynamicResource keyword says to look for this resource, but if you can’t find that resource, then just do not set the Style at this time. If after loading a new resource dictionary the style becomes available, then go ahead and apply the style.
Loading a Resource
To load a XAML file from disk, you will first need to open that file as a file stream. Next you can use the XamlReader class from the System.Windows.Markup namespace to load that file stream and cast it into a ResourceDictionary. You can then clear any old dictionaries that were previously loaded. Next you add to the MergedDictionaries collection of the current Window’s Resources property the dictionary you just loaded from disk.
C#
private void DynamicLoadStyles()
{
string fileName;
fileName = Environment.CurrentDirectory() +
@"\Dictionaries\" + txtXamlName.Text;
if (File.Exists(fileName))
{
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
// Read in ResourceDictionary File
ResourceDictionary dic =
(ResourceDictionary)XamlReader.Load(fs);
// Clear any previous dictionaries loaded
Resources.MergedDictionaries.Clear();
// Add in newly loaded Resource Dictionary
Resources.MergedDictionaries.Add(dic);
}
}
else
MessageBox.Show("File: " + txtXamlName.Text +
" does not exist. Please re-enter the name.");
}
VB.NET
Private Sub DynamicLoadStyles()
Dim fileName As String
Dim dic As ResourceDictionary = Nothing
fileName = Environment.CurrentDirectory & _
"\Dictionaries\" & txtXamlName.Text
If File.Exists(fileName) Then
Using fs As FileStream = New FileStream(fileName, _
FileMode.Open)
' Read in ResourceDictionary File
dic = CType(XamlReader.Load(fs), ResourceDictionary)
' Clear any previous dictionaries loaded
Resources.MergedDictionaries.Clear()
' Add in newly loaded Resource Dictionary
Resources.MergedDictionaries.Add(dic)
End Using
Else
MessageBox.Show("File: " + fileName + _
" does not exist. Please re-enter.")
End If
End Sub
To ensure that you can access the Green.xaml and the Blue.xaml files you need to add each of them to your Visual Studio solution in a folder called \Dictionaries. Next you need to click on each of these files in turn and bring up the Properties Window. Change the Build Action to “Content” and the Copy to Output Directory to “Copy Always”. This ensures that the Environment.CurrentDirectory plus the directory plus the file name will find the appropriate file at runtime.
Summary
Having the ability to load resource dictionaries at runtime can aid you a lot in changing the look and feel of a WPF application. You can have a set of styles that a user is allowed to choose from. You could then store their choice in a user table, and load that value when the application starts up. This allows you to customize each user’s experience with your application. You can control color, font size and many other attributes about a user’s experience with your user interface.
NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "WPF: Dynamically Load Resources" from the drop-down.
Good Luck With Your Coding,
Paul Sheriff
** SPECIAL OFFER FOR MY BLOG READERS **
Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".
Past Blog Content
Blog Archive
-
2015
-
2014 (18)
-
2013 (11)
-
2012 (19)
-
2011 (29)
-
2010 (19)
-
2009 (28)
-
2008 (0)
-
2007 (14)
-
2006 (6)