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

C# WPF:如何在MVVM中实现广泛的数据验证,c#,.net,wpf,vb.net,validation,C#,.net,Wpf,Vb.net,Validation,可能重复: 如何在WPF中使用数据验证的常见示例通常只涉及为控件定义错误模板并在控件工具提示中显示错误消息。我要做的是创建所有ValidationErrors的集合,在ItemsControl中向用户显示它,并在用户单击ItemsControl中的关联(错误)项时聚焦无效控件 在非MVVM项目中,这不是什么大问题,因为我可以在每个控件上调用Validation.GetErrors(obj),并用它构建我的集合。但是在MVVM中,ViewModel没有与视图的直接链接,因此我无法调用ViewMo

可能重复:

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

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


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

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


为绑定到的控件设置焦点的技巧是使用绑定属性名。乔希·史密斯在博客上写过这件事

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

请注意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
      }
    }
  }
}
公共IList验证错误
{
获取{return GetPropertyValue(()=>ValidationErrors);}
保护集
{
List obsoleteValidationErrors=null;
//收集不再有错误的属性的名称
#如果银光
if(ErrorsChanged!=null)
#否则
if(PropertyChanged!=null)
#恩迪夫
{
var oldErrorsCollection=ValidationErrors!=null&&ValidationErrors.Count>0?ValidationErrors:new List();
var newErrorsCollection=value!=null&&value.Count>0?值:新列表();
var newPropertyNames=newErrorsCollection.Select(x=>x.PropertyName).Distinct().ToDictionary(x=>x);
//找出哪些错误不再是新验证错误集合的一部分
obsoleteValidationErrors=oldErrorsCollection.Where(x=>
!newPropertyName.ContainsKey(x.PropertyName)).Select(x=>x.PropertyName.Distinct().ToList();
}
if(SetPropertyValue(()=>ValidationErrors,value))
{
//不再有错误的属性的激发事件
if(废弃验证错误!=null)
{
foreach(obsoleteValidationErrors中的变量obsoleteValidationErrorPropertyName)
#如果银光
ErrorsChanged(这是新数据ErrorsChangeDeventargs(废弃验证ErrorPropertyName));
#否则
OnPropertyChanged(obsoleteValidationErrorPropertyName);
#恩迪夫
}
//现在有错误的属性的激发事件
#如果银光
如果(值!=null&&ErrorsChanged!=null)
#否则
if(value!=null&&PropertyChanged!=null)
#恩迪夫
{
var propertyNames=value.Select(x=>x.PropertyName.Distinct().ToList();
foreach(propertyNames中的var failedProperty)
#如果银光
ErrorsChanged(这是新数据ErrorsChangeDeventargs(failedProperty));
#否则
OnPropertyChanged(failedProperty);
#恩迪夫
}
}
}
}

为此使用代码隐藏并不违法。只要加一点C#。