Что за концепция INotifyPropertyChanged?
Все примеры Silverlight, использующие интерфейс MVVM, называются IPropertyChanged. Какова концепция этого и почему нам нужно поднять событие всякий раз, когда мы устанавливаем какое-то значение?
Например: -
public class UserNPC:INotifyPropertyChanged
{
private string name;
public string Name {
get { return name; }
set { name = value; onPropertyChanged(this, "Name"); }
}
public int grade;
public int Grade {
get { return grade; }
set { grade = value; onPropertyChanged(this, "Grade"); }
}
// Declare the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
// OnPropertyChanged will raise the PropertyChanged event passing the
// source property that is being updated.
private void onPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
}
Какова цель INotifyPropertyChanged?
Ответы
Ответ 1
У вас есть следующие зависимости:
Вид → Переплет → Модель
Теперь понятие выглядит следующим образом:
Если некоторые данные в объекте Model меняются, вам необходимо поднять событие PropertyChanged
. Зачем? Поскольку объект Binding зарегистрировал метод с событием объекта данных PropertyChanged
.
Итак, все, что вам нужно сделать, когда что-то изменится в объекте Model, - это поднять событие, и все готово.
Когда вы это сделаете, объект Binding получит уведомление об изменении через ваше событие. Объект Binding, в свою очередь, позволяет объекту View знать, что что-то произошло. Затем объект "Вид" может обновить интерфейс.
Пример кода
Здесь у вас есть компилируемый пример. Установите несколько точек останова, выполните код с помощью F11 и посмотрите, что происходит за кулисами. Обратите внимание, что этот пример имеет следующую зависимость: Вид → Модель. Я оставил объект Binding.
using System;
using System.ComponentModel;
namespace INotifyPropertyChangedDemo
{
class Program
{
static void Main(string[] args)
{
// Create 2 listeners.
View1 view1 = new View1();
View2 view2 = new View2();
// Create 1 data object.
Model model = new Model();
// Connect listener with data object.
model.PropertyChanged += new PropertyChangedEventHandler(view1.MyPropertyChangedEventHandler);
model.PropertyChanged += new PropertyChangedEventHandler(view2.MyPropertyChangedEventHandler);
// Let data object publish change notification.
model.FirstName = "new name";
// Check whether all listeners got notified.
// ... via console.
}
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
}
}
}
public class View1
{
public void MyPropertyChangedEventHandler(object source, PropertyChangedEventArgs arg)
{
Console.WriteLine("Listener 1: Changed Property: {0}", arg.PropertyName);
string newValue = ((Model) source).FirstName;
Console.WriteLine("Listener 1: Changed Property Value: {0}", newValue);
}
}
public class View2
{
public void MyPropertyChangedEventHandler(object source, PropertyChangedEventArgs arg)
{
Console.WriteLine("Listener 2: Changed Property: {0}", arg.PropertyName);
string newValue = ((Model)source).FirstName;
Console.WriteLine("Listener 2: Changed Property Value: {0}", newValue);
}
}
}
}
Ответ 2
MVVM в WPF и Silverlight реализуется путем привязки элементов пользовательского интерфейса к модели представления. Однако, когда модель представления изменяется, как узнает пользовательский интерфейс, чтобы обновить себя?
INotifyPropertyChanged
просто предоставляет событие, к которому пользовательский интерфейс может "слушать", поэтому, когда элемент управления "слышит", что свойство, к которому оно привязано, изменилось, оно может "обновить себя".
Например, скажем, что у вас есть TextBlock
, который показывает цену акции, и привязан к свойству string Price
модели представления. Модель просмотра, в свою очередь, использует услугу для обновления цен на акции каждые 30 секунд. Итак, каждые 30 секунд свойство Price
изменяется: 30 секунд назад это было "$ 29,20", теперь это "$ 29,12", а через 30 секунд это будет "$ 28,10". Связывание TextBlock
применяется, когда загружается TextBlock
, но не каждый раз, когда изменяется значение Price
. Если, однако, вы реализуете INotifyPropertyChanged
и поднимите событие для свойства "Цена" в установщике Price
, тогда TextBlock
может подключиться к событию и, таким образом, "знать", когда нужно вернуться и "перечитать", свойство Price
и обновить отображаемый текст.
Ответ 3
Большинство элементов управления Silverlight прослушивают изменения отображаемых данных, просто подписываясь на события PropertyChanged.
например. управление делает что-то подобное за кулисами:
public void Loaded()
{
if (myDataObject is INotifyPropertyChanged)
{
(myDataObject as INotifyPropertyChanged).PropertyChanged +=new PropertyChangedEventHandler(onPropertyChanged);
}
}
Вот почему ObservableCollection используется вместо простых списков в приложениях Silverlight. Они реализуют INotifyPropertyChanged, поэтому элементы управления, отображающие коллекции, могут видеть изменения, происходящие в списке, а также отдельные элементы в списке.
Ответ 4
Недавно я создал трехуровневую программу для удовольствия и хотел убедиться, что все части, где они как можно более разделены.
В моем графическом интерфейсе пользователь мог ввести имя, однако они хотели, однако у моего бизнес-класса была логика, чтобы изменить все имена в Title Case. Это сработало, однако, GUI никогда не рассказывал об этом обновлении, которое сделал бизнес-класс.
Так что моя работа в то время была простой... но не выглядела правильно. Что-то вроде следующего
var _person = new Person();
// In some form event handler like button click
_person.Name = txtName.Text;
txt.Name.Text = _person.Name;
Это помогло обновить графический интерфейс, не отвлекая его от бизнес-логики. Я хотел создать событие, которое будет срабатывать, когда бизнес-логика изменит значение из того, что было напечатано в графическом интерфейсе, и GUI будет прослушивать это событие.
Итак, теперь у меня будет что-то вроде...
var _person = new Person();
// In some form event handler like button click
_person.Name = txtName.Text;
// In the GUI class
public void OnInternalPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
txtName.Text = _person.Name;
}
ПРИМЕЧАНИЕ. Я не делаю этого во всех изменениях свойств... только те, которые отклоняются от того, что пользователь ожидает от него... меняя все строчные буквы на Title Case и показывая это пользователю.