如何在 MVVM 模型中实现数据验证?
How to implement data validation in model of MVVM?
我有一个需要数据验证的 MVVM 应用程序。我想将验证保留在模型中,这样模型就可以很容易地与其他视图模型一起重用,而不必复制验证代码。大多数使用 WPF 进行数据验证的示例要么在 MVVM 设置的视图模型中完成验证,要么在模型直接绑定到视图且未使用视图模型时在模型中完成数据验证。
我想在我的模型中进行数据验证,让视图模型向视图公开我的模型,并且让视图仍然能够接收来自模型的验证反馈。我计划使用 IDataErrorInfo 或数据注释进行验证。我发现了一些这样做的例子,但这两种方法似乎都不理想。
它是通过数据注释 here 完成的,尽管它使用了适量的自定义代码。
我更喜欢 this 使用 IDataErrorInfo 的方法,因为它不涉及自定义代码,但是我不知道我是否对所采用的方法感到满意,其中整个模型作为单个 属性 到视图,而不是仅公开所需的单个属性。
是否有更好或更推荐的方法来完成此操作?
您链接的数据注释方法有些多余,因为框架已经有了可绑定验证规则的概念(请参阅 Taking data binding, validation and MVVM to the next level)。数据注释方法唯一有用的时候是在完全脱离 UI.
的地方进行验证时
除了这两个选项之外,使用 IDataErrorInfo 是您唯一的选择。请注意,您不是 "exposing the entire model to the view as a single property",而是使用接口将您的自定义实体暴露给绑定框架 - 这是一个很大的区别。如果选择此方法,请确保使用资源字符串来保存错误消息,而不是使用硬编码文本。如果您有不同的人处理视图和视图模型,则 IDataErrorInfo 方法很有用 - 执行视图的人不需要了解有关视图模型的特定验证的任何信息。
我最终避免使用 IDataErrorInfo
东西,因为使用起来非常痛苦。我们使用属性对模型进行了验证。
public class User : ValidatableBase, INotifyPropertyChanged
{
private string password = string.Empty;
public event PropertyChangedEventHandler PropertyChanged;
[ValidateObjectHasValue(
FailureMessage = "E-Mail can not be left blank.",
ValidationMessageType = typeof(ValidationErrorMessage))]
public string Email
{
get
{
return this.email;
}
set
{
this.email = value;
this.OnPropertyChanged("Email");
}
}
[ValidateStringIsGreaterThan(
GreaterThanValue = 6,
ValidateIfMemberValueIsValid = "Email",
FailureMessage = "Password must be greater than 6 characters.",
ValidationMessageType = typeof(ValidationErrorMessage))]
[ValidateStringIsLessThan(
LessThanValue = 20,
ValidateIfMemberValueIsValid = "Email",
FailureMessage = "Password must be less than 20 characters.",
ValidationMessageType = typeof(ValidationErrorMessage))]
public string Password
{
get
{
return this.password;
}
set
{
this.password = value;
this.OnPropertyChanged("Password");
}
}
/// <summary>
///
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged(string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
在上面的示例中,执行验证以确保电子邮件 属性 不为空,并且密码验证会进行范围检查。在 Email
属性 被认为处于有效状态之前,不会触发密码验证。
validation project is hosted on GitHub 并支持范围广泛的事物。
自定义验证委托
拦截自定义委托中的验证结果
- 本地化验证失败消息
- INotifyPropertyChanged 支持
- 验证 1 个方法调用中的所有属性
- 根据需要验证各个属性。
- 对整个模型重新查询验证,或根据需要每个属性。
- 来自视图模型的验证注入
- 验证代理。 (执行验证并将结果传递给代理)。
- 根据另一个 属性 的结果进行验证。支持嵌套属性。
- 根据其他属性的状态有条件地验证。
- 易于模板化,因此您可以获得验证警告、信息和错误。
我已经在几个项目中使用过它并且效果很好。我从中得到的一个真正的大好处是我现在可以在多个对象之间重复使用验证规则(IDataErrorInfo 遭受很多 copy/paste)并且我可以围绕规则提供什么样的验证消息创建 DataTemplates .我们有警告模板和错误模板。给你很大的灵活性。
该项目的初衷是将其用于 WinRT 项目,但它很快发展成为我在手机 和 WPF 应用程序上使用的东西。
我认为您无法在模型中进行所有验证,而这正是 ViewModel 的目的。
原因是在视图中您可能有一些文本框将它们的文本 属性 绑定到数字、日期和其他不能直接从字符串分配的数据类型。
如果用户清除该框,或正在输入一个值(可能他们以减号或小数点开始数字,或者正在编辑日期),则 TextBox.Text 无法转换为WPF 发送到模型的正确类型。所以模型没有更新——它仍然有它的旧值并且认为它是有效的。如果用户点击“保存”按钮,它将保存最后一个有效值,而不是屏幕上显示的值。
除非您将 UI 限制为始终有效的控件(复选框、列表框等),否则您需要一个视图模型来验证值,然后才能将它们传递给模型。
我有一个需要数据验证的 MVVM 应用程序。我想将验证保留在模型中,这样模型就可以很容易地与其他视图模型一起重用,而不必复制验证代码。大多数使用 WPF 进行数据验证的示例要么在 MVVM 设置的视图模型中完成验证,要么在模型直接绑定到视图且未使用视图模型时在模型中完成数据验证。
我想在我的模型中进行数据验证,让视图模型向视图公开我的模型,并且让视图仍然能够接收来自模型的验证反馈。我计划使用 IDataErrorInfo 或数据注释进行验证。我发现了一些这样做的例子,但这两种方法似乎都不理想。
它是通过数据注释 here 完成的,尽管它使用了适量的自定义代码。
我更喜欢 this 使用 IDataErrorInfo 的方法,因为它不涉及自定义代码,但是我不知道我是否对所采用的方法感到满意,其中整个模型作为单个 属性 到视图,而不是仅公开所需的单个属性。
是否有更好或更推荐的方法来完成此操作?
您链接的数据注释方法有些多余,因为框架已经有了可绑定验证规则的概念(请参阅 Taking data binding, validation and MVVM to the next level)。数据注释方法唯一有用的时候是在完全脱离 UI.
的地方进行验证时除了这两个选项之外,使用 IDataErrorInfo 是您唯一的选择。请注意,您不是 "exposing the entire model to the view as a single property",而是使用接口将您的自定义实体暴露给绑定框架 - 这是一个很大的区别。如果选择此方法,请确保使用资源字符串来保存错误消息,而不是使用硬编码文本。如果您有不同的人处理视图和视图模型,则 IDataErrorInfo 方法很有用 - 执行视图的人不需要了解有关视图模型的特定验证的任何信息。
我最终避免使用 IDataErrorInfo
东西,因为使用起来非常痛苦。我们使用属性对模型进行了验证。
public class User : ValidatableBase, INotifyPropertyChanged
{
private string password = string.Empty;
public event PropertyChangedEventHandler PropertyChanged;
[ValidateObjectHasValue(
FailureMessage = "E-Mail can not be left blank.",
ValidationMessageType = typeof(ValidationErrorMessage))]
public string Email
{
get
{
return this.email;
}
set
{
this.email = value;
this.OnPropertyChanged("Email");
}
}
[ValidateStringIsGreaterThan(
GreaterThanValue = 6,
ValidateIfMemberValueIsValid = "Email",
FailureMessage = "Password must be greater than 6 characters.",
ValidationMessageType = typeof(ValidationErrorMessage))]
[ValidateStringIsLessThan(
LessThanValue = 20,
ValidateIfMemberValueIsValid = "Email",
FailureMessage = "Password must be less than 20 characters.",
ValidationMessageType = typeof(ValidationErrorMessage))]
public string Password
{
get
{
return this.password;
}
set
{
this.password = value;
this.OnPropertyChanged("Password");
}
}
/// <summary>
///
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged(string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
在上面的示例中,执行验证以确保电子邮件 属性 不为空,并且密码验证会进行范围检查。在 Email
属性 被认为处于有效状态之前,不会触发密码验证。
validation project is hosted on GitHub 并支持范围广泛的事物。
自定义验证委托
拦截自定义委托中的验证结果
- 本地化验证失败消息
- INotifyPropertyChanged 支持
- 验证 1 个方法调用中的所有属性
- 根据需要验证各个属性。
- 对整个模型重新查询验证,或根据需要每个属性。
- 来自视图模型的验证注入
- 验证代理。 (执行验证并将结果传递给代理)。
- 根据另一个 属性 的结果进行验证。支持嵌套属性。
- 根据其他属性的状态有条件地验证。
- 易于模板化,因此您可以获得验证警告、信息和错误。
我已经在几个项目中使用过它并且效果很好。我从中得到的一个真正的大好处是我现在可以在多个对象之间重复使用验证规则(IDataErrorInfo 遭受很多 copy/paste)并且我可以围绕规则提供什么样的验证消息创建 DataTemplates .我们有警告模板和错误模板。给你很大的灵活性。
该项目的初衷是将其用于 WinRT 项目,但它很快发展成为我在手机 和 WPF 应用程序上使用的东西。
我认为您无法在模型中进行所有验证,而这正是 ViewModel 的目的。
原因是在视图中您可能有一些文本框将它们的文本 属性 绑定到数字、日期和其他不能直接从字符串分配的数据类型。
如果用户清除该框,或正在输入一个值(可能他们以减号或小数点开始数字,或者正在编辑日期),则 TextBox.Text 无法转换为WPF 发送到模型的正确类型。所以模型没有更新——它仍然有它的旧值并且认为它是有效的。如果用户点击“保存”按钮,它将保存最后一个有效值,而不是屏幕上显示的值。
除非您将 UI 限制为始终有效的控件(复选框、列表框等),否则您需要一个视图模型来验证值,然后才能将它们传递给模型。