2012-01-29 2 views
2

Я относительно новичок в MVVM, и я пытаюсь понять, как работает интерфейс INotifyPropertyChanged и как его реализовать в моих моделях. Подход, который я решил принять, - это реализовать его в каждом из моих классов бизнес-объектов.
Проблема с этим подходом заключается в том, что когда я привязываю свой вид к свойству в базовом классе, событие PropertyChanged в этом базовом классе никогда не инициализируется (равно null), и поэтому представление не обновляет данные для этого элемента при изменении моей модели , Я смог воспроизвести проблему с приведенным ниже примером.MVVM INotifyPropertyChanged конфликт с базовым классом PropertyChange

У меня есть класс Person Base:

public class Person : INotifyPropertyChanged 
    { 
     #region INotifyProperty 

     public event PropertyChangedEventHandler PropertyChanged; 

     public void RaisePropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
     #endregion 

     public String Name 
     { 
      get 
      { 
       return _name; 
      } 
      set 
      { 
       _name = value; 
       RaisePropertyChanged("Name"); 
      } 
     } 
     private String _name; 
    } 

И у меня есть класс Employee, унаследованный от моего лица Базового класса:

public class Employee : Person,INotifyPropertyChanged 
    { 
     #region INotifyProperty 

     public event PropertyChangedEventHandler PropertyChanged; 

     public void RaisePropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
     #endregion 


     public String EmployeeID 
     { 
      get 
      { 
       return _employeeId; 
      } 
      set 
      { 
       _employeeId = value; 
       RaisePropertyChanged("EmployeeID"); 
      } 
     } 
     private String _employeeId; 
    } 

Вот мой вид Модели:

public class ViewModel : ViewModelBase<ViewModel> 
    { 
     private Employee _employee; 

     public ViewModel() 
     { 
      ChangeModelCommand = new RelayCommand(param=>this.ChangeModel() , param=>this.CanChangeModel); 
      Employee = new Employee() 
         { 
          Name = "BOB",EmployeeID = "1234" 
         }; 
     } 

     public ICommand ChangeModelCommand { get; set; } 


     public Employee Employee 
     { 
      get 
      { 
       return _employee; 
      } 
      set 
      { 
       this._employee = value; 
       NotifyPropertyChanged(m=>m.Employee); 
      } 
     } 

     public void ChangeModel() 
     { 
      MessageBox.Show("CHANGING MODEL"); 
      this.Employee.Name = "MIKE"; 
      this.Employee.EmployeeID = "5678"; 
     } 
     public bool CanChangeModel 
     { 
      get{ return true;} 
     } 
    } 

И, наконец, мой Вид:

<Window.Resources> 
    <MVVM_NotificationTest:ViewModel x:Key="Model"></MVVM_NotificationTest:ViewModel> 
</Window.Resources> 
<Grid DataContext="{StaticResource Model}"> 
<StackPanel> 
    <Label Content="Employee Name"/> 
    <TextBox Text="{Binding  Path=Employee.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> 
    <Label Content="Employee ID"/> 
    <TextBox Text="{Binding Path=Employee.EmployeeID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> 
    <Button Content="Change Model" Height="30" Width="100" Margin="5" Command="{Binding Path=ChangeModelCommand}"/> 
</StackPanel> 
</Grid> 

В этом примере я инициализирую свое свойство VM сотрудника в конструкторе виртуальной машины, а затем у меня есть команда изменить EmployeeID (из класса Employee) и Name (из класса Person). Однако единственным элементом пользовательского интерфейса в представлении, который обновляется, является EmployeeID, а не имя (я ожидал, что Боб обновится до Майка).

Во время отладки я обнаружил, что событие PropertyChanged всегда было нулевым в моем базовом классе (Person). Я также заметил, что когда я удаляю весь класс #INotifyProperty из моего класса Employee, все работает отлично, поскольку он использует событие и методы Base Type.

Проблема, с которой я сталкиваюсь, заключается в том, что все мои текущие классы моделей реализуют явно INotifyPropertyChanged. Все они определяют событие PropertyChanged и реализуют метод RaisePropertyChanged, который, очевидно, повлияет на мои привязки в моем приложении MVVM.

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

В целом, я хотел бы знать, что является лучшим способом реализации INPC в моей глубоко иерархической модели, так что наследование не нарушает распространение PropertyEvent, как мы видели в этом примере, и поэтому мои независимые классы могут быть самодостаточными также. Любые идеи или предложения будут очень благодарны :)

ответ

5

Просто сделайте защиту RaisePropertyChanged и переместите ее в базовый класс. В настоящее время у вас будет много дублирования, что не обязательно.

Что-то вроде этого:

protected virtual void RaisePropertyChanged(string propertyName); 

Многие структуры MVVM обеспечивают это для вас. Например, PRISM имеет базовый класс ViewModel NotificationObject.

+0

Именно то, о чем я думал: p –

+0

Привет @RichardOD, я просто попробовал это, но, похоже, не работал. Он работает только тогда, когда я перемещаю как объявление PropertyChanged, так и метод RaisePropertyChanged в базовый класс ... –

+0

Проблема в том, что у меня есть много классов, генерируемых динамически, поэтому реализация INPC была добавлена ​​массивно для каждого класса. Если это решение, тогда мне нужно будет иметь более сложную логику, чтобы исключить реализацию, когда она унаследована от базового класса :( –

2

Вы должны использовать только INPCодин раз, вы можете использовать тот же метод подъема в подклассах.

2

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

+0

Это хорошее наблюдение @tsells. Причина заключалась в том, что я использую ViewModelBase из Simple MVVM Framework от Tony Sneed, который внедряет INPC таким образом. Мои классы моделей должны будут реализовать его аналогичным образом, чтобы избежать опечаток, как вы сказали. –

+1

Чтобы прояснить, простой MVVM Toolkit имеет базовый класс, который реализует INotifyPropertyChanged и имеет метод NotifyPropertyChanged, который принимает выражение lambda для имени свойства, а не строку, что делает его безопасным для типа. Метод также упорядочивает вызов к потоку пользовательского интерфейса. –

+2

, пожалуйста, также ознакомьтесь с NotifyPropertyWeaver. Спасет вас от большой боли;) http://code.google.com/p/notifypropertyweaver/ – doerig

Смежные вопросы