MatthiasGは、MEFでモジュールを定義する方法を示しています。ビュー自体はIModuleを実装していないことに注意してください。ただし、PRISMでMEFを使用することの興味深い部分は、起動時にモジュールをUIにインポートする方法です。
ここでは原則としてシステムの説明しかできませんが、正しい方向を示している可能性があります。すべてに対して常に多くのアプローチがありますが、これは私がベストプラクティスであると理解したことであり、私が非常に良い経験をしたことです。
ブートストラップ
PrismやUnityと同様に、すべてはで派生したBootstrapperから始まりMefBootstrapper
ますMicrosoft.Practices.Prism.MefExtensions
。ブートストラッパーはMEFコンテナーをセットアップし、サービス、ビュー、ViewModels、モデルを含むすべてのタイプをインポートします。
ビュー(モジュール)のエクスポート
これはMatthiasGが参照している部分です。私の練習は、GUIモジュールの次の構造です。
モデルは、属性を使用して、それ自体を具象型(インターフェースにすることもできます。MatthiasGを参照)としてエクスポートし[Export(typeof(MyModel)]
ます。マークを付け[PartCreationPolicy(CreationPolicy.Shared)]
て、インスタンスが1つだけ作成されることを示します(シングルトン動作)。
ViewModelは、モデルと同じようにそれ自体を具象型としてエクスポートし、コンストラクターインジェクションを介してモデルをインポートします。
[ImportingConstructor] public class MyViewModel(MyModel model){_model = model; }
ViewModelがモデルをインポートするのと同じ方法で、Viewはコンストラクタインジェクションを介してViewModelをインポートします
そして今、これは重要です。ビューは、「標準」[Export]
属性から派生した特定の属性を使用して自身をエクスポートします。次に例を示します。
[ViewExport(RegionName = RegionNames.DataStorageRegion)]
public partial class DataStorageView
{
[ImportingConstructor]
public DataStorageView(DataStorageViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
[ViewExport]属性
属性は2つのことを行います。属性[ViewExport]
から派生しているため[Export]
、MEFコンテナにビューをインポートするように指示します。何として?これはその定義に隠されています。コンストラクターの署名は次のようになります。
public ViewExportAttribute() : base(typeof(UserControl)) {}
[Export]
タイプが。のコンストラクターを呼び出すことによりUserControl
、すべてのビューがUserControl
MEFコンテナーのように登録されます。
RegionName
次に、ビューをプラグインするシェルUIのリージョンを決定するために後で使用されるプロパティを定義します。RegionNameプロパティは、インターフェイスの唯一のメンバーですIViewRegionRegistration
。属性クラス:
/// <summary>
/// Marks a UserControl for exporting it to a region with a specified name
/// </summary>
[Export(typeof(IViewRegionRegistration))]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[MetadataAttribute]
public sealed class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
{
public ViewExportAttribute() : base(typeof(UserControl)) {}
/// <summary>
/// Name of the region to export the View to
/// </summary>
public string RegionName { get; set; }
}
ビューのインポート
さて、システムの最後の重要な部分は、シェルの領域にアタッチする動作です:動作AutoPopulateExportedViews
。これにより、次の行を使用してMEFコンテナからすべてのモジュールがインポートされます。
[ImportMany]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
UserControl
これは、を実装するメタデータ属性がある場合、コンテナからとして登録されたすべてのタイプをインポートしますIViewRegionRegistration
。属性がそうであるため[ViewExport]
、これは、でマークされたすべてのタイプをインポートすることを意味します[ViewExport(...)]
。
最後のステップは、ビューをリージョンにプラグインすることです。これは、bahviorがそのOnAttach()
プロパティで実行します。
/// <summary>
/// A behavior to add Views to specified regions, if the View has been exported (MEF) and provides metadata
/// of the type IViewRegionRegistration.
/// </summary>
[Export(typeof(AutoPopulateExportedViewsBehavior))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification
{
protected override void OnAttach()
{
AddRegisteredViews();
}
public void OnImportsSatisfied()
{
AddRegisteredViews();
}
/// <summary>
/// Add View to region if requirements are met
/// </summary>
private void AddRegisteredViews()
{
if (Region == null) return;
foreach (var view in _registeredViews
.Where(v => v.Metadata.RegionName == Region.Name)
.Select(v => v.Value)
.Where(v => !Region.Views.Contains(v)))
Region.Add(view);
}
[ImportMany()]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
}
注意してください.Where(v => v.Metadata.RegionName == Region.Name)
。これは、属性のRegionNameプロパティを使用して、ビヘイビアーをアタッチしている特定のリージョンにエクスポートされたビューのみを取得します。
動作は、ブートストラッパーのシェルの領域に関連付けられます。
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
ViewModelInjectionBehavior.RegionsToAttachTo.Add(RegionNames.ElementViewRegion);
var behaviorFactory = base.ConfigureDefaultRegionBehaviors();
behaviorFactory.AddIfMissing("AutoPopulateExportedViewsBehavior", typeof(AutoPopulateExportedViewsBehavior));
}
私たちは完全に一周しました。これにより、MEFとPRISMで物事がどのように機能するかがわかります。
そして、あなたがまだ退屈していないなら:これは完璧です:
MikeTaultyのスクリーンキャスト