車のナビゲーションシステムを構築しているとしましょう:
- メイン ウィンドウには、画面、モード ボタン、およびボリューム コントロールが含まれます。
- システムのモードに応じて、画面にはオーディオ、気候、またはナビゲーション パネルが表示されます。
- オーディオ モードのときは、別のモード ボタン セットと、ラジオ、CD、または MP3 コントロールのいずれかを表示できるパネルがあります。
これまでのこのような配置に対する私の戦略は、ビュー モデルがビューとまったく同じ階層に従うようにすることでした。そう:
- MainViewModel には ScreenViewModel があります。
- ScreenViewModel には、AudioViewModel、ClimateViewModel、および NavigationViewModel があります。システム モードに応じて、オーディオ、気候、またはナビゲーション ビュー モデルのいずれかに設定される CurrentViewModel プロパティも含まれます。
- AudioViewModel は ScreenViewModel に似ており、オーディオ システムの各モード (ラジオ、CD、および MP3) のビュー モデルと、現在のモードのビュー モデルを格納するためのプロパティを保持します。
ビューをビュー モデルにバインドするための XAML は次のようになります。
<Window.Resources>
<DataTemplate DataType="{x:Type vm:AudioViewModel}">
<view:AudioPanel />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ClimateViewModel}">
<view:ClimatePanel />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:NavigationViewModel}">
<view:NavigationPanel />
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentViewModel}" />
ユーザーがラジオを聞いていて、ナビゲーション システムに目的地を入力することにした場合、ユーザーは [ナビゲーション モード] ボタンをクリックします。システム モードを「Navigation」に変更し、CurrentViewModel を NavigationViewModel に設定する MainWindowViewModel のコマンドがあります。これにより、NavigationView がスワップインされます。非常にクリーンなソリューションです。
残念ながら、この方法は実行モードではうまく機能しますが、Expression Blend で下位ビュー (AudioPanel など) を操作しようとするとうまくいきません。これは、AudioViewModel を提供する親ビュー モデル (MainWindowViewModel) が存在しないためです。
MVVM Light や Simple MVVM などのツールキットでサポートされていると思われる解決策は、代わりに ViewModelLocator を使用し、ロケーターの正しいプロパティにバインドしてビューを独自の DataContext に設定することです。次に、ロケーターはビュー モデルのインスタンスを提供します。
「ViewModelLocator のやり方」は「デザイン性」の問題を解決しますが、階層関係をどのように表現し、あるビューから別のビューへの交換を処理するかは明確ではありません。概念的には、ビュー モデルに子ビュー モデルを保持させる方が理にかなっています。ビューの階層を正しく表現し、ビューの交換は簡単です。ビューが不要になった場合、関連するビュー モデルとそのすべての下位モデルは、親への参照を削除するだけでガベージ コレクションされます。
質問
ViewModelLocator を設計して、階層ビュー、システム モードに基づくビューのスワップ インとアウト、およびビューの削除を処理するためのベスト プラクティスは何ですか?
具体的には:
- 階層関係が明確に表現されるように、ビュー モデルをどのように編成しますか?
- ある既存のビューを別のビューに交換する方法 (オーディオ パネルをナビゲーション パネルに置き換えるなど) を処理するにはどうすればよいですか?
- 関連する親ビューが不要になったときに、親ビュー モデルと子ビュー モデルがガベージ コレクションのために解放されるようにするにはどうすればよいでしょうか?