かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

XAMLからMEFのコンテナにアクセスするマークアップ拡張

何となく作ってみた。
まず、コンテナを保持しておくためのstaticなクラスを用意する。

using System.ComponentModel.Composition.Hosting;

namespace WpfMVVMBase.Markup
{
    public static class ContainerProvider
    {
        public static CompositionContainer Container { get; set; }
    }
}

そして、上のコンテナからインスタンスを返すマークアップ拡張

using System;
using System.Linq;
using System.Windows;
using System.Windows.Markup;

namespace WpfMVVMBase.Markup
{
    public class MefExtension : MarkupExtension
    {
        public string Contract { get; set; }
        public Type ContractType { get; set; }

        public MefExtension()
        {
            ContractType = typeof(FrameworkElement);
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            // コンテナが無い場合は何もできないよね
            if (ContainerProvider.Container == null) return null;
            var exports = ContainerProvider.Container.GetExports(
                ContractType, null, Contract);
            // みつからない場合は何も返せない
            if (exports.Count() == 0) return null;
            // 単一項目の場合は、それを返す
            if (exports.Count() == 1) return exports.ElementAt(0).Value;
            // 複数ある場合はIEnumerableで返す
            return exports.Select(lazy => lazy.Value);
        }
    }
}

使うときのイメージはこんな感じ。

using System.ComponentModel.Composition;

namespace WpfMVVMBase
{
    // とりあえず何も無いけど登録しておく
    [InheritedExport]
    public interface IShellModel
    {
    }

    public class ShellModel : IShellModel
    {
    }
}

そして、何処かで1回コンテナを作成して、ContainerProviderのContainerに入れておきます。

var catalog = new AggregateCatalog(
    new AssemblyCatalog(Assembly.GetExecutingAssembly()));
var container = new CompositionContainer(catalog);
ContainerProvider.Container = container;

あとは、XAMLからさくっと使えます。

<Window x:Class="WpfMVVMBase.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Shell" Height="300" Width="300"
    xmlns:mef="clr-namespace:WpfMVVMBase.Markup"
    xmlns:l="clr-namespace:WpfMVVMBase"
    Content="{mef:Mef ContractType=l:IShellModel}">
</Window>