Что за концепция 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 и показывая это пользователю.