数据绑定机制之INotifyPropertyChanged
INotifyPropertyChanged 是 WPF 中的一个接口,用于实现 数据绑定 中的 属性更改通知。它的主要作用是,当对象的某个属性值发生更改时,通知绑定到该属性的 UI 控件更新其显示内容。
以下是有关 INotifyPropertyChanged 的详细信息和实现方法:
1. INotifyPropertyChanged 简介
INotifyPropertyChanged 定义在 System.ComponentModel 命名空间中,它只包含一个事件:
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
核心机制
- PropertyChanged 是事件,当对象的属性发生更改时,触发此事件。
- WPF 数据绑定引擎会监听这个事件,并根据通知更新 UI。
2. 使用场景
在 MVVM 模式中,INotifyPropertyChanged 通常用于 ViewModel 层,确保当属性值更改时,UI 会自动更新。
3. 使用封装提高代码复用性
可以将 INotifyPropertyChanged 的逻辑封装到基类中,减少重复代码:
public abstract class BindableBase : INotifyPropertyChanged
{
//
// 摘要:
// Occurs when a property value changes.
public event PropertyChangedEventHandler PropertyChanged;
//
// 摘要:
// Checks if a property already matches a desired value. Sets the property and notifies
// listeners only when necessary.
//
// 参数:
// storage:
// Reference to a property with both getter and setter.
//
// value:
// Desired value for the property.
//
// propertyName:
// Name of the property used to notify listeners. This value is optional and can
// be provided automatically when invoked from compilers that support CallerMemberName.
//
//
// 类型参数:
// T:
// Type of the property.
//
// 返回结果:
// True if the value was changed, false if the existing value matched the desired
// value.
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
RaisePropertyChanged(propertyName);
return true;
}
//
// 摘要:
// Checks if a property already matches a desired value. Sets the property and notifies
// listeners only when necessary.
//
// 参数:
// storage:
// Reference to a property with both getter and setter.
//
// value:
// Desired value for the property.
//
// propertyName:
// Name of the property used to notify listeners. This value is optional and can
// be provided automatically when invoked from compilers that support CallerMemberName.
//
//
// onChanged:
// Action that is called after the property value has been changed.
//
// 类型参数:
// T:
// Type of the property.
//
// 返回结果:
// True if the value was changed, false if the existing value matched the desired
// value.
protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
onChanged?.Invoke();
RaisePropertyChanged(propertyName);
return true;
}
//
// 摘要:
// Raises this object's PropertyChanged event.
//
// 参数:
// propertyName:
// Name of the property used to notify listeners. This value is optional and can
// be provided automatically when invoked from compilers that support System.Runtime.CompilerServices.CallerMemberNameAttribute.
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
}
//
// 摘要:
// Notifies listeners that a property value has changed.
//
// 参数:
// propertyName:
// Name of the property used to notify listeners. This value is optional and can
// be provided automatically when invoked from compilers that support System.Runtime.CompilerServices.CallerMemberNameAttribute.
[Obsolete("Please use the new RaisePropertyChanged method. This method will be removed to comply wth .NET coding standards. If you are overriding this method, you should overide the OnPropertyChanged(PropertyChangedEventArgs args) signature instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
//
// 摘要:
// Raises this object's PropertyChanged event.
//
// 参数:
// args:
// The PropertyChangedEventArgs
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
this.PropertyChanged?.Invoke(this, args);
}
//
// 摘要:
// Raises this object's PropertyChanged event.
//
// 参数:
// propertyExpression:
// A Lambda expression representing the property that has a new value.
//
// 类型参数:
// T:
// The type of the property that has a new value
[Obsolete("Please use RaisePropertyChanged(nameof(PropertyName)) instead. Expressions are slower, and the new nameof feature eliminates the magic strings.")]
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
string propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
OnPropertyChanged(propertyName);
}
protected void RaisePropertyChanged(object sender, [CallerMemberName] string propertyName = null, object oldvalue = null, object newvalue = null)
{
PropertyChanged?.Invoke(sender, new ValuePropertyChangedEventArgs(propertyName, oldvalue, newvalue));
}
protected void RaisePropertyChanged(object sender, PropertyChangedEventArgs args)
{
PropertyChanged?.Invoke(sender, args);
}
}
PropertySupport:
public static class PropertySupport
{
//
// 摘要:
// Extracts the property name from a property expression.
//
// 参数:
// propertyExpression:
// The property expression (e.g. p => p.PropertyName)
//
// 类型参数:
// T:
// The object type containing the property specified in the expression.
//
// 返回结果:
// The name of the property.
//
// 异常:
// T:System.ArgumentNullException:
// Thrown if the propertyExpression is null.
//
// T:System.ArgumentException:
// Thrown when the expression is:
// Not a System.Linq.Expressions.MemberExpression
// The System.Linq.Expressions.MemberExpression does not represent a property.
// Or, the property is static.
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
return ExtractPropertyNameFromLambda(propertyExpression);
}
//
// 摘要:
// Extracts the property name from a LambdaExpression.
//
// 参数:
// expression:
// The LambdaExpression
//
// 返回结果:
// The name of the property.
//
// 异常:
// T:System.ArgumentNullException:
// Thrown if the expression is null.
//
// T:System.ArgumentException:
// Thrown when the expression is:
// The System.Linq.Expressions.MemberExpression does not represent a property.
// Or, the property is static.
internal static string ExtractPropertyNameFromLambda(LambdaExpression expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
MemberExpression obj = (expression.Body as MemberExpression) ??
throw new ArgumentException("NotMemberAccessExpression", "expression");
//throw new ArgumentException(Resources.PropertySupport_NotMemberAccessExpression_Exception, "expression");
PropertyInfo obj2 = obj.Member as PropertyInfo;
if (obj2 == null)
{
throw new ArgumentException("ExpressionNotProperty", "expression");
//throw new ArgumentException(Resources.PropertySupport_ExpressionNotProperty_Exception, "expression");
}
if (obj2.GetMethod.IsStatic)
{
throw new ArgumentException("StaticExpression", "expression");
// throw new ArgumentException(Resources.PropertySupport_StaticExpression_Exception, "expression");
}
return obj.Member.Name;
}
}
ValuePropertyChangedEventArgs:
public class ValuePropertyChangedEventArgs : PropertyChangedEventArgs
{
public ValuePropertyChangedEventArgs(string propertyName, object oldValue, object newValue) : base(propertyName)
{
OldValue = oldValue;
NewValue = newValue;
}
public object OldValue { get; set; }
public object NewValue { get; set; }
}
触发过程
属性的 set 访问器检测到值发生了变化。
调用 OnPropertyChanged 方法。
OnPropertyChanged 方法触发 PropertyChanged 事件。
WPF 或其他数据绑定框架监听到 PropertyChanged 事件后,更新绑定到该属性的 UI。
Event PropertyChangedEventHandler 用函数包裹起来触发

浙公网安备 33010602011771号