什么是 Change Notification,为什么它很重要(译)
[我自己尝试做的翻译,呵呵]
属性改变通知(Property Change Notification)
有关Windows窗体数据绑定的最重要的一个概念是,它是由更改通知(change notification)驱动的。也就是说,除非数据源通知Windows窗体数据绑定运行时数据已经发生了改变(通过提供一个更改事件),否则,Windows窗体不会更新一个用户界面元素(控件)。对于简单的属性对属性的绑定,数据源需要提供一个属性更改通知,这可以通过提供一个属性的“属性名”已更改事件(比如为Customer对象的Name属性提供NameChanged事件)或者实现“INotifyPropertyChanged”接口,INotifyPropertyChanged是VS 2005中新增的一个接口,它可以与BindingList<T>一起使用来创建可绑定的列表(下面会有描述)
例子:不用更改通知的简单绑定
/*******************************************************************
* 设置(使用 Visual Studio 窗体设计器):
*
* 添加一个 Label 控件到窗体Form (label1)
* 添加一下代码到 Form.Load 事件处理方法中
******************************************************************/
CurrentTime currentTime = new CurrentTime();
/*******************************************************************
* 绑定Label.Text (string) 到 CurrentTime.Now
* 注意: 当“Now”改变时Label控件将不会更新
* 因为“Now”没有提供更改通知.
******************************************************************/
Binding binding = new Binding("Text", currentTime, "Now", true);
binding.FormatString = "MM/dd/yyyy hh:MM:ss";
this.label1.DataBindings.Add(binding);
/***********************************************************************
*设置(使用 Visual Studio 窗体设计器):
*
* 添加一个新的类文件到你的工程,叫做 "CurrentTime"
* 添加一下代码到类的实现中
**********************************************************************/
public class CurrentTime
{
System.Windows.Forms.Timer _timer;
public CurrentTime()
{
/***************************************************************
* 使用一个跟踪当前时间的计时器
***************************************************************/
_timer = new System.Windows.Forms.Timer();
/***************************************************************
* 每秒钟更新一次时间
***************************************************************/
_timer.Interval = 1000;
_timer.Tick += delegate { this.Now = DateTime.Now; };
_timer.Start();
}
/*******************************************************************
* 使用一个保存当前时间的变量
*******************************************************************/
private DateTime _now = DateTime.Now;
/*******************************************************************
*没有更改通知 – 被绑定的控件将不会被更新
*******************************************************************/
public DateTime Now
{
get { return _now; }
private set
{
if (_now != value)
{
_now = value;
}
}
}
}
例子:含有“属性”已更改的更改通知的简单数据绑定
/***********************************************************************
* 设置(使用 Visual Studio 窗体设计器):
* 使用和上面的例子中一样的窗体,但是改变了 “CurrentTime”类型,为“Now”属性提供了更改通知
* 这个例子使用“属性”已更改模式。Label控件的文本在这个例子中将会正确的更新
***********************************************************************/
public class CurrentTime
{
System.Windows.Forms.Timer _timer;
public CurrentTime()
{
/***************************************************************
*使用一个跟踪当前时间的计时器
***************************************************************/
_timer = new System.Windows.Forms.Timer();
/***************************************************************
*每秒钟更新一次时间
***************************************************************/
_timer.Interval = 1000;
_timer.Tick += delegate { this.Now = DateTime.Now; };
_timer.Start();
}
/*******************************************************************
*使用一个保存当前时间的变量
*******************************************************************/
private DateTime _now = DateTime.Now;
/*******************************************************************
* 属性更改通知被激发 – 当这个属性更改时,被绑定的用户界面元素将会更新
* 这里使用的是“属性”已更改模式
*******************************************************************/
public DateTime Now
{
get { return _now; }
private set
{
if (_now != value)
{
_now = value;
OnNowChanged(EventArgs.Empty);
}
}
}
/*******************************************************************
* 提供一个属性已更改事件
*******************************************************************/
public event EventHandler NowChanged;
protected virtual void OnNowChanged(EventArgs e)
{
if (null != NowChanged)
{
NowChanged(this, e);
}
}
}
例子:含有INotifyPropertyChanged更改通知的简单数据绑定
/***********************************************************************
*设置(使用 Visual Studio 窗体设计器):
*
* 使用上个例子中一样的窗体,但使用这个版本的CurrentTime类型做了替换.
*
* 这个例子实现了 System.ComponentModel.INotifyPropertyChanged接口.
* 这个例子中的label的文本也会正确的被更新.
**********************************************************************/
using System.ComponentModel;
public class CurrentTime : INotifyPropertyChanged
{
System.Windows.Forms.Timer _timer;
public CurrentTime()
{
/***************************************************************
*使用一个跟踪当前时间的计时器
***************************************************************/
_timer = new System.Windows.Forms.Timer();
/***************************************************************
*每秒钟更新一次时间
***************************************************************/
_timer.Interval = 1000;
_timer.Tick += delegate { this.Now = DateTime.Now; };
_timer.Start();
}
/*******************************************************************
*使用一个保存当前时间的变量
*******************************************************************/
private DateTime _now = DateTime.Now;
/*******************************************************************
*属性更改通知被激发 – 当这个属性更改时,被绑定的用户界面元素将会更新
*
* 这里使用 INotifyPropertyChanged 接口
*******************************************************************/
public DateTime Now
{
get { return _now; }
private set
{
if (_now != value)
{
_now = value;
OnPropertyChanged(“Now”);
}
}
}
/*******************************************************************
* 提供 INotifyPropertyChanged.PropertyChanged 事件
*******************************************************************/
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}
}
列表更改通知
当数据源是一个列表的时候(复杂的或简单的绑定列表),数据源需要通过IbindingList接口提供列表和属性2个更改通知。列表更改通知用来在一个条目从列表中移除、删除或者新增到列表的时候通知用户界面元素。一个列表上的属性更改通知用来在列表中的某一项上的一个属性(比如列表的第N个位置上的Customer实例的“Name”属性)发生更改时通知用户界面元素。Windows窗体中列表绑定的一个关键概念是,IbindingList接口既提供依据列表的更改通知,又提供基于属性的更改通知。换句话说,这是一个比较关键的地方,被绑定到一个列表的Windows窗体控件将不会监听列表中的项目提供的属性更改通知,而是,Windows窗体需要列表为它所包含的项目提供这种通知。
在VS 2005之前,实现IBindingList会在列表和列表中所包含的项之间形成强耦合,因为包含的那些项需要在他们发生改变时告诉列表。在VS2005中,这通过引入BindingList<T>和INotifyPropertyChanged接口得到简化。BindingList<T>是一个IBindingList的泛型实现,它可以自动的将子项的INotifyPropertyChanged事件转换到IBindingList.ListChanged事件(译者注:这里有些疑问,为什么不转换成PropertyChanged事件呢,还需要求证)
注意,如果列表是通过BindingSource绑定的话,那种绑定到一个不是IBindingList的基于列表的数据源的体验可能会改善很多。