WPF:如何在MVVM中实现广泛的数据验证

本文关键字:数据 验证 实现 MVVM WPF | 更新日期: 2023-09-27 18:02:17

可能重复:
如何处理ViewModel而非View';中的Validation.Error;背后的代码是什么?

如何在WPF中使用数据验证的常见示例通常只涉及为控件定义错误模板并在控件工具提示中显示错误消息。我想做的是创建一个所有ValidationErrors的集合,将其显示在ItemsControl中给用户,并在用户单击ItemsControl中关联的(错误(项时聚焦无效控件。

在非MVVM项目中,这不是什么大问题,因为我可以在每个控件上调用Validation.GetErrors(obj(,并用它构建我的集合。但是在MVVM中,ViewModel没有到View的直接链接,因此我无法调用ViewModel中的GetErrors((来构建我的集合,因为我没有对View上的控件的任何引用。

是否有将ValidationErrors从视图绑定或路由到ViewModel的技巧,或者这在MVVM中是不可能实现的?

WPF:如何在MVVM中实现广泛的数据验证

数据验证和mvvm的常用方法是使用IDataErrorInfo。你会在网上找到很多信息。

将焦点设置为绑定到的控件的诀窍是使用Binding属性名称。Josh Smith发布了关于它的博客。

下面是ViewModel基类的ValidationErrors属性的代码。我们的视图模型基类实现了INotifyPropertyChanged和IDataErrorInfo,对于Silverlight 4+还实现了INnotifyDataErrorInfo。

请注意Silverlight和WPF的条件编译语句。对于Silverlight,我们只需使用标准的INotifyDataErrorInfo机制通知视图,而对于WPF,我们触发PropertyChanged事件来刷新受影响的控件。

用法非常简单。每个派生视图模型类只需要设置ValidationErrors属性,其他一切都由属性设置器处理。ValidationFailure失败类属于FluentValidation库,我们使用它来实现实际的验证逻辑。

public IList<ValidationFailure> ValidationErrors
{
  get { return GetPropertyValue(() => ValidationErrors); }
  protected set
  {
    List<string> obsoleteValidationErrors = null;
    // collect names of properties that do not longer have errors
#if SILVERLIGHT
    if (ErrorsChanged != null)
#else
    if (PropertyChanged != null)
#endif
    {
      var oldErrorsCollection = ValidationErrors != null && ValidationErrors.Count > 0 ? ValidationErrors : new List<ValidationFailure>();
      var newErrorsCollection = value != null && value.Count > 0 ? value : new List<ValidationFailure>();
      var newPropertyNames = newErrorsCollection.Select(x => x.PropertyName).Distinct().ToDictionary(x => x);
      // figure out which errors are no longer part of the new validation error collection
      obsoleteValidationErrors = oldErrorsCollection.Where(x =>
        !newPropertyNames.ContainsKey(x.PropertyName)).Select(x => x.PropertyName).Distinct().ToList();
    }
    if (SetPropertyValue(() => ValidationErrors, value))
    {
      // fire event for properties that do not longer have errors
      if (obsoleteValidationErrors != null)
      {
        foreach (var obsoleteValidationErrorPropertyName in obsoleteValidationErrors)
#if SILVERLIGHT
          ErrorsChanged(this, new DataErrorsChangedEventArgs(obsoleteValidationErrorPropertyName));
#else
          OnPropertyChanged(obsoleteValidationErrorPropertyName);
#endif
      }
      // fire event for properties that now have errors
#if SILVERLIGHT
      if (value != null && ErrorsChanged != null)
#else
      if (value != null && PropertyChanged != null)
#endif
      {
        var propertyNames = value.Select(x => x.PropertyName).Distinct().ToList();
        foreach (var failedProperty in propertyNames)
#if SILVERLIGHT
          ErrorsChanged(this, new DataErrorsChangedEventArgs(failedProperty));
#else
          OnPropertyChanged(failedProperty);
#endif
      }
    }
  }
}