6

私はすでにいくつかのチュートリアルを検索しており、PRISM の紹介サイトの複数も調べています。ただし、ほとんどの例は unity コンテナーの使用に基づいており、この機能を Mef コンテナーで実装する方法に関する情報が不足しています。私の単純な helloworld モジュールは、 web tutorialに基づいています。チュートリアルが示すように、HelloModule だけに固執し、Unity ではなく Mef を使用していることを除いて、私のコードは同じです。

私の主な問題は、ビューモデルでビューを初期化する方法です。実験で見つけた唯一の有効な方法は、ビュー コンストラクターでビュー モデルを初期化することです。

HelloView.xaml.cs
namespace Hello.View
{
    [Export]
    public partial class HelloView : UserControl, IHelloView
    {
        public HelloView()
        {
            InitializeComponent();
            Model = new HelloViewModel(this);
        }

        public IHelloViewModel Model
        {
            //get { return DataContext as IHelloViewModel; }
            get { return (IHelloViewModel)DataContext; }
            set { DataContext = value; }
        }
    }
}

標準モジュールの初期化コード:

[ModuleExport(typeof(HelloModule), InitializationMode=InitializationMode.WhenAvailable)]
    public class HelloModule : IModule
    {
        IRegionManager _regionManager;

        [ImportingConstructor]
        public HelloModule(IRegionManager regionManager)
        {
            _regionManager = regionManager;
        }

        public void Initialize()
        {
            _regionManager.Regions[RegionNames.ContentRegion].Add(ServiceLocator.Current.GetInstance<HelloView>());
        }
    }

ただし、誰かがこのことの正しい方法を教えてもらえますか?私はこれをモジュールの初期化セクションで行う必要があります。

4

2 に答える 2

11

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、すべてのビューがUserControlMEFコンテナーのように登録されます。

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のスクリーンキャスト

于 2013-03-08T09:21:36.570 に答える
1

あなたが実装した方法HelloViewは、一部のシナリオでは問題ないView正確な実装を が知るIHelloViewModel必要があることを意味しますが、 this は必要ないことを意味しますinterface

私が提供する例では、 を使用していますproperty injectionが、それでもconstructor injection問題ありません。

を使用するinterface場合は、次のように実装できます。

[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
    public HelloView()
    {
        InitializeComponent();
    }

    [Import]
    public IHelloViewModel Model
    {
        get { return DataContext as IHelloViewModel; }
        set { DataContext = value; }
    }
}

[Export(typeof(IHelloViewModel))]
public class HelloViewModel : IHelloViewModel
{
}

それ以外の場合は、次のようになります。

[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
    public HelloView()
    {
        InitializeComponent();
    }

    [Import]
    public HelloViewModel Model
    {
        get { return DataContext as HelloViewModel; }
        set { DataContext = value; }
    }
}

[Export]
public class HelloViewModel
{
}

もう 1 つ: を変更したくない場合Viewsや、それらの実装をいくつか提供したくない場合は、interfacefor は必要ありません。

于 2013-03-08T08:28:14.680 に答える