Easy Composable Theming in WPF (.NET 4)

In .NET 4 there is a new namespace to be aware of and that is System.ComponentModel.Composition. Also known as MEF (Managed Extensibility Framework). This post outlines a very simple way to take advantage of the new Composition tools to add simple themeing to your WPF applications.

http://cid-dfcd2d88d3fe101c.skydrive.live.com/embedrowdetail.aspx/blog/justnbusiness/ComposableThemes.zip

 

 image image

 

Step 1: Create a shared project

The shared project should define interfaces and classes that are needed by both the application and each of the composable parts. In this sample application I have a project called Core which defines a single interface. All other projects reference this core project.

IThemeService.cs

namespace Core
{
    public interface IThemeService
    {
        ResourceDictionary Theme { get; }
    }
}

 

Step 2: Create Theme Projects

A theme project will reference the core project and implement the IThemeService interface.

ThemeXService.cs

using System.ComponentModel.Composition;
using Core;

namespace ThemeX
{
    [Export("ThemeX", typeof(IThemeService))]
    internal class ThemeXService : IThemeService
    {
        public System.Windows.ResourceDictionary Theme
        {
            get { return new Themes.Generic(); }
        }
    }
}

Additionally this project will contain a single resource dictionary class with all of our named resources defined.

Themes\Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:ThemeX"
    x:Class="ThemeX.Themes.Generic">

    <system:String x:Key="ThemeTitle">Theme X!</system:String>
    <SolidColorBrush x:Key="ThemeBrush" Color="PowderBlue" />
</ResourceDictionary>

Themes\Generic.cs

using System.Windows;

namespace ThemeX.Themes
{
    public partial class Generic : ResourceDictionary
    {
        public Generic()
        {
            this.InitializeComponent();
        }
    }
}

 

Step 3: Create Your Application and Load a Theme

In this application I am loading the first theme at startup then changing the theme based on a selection of a ComboBox. In a real application it might make more sense to store the name of the theme in user settings somehow and load the theme using that value.

Here is the very simple window that is to be themed.

MainWindow.cs

<Window 
    x:Class="ComposableThemes.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:ComposableThemes"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ComboBox ItemsSource="{Binding ThemeNames, Source={x:Static app:App.Current}}"
                SelectionChanged="ComboBox_SelectionChanged" />
        <Border 
            Grid.Row="1"
            Margin="25"
            BorderThickness="2"
            CornerRadius="5"
            Background="{DynamicResource ThemeBrush}">
            <TextBlock 
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="{DynamicResource ThemeTitle}" />
        </Border>
    </Grid>
</Window>

Notice the {DynamicResource …} references. These will give warnings in your design view because they cannot be resolved. They will be resolved at runtime because we will be loading the resource dictionary object created in step 2 at startup.

Here is how you can load an exported object and load a resource dictionary at runtime.

App.xaml.cs

public void SetTheme(string themeName)
{
    string location = Path.GetDirectoryName(typeof(App).Assembly.Location);
    using (var addinCatalog = new DirectoryCatalog(location))
    {
        using (CompositionContainer container = new CompositionContainer(addinCatalog))
        {
            if (!string.IsNullOrEmpty(themeName))
            {
                IThemeService theme = container.GetExport<IThemeService>(themeName).Value;
                if (theme != null)
                {
                    var themeDictionary = theme.Theme;
                    if (themeDictionary != null)
                    {
                        this.Resources.MergedDictionaries.Add(themeDictionary);
                    }
                }
            }
        }
    }
}

In this method we are using the applications directory to look for composable parts (plug-ins). A catalog is basically a container that loads assemblies and finds Exports / Imports for you. There is a very handy AggregateCatalog class that will allow you to have multiple directories or multiple assemblies or whatever type of catalog you want.

It’s useful to know that you can also use an Import attribute on a class to have the container automatically bind together the imported and exported parts. I find that to be a little more magical than I prefer so I did it this way instead.

Also, you can get a list of all theme names by inspecting the metadata of exported parts. Here is a snippet I used to populate my ComboBox with theme names:

App.xaml.cs

public IEnumerable<string> ThemeNames
{
    get
    {
        string location = Path.GetDirectoryName(typeof(App).Assembly.Location);
        using (var addinCatalog = new DirectoryCatalog(location))
        {
            foreach (var part in addinCatalog.Parts)
            {
                foreach (var definition in part.ExportDefinitions)
                {
                    var value = (string)definition.Metadata["ExportTypeIdentity"];
                    if (value == typeof(IThemeService).FullName)
                    {
                        yield return definition.ContractName;
                    }
                }
            }
        }
    }
}

 

Conclusion

All in all, I am very happy with the new Composition framework. It’s very easy to use and because of that, very powerful. One thing you should keep in mind however is that there is no magic here when it comes to loading and unloading assemblies, if you use a directory catalog it will load all assemblies in that directory into your current AppDomain and you cannot unload them (as is normal).

For another example of MEF in action check out my MetaTask in the MetaSharp source code. This is how all pipelines are loaded in MetaSharp.

Expression Photoshop Importer – By Soma

In his latest blog post Soma did a feature preview of the new version of Expression Web and the Photoshop importer gets an honorable mention.

http://blogs.msdn.com/somasegar/archive/2009/06/05/expression-web-3.aspx

With Expression Web 3, we have significantly improved capabilities and workflow when working with Photoshop files. When you import a PSD file, you can choose just the layers you want to import in to your website. You can save the layers as JPG, PNG, or GIF and scale the image* before saving the individual layers in Expression Web 3. If the source file has changed, Expression Web 3 prompts you to update it.

*Actually, you can’t scale the imported image in this window but you can zoom in or out of the preview to view it scaled and you can adjust the quality if you select jpeg. Once it is imported you can scale the dimensions of the image with your image editor of choice.

One interesting thing about the Photoshop Importer is that it is an expression wide feature rather than simply for Blend. Specifically it will be a feature available in Blend, Web and Design for this release. In each application the importer will have slightly different behavior. For example in the window shown here you can see there are options for Encoding and Quality as well as a field displaying the estimated size. When you import a Photoshop file in Web you will get a single flattened image rather than multiple resources like in Blend and Design.

Designing the importer so that it could be extended to support different feature sets for each application was one of my main roles so far. It was a lot of fun and I learned a lot!

Derived Styles based on unnamed default Styles

This seems so obvious in retrospect but I just learned something very useful today so I thought I’d put it out there just in case anyone else wasn’t aware of this.

Specifically the problem occurs when you create a style in Xaml that is supposed to be the default style for a particular type of Control. For example:

<Style TargetType="{x:Type ComboBox}">
</Style>

This will allow you to create a style for all ComboBoxes. If a ComboBox does not specify a specific Style it will get this one automatically. The tricky part comes when you want a single particular ComboBox to deviate from the style just a little bit. So typically what I do is create the main Style that is named then create the default Style to be based on that main one like:

<Style x:Key="ComboBoxBase" TargetType="{x:Type ComboBox}">
</Style>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ComboBoxBase}">
</Style>

Then if you make any further custom styles they can just be based on ComboBoxBase and change as necessary. While this works I just found a way that is potentially cleaner. It turns out that you can pass in a Type to the StaticResource instead of a key and get at the default Style for that Type. Very handy, like so:

<Style TargetType="{x:Type ComboBox}">
</Style>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
</Style>

How did I not know this sooner? This way you put all of your main styling into the official default Style, you also don’t need to remember the name of that default Style to use it.

Disclaimer: I haven’t tried this on Silverlight, only WPF.

Actipro has a WPF MGrammar syntax editor

http://www.actiprosoftware.com/Products/DotNet/WPF/SyntaxEditor/Default.aspx

I haven’t tried to use this yet but it seems pretty interesting.

SyntaxEditor is a powerful text editing control that is packed with features for efficient code editing, including syntax highlighting, line numbers, block selection, IntelliPrompt UI, split views, zooming, bi-di support, and much more. It has many of the same code editing features found in the Visual Studio code editor.

SyntaxEditor is built on top of our next-generation extensible text/parsing framework. While over 20 sample languages are available to get you started (such as C#, VB, XML, and more), custom language definitions can be developed and distributed with your applications as well. SyntaxEditor is designed for use in IDE (integrated development environment) applications, however there are many other applications out there than can take advantage of such a control.

A free add-on is included that integrates domain-specific language (DSL) parsers created using Microsoft Olso’s MGrammar with SyntaxEditor, allowing it to syntax highlight code based on the DSL parser.

Photoshop Import in Blend 3 – by Janete Perez

Janete Perez has created a great series of blog posts on importing Photoshop files into Expression Blend. I have been apart of the Expression team for almost a year now and I have been working on the Photoshop Import feature, so these posts are especially interesting to me.

http://blogs.msdn.com/janete/archive/2009/05/27/photoshop-import-in-blend-3.aspx