INotifyPropertyChanged背后的概念是什么?

时间:2010-08-24 15:09:13

标签: silverlight

使用MVVM的Silverlight的所有示例都使用名为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的确切目的是什么?

4 个答案:

答案 0 :(得分:15)

您有以下依赖项:

查看→绑定→模型

现在,概念如下:

如果 Model 对象中的某些数据发生更改,则需要引发PropertyChanged事件。为什么?因为 Binding 对象已使用数据对象的PropertyChanged事件注册了一个方法。

因此,当您的 Model 对象中发生更改时,您需要做的就是提升事件并完成。

当您这样做时, Binding 对象会通过您的事件获得有关更改的通知。 Binding 对象依次让 View 对象知道发生了什么。然后, View 对象可以在必要时更新UI。

代码示例

这里有一个可编辑的例子。设置几个断点,使用 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);
            }
        }
    }
}

答案 1 :(得分:5)

WPF中的MVVM& Silverlight是通过将UI元素绑定到视图模型来实现的。但是,当视图模型发生变化时,UI将如何知道更新自身?

INotifyPropertyChanged只是公开了一个UI可以“监听”的事件,所以当一个控件“听到”它所绑定的属性发生了变化时,它就可以“自我更新”。

例如,假设您有TextBlock显示股票价格,并且它绑定到视图模型的string Price属性。反过来,视图模型使用服务每30秒更新一次股票价格。因此,Price属性每30秒更改一次:30秒前它是“$ 29.20”现在是“$ 29.12”,从现在开始30秒将是“$ 28.10”。加载TextBlock时会应用TextBlock绑定,但Price每次更改时都不会应用INotifyPropertyChanged绑定。但是,如果您实施Price并在TextBlock设置器中提升属性“价格”的事件,则Price可以连接到该事件,从而“知道”何时返回并“重新读取”​​{{1}}属性并更新显示的文本。

答案 2 :(得分:3)

大多数Silverlight控件通过简单地订阅PropertyChanged事件来监听他们显示的数据的更改。

e.g。控件在幕后做了类似的事情:

    public void Loaded()
    {
        if (myDataObject is INotifyPropertyChanged)
        {
            (myDataObject as INotifyPropertyChanged).PropertyChanged +=new PropertyChangedEventHandler(onPropertyChanged);
        }
    }

这也是使用ObservableCollection而不是Silverlight应用程序中更简单列表的原因。它们实现了INotifyPropertyChanged,因此控件显示集合能够看到列表中的更改以及列表中的各个项目。

答案 3 :(得分:0)

我最近为了好玩而创建了一个3层程序,并希望确保所有部分尽可能分开。

在我的GUI中,用户可以输入他们想要的名称,但是,我的业务类中有逻辑将所有名称更改为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的工作,同时将其与业务逻辑分开。我想要的是创建一个事件,当业务逻辑从GUI中输入的内容改变了值时将触发该事件,并且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,并向用户显示。