Bringing MEF to Xaml via MarkupExtension


Nearly a year ago I was playing with Unity container in the scope of pure Xaml and Markup extensions. There was a 4-part series of articles on my old blog: 

Injecting Xaml with Unity Application Block using Markup Extensions. Part 4

This time I’ve decided to continue my extreme development and to try injecting UI controls into WPF layout directly from MEF 🙂

Note that this is an experimental stuff, I know about Prism/CompositeWPF and many other things. I was just intrested whether it is possible do it the way I did  😉

1. Preparing Layout

I wanted to have something like the following:

<Window
  x:Class="XamlifiedMEF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:XamlifiedMEF"
  Title="Xamlified MEF" Height="600" Width="800">

  <StackPanel>
    <local:MefPart Contract="TextBoxPart"/>
    <ItemsControl ItemsSource="{local:MefPart Contract=ButtonPart}"/>
  </StackPanel>

</Window>

where the StackPanel element imports one TextBox (via the “TextBoxPart” contract) and additionally nests a single ItemsControl having an ItemsSource as a collection of exported Button elements (via the “ButtonPart” contract).

2. MEF Locator

As a common Xaml MarkupExtension cannot be imported or built up via some sort of ObjectBuilder (in order to get dependencies injected) you might need some static entry point (Service Locator). I recommend reading the article “CommonServiceLocator for MEF, a service is a service”  by Glenn Block or referring to codeplex version of CommonServiceLocator

In my case I simply decided to use the static class in order to get access to CompositionContainer:

namespace XamlifiedMEF
{
  internal static class Mef
  {
    private static readonly CompositionContainer _container;
    public static CompositionContainer Container
    {
      get { return _container; }
    }

    static Mef()
    {
      _container = new CompositionContainer(
        new AssemblyCatalog(
          Assembly.GetExecutingAssembly()));
    }
  }
}

 Of course in “real” implementations this service might require proper implementaiton and configuration but I left it simple for clarification purposes.

3. Parts

Next thing you will need is defining a couple of parts to be injected/imported into the layout. I’ve created a dummy textbox and three buttons:

namespace XamlifiedMEF
{
  [Export("TextBoxPart")]
  public class TextBoxPart : TextBox
  {
    public TextBoxPart()
    {
      this.Text = "Hi, I'm imported text part";
    }
  }

  [Export("ButtonPart")]
  public class Button1 : Button
  {
    public Button1()
    {
      this.Content = "I'm a button part 1";
    }
  }

  [Export("ButtonPart")]
  public class Button2 : Button
  {
    public Button2()
    {
      this.Content = "I'm a button part 2";
    }
  }

  [Export("ButtonPart")]
  public class Button3 : Button
  {
    public Button3()
    {
      this.Content = "I'm a button part 3";
    }
  }
}

The samples above are fairly simple and guess need no comments 😉

4. Markup Extension

And finally you will need a simple markup extension that will get one or several parts from the MEF container. Here’s the smallest extension that could be implemented in this case:

namespace XamlifiedMEF
{
  [MarkupExtensionReturnType(typeof(FrameworkElement))]
  public class MefPartExtension : MarkupExtension
  {
    [ConstructorArgument("Contract")]
    public string Contract { get; set; }

    public MefPartExtension()
    {
    }

    public MefPartExtension(string contract)
    {
      this.Contract = contract;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      ExportCollection<FrameworkElement> exports =
        Mef.Container.GetExports<FrameworkElement>(Contract);

      if (exports.Count == 0) return null;
      if (exports.Count == 1) return exports[0].GetExportedObject();

      var items = from Export<FrameworkElement> item
                    in exports
                  select item.GetExportedObject();

      return items;
    }
  }
}

Note:  The extension is marked with FrameworkElement return type in case a single UI part is imported.

The core of the extension is “ProvideValue” method that should be always implemented. You will need to get the collection of exports by provided contract name. Next I determine whether ther resulting collection contains one element or several and return the values according to the result.

When you will launch the application you will see that all the declared elements were successfully imported into the layout. 

Demo project can be downloaded here…

Advertisements

One thought on “Bringing MEF to Xaml via MarkupExtension

  1. Hi Denis,
    A very intesting idea but in VS2010 and Net4 it no longer works. GetExports(Contract) does not get anything, and it seems that the Type must be exact, ie TextBoxPart or Button1 etc. And for the ProvideValue operating on the TextBoxPart you cannot return null to a UIElementCollection in the case that there were no exports of TextBoxPart.

    Perhaps you have some time to update?

    thanks
    John

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s