C #, WPF, MVVM и INotifyPropertyChanged - PullRequest
       26

C #, WPF, MVVM и INotifyPropertyChanged

9 голосов
/ 11 февраля 2011

Я запутался; Я думал, что понял INotifyPropertyChanged.

У меня есть небольшое приложение WPF с внешним интерфейсом класса MainWindow, моделью представления в середине и моделью сзади.

Модель в моем случае это класс Simulator.

SimulatorViewModel почти прозрачен и просто взаимодействует со свойствами между MainWindow и Simulator.

Имитатор реализует INotifyPropertyChanged, и каждый установщик свойств в имитаторе вызывает метод RaisePropertyChanged:

private string serialNumber;
public string SerialNumber
{
    get { return serialNumber; }
    set
    {
        serialNumber = value;
        RaisePropertyChanged("SerialNumber");
    }
}

public event PropertyChangedEventHandler PropertyChanged;

public void RaisePropertyChanged(string propName)  
{  
    if (this.PropertyChanged != null)  
    {  
        this.PropertyChanged(this, new PropertyChangedEventArgs(propName));  
    }  
}    

В xaml у меня есть TextBox с привязкой:

Text="{Binding Path=SerialNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

и DataContext - это SimulatorViewModel (но см. Мой комментарий об изменении DataContext на модель)

ViewModel просто передает свойства через:

public string SerialNumber  
{  
    get { return Simulator.SerialNumber; }  
    set { Simulator.SerialNumber = value; }  
}  

Программные обновления свойства SerialNumber в Simulator не распространяются вплоть до пользовательского интерфейса, хотя, что любопытно, начальное значение, установленное в конструкторе Simulator, достигает этого.

Если я сломаюсь на установщике SerialNumber в симуляторе и последую в RaisePropertyChanged, я обнаружу, что PropertyChanged имеет значение null, и поэтому событие не распространяется вверх в GUI.

Что приводит меня к нескольким вопросам:

  1. Что именно должно быть подключено к событию PropertyChanged? Я хочу быть более конкретным, чем просто "WPF". Какая связь между этим событием и оператором Binding в xaml?

  2. Почему начальное значение свойства доходит до пользовательского интерфейса при запуске, но не впоследствии?

  3. Прав ли я, чтобы Simulator (модель), реализующий INotifyPropertyChanged, или это должен был быть ViewModel? Если ViewModel делает это, то программные изменения в модели не вызывают PropertyChanged, поэтому я не уверен в правильности шаблона. Я понимаю, что моя ViewModel практически избыточна, но это связано с простотой проекта; более сложный - сделать концепцию ViewModel более сложной. Насколько я понимаю, ViewModel - это место для интерфейса моих модульных тестов.

Ответы [ 2 ]

7 голосов
/ 11 февраля 2011
  1. Проблема в том, что вы поднимаете PropertyChanged на своей модели, но ваш вид привязан к ViewModel.Таким образом, ваш View подписывается только на события ViewModel (не Model) - поэтому текстовое поле не обновляется - потому что оно никогда не получает событие PropertyChanged.Одним из возможных решений является прослушивание во ViewModel событий Model PropertyChanged и соответственно создание того же события во ViewModel.

  2. Начальное значение распространяется, потому что ваши установщики / получатели в ViewModel верны, проблема в событиях.

  3. Да, вы правы (см. Мой # 1), вы должны поднять PropertyChanged на вашей ViewModel, потому что View связан с ним.Эти события не будут запускаться автоматически после изменений в Model, поэтому вы должны прослушивать Model.PropertyChanged во ViewModel.

Самое простое грязное исправление, чтобы понять идею:

SimulatorViewModel(Simulator model)
{
    // this will re-raise Model's events on ViewModel
    // VM should implement INotifyPropertyChanged
    // method OnPropertyChanged should raise INPC for a given property
    model.PropertyChanged += (sender, args) => this.OnPropertyChanged(args.PropertyName);
}
1 голос
/ 16 октября 2013
  1. Всегда проверяйте, что экземпляр класса, который был установлен как Data-context, должен реализовывать INotifyPropertyChanged
  2. В вашем случае Simulator реализует INotifyPropertyChanged, но SimulatorViewModel устанавливается как DataContext, в этом случае пользовательский интерфейс узнает, только если есть изменения в SimulatorViewModel, но не в Simulator.

Что вы можете сделать, это:

Вы можете представить некоторые события или делегаты в классе Simulator и подключить некоторые методы в классе SimulatorViewModel. Затем, когда изменятся значения в классе Simulator, вызовите событие или делегат, который получит Execute в SimulatorViewModel, теперь вы можете получить обновленные значения из симулятора и обновить свойства в SimulatorViewModel. Убедитесь, что вы используете разные свойства в обоих классах

...