Sharing resources and styles between projects in Silverlight

I have lately been working on a somewhat larger Silverlight application for a client here in New Zealand. The application is being built using PRISM, which means that it is loaded in a composite/ modular way. And with this modular/composite loading, being able to share resources between different modules becomes pretty interesting as it makes it easier to get all modules to share the same look and feel. So this got me to rehash a topic I have already talked about before, merged dictionaries.

This is obviously not a PRISM only thing to do, but it often becomes a little more sought after when the application starts getting spread out over several different projects… Luckily, it is very easy to share resources between projects in Silverlight.

The first thing to do is to create a class library project in Visual Studio, which will create an almost empty project for you. All you have to do to make it completely empty is to delete the Class1.cs file.

After you have created a completely empty project, it is time time to create some resources. This is easiest to do by adding a new item to the project, and selecting the “Silverlight Resource Dictionary” item. This will create a new XAML file with a ResourceDictionary as the root element.

I’m working with VS2010 and Silverlight 4. In earlier versions, I believe the “Silverlight Resource Dictionary” item was missing, and we had to create our own, which isn’t terribly hard, but still annoying. Just create a new XML file and add the following XML to it

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

</ResourceDictionary>

The above is exactly the same XAML that gets create for us automatically when adding a new “Silverlight Resource Dictionary”.

If you look at the build action for the new XAML file, you will notice that it is set up to be built as a “Page”. This makes it fairly easy to reference it from XAML, and thus fairly easy to add it as a “merged dictionary” to your pages or application.

There are 2 interesting things to mention about this. The first one is the idea of a “merged dictionary”. All resource dictionaries in Silverlight has a property called “MergedDictionaries”. You normally don’t see it as you generally use a shorthand syntax for adding resources to the dictionaries as follows

<UserControl x:Class="ExternalResourceConsumer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<ControlTemplate x:Key="MyButtonTemplate">
<Grid Background="Goldenrod">
<ContentPresenter Margin="10" />
</Grid>
</ControlTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button Template="{StaticResource MyButtonTemplate}" Content="Hello World" />
</Grid>
</UserControl>

But if you extend that syntax and specifically write out the ResourceDictionary element, you will see that you get access to the MergedDictionaries property as follows

<UserControl x:Class="ExternalResourceConsumer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary ... />
</ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="MyButtonTemplate">
<Grid Background="Goldenrod">
<ContentPresenter Margin="10" />
</Grid>
</ControlTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button Template="{StaticResource MyButtonTemplate}" Content="Hello World" />
</Grid>
</UserControl>

By adding additional ResourceDictionary elements to the MergedDictionaries, we can merge resources from several resource dictionaries into one. The ResourceDictionary element contains only one interesting attribute, Source. The Source attribute expects a Uri to another resource dictionary. And if the other resource dictionary is in the same project, this Uri can simply be “MyResourceFolder/MyResource.xaml” or whatever location you have placed the resource file in.

But when we have a resource that is in a separate assembly, we get into the second interesting thing I talked about before. Because this requires us to do 2 things to get it to work. First of all, we obviously need to add a reference to the assembly that contains the resource dictionary. Otherwise we can obviously not reference it. Next, we need to use a special Uri when setting the Source. The Uri format is “/{assembly name};component/{path to resource}”.

So if we move the beautiful MyButtonTemplate to an external resource dictionary called MyResource.xaml and is located in an assembly called ExternalResourceDemo the XAML looks like this

<UserControl x:Class="ExternalResourceConsumer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ExternalResourceDemo;component/MyResource.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button Template="{StaticResource MyButtonTemplate}" Content="Hello World" />
</Grid>
</UserControl>

That’s all there is to it. This makes it very easy to place all of our common resources (styles, templates, colors, fonts etc) in external libraries that can then just be referenced to give you access to them. This makes it a lot simpler to get a consistent look and feel across multiple applications or modules.

And you can obviously, especially obvious if you look at the name of the property name, add as many merged dictionaries as you want. Just add more ResourceDictionary elements.

Merged dictionaries work well with default styles as well. Just give your style a target type and skip the x:Key and you are good to go as along as it is set as a MergedDictionary. A good idea is also to merge the external dictionaries in the App.xaml resources when possible, as it causes less overhead and duplication of information.

And even though there is very little code to even look at, I have made a solution available for down load here: ExternalResourceDemo.zip (42.46 kb)

Cheers!

Comments (8) -

Great post.

But I think there's a problem with this approach.
Correct me if I'm wrong, but I read somewhere that each time a UserControl references a ResourceDictionary a copy of the ResourceDictionary is made for the control instance.

If you use the control in a list or something, it means for each of them a copy of the ResourceDictionary will be created and this will result in a bad memory consumption.

Hi Mohammadreza!
I have read that as well, and I know that there are issues with memory consumption. At least there used to be, so I assume it is still there.
But I guess you have to make a judgement call whether or not managability and ease of development is more important than memory consumption.
If you don't want to put it in the usercontrol as it will crate loads of instances, you could merge it in to the app.xaml and get rid of that problem. Unfortunately this will take some work in a modul based system though...
Cheers,
Chris

How about using Theme control instead?

I think that if you have a look at the Theme control code, it does quite a lot of work. You might gain a little memory, but I think you might load up more CPU cycles.
And it is also for styles. There are a lot of other resources that you might want to share.
And finally, I don't see how the Theme control would be a lot better when doing composite apps. You would still need to place the styles in a resource that is merged in to get it to work in the individual pieces...

Another alternative is to do the following:
1) Have a separate styles-project (as you do)
2) Include a reference to it in the App.xaml merged dictionaries
3) Add a reference to it in consuming projects
4) Just start using it in the XAML in the consuming projects.
When you open a view in Blend 4 that references styles that don't exist in that project, Blend picks that up and asks you if you want to select another resource library for design time support and will modify your project accordingly.
At run time it all works fine since the dictionary is merged into your app.xaml which is universally available. This way you have your design time support, but still only have a single merged dictionary being created so there's no wasted resources.

Hi Phil!
Thanks for joining the discussion...
Your comment is very valid and good. This is absolutely an option, but once again we are back to only styles.
Also, how does VS handle Blends use of external style asemblies? Just asking as I haven't tried it... Smile
Cheers,
Chris

There shouldn't be any restriction on just using styles - it should work with any resource that you might put in a merged dictionary.

Your point about VS' use of Blend external style assemblies is valid - it doesn't recognise them. This has never been a problem for me though since I tend to always stay in XAML view in VS and use Blend for styling.

bahman 09125796851 6/3/2012 10:46:49 AM

i am using a app for any module in  my application

Comments are closed