如何在不以字符串形式传递属性名的情况下触发PropertyChanged事件

本文关键字:属性 情况下 事件 PropertyChanged 字符串 | 更新日期: 2023-09-27 18:03:06

在wpf中,我们通常使用以下模式来设置可绑定属性:

private Foo _bar = new Foo();
public Foo Bar
{
    get { return _bar; }
    set
    {
        _bar = value;
        OnPropertyChanged();
    }
}
public void OnPropertyChanged([CallerMemberName] string property = "")
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(property));
}

CallerMemberNameAttribute做了很好的魔术,从setter名称为我们生成"Bar"参数。

但是,通常存在没有setter或依赖属性的属性:

private Foo _bar;
public Foo Bar
{
    get { return _bar; }
    set
    {
        _bar = value;
        OnPropertyChanged();
    }
}
public bool IsBarNull
{
    get { return _bar == null; }
}

在给定的例子中,当Bar发生变化时,IsBarNull也需要发生事件。我们可以将OnPropertyChanged("IsBarNull");添加到Bar设置中,但是……使用string的属性:

  • 丑陋的;
  • 很难重构(VS的"Rename"不会重命名string中的属性名);
  • 可能是各种错误的根源。

WPF存在了这么久。是否还没有神奇的解决方案(类似于CallerMemberNameAttribute)?

如何在不以字符串形式传递属性名的情况下触发PropertyChanged事件

使用c# 6和nameof特性:

OnPropertyChange(nameof(IsBarNull));

生成等价的代码:

OnPropertyChange("IsBarNull");

…但没有脆弱。

如果你还在使用早期版本的c#,你可以使用表达式树,但是我认为这是一个hack和潜在的性能问题(因为树是在每次调用时重新创建的)。nameof不需要任何库支持,只需要一个新的编译器-所以如果你升级到VS 2015(或更高版本,亲爱的读者从未来…)你应该没事。

如果c# 6不在手边,这里有一些方法我经常使用表达式:

public static class ExpressionExtensions
{
    public static string GetMemberName<T>(this Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }
    public static string GetMemberName (this Expression propertyExpression)
    {
        var lambda = propertyExpression as LambdaExpression;
        MemberExpression memberExpression = null;
        if (propertyExpression is UnaryExpression)
        {
            var unaryExpression = propertyExpression as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else if (lambda != null && lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else if (lambda != null)
        {
            memberExpression = lambda.Body as MemberExpression;
        }
        else
        {
            var expression = propertyExpression as MemberExpression;
            if (expression != null)
                memberExpression = expression;
        }
        if (memberExpression == null) return null;
        var propertyInfo = memberExpression.Member;
        return propertyInfo.Name;
    }
}

用法:

var propertyName = ExpressionExtensions.GetMemberName<DateTime>(item => item.Day);

DevExpress在它的BindableBase类中提供了一个方法SetProperty,它反映了您想设置的属性:

private Foo _bar;
public Foo Bar
{
    get { return _bar; }
    set
    {
        SetProperty(ref _bar, value, () => Bar);
    }
}

它自动查找要更新的正确属性,并且只在必要时发送属性更改事件

相关文章: