(X) Hide this
    • Login
    • Join
      • Say No Bots Generate New Image
        By clicking 'Register' you accept the terms of use .

Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1

(16 votes)
Andrej Tozon
>
Andrej Tozon
Joined Jan 22, 2009
Articles:   6
Comments:   6
More Articles
9 comments   /   posted on Jan 07, 2010
Tags:   mef , gallery , controls , andrej-tozon
This article is Part 1 of the series 'Honey, I MEF-fed the Silverlight Gallery (for Christmas)'

One of my early experiments with Bing Maps Silverlight Control eventually turned into a Halloween Live Gallery. This photo viewer application is based on the CircularPanel3D control from Expression Blend’s Wall3D sample that shipped with the product. It pulls geotagged Halloween photos from Flickr service and displays them in a 3D photo wall that can be rotated, zoomed in, etc. A detailed view of the photo includes a zoomable Bing Maps control, pinpointing the location of where that photo was shot.

Halloween Live Gallery

I really like showing off this application in my Silverlight/Expression talks I’m giving, and with Halloween long gone by now, I needed to change its theme – for… let’s say… Christmas? Even better – I thought it would be great if I had a few themes always ready and packed to go so I could plug in any of them at any time, without getting too much involved with changing the existing code. And it should be easy to later create new themes and add them to the library.

The answer was clear – I needed MEF.

This article will guide you through the process of rewriting the Gallery application to be easily themable through external theme plugins. The main goal was to create a new Christmas theme that I could show off during the last weeks of the year, but also keep the Halloween theme as it was. The article is split in two parts - the first part will cover the basics, leaving some more advanced stuff for the second part.

What is MEF?

MEF (Managed Extensibility Framework) lets you build extensible applications by breaking them into smaller, discoverable parts that can be properly (re)composed on demand. It’s built into .NET Framework 4 and Silverlight 4 (SDK), but also available for earlier framework versions through a separate download from Codeplex.

There are just a few concepts one needs to be familiar with to get things going with MEF: you work with parts. On composition, exported parts are imported to expected places in application. When architecting a MEF application, these are the three core terms you need to be familiar with. Let’s review them:

  • exports: parts, marked as discoverable
  • imports: “placeholder” parts to be replaced with exports when composition happens
  • composition: discovering exports and “satisfying imports” - replacing import “placeholders” with exports

The best thing about MEF is that for many cases, this might be all you would need to know. It’s really that simple! On the other hand, MEF allows you to dig down much deeper into the framework and solve much more complex needs your solution might require.

Where to start?

The first step after deciding which framework + MEF combo to use, is identifying parts of your existing application you want to have “pluggable”. These are best found while running your application. If you take a look at Halloween Gallery, for example, the first thing you’ll probably notice are the colors used. Black and orange may work great for Halloween, but Christmas colors have always been red and white. Then there are the photos of course – they are pulled from Flickr and are based on search terms – tags, describing those photos. If the search tag for Halloween was ‘halloween’, then it should be ‘christmas’ for Christmas gallery, right? The last thing to notice is the pumpkin-shaped pushpin icon on the Bing Maps control, marking the photo location; that one should be replaced with an icon of Santa’s hat, for example.

Just for a start, we identified the following as a definite must for making the gallery configurable through themes:

  • colors
  • search terms
  • location icon

Now let’s take a break and actually do something about it.

Dissecting the project

Although not a requirement, it’s best to have themes separated from the main project - for better maintainability, if not other reasons. But regardless of their container, they should be discoverable through some kind of a contract that the main project would understand. MEF lets you draw the line of separation on many levels: we could, for example, put themes:

  • in the main (entry) assembly,
  • in a separate assembly/ies, but in the same XAP package as the main assembly,
  • in an assembly, contained in a separate XAP package.

And those are just your basic options for part discovery, available from the framework (and the Silverlight Toolkit, to be fair – but more on that in the second part). For the sake of this first part of the article I decided to put all initial themes in a project called Gallery.Themes and include it in the main XAP package. Having done that, I could create additional themes later and put them in a number of separate projects (call them Gallery.Themes.Winter, etc.) – that would make no difference to the main project, which would still act as the driver, discovering the themes across all the assemblies in the package and picking one of them to load. In the second part of the article I’m going to extend the gallery to discover themes included in separate XAP packages.

But first, let’s bring some MEF into the gallery.

Each project should reference the System.ComponentModel.Composition.dll. For Silverlight 4, that’s located in \Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client folder. That will give you a full access to MEF API.

Having previously identified what an initial theme will consist of, we now need to define it in code. The following interface reflects the variables identified as a theme and will serve as a discoverable contract:

public interface ITheme
{
    string SearchString { get; }
    Color Foreground { get; }
    Color Background { get; }
    BitmapImage Pushpin { get; }
}

I put the interface in a separate - ‘contract’ – project, called it PhotoGallery.Extensibility and referenced it from both main and themes project. This makes maintaining projects easier while removing any need for other dependencies between the projects.

Solution structrure

The magic of MEF

With interface in place, the project is ready for a touch of MEF magic. We need to pull all Halloween-dependent parts of code out of the main project and move them into a Themes project.

I described the three core principles of MEF before, let’s review them again, in practice:

Export

Here’s a complete HalloweenTheme class, with the Export attribute sprinkled on top. This attribute makes the class discoverable through the ITheme conract, making it a MEF export.

[Export(typeof(ITheme))]
public class HalloweenTheme : ITheme
{
    public string SearchString
    {
        get { return "halloween"; }
    }
 
    public Color Foreground
    {
        get { return Color.FromArgb(255, 255, 100, 0); }
    }
 
    public Color Background
    {
        get { return Colors.Black; }
    }
 
    public BitmapImage Pushpin
    {
        get { return new BitmapImage( 
                new Uri("/PhotoGallery.Themes;component/Resources/Pumpkin.png",
                UriKind.Relative)); }
    }
}
Import

The same contract (ITheme) is used to import themes into the gallery. Because we’re expecting more than just one theme to be available for import, the property is marked with ImportMany attribute (in contrast to the Import attribute, which would require exactly one ITheme export to be found).

[ImportMany(typeof(ITheme))]
public ITheme[] Themes { get; set; }

Compose

Lastly, we need to trigger the composition by calling a single method on the PartInitializer class.

PartInitializer.SatisfyImports(this);

Where ‘this’ is the main page, exposing the importable Themes property. After calling this method, the Themes property collection should contain references to all discovered themes; now we only need to pick which one to display.

Let there be hints

Right… so when more than one theme is discovered, which one should be used? The application could just pick the first one in the collection and let the user change it later. Or…

MEF lets you attach additional metadata to exports. This metadata is not part of an export, but may provide some additional information to the imports resolver, like hints about how a part is to be imported.

Gallery themes provide the metadata about a date range, when the theme is most likely to be used. For example – the Christmas theme will have the date range of 12/1 – 1/31 (meaning whole December and January), while Halloween theme would have 10/1 – 11/30 (October and November).

The ITheme metadata is defined as:

public interface IThemeMetadata
{
    int StartMonth { get; }
    int StartDay { get; }
    int EndMonth { get; }
    int EndDay { get; }
}

The best way to expose metadata with an export is to wrap it in a custom attribute:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ExportPeriodAttribute : ExportAttribute, IThemeMetadata
{
    public ExportPeriodAttribute()
        :base(typeof(ITheme))
    {
    }
 
    public int StartMonth { get; set; }
    public int StartDay { get; set; }
    public int EndMonth { get; set; }
    public int EndDay { get; set; }
}

ExportPeriodAttribute derives from the ExportAttribute thus can be used as a replacement for existing Export attribute, letting us provide the date range metadata at the same time. Here’s a complete ChristmasTheme class:

[ExportPeriod(StartMonth = 12, StartDay = 1, EndMonth = 1, EndDay = 31)]
public class ChristmasTheme : ITheme
{
    public string SearchString
    {
        get { return "christmas"; }
    }
 
    public Color Foreground
    {
        get { return Colors.White; }
    }
 
    public Color Background
    {
        get { return Colors.Red; }
    }
 
    public BitmapImage Pushpin
    {
        get { return new BitmapImage(                     new Uri("/PhotoGallery.Themes;component/Resources/Hat.png",                     UriKind.Relative)); }
    }
}

The part where themes get imported needs to be changed as well:

[ImportMany(typeof(ITheme))]
public Lazy<ITheme, IThemeMetadata>[] Themes { get; set; }

MEF’s Lazy<T,M> type derives from Lazy<T>, which is a new type in .NET Framework 4 and Silverlight 4, used for delayed object instantiation. This is great because we can access theme’s metadata without creating an actual theme instance. The following LINQ query with a helping function finds the right theme to load, based on provided metadata. If no suitable date range were found, the first theme still serves as the default.

public void OnImport()
{
    // Found nothing to import?
    if (Themes.Count() == 0)
    {
        return;
    }
 
    DateTime date = DateTime.Today;
 
    var dateItem = from item in Themes
                   where GetIsDateInRange(date, item.Metadata)
                   select item;
    var finalItem = dateItem.FirstOrDefault() ?? Themes[0];
 
    // Accessing Lazy<T>'s Value creates an instance of T
    ITheme theme = finalItem.Value;
    
    // Read theme's properties and set them
}
 
private bool GetIsDateInRange(DateTime date, IThemeMetadata metadata)
{
    int year = date.Year;
 
    DateTime start = new DateTime(year, metadata.StartMonth, metadata.StartDay);
    DateTime end = new DateTime(year, metadata.EndMonth, metadata.EndDay);
 
    if (metadata.StartMonth > metadata.EndMonth)
    {
        if (DateTime.Today.Month >= metadata.StartMonth)
        {
            end = end.AddYears(1);
        }
        else
        {
            start = start.AddYears(-1);
        }
    }
    return start <= date && date <= end;
}

To be continued…

This first part of this article covered the very basics of MEF. In the next part, we’ll take the gallery a bit further – we’re going to look at how MEF can help us developing with MVVM pattern, expand the themes, import more complex objects (like behaviors), load themes from separate XAPs and take a deeper look at some of MEF’s concepts.

See you in part 2 of this article…

Christmas theme

The gallery can be viewed here [Requires Silverlight 4]


Subscribe

Comments

  • -_-

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by Fallon Massey on Jan 07, 2010 22:07

    I tried to read this article, I really did, but those Ghosties scared the begeezers out of me.

    Seriously, I'm a huge fan of MEF, and I enjoyed your article.

  • andrejt

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by andrejt on Jan 08, 2010 12:39
    :) Thanks, glad you enjoyed it.
  • -_-

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by Glenn Block on Jan 09, 2010 07:20

    Hi Andre, Nice Post!

    One thing, where you discussed the references, you need to also add System.ComponentModel.Composition.Initialization which is where PartInitializer is located.

  • -_-

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by Yazid on Jan 09, 2010 12:11

    Hello,

    Excellent article,

    -    all the article I have seen use Silverlight rather than WPF. Can we do the same thing with WPF?

    -    Is MEF going to replace UNITY or PRISM?

    TIA

    Yaz

  • andrejt

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by andrejt on Jan 09, 2010 13:06
    @Glenn: Right, I should have mentioned that. Will update the article, thanks!
  • andrejt

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by andrejt on Jan 09, 2010 13:26
    @Yaz:
    1. Yes, you could do the same with WPF - MEF will ship with .NET 4. In fact, MEF for Silverlight was "ported" from the full     version framework.
    2. It's hard to say because each of the three have some overlapping with the others, and there is no 1:1 replacement. You can actually use all of three in the same project. With a decent MVVM framework under the belt (there are a few out there), I would (had) definitely go with MEF instead of PRISM. Nonetheless, PRISM's documentation/guidance is still worth going through for learning patterns.
    While MEF can provide some of the features of an IoC container, it's not meant to be one, so I would say "no" to that.
    But then again, I don't know the future plans for development with any of the three.
  • -_-

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by Jon P. on Jun 10, 2010 22:20
    Tried to look at the gallery and I got the "compiled to work with an expired beta version of Silverlight" error.  I thought your Halloween project rocked.  So, was looking forward to seeing the Christmas version.  BTW, I tried to open the link in IE, Firefox and Chrome.  Same error message.
  • -_-

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by Michaud Venant on Jun 13, 2010 16:46
    Doesn't Silverlight allready have a theming system with Styles in Resources? Isn't this a developer solution? I've come up to this problem with MEF and using ResourceDictionaries. The are not available when MEF instanciates the Class. Have you had any experience with this?
  • -_-

    RE: Honey, I MEF-fed the Silverlight Gallery (for Christmas), Part 1


    posted by Mark Farrer on Sep 10, 2010 12:06

    Hi

    I am new to SilverLight/MEF

    Reference your code:

    var dateItem = from item in Themes where GetIsDateInRange(date, item.Metadata) select item;
    var finalItem = dateItem.FirstOrDefault() ?? Themes[0];

    I have some similiar code, however, instead of wanting to get the FirstOrDefault element/usercontrol within the xap file I would like to specify the element/usercontrol to get by id or name. At the moment I am using this temporary hack because I know the order of the elements/usercontrols:

    .ElementAtOrDefault(intSelectedUserControl)

    **intSelectedUserControl is an integer

    Thanks in advance

    Mark

     

     

Add Comment

Login to comment:
  *      *       

From this series