Skip to content
This repository has been archived by the owner on Sep 2, 2021. It is now read-only.

A guide to using MEF

Koen Zwikstra edited this page Feb 9, 2015 · 1 revision

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.

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.