【问题标题】:is it a good idea that the view model implements the IDataErrorInfo for validation?视图模型实现 IDataErrorInfo 进行验证是个好主意吗?
【发布时间】:2013-07-23 14:58:59
【问题描述】:

我有一个使用 MVVM 模式的应用程序,我想在用户填写信息时实现验证。

我想使用 IDataErrorInfo,但我不知道我的视图模型实现该接口是否是个好主意,或者我创建一个新类是否更好。使用 IDataErrorInfo 和 MVVM 模式实现验证的最佳方法是什么?

编辑:我看到在某些示例中,实现在模型中(它与视图模型不同),但在我的情况下,模型基本上是我在创建我的数据库时从我的数据库创建的 POCO 实体edmx 模型与实体框架,所以我想避免需要修改这个实体,因为如果我不需要更新我的模型,我将不得不再次做这项工作。

谢谢。

【问题讨论】:

  • 我会说VM实现IDataErrorInfo是一个非常好的主意。该界面非常“协助”将信息传递给用户(视图)以查找背景错误(数据错误)。这正是 VM 的用途,将它放在那里是完全有效的。 Comments in this answer 和它下面的一个争论把它放在模型中。将它定义在另一个类中比模型和 VM 抽象也不会“错误”,但可能不值得。
  • ..继续。如果您使用IDataErrorInfo 检查的逻辑如此复杂以至于您想重新使用它,那么我会将这些功能检查移到服务中,然后让VM 实现接口并在VM 使用该服务来访问复杂的验证逻辑。这样,您就可以重复使用和共享代码,并且每个 VM 只需自己实现 IDataErrorInfo 就可以保持干净和简单

标签: wpf validation mvvm idataerrorinfo


【解决方案1】:

将验证逻辑与 UI 分开总是一个好主意。这样,使用 IDataErrorInfo 是正确的。

在视图模型和模型之间,我更喜欢在视图模型上实现 IDataErrorInfo,因为 UI 使用该接口。您可以通过直接在测试代码中调用索引器来模拟 UI,但如果您确实需要业务逻辑层中的验证逻辑,那么这样的调用没有多大意义。

在我们的项目中,validation是一个比较独立的组件,可以通过配置同时供表现层和业务逻辑层使用。从视图模型的角度来看,它很薄,只包含一个调用和在索引器内部构造验证结果。

另外,另一个考虑因素是 INotifyDataErrorInfo,它由 .Net 4.5 和 Silverlight 提供。它为一个耗时的验证提供了来自一个属性的更多验证结果和异步验证,这是我们计划更新到 .Net 4.5 后想要的。

希望对您有所帮助。

【讨论】:

    【解决方案2】:

    我同意绝大多数 cmets 关于这个主题的看法,但我会回答提供我对这个界面的“升级”。

    我看到的IDataErrorInfo 接口的问题是它一次只能解决一个错误。因此,我在BaseDataType 类(我所有数据类型的基类)中添加了一个额外的字段:

    protected ObservableCollection<string> errors = new ObservableCollection<string>();
    

    然后我添加了以下属性:

    // this just enables me to add into the error collection from outside this class
    public ObservableCollection<string> ExternalErrors
    {
        get { return externalErrors; }
    }
    
    public override ObservableCollection<string> Errors
    {
        get
        {
            errors = new ObservableCollection<string>();
            // add properties to validate
            errors.AddUniqueIfNotEmpty(this["Property1ToValidate"]);
            errors.AddUniqueIfNotEmpty(this["Property2ToValidate"]);
            errors.AddUniqueIfNotEmpty(this["Property3ToValidate"]);
            // add external errors (from view models)
            errors.AddRange(ExternalErrors);
            return errors;
        }
    }
    
    public virtual bool HasError
    {
        get { return Errors != null && Errors.Count > 0; }
    }
    

    AddUniqueIfNotEmpty 方法是一种扩展方法,相信大家都能猜到它的作用。

    使用它,我可以直接绑定到视图中的错误集合,甚至更好的是,使用BoolToVisibilityConverter 绑定到HasError 属性,以便在集合为空时隐藏显示错误的控件。

    【讨论】:

      【解决方案3】:

      如果您有一个保存数据的实体或自定义类型(如 Person、Student 等),那么您必须在您的实体或自定义类型中实现 IDataErrorInfo。假设您有一个允许输入学生数据的视图,并且您有它的 ViewModel StudentViewModel,并且此 ViewModel 具有 Student 类型的属性 Student,其属性(如 Name、Age 等)绑定到 View 的控件。要触发验证并更改以反映 UI,您必须在此 Student 类中实现 IDataErrorInfo 不在您的 ViewModel 中,并且您还必须在此类中实现 INotifyPropertyChanged 。所以我的理解是,如果您的 ViewModel 中的属性很少,它们的类型(字符串和 ValueTypes)并绑定到 View,并且您想对它们应用验证,那么您必须在 ViewModel 中实现 IDataErrorInfo。如果你有 CustomType /Entity 那么你必须在那些不在 ViewModel 的类中实现接口。

      我不理解,如果您希望验证触发,则必须在其 EndProperties(如学生的姓名、年龄)绑定到视图控件的类中实现 IDataErrorInfo 和 INotifyPropertyChanged。

          //Interface which has fields of Student class on which ValidationAttribute are to be applied
      public interface IStudent
      {
          [Required(ErrorMessage = "Name is required")]
          string Name { get; set; }
      
          [Range(10, 90, ErrorMessage = "Age should be between 10 and 90")]
          int Age { get; set; }
      }
      //Partial Class to implement IStudent
      public partial class Student : IStudent
      {
      
      }
      
      //POCO
      public partial class Student : INotifyPropertyChanged
      {
          private string name;
          private int age;
      
          public string Name
          {
              get
              {
                  return name;
              }
              set
              {
                  name = value;
      
                  Notify("Name");
              }
          }
      
          public int Age
          {
              get
              {
                  return age;
              }
              set
              {
                  age = value;
      
                  Notify("Age");
              }
          }
          private void Notify(string propName)
          {
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs(propName));
          }
      
          public event PropertyChangedEventHandler PropertyChanged;
      }
      

      【讨论】:

      • 我理解这个想法。那么如何在我的 POCO 实体中实现验证呢?这是由 T4 模板从我的数据库创建的 edmx 模型生成的。如果我更新我的数据库并且我需要再次创建实体,我会丢失所有更改(在这种情况下是验证)。有没有办法在从数据库生成的实体中实现验证?
      • 你在使用基于属性的验证吗?
      • 目前我不使用任何类型的验证,我正在尝试确定最佳选择。
      • 在使用 T4 模板生成的 POCO 上应用验证并非易事。几个月前我也遇到过同样的问题。我解决了这个问题,就像我更新的代码一样,但是使用这种方式你必须创建 ValidationAttributes。
      • 我将 POCO 创建为部分,然后为该 POCO 创建了一个 Partial 类,并创建了具有 POCO 字段的接口,在这些字段上应用验证,并在这些接口成员上应用属性。我希望你明白我的想法。
      【解决方案4】:
      <TextBox Text="{Binding Path=MyCoolProperty, ValidationOnDataErrors=true}"
      

      也许我错过了一些东西,但如果你有这样的绑定——你的“MyCoolProperty”类必须实现 INotifyPropertyChanges 和 IDataErrorInfo——否则它将无法工作。

      所以我想说的问题不是:“应该实现 IDataErrorInfo”,而是应该如何实现 IDataErrorInfo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-08
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        相关资源
        最近更新 更多