如何在不将属性名称作为字符串传递的情况下上升PropertyChanged事件?

时间:2015-07-17 09:14:06

标签: c# wpf inotifypropertychanged

在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或dependent属性的属性:

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 setter中,但是...使用string属性是:

  • 难看;
  • 难以重构(VS的“重命名”不会重命名string中的属性名称);
  • 可能是各种错误的来源。

WPF存在了这么久。还没有魔法解决方案(类似于CallerMemberNameAttribute)?

3 个答案:

答案 0 :(得分:7)

使用C#6和nameof功能:

OnPropertyChange(nameof(IsBarNull));

生成等效代码:

OnPropertyChange("IsBarNull");

......但没有脆弱。

如果您坚持使用早期版本的C#,可以使用表达式树,但我认为这有点像黑客和潜在的性能问题(因为树被重新创建)每次通话)。 nameof不需要任何库支持,只需要一个新的编译器 - 所以如果你升级到VS 2015(或者以后,亲爱的未来的读者......)你应该没问题。

答案 1 :(得分:1)

如果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);

答案 2 :(得分:0)

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

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

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