一种优雅的方式实现属性改变通知

一种优雅的方式实现属性改变通知

在WPF中一种非常通用和流行的方式在实体和视图之间同步数据是使用数据绑定。当绑定初始化时,实体的值被一次性地传输到视图。但是每次随后地改变,视图必须通知绑定再次传输值。通过在实体内实现INofityPropertyChanged接口实现。
在每个绑定属性的Set访问器中必须引发PropertyChanged事件,以字符串形式传递已修改的属性名称。这有一些缺点:

  • 你无法使用自动属性
  • 属性名称被以字符串形式传递。当你改名却忘记改变字符串时,这将带来一个严重的错误。
  • 每个属性需要额外的代码。

基本方法

private string _name;
public string Name
{
    get { return _name; }
    set 
    { 
        _name = value;
        PropertyChanged("Name");
    }
}

private void PropertChanged(string prop)
{
    if(PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }
}

更加优雅的方法

基本实现需要在set访问器添加两行代码,并且使用字符串传递属性名,这不是非常健壮的。一个非常优雅的解决方案时使用表达式树的功能从表达式树中获取属性名。

private string _name;
public string Name
{
    get { return _name; }
    set { PropertyChanged.ChangeAndNotify(ref _name, value, () => Name); }
}
public static bool ChangeAndNotify<T>(this PropertyChangedEventHandler handler, ref T field, T value, Expression<Func<T>> memeberExpression)
{
    if(memberExpression == null)
    {
        throw new ArgumentNullException("memberExpression");
    }
    var body = memberExpression.Body as MemberExpression;
    if(body == null)
    {
        throw new ArgumentException("Lambda must return a property.");
    }
    if(EqualityComparer<T>.Default.Equals(field, vlaue))
    {
        return false;
    }
    var vmExpression = body.Expression as ConstantExpression;
    if(vmExpression != null)
    {
        LambdaExpression lambda = Expression.Lambda(vmExpression);
        Delegate vmFunc = labda.Comile();
        object sender = vmFunc.DynamicInvoke();
        
        if(handler != null)
        {
            handler(sender, new PropertyChangedEventArgs(body.Member.Name))
        }
    }

    field = vlaue;
    return true;
}
posted @ 2023-08-18 17:30  Juston007  阅读(54)  评论(0)    收藏  举报