如何为 属性 更改通知编写更少的样板文件?

How to write less boilerplate for property changed notifications?

我有一个 MVVM 风格的应用程序,它以一个包含太多 属性 更改通知的模型告终。具体来说,我有时会错过一些通知,因为它们太多了。

例如,我最终得到这样的属性:

public string CustomEmail {
    get => customEmail;
    set
    {
        customEmail = value;
        OnChanged("CustomEmail");
        OnChanged("IsSendAllowed");
        OnChanged("IsNotFaxEmail");
    }
}

有没有更好的组织方式?例如有没有办法标记 属性 [DependsOn("CustomEmail")] bool IsNotFaxEmail { ... }?

或者,如果大多数属性都用于绑定,我是否应该全部使用转换器?我宁愿不要像 {Binding CustomEmail, Converter=EmailIsFaxToElementVisibilityConverter}.

这样愚蠢的转换器

我是否遗漏了一些更简单的解决方案?

您可以编写接受多个 属性 名称的 NotifyPropertyChanged 方法。它并没有真正减少代码量,但至少允许只进行一次方法调用。

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(params string[] propertyNames)
    {
        var propertyChanged = PropertyChanged;

        if (propertyChanged != null)
        {
            foreach (var propertyName in propertyNames)
            {
                propertyChanged.Invoke(this,
                   new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

public class ViewModel : ViewModelBase
{
    private string customEmail;

    public string CustomEmail
    {
        get => customEmail;
        set
        {
            customEmail = value;

            NotifyPropertyChanged(
                nameof(CustomEmail),
                nameof(IsSendAllowed),
                nameof(IsNotFaxEmail));
        }
    }
}

我不经常发现这么多依赖关系,但我可以概述一个我见过的解决方案。 创建一个属性。将其称为带有字符串参数的 AlsoRaise 属性。 您可能会想到一个更好的名字。但是我觉得 dependson 不太对,因为它是反过来的。

[AlsoRaise(nameof(IsSendAllowed))]
[AlsoRaise(nameof(IsNotFaxEmail))]
public string CustomEmail

然后你就有了一些东西可以驱动你要为 CustomEmail 发出更改通知的其他属性的列表。

在静态构造函数中,您使用这些属性填充字典。您迭代 public 个属性,获取这些属性。

在 OnChanged 中,您在该字典中查找您的 属性 姓名,并针对 属性 姓名以及您找到的任何字符串提出 属性 changed。或者当然是 none。

我可能忘记了一些部分,因为我看到了这个实现。

我觉得PropertyChanged.Fody is exactly what you are looking for. It is a specialized library for Fody,这是一个代码编织工具。它允许在构建时操纵程序集的 IL 代码,例如用于向 类 添加样板代码,例如实施 属性 更改通知。

PropertyChanged.Fody非常强大,可以自动实现INotifyPropertyChanged。下面是来自官方存储库的示例。这就是您要写的全部,Fody 将添加(编织)其余部分。

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }
    public string FullName => $"{GivenNames} {FamilyName}";
}

此外,检测到此示例中的 GivenNamesFamilyName 等依赖属性,如果它们也发生更改,也会通知 FullName 的 属性 更改。有很多选项可以通过属性手动配置 属性 更改通知,例如:

这些属性对您的方案最有用。您的样本将减少为:

[AlsoNotifyFor("IsSendAllowed")]
[AlsoNotifyFor("IsNotFaxEmail")]
public string CustomEmail { get; set; }

或者反过来 DependsOn:

[DependsOn("CustomEmail")]
public string IsSendAllowed { get; set; }

[DependsOn("CustomEmail")]
public string IsNotFaxEmail { get; set; }

有关 Fody 的所有属性和其他强大机制的更多示例和概述,请查看 GitHub Wiki