A guide to using MEF

Modern UI (MUI) works great with MEF. This guide explains the necessary steps for integrating MEF into a MUI app. The idea of using MEF in MUI is to make all displayable content exportable and replace the default content loader with a loader that is able to access that content based on a content uri.

Download the sample MEF-in-MUI source code: MefMuiApp.zip

Create an export attribute

The ModernFrame control is used to host content in MUI apps. Content is identified by a URI. We need to mark content as exportable in MEF and at the same time provide the URI that identifies it. This can be achieved by using metadata.

The following ContentAttribute class derives from ExportAttribute and adds a ContentUrl metadata value. The contractType specifies the IContent interface which must be implemented by all exportable content.

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ContentAttribute : ExportAttribute
{
  public ContentAttribute(string contentUri) : base(typeof(IContent))
  {
    this.ContentUri = contentUri;
  }
  
  public string ContentUri { get; private set; }
}
In order to use strong-typed metadata in MEF, we also define a IContentMetadata interface that will be used later when content is consumed.

public interface IContentMetadata
{
  string ContentUri { get; }
}

Export content

Our next step is to mark the content pages as exportable by using the ContentAttribute and implement the IContent interface for each page. The following snippet marks MyPage as exportable and specifies the content uri /MyPage.

[Content("/MyPage")]
public partial class MyPage: UserControl, IContent
{
  public void OnFragmentNavigation(FragmentNavigationEventArgs e)
  {
  }

  public void OnNavigatedFrom(NavigationEventArgs e)
  {
  }

  public void OnNavigatedTo(NavigationEventArgs e)
  {
  }

  public void OnNavigatingFrom(NavigatingCancelEventArgs e)
  {
  }
}

Implement the content loader

With content exported, we now need a custom IContentLoader implementation that is able to consume the exported content and return the content based on a content uri. For that we create a new MefContentLoader deriving from DefaultContentLoader. The content loader imports all IContent exports and includes IContentMetadata so we can access the ContentUri.

When content is requested using the LoadContent method, the requested uri is compared to the metadata. When matched, the content is returned. Content instantiation is deferred by using Lazy.

[Export]
public class MefContentLoader : DefaultContentLoader
{
  [ImportMany]
  private Lazy<IContent, IContentMetadata>[] Contents { get; set; }

  protected override object LoadContent(Uri uri)
  {
    // lookup the content based on the content uri in the content metadata
    var content = (from c in this.Contents
      where c.Metadata.ContentUri == uri.OriginalString
      select c.Value).FirstOrDefault();

    if (content == null) {
      throw new ArgumentException("Invalid uri: " + uri);
    }

    return content;
  }
}
Please note that the MefContentLoader itself is also exported.

Putting it all together

With all the required elements in place, we now need make sure all Modern controls use the new MefContentLoader instead of the default content loader. Add the following default styles to App.xaml where the DynamicResource reference will be explained later.

<Style TargetType="mui:ModernFrame">
  <Setter Property="ContentLoader" Value="{DynamicResource MefContentLoader}" />
</Style>
<Style TargetType="mui:ModernTab">
  <Setter Property="ContentLoader" Value="{DynamicResource MefContentLoader}" />
</Style>
<Style TargetType="local:MainWindow">
  <Setter Property="ContentLoader" Value="{DynamicResource MefContentLoader}" />
</Style>
MEF composition happens on start of the application. Add the following snippet to the Application OnStartup method (in App.xaml.cs).

protected override void OnStartup(StartupEventArgs e)
{
  base.OnStartup(e);

  // bootstrap MEF composition
  var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
  var container = new CompositionContainer(catalog);

  var contentLoader = container.GetExport<MefContentLoader>().Value;
  this.Resources.Add("MefContentLoader", contentLoader);
}
MEF composition consists of creating an assembly catalog and feeding it to a CompositionContainer. The MefContentLoader instance is retrieved from the container and added to the global application resource dictionary. Once the loader is added, the DynamicResource reference used in the default styles can be resolved.

And that's it. Whenever a ModernFrame needs to load content it uses the MefContentLoader which is defined in the default style. The MefContentLoader looks up the content based on the ContentUri that is specified in the export metadata.

Last edited Apr 17, 2013 at 2:01 PM by kozw, version 16

Comments

varunji Apr 3, 2014 at 1:03 PM 
I have a very big problem. Please Help. I want to load xaml by Code. because If i load xaml by Link or as a ContentSource then i can not get xaml class Object.

kozw Apr 20, 2013 at 11:52 AM 
Interesting, are you exporting App as well?

iwhp1 Apr 14, 2013 at 11:42 AM 
Thankx kozw!

It is not a exception from MEF. The exception says, that it cannot create another instance of "App", since "App" is already instantiated. Why would it try to create another instance of app, I just want to have the exported Value of MyData?

kozw Apr 13, 2013 at 9:34 PM 
MEF exception messages usually contain detailed info on what went wrong.

iwhp1 Apr 10, 2013 at 7:04 PM 
Thankx kozw!
Sofar I could rebuild your sample.
What I try to do now is, add an Export definition in app.xaml.cs as follow

[Export]
public MyData MyData { get; set; }

and in HomeViewModel.cs, I try to get an instance of it.

[Import]
public MyData MyData { get; set; }

But now, the application crashes. Should this scenario not work also?
Thankx, Harry