如何为 属性 更改通知编写更少的样板文件?
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}";
}
此外,检测到此示例中的 GivenNames
和 FamilyName
等依赖属性,如果它们也发生更改,也会通知 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。
我有一个 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 发出更改通知的其他属性的列表。
在静态构造函数中,您使用这些属性填充字典
在 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}";
}
此外,检测到此示例中的 GivenNames
和 FamilyName
等依赖属性,如果它们也发生更改,也会通知 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。