web-dev-qa-db-ja.com

ViewModelLocator MVVM LightのViewModel

すべてのViewModelをSimpleIocに保存するのは正しいですか?たとえば、MainPage、Photos、Directoriesの3つのページがあります(したがって、3つのViewModels-> MainVM、PhotosVM、DirectoriesVM)。各ページのDataContextをViewModelLocatorのView Model Propertyに設定するか、ViewVMをMainVMのプロパティとしてネストし、各ページDataContextをMain.PhotosVMProperty、Main.DirectoriesVMPropertyなどにバインドする必要がありますか?誰もがIoCのアイデアと目的を説明できますか?

33
fex

まず、ViewModelLocatorの機能と、それを使用する理由を見てみましょう。

ViewModelLocatorは、App.xamlページでオブジェクトとして宣言され、アプリケーションシングルトンです。アプリケーションを実行するときに、アプリケーションで使用できるのはそのうちの1つだけです。

ViewModelLocatorは、MVVM LightのすべてのViewModelのソースです。 ViewModelごとに、ViewModelLocatorにプロパティがあり、ViewのViewModelを取得できます。このコードは次のようになります。

public class ViewModelLocator
{
    public MainPageViewModel MainPage
    {
        get { return new MainPageViewModel(); }
    }
}

これは私のApp.xamlの一部です:

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

これはView.xamlの一部です

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

ここまでは順調ですね。最初の質問に答えるには、MVVM LightでIocを使用する必要がありますか?いいえ。ViewModelLocatorによって完全に構築およびインスタンス化されたビューにビューモデルが提供されるため、必要はありません。

2番目の質問:IoCの目的は何ですか?

IoCは、次のことを行えるように設計されています。

Mvvm Lightを使用すると、次のように上記を実行できます。

public class ViewModelLocator
{
    public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        if (ViewModelBase.IsInDesignModeStatic)
        {
            SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
        }
        else
        {
            SimpleIoc.Default.Register<IDataService, DataService>();         
        }

        SimpleIoc.Default.Register<MainViewModel>();
    }

    public MainViewModel Main
    {
        get { return SimpleIoc.Default.GetInstance<MainViewModel>(); }
    }
}

public class MainViewModel
{
    public ObservableCollection<Foo> Foos { get; set; }

    public MainViewModel(IDataService dataService)
    {
        _dataService=dataService;
        Foos=_dataService.GetFoos();
    }
}

呼び出すときにMainViewModelを解決するとき

SimpleIoc.Default.GetInstance<MainViewModel>()

内部的には、SimpleIocがMainViewModelに依存関係(コンストラクターのパラメーター)があるかどうかを確認します。次に、登録されているインターフェイスを見て、これらのパラメーターを解決しようとします。これは再帰的に行われるため、DataServiceに依存関係がある場合は、インスタンス化されているときにインスタンス化され、DataServiceコンストラクターに渡されます。

なぜこの作業をすべて行うのですか?

  1. クラスを簡単にユニットテスト可能にする
  2. コードをインターフェイス駆動にします。これは、具体的なクラスではなくインターフェイスを参照していることを意味します
  3. コードを疎結合にします。これは、誰かがインターフェースの実装を変更でき、そのインターフェースを消費するクラスは気にせず、再コーディングする必要がないことを意味します。
  4. クラスの依存関係を自動化された方法で解決します。
  5. MVVM Lightでは、デザインモード(ViewModelBase.IsInDesignModeStatic)、これは、Visual Studioのビューに実際のデータが含まれるように、ビューモデルデータを提供するデザインタイムサービスを作成できることを意味します。
124

MVVM Lightには多くの素晴らしい機能がありますが、Service Locatorはビューのビューに対するビューの不要な依存関係を作成しているように見えます。理想的には、ライブラリAにViewModelLocator、ライブラリBにビューモデル、ライブラリCにビューがあることを望みます。その後、将来のプロジェクトで必要に応じてこれらを組み合わせて使用​​できます。ただし、MVVM Lightの設計では、ビュー(ライブラリC)は常にViewModelLocatorに依存しています(これは問題ありません)が、ViewModelLocator(ライブラリA)は常にビューモデル(ライブラリB)の場合、ビューは常にビューモデルに依存します(すべての製品でこれまで使用されていたすべてのビューモデルライブラリをビューに含める必要があるため、これは問題ではありません)。

私は、Prismが何らかの方法で文字列キーを使用することでこの問題を回避すると考えています。何か不足していますか?

おっとっと!私は自分の質問に答えただけだと思います。解決策は、特定のソリューション(製品)に固有のライブラリA(ServiceLocator)を作成することです。次に、そのソリューションのみのビューモデルへの参照が含まれます。次に、ビューはこのServiceLocatorに依存し、このServiceLocatorはその製品のすべてのビューモデルに依存します。最終結果は、ビューがその製品で使用されるビューモデルのみに依存することです。このモジュールにはソリューションに固有のコードのみが含まれているため、ソリューションごとにServiceLocatorを複製しているという事実に問題はありません。 SimpleIocクラスなどのServiceLocatorのコンポーネントは、もちろんすべてのソリューションに共通ですが、これらはServiceLocatorで呼び出す再利用可能なクラスに組み込まれています。

要約すると、解決しようとしている問題は、ソリューションに6つのビューモデルがあり、そのうち4つが密接に関連し、2つが密接に関連していると仮定しています。したがって、2つのアセンブリを作成します。各アセンブリには、密接に関連するビューモデルが含まれています。ビューモデルの1つのセットを使用する製品を設計し、ソリューションがWindows 8を実行するように設計されているとします。ビューはすべて異なり、ビューモデルの1つのセット(アセンブリ)のみを再使用します。したがって、このビューモデルのアセンブリと必要な他のアセンブリを指す新しいServiceLocatorアセンブリを作成するだけです。新しいWindows 8ビューは、この新しいServiceLocatorアセンブリと、新しい製品(ソリューション)で使用されるビューモデルのみに依存するようになりました。

1
Richard