web-dev-qa-db-ja.com

ViewModelLocatorとは何ですか?DataTemplatesと比較した場合の賛否両論は何ですか?

ViewModelLocatorとは何か、どのように機能するのか、DataTemplateと比較してそれを使用することの賛否両論について簡単な概要を教えてもらえますか?

私はGoogleで情報を見つけようとしましたが、それの多くの異なる実装があり、それが何であるか、そしてそれを使用することの賛否両論について明確なリストがないようです。

109
Rachel

イントロ

MVVMでの通常のプラクティスは、ビューが 依存性注入 (DI)コンテナーから解決することにより、ViewModelを見つけるようにすることです。これは、コンテナがViewクラスのインスタンスを提供(解決)するように求められたときに自動的に発生します。コンテナーinjects ViewModelパラメーターを受け入れるViewのコンストラクターを呼び出して、ViewModelをViewに入れます。このスキームは制御の反転(IoC)と呼ばれます。

DIの利点

ここでの主な利点は、実行時にコンテナを構成できることであり、要求する型を解決する方法についての指示があります。これにより、アプリケーションを実際に実行するときに使用する型(ViewsおよびViewModels)を解決するように指示することにより、テスト性が向上しますが、アプリケーションの単体テストを実行する場合は異なる指示をします。後者の場合、アプリケーションはUIさえも持たないので(実行されず、テストのみが実行されます)、コンテナはアプリケーションの実行時に使用される「通常の」タイプの代わりに mocks を解決します。

DIに起因する問題

これまで、DIアプローチでは、アプリケーションコンポーネントの作成の上に抽象化レイヤーを追加することで、アプリケーションのテスト容易性を実現できることがわかりました。このアプローチには1つの問題があります:Microsoft Expression Blendなどのビジュアルデザイナーとうまく機能しません。

問題は、通常のアプリケーションの実行と単体テストの実行の両方で、誰かがset up解決するタイプの指示を含むコンテナを必要とすることです。さらに、ViewModelsがビューに挿入できるように、ビューを解決するために誰かがaskコンテナを作成する必要があります。

ただし、設計時には実行中のコードはありません。デザイナーはリフレクションを使用してビューのインスタンスを作成しようとします。つまり、次のことを意味します。

  • ViewコンストラクターがViewModelインスタンスを必要とする場合、デザイナーはViewをインスタンス化することはできません-制御された方法でエラーが発生します
  • ビューにパラメーターなしのコンストラクターがある場合、ビューはインスタンス化されますが、そのDataContextnullになるため、デザイナーで「空の」ビューを取得します。これはあまり有用ではありません

ViewModelLocatorを入力します

ViewModelLocatorは、次のように使用される追加の抽象化です。

  • ビュー自体は resources の一部としてViewModelLocatorをインスタンス化し、そのDataContextをロケーターのViewModelプロパティにデータバインドします
  • ロケーターはどういうわけかデザインモードになっているかどうかを検出
  • デザインモードでない場合、ロケーターは、上記で説明したように、DIコンテナーから解決するViewModelを返します。
  • 設計モードの場合、ロケーターは独自のロジックを使用して、固定の「ダミー」ViewModelを返します(設計時にコンテナーがないことに注意してください)。通常、このViewModelにはダミーデータが事前に入力されています

もちろん、これは、ビューが最初にパラメーターなしのコンストラクターを持たなければならないことを意味します(そうでない場合、デザイナーはインスタンス化できません)。

概要

ViewModelLocatorは、MVVMアプリケーションでDIの利点を維持しながら、ビジュアルデザイナーとコードをうまく連携させるイディオムです。これは、アプリケーションの「ブレンド可能性」と呼ばれることもあります(Expression Blendを参照)。

上記を消化した後、実際の例を参照してください here

最後に、データテンプレートを使用することは、ViewModelLocatorを使用する代わりではなく、UIの一部に明示的なView/ViewModelペアを使用する代わりになります。多くの場合、代わりにデータテンプレートを使用できるため、ViewModelのViewを定義する必要はないことがわかります。

190
Jon

@ Jon's answer の実装例

ビューモデルロケータークラスがあります。各プロパティは、ビューに割り当てるビューモデルのインスタンスになります。コードがデザインモードで実行されているか、DesignerProperties.GetIsInDesignModeを使用していないかを確認できます。これにより、設計時に模擬モデルを使用し、アプリケーションを実行しているときに実際のオブジェクトを使用できます。

public class ViewModelLocator
{
    private DependencyObject dummy = new DependencyObject();

    public IMainViewModel MainViewModel
    {
        get
        {
            if (IsInDesignMode())
            {
                return new MockMainViewModel();
            }

            return MyIoC.Container.GetExportedValue<IMainViewModel>();
        }
    }

    // returns true if editing .xaml file in VS for example
    private bool IsInDesignMode()
    {
        return DesignerProperties.GetIsInDesignMode(dummy);
    }
}

そして、それを使用するには、ロケーターをApp.xamlリソースに追加します。

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

<Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

次に、ビュー(例:MainView.xaml)をビューモデルに接続します。

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
8
BrunoLM

この質問の他の回答がデザイナーを包み込む理由がわかりません。

View Model Locatorの目的は、Viewでこれをインスタンス化できるようにすることです(はい、View Model Locator = View First)。

public void MyWindowViewModel(IService someService)
{
}

これだけではなく:

public void MyWindowViewModel()
{
}

これを宣言することにより:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"

ViewModelLocatorはクラスであり、IoCを参照し、それが公開するMainWindowModelプロパティを解決する方法です。

ビューにモックビューモデルを提供することとは関係ありません。あなたがそれを望むなら、ただやる

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"

View Model Locatorは、Unityなどの(任意の)Inversion of Controlコンテナーのラッパーです。

参照する:

2
Hristo Yankov