1

車のナビゲーションシステムを構築しているとしましょう:

  • メイン ウィンドウには、画面、モード ボタン、およびボリューム コントロールが含まれます。
  • システムのモードに応じて、画面にはオーディオ、気候、またはナビゲーション パネルが表示されます。
  • オーディオ モードのときは、別のモード ボタン セットと、ラジオ、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 を設計して、階層ビュー、システム モードに基づくビューのスワップ インとアウト、およびビューの削除を処理するためのベスト プラクティスは何ですか?

具体的には:

  • 階層関係が明確に表現されるように、ビュー モデルをどのように編成しますか?
  • ある既存のビューを別のビューに交換する方法 (オーディオ パネルをナビゲーション パネルに置き換えるなど) を処理するにはどうすればよいですか?
  • 関連する親ビューが不要になったときに、親ビュー モデルと子ビュー モデルがガベージ コレクションのために解放されるようにするにはどうすればよいでしょうか?
4

2 に答える 2

1

ビューの階層内の現在のビューはビューの「状態」の一部であるように見えるため、この関係を管理する独自の「モデル」(ビューモデル) エンティティがあります。そのために IoC コンテナーは使用しませんが、「ビュー マネージャー」が「サブビュー」を作成するために使用するファクトリを登録するために使用します。

于 2011-09-26T20:21:40.913 に答える
1

DataContextVisual Studio/Blend には、要素のデザイン時間を設定できる XAML デザイン属性があります。これは設計時にのみ適用されるため、引き続きDataContextデータ テンプレートを使用して接続できるはずです (つまり、ViewModelLocator または ViewManager はまったく必要ない場合があります)。

たとえば、 というビューAudioPanelと というビュー モデルがあるとしAudioViewModelます。

いくつかの設計時のデータを初期化する必要があるだけですAudioViewModel...

public class AudioViewModel : ViewModelBase
{
    public int Volume { get; set; }
    public AudioMode Mode { get; set; }
    public ViewModelBase ModePanelViewModel { get; set; }

    public AudioViewModel()
    {
        if (IsInDesignMode)
        {
            Volume = 5;
            Mode = AudioMode.Radio;
            ModePanelViewModel = new RadioViewModel();
        }
    }
}

d:DataContext...その後、ビューでは、属性を宣言するだけで済みます...

<UserControl x:Class="NavSystem.Views.AudioPanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="clr-namespace:NavSystem.ViewModels"
             mc:Ignorable="d"
             d:DataContext="{d:DesignInstance vm:AudioViewModel, IsDesignTimeCreatable=True}">

デザイン時に使用されるビュー モデルごとに既定のコンストラクターを作成する限り、VS または Blend デザイナーで複合ユーザー インターフェイスを表示できるはずです。

詳細については、このブログ投稿を参照してください: http://karlshifflett.wordpress.com/2009/10/28/ddesigninstance-ddesigndata-in-visual-studio-2010-beta2/

于 2011-09-27T20:33:14.343 に答える