如何在MVVM模型中实现数据验证?

时间:2015-05-17 21:22:02

标签: wpf mvvm validation

我有一个需要数据验证的MVVM应用程序。我想在模型中保留验证,因此模型可以很容易地与其他视图模型一起使用,而无需复制验证代码。使用WPF进行数据验证的大多数示例要么在MVVM设置中的视图模型中完成验证,要么在模型直接绑定到视图并且未使用视图模型时在模型中完成数据验证。

我想在我的模型中进行数据验证,让视图模型将我的模型暴露给视图,并使视图仍然能够从模型接收验证反馈。我计划使用IDataErrorInfo或Data Annotations进行验证。我已经找到了一些这样做的例子,但这两种方法似乎都不理想。

使用数据注释完成here,但需要适量的自定义代码。

我更喜欢使用IDataErrorInfo的this方法,因为它不涉及自定义代码,但我不知道我是否对所采用的方法感到满意,其中整个模型作为单个属性公开给视图,而不仅仅暴露所需的个人财产。

是否有更好或更推荐的方法来实现这一目标?

3 个答案:

答案 0 :(得分:2)

我最终避开了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属性不为空,并且密码验证会进行范围检查。在Email属性被视为处于有效状态之前,密码验证不会触发。

validation project is hosted on GitHub并支持各种各样的事情。

  1. 自定义验证代理

  2. 在自定义代理中拦截验证结果

  3. 本地化验证失败消息
  4. INotifyPropertyChanged支持
  5. 验证1方法调用中的所有属性
  6. 根据需要验证各个属性。
  7. 根据需要对整个模型或每个属性进行重新验证。
  8. 来自视图模型的验证注入
  9. 验证代理。 (执行验证并将结果传递给代理)。
  10. 根据其他媒体资源的结果进行验证。支持嵌套属性。
  11. 根据其他财产的状况进行有条件的验证。
  12. 容易模板化,因此您可以获得验证警告,信息和错误。
  13. 我已经在几个项目中使用过它并且效果很好。我得到的一个真正的好处是,我现在可以在多个对象中重用验证规则(IDataErrorInfo遭受大量复制/粘贴),我可以围绕规则提供的验证消息创建DataTemplates 。我们有警告模板和错误模板。给你很大的灵活性。

    该项目的最初目的是将其用于WinRT项目,但它很快就成长为我在移动 WPF应用程序中使用的内容。

答案 1 :(得分:1)

您链接的数据注释方法有点多余,因为框架已经具有可绑定验证规则的概念(请参阅Taking data binding, validation and MVVM to the next level)。数据注释方法唯一有用的时候是在与UI完全分离的位置进行验证。

除了这两个选项之外,使用IDataErrorInfo是您唯一的选择。请注意,您不是“将整个模型作为单个属性公开给视图”,而是使用接口将自定义实体公开给绑定框架 - 这有很大的不同。如果选择此方法,请确保使用资源字符串来保存错误消息,而不是使用硬编码文本。如果你有不同的人在视图和视图模型上工作,那么IDataErrorInfo方法很有用 - 执行视图的人不需要了解有关视图模型的特定验证的任何信息。

答案 2 :(得分:0)

我认为您无法在模型中进行所有验证,这就是ViewModel的目的。

原因是,在“视图”中,您可能会有一些文本框,将其“文本”属性绑定到数字,日期和其他不能直接从字符串分配的数据类型。

如果用户清除该框,或者在输入值的途中(例如,他们以负号或小数点开头的数字,或者正在编辑日期),则无法将TextBox.Text转换为正确的类型WPF发送到模型。因此该模型未更新-它仍然具有其旧值,并认为它是有效的。如果用户单击“保存”按钮,它将保存最后一个有效值,而不是屏幕上的值。

除非将UI限制为始终有效的控件(复选框,列表框等),否则您需要使用视图模型来验证值,然后才能将其传递给模型。