Use initParams to swap ResourceDictionaries and load a Theme in Silverlight 3

I’ve been looking for a way the use themes just swapping ResourceDictionaries (RD’s) for some time. Now, with Silverlight 3 being able to use MergedDictionaries, this becomes possible.

It is Blendable, but you can only use RD’s with Build Action Resource and not Content. This is annoying enough to give it another go another time. You can use the workaround of using a the RD’s with build action as Resource for use with Blend only. You’ll have to change it back before you distribute your app. Want you ultimately want is to address the RD’s as Resource…

This method can help you when you:

  • want to load a theme once on application startup
  • want to set the theme as an initParam on the Silverlight plug-in
  • don’t want to use implicit styling: you’ll need two or more RD’s with identically named Styles and Resources

Restrictions of this method are:

  • Blendable using workarounds
  • You have to use one Default theme (which can have any name you want).
  • You cannot dynamically update the theme: you can set a certain theme only once at application startup.

If this is the method for you, this is what you need to do:

  • Create ResourceDictionaries for the Default theme and for one or more other themes that you want to load in the root of your Silverlight project.
  • Use x:Keys for Styles and Resources and keep them the same between RD’s.
  • Set the Build Action for these RD’s to CONTENT in Visual Studio or by editing your .CSPROJ file by hand.
  • Insert an initParam in the <Object> tag in your startup file like this:

[sourcecode language=”XML”]

<param name="initParams" value="Theme=TheTheme" />
[/sourcecode]

  • Add a Default MergedDictionary in App.xaml:

[sourcecode language=”XML”]

<Application.Resources> 
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Default.xaml"/>
        </ResourceDictionary.MergedDictionaries>

[/sourcecode]

  • Declare a private currentTheme variable in App.xaml.cs

[sourcecode language=”XML”]

public partial class App : Application
{
    private string currentTheme = "Default";
    …

[/sourcecode]

  • Set the Default theme, get the initParam, make an URL to the filename and create and RD from that. Then add the RD to the MergedDictionaries:

[sourcecode language=”XML”]

private void Application_Startup(object sender, StartupEventArgs e)
{

    //set the current theme to the currentTheme variable
    string currentThemeName = string.Format("{0}.xaml", currentTheme);

    //get the theme name from the initParam
    string themeParameter = e.InitParams["Theme"];

    if (!string.IsNullOrEmpty(currentThemeName))
    { 
        //set the current theme to the theme parameter and create filename
        currentThemeName = string.Format("{0}.xaml", themeParameter);

        //create an URL from the currentThemeName
        Uri themeUrl = new Uri(currentThemeName, UriKind.RelativeOrAbsolute);

        try
        {
            //create a RD and set the source to the URL
            ResourceDictionary themeDictionary = new ResourceDictionary
            {
                Source = themeUrl
            };

            //Add the RD as a MergedDictionary
            Application.Current.Resources.MergedDictionaries.Add(themeDictionary);
        }
        catch (Exception ex)
        { 
            //communicate theme doesn’t exist
            throw; 
        } 
    }

    this.RootVisual = new MainPage();

}

[/sourcecode]

Many thanks to Tony Tromp and Michaud Venant for helping me out with the code.

The error “Error HRESULT E_FAIL has been returned from a call to a COM component” occurs when you forgot to change the build properties of the ResourceDictionaries to CONTENT. You can do this in Visual Studio in de Solution Explorer with the properties of the specific XAML files. This error also occurs when you maken a mistake in the name of the Theme in the initParam by messing up the quotes for example.

When you make a mistake in the Styles or the Resources used by the Styles you’ll get another error: Cannot find a Resource with the Name/Key AppBg [Line: y Position: x]. Actually if the style is OK in the Default theme it wil show that. Happily this error is easier to track and to fix, but a small error in your RD’s may have unpredictable results, so make sure they are sound.

I’ve been working with Themes from initParams for weeks. You can use Blend when you set the build action in VS to RESOURCE and restart Blend. Blend should show all the RD’s as Resources in the Resources Panel and you can edit them in the Blend artboard. You can even build in Blend and change the Theme using the initParam in the startup file. Make sure the startupfiles are the same in VS and Blend :). Before you deliver your project make sure you set the build action back to CONTENT in VS to make the theme system from initParams kick in.

I’ve placed working code on my SkyDrive.

Njoy!