-
Notifications
You must be signed in to change notification settings - Fork 753
A guide to using MEF
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.
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; }
}
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)
{
}
}
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.
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.
(c) 2013-2015 First Floor Software
Getting started
- Screenshots
- Getting started
- Getting started without templates
- Predefined page layout
- Handle navigation events
Tips and tricks
Appearance
BBCode