Подтвердить что ты не робот

Хороший способ обновить привязку данных по всем свойствам ViewModel при изменении модели

Краткая версия

Если я обновляю объект Model, который обертывает мой ViewModel, какой способ включить уведомления об изменении свойств для всех свойств модели, которые предоставляет моя ViewModel?

Подробная версия

Я разрабатываю WPF-клиент, следуя шаблону MVVM, и пытаюсь обрабатывать входящие обновления от службы до данных, отображаемых в моих представлениях. Когда клиент получает обновление, обновление появляется в виде 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");
    }    
}

Проблема:

Когда приходит обновленная модель, я устанавливаю свойство Model ViewModel так:

instanceOfFooVM.Model = newModel;

Это приводит к тому, что OnPropertyChanged("Model") запускается, но не OnPropertyChanged("FooViewModelProperty"), если я не вызвал последнее явно из установщика Model. Таким образом, просмотр, связанный с FooViewModelProperty, не будет обновляться, чтобы отображать это новое значение свойства при изменении модели.

Явный вызов OnPropertyChanged для каждого открытого свойства модели явно не является желательным решением, и ни один из них не принимает newModel и выполняет итерацию через свои свойства, чтобы обновлять свойства ViewModel один за другим.

Какой лучший подход к этой проблеме обновления целой модели и необходимости запускать уведомления об изменениях для всех ее открытых свойств?

4b9b3361

Ответ 1

В соответствии с docs:

Событие PropertyChanged может указывать, что все свойства объекта были изменены с помощью null или String.Empty в качестве имени свойства в PropertyChangedEventArgs.

Ответ 2

Один из вариантов - прослушивать свои собственные события и создавать вспомогательную подпрограмму, чтобы поднять другие уведомления по мере необходимости.

Это может быть так же просто, как добавить в свой конструктор:

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

Ответ 3

Всякий раз, когда установлено свойство 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"));
    }
}

Ответ 4

    Implements INotifyPropertyChanged
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged 

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

Для VB.net, если кому-то это нужно. Если вы уже внедрили "INotifyPropertyChanged", тогда вам понадобится последняя строка.