web-dev-qa-db-ja.com

モデルが変更されたときにViewModelのすべてのプロパティのデータバインディングを更新する良い方法

短縮版

ViewModelがラップするModelオブジェクトを更新する場合、ViewModelが公開するすべてのモデルのプロパティのプロパティ変更通知を起動するgood方法は何ですか?

詳細バージョン

私はMVVMパターンに従ってWPFクライアントを開発しており、サービスから、ビューに表示されているデータへの着信更新を処理しようとしています。クライアントが更新を受信すると、更新はモデルとして使用するDTOの形式で表示されます。

このモデルがビューに表示されている既存のモデルの更新である場合、関連付けられたViewModelがそのデータバインドされたプロパティを更新して、ビューが変更を反映するようにします。

例を挙げて説明します。 モデルを検討:

_class FooModel
{
  public int FooModelProperty { get; set; }
}
_

ViewModelにラップされています:

_class FooViewModel
{
  private FooModel _model;

  public FooModel Model 
  { 
    get { return _model; }
    set 
    { 
      _model = value; 
      OnPropertyChanged("Model"); 
    }
  }

  public int FooViewModelProperty
  {
    get { return Model.FooModelProperty; }
    set 
    {
      Model.FooModelProperty = value;
      OnPropertyChanged("FooViewModelProperty");
    }    
}
_

問題:

更新されたモデルが到着したら、ViewModelのModelプロパティを次のように設定します。

_instanceOfFooVM.Model = newModel;
_

これにより、Modelのセッターから後者を明示的に呼び出さない限り、OnPropertyChanged("Model")は起動しますが、OnPropertyChanged("FooViewModelProperty")は起動しません。したがって、FooViewModelPropertyにバインドされたビューは、モデルを変更しても更新されず、そのプロパティの新しい値が表示されません。

公開されたすべてのモデルプロパティに対してOnPropertyChangedを明示的に呼び出すことは明らかに望ましい解決策ではありません。また、newModelを取得してそのプロパティを反復処理し、ViewModelのプロパティを1つずつ更新することもできません。

モデル全体を更新し、公開されているすべてのプロパティに対して変更通知を発行する必要があるというこの問題に対するより良いアプローチは何ですか?

31
Dan J

docs によると:

PropertyChangedイベントは、PropertyChangedEventArgsのプロパティ名としてnullまたはString.Emptyを使用して、オブジェクトのすべてのプロパティが変更されたことを示すことができます。

59
Joe White

1つのオプションは、独自のイベントをリッスンし、必要に応じて他の通知を発生させるヘルパールーチンを作成することです。

これは、コンストラクタに追加するのと同じくらい簡単です。

public FooViewModel()
{
    this.PropertyChanged += (o,e) =>
      {
          if (e.PropertyName == "Model")
          {
               OnPropertyChanged("FooViewModelProperty");
               // Add other properties "dependent" on Model here...
          }
      };
}
7
Reed Copsey

Modelプロパティが設定されるたびに、独自のPropertyChangedイベントをサブスクライブします。ハンドラーが呼び出されたら、独自のPropertyChangedイベントを発生させます。 Modelが別の値に設定されている場合は、古いModelからハンドラーを削除してください。

例:

class FooViewModel
{
    private FooModel _model;

    public FooModel Model 
    { 
        get { return _model; }
        set 
        { 
            if (_model != null)
            {
                _model.PropertyChanged -= ModelPropertyChanged;
            }

            if (value != null)
            {
                value.PropertyChanged += ModelPropertyChanged;
            }

            _model = value; 
            OnPropertyChanged("Model"); 
        }
    }

    public int FooViewModelProperty
    {
        get { return Model.FooModelProperty; }
        set 
        {
            Model.FooModelProperty = value;
            OnPropertyChanged("FooViewModelProperty");
        } 
    }

    private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // Here you will need to translate the property names from those
        // present on your Model to those present on your ViewModel.
        // For example:
        OnPropertyChanged(e.PropertyName.Replace("FooModel", "FooViewModel"));
    }
}
1
Jon
    Implements INotifyPropertyChanged
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged 

    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(String.Empty))

VB.netの場合、他の誰かがそれを必要とする場合。 「INotifyPropertyChanged」をすでに実装している場合は、最後の行で十分です。

0
VampireMonkey