【问题标题】:How to rise PropertyChanged event without passing property name as a string?如何在不将属性名称作为字符串传递的情况下引发 PropertyChanged 事件?
【发布时间】:2015-07-17 09:14:05
【问题描述】:

在 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 的“重命名”不会重命名string 中的属性名称);
  • 可能是各种错误的根源。

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

【问题讨论】:

    标签: c# wpf inotifypropertychanged


    【解决方案1】:

    使用 C# 6 和 nameof 功能:

    OnPropertyChange(nameof(IsBarNull));
    

    生成与以下内容等效的代码:

    OnPropertyChange("IsBarNull");
    

    ...但没有脆弱性。

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

    【讨论】:

    • 这是一个在 C# 5 中使用 lambda 表达式的好例子。不幸的是,这不是一个有效的解决方案,而是在应用程序中遇到的。 - stackoverflow.com/questions/7728465/…
    • @PawelMaga:你在想哪个?我在那里看不到任何使用反射的例子。你在考虑表达式树吗?
    • 看到热点问题。 “嘿 Jon Skeet 的视频向我展示了如何使用 C# 6 做到这一点!”。 Jon Skeet 已经回答了...
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2019-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多