【问题标题】:Minimizing WPF boilerplate code on bound properties最小化绑定属性的 WPF 样板代码
【发布时间】:2016-11-16 13:13:56
【问题描述】:

考虑一个包含大量输入字段的 WPF 对话,这些输入字段绑定到视图模型中的属性。例如

...
<TextBox Text="{Binding FirstName}">
...

public string FirstName {
  get { return mFirstName; }
  set {
    if (mFirstName == value) return;
    mFirstName = value;
    OnPropertyChanged("FirstName");
  }
}

由于有数十个这样的字段,我想尽量减少要编写的样板 C# 代码。我有什么选择?

【问题讨论】:

  • 不需要传递名称,使用 void OnPropertyChanged ( [CallerMemberName] String propertyName = null )。 Imo 有一些框架可以用属性解决其余的样板文件,但我已经在不久前研究过它。另一种选择是使用带有通用代码的 Dictionary 包装器,用于获取/设置任何值(也使用 CallerMemberName 技巧)并引发事件。然后你只有get{ return props.Get&lt;int&gt;() }set{ props.Set(value); }
  • 您还可以使用适当的工具,例如 Resharper,它可以为您生成样板代码(或至少使其更容易)。

标签: c# wpf data-binding


【解决方案1】:

如果您可以选择使用基类,请考虑从以下方式继承视图模型对象:

public abstract class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        storage = value;

        // ReSharper disable once ExplicitCallerInfoArgument
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void OnPropertiesChanged(params string[] propertyNames)
    {
        foreach (string propertyName in propertyNames)
        {
            // ReSharper disable once ExplicitCallerInfoArgument
            OnPropertyChanged(propertyName);
        }
    }
}

示例用法,显示样板大大减少:

public sealed class ViewModel : BindableBase
{
    private string name;

    public string Name
    {
        get { return name; }
        private set { SetProperty(ref name, value); }
    }
}

(如果您不能使用基类(例如,您已经有一个或正在使用框架元素的属性),您仍然可以选择直接在相关类中添加类似的支持。)

【讨论】:

  • 不错,但没必要是抽象类。
【解决方案2】:

我可以让你的代码更容易转换成 sn-p。

if (mFirstName != value) {
    mFirstName = value;
    OnPropertyChanged("FirstName");
}

如果只是写它的时间很痛苦,并且您经常使用 WPF,那么 sn-ps 也可能有用。我知道在 Sublime Text、VS Code 和 Visual Studio 中,片段是无价的。否则,我认为这是你能得到的最简单的骨头,除非有什么我没有看到

【讨论】:

    【解决方案3】:

    我使用 Fody 在编译时注入属性更改代码。你的类得到一个 [ImplementPropertyChanged] 属性,然后你的 { get;放; } 属性成为编译代码中的通知属性。

    https://github.com/Fody/PropertyChanged

    【讨论】:

      【解决方案4】:

      首先,我猜您已经在使用 Microsoft.Prism,您可以删除字符串并在幕后为您从CallerMemberNameAttribute 中获利,这样您的代码将如下所示:

      public string FirstName {
        get { return mFirstName; }
        set {
          if (mFirstName == value) return;
          mFirstName = value;
          OnPropertyChanged();
        }
      }
      

      这也相当于 c# 6.0 nameof(FirstName) 运算符。

      其次,您可以深入研究AOP 并将样板抽象为一个属性。处理此问题的 AOP 框架之一是 PostSharp,使用它您的代码可能如下所示:

      [NotifyPropertyChanged]
      public class Customer
      {
          public string FirstName { get; set; }
      

      虽然它不是免费的,但 AOP has it's drawbacks(感谢 Evk)。

      1,2 已经提出了类似的问题,遗憾的是,现在似乎没有最佳答案,因为这是每个人的痛苦。

      【讨论】:

      • AOP 工具(至少在 .NET 中)通常不使用反射——它们直接重写你的汇编 IL 代码(所以真正修改你的方法\类等等)。
      猜你喜欢
      • 2013-10-17
      • 1970-01-01
      • 2014-08-15
      • 1970-01-01
      • 2015-11-07
      • 1970-01-01
      • 2013-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多