WPF绑定属性的代码最小化

4
考虑一个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)。我认为有些框架可以通过属性解决其余的模板代码,但是我已经很久没有再深入研究了。另一个选项是使用类似于Dictionary<string, object>包装器的通用代码,用于获取/设置任何值(也使用CallerMemberName技巧)并触发事件。然后你只需要像get {return props.Get<int>();}set {props.Set(value); }这样做。 - stijn
你也可以使用适当的工具,比如Resharper,它可以为你生成样板代码(或者至少让它变得更容易)。 - Evk
4个回答

2
如果您有使用基类的选项,请考虑从类似以下内容继承视图模型对象:
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); }
    }
}

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

还不错,但没必要成为一个抽象类。 - cdiggins
不,它并不需要。但是实例化 BindableBase 类并没有什么用处。 - Petter Hesselberg

0

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

https://github.com/Fody/PropertyChanged


0

首先,我猜你已经在使用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; }

虽然它不是免费的,且 面向切面编程也有它的缺点(感谢 Evk)。

类似的问题已经被问过1, 2, 但目前很遗憾似乎没有最佳答案,因为这也是每个人的痛处。


AOP工具(至少在.NET中)通常不使用反射 - 它们直接重写您的程序集IL代码(因此真正修改您的方法\类等)。 - Evk

0

我可以让你的代码更容易转换成片段。

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

如果编写代码的时间是一种痛苦,而且你经常使用WPF,那么代码片段可能也会有用。我知道在Sublime Text、VS Code和Visual Studio中,代码片段非常有价值。否则,除非有我没有看到的东西,否则我认为这就是最基本的了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接