2010-09-17 64 views
2

我使用Prism MVVM框架实现数据验证在WPF。我在ViewModel中使用干净的数据实体,这些实体绑定到表示层。棱镜IDataErrorInfo的验证与DataAnnotation在视图模型实体

<TextBox Text="{Binding User.Email, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" /> 

我已经实现了一个通用实现IDataErrorInfo的在运行验证对DataAnnotation基ViewModel类的属性在我的实体(在这种情况下用户)。

的问题是,结合实体时,WPF框架查找IDataErrorInfo的有关实体,而不是视图模型这是我想这样的逻辑存在。如果我用我的ViewModel中的属性包装我的实体,那么一切正常,但我不想在ViewModel内损害实体的使用。

有没有办法来告诉WPF来寻找IDataErrorInfo的在视图模型,而不是多数民众赞成被绑定的子对象?

谢谢, 迈克

回答

8

我去的选择是在被所有的ViewModels和实体扩展一个基类明确实现IDataErrorInfo的。这似乎是让事情与WPF结合在一起的最佳折衷办法,并且至少将调用者隐藏的IDataErrorInfo的实现保留为至少显得干净。我公开了一个受保护的ValidateProperty,如果需要,可以在子类中覆盖任何自定义行为(如Password/PasswordConfirmation方案)。

public abstract class DataErrorInfo : IDataErrorInfo 
{ 
    string IDataErrorInfo.Error 
    { 
     get { return null; } 
    } 

    string IDataErrorInfo.this[string columnName] 
    { 
     get { return ValidateProperty(columnName); } 
    } 

    protected virtual string ValidateProperty(string columnName) 
    { 
     // get cached property accessors 
      var propertyGetters = GetPropertyGetterLookups(GetType()); 

      if (propertyGetters.ContainsKey(columnName)) 
      { 
       // read value of given property 
       var value = propertyGetters[columnName](this); 

       // run validation 
       var results = new List<ValidationResult>(); 
       var vc = new ValidationContext(this, null, null) { MemberName = columnName }; 
       Validator.TryValidateProperty(value, vc, results); 

       // transpose results 
       var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage); 
       return string.Join(Environment.NewLine, errors); 
      } 
      return string.Empty; 
    } 

    private static readonly Dictionary<string, object> PropertyLookupCache = 
     new Dictionary<string, object>(); 

    private static Dictionary<string, Func<object, object>> GetPropertyGetterLookups(Type objType) 
    { 
     var key = objType.FullName ?? ""; 
     if (!PropertyLookupCache.ContainsKey(key)) 
     { 
      var o = objType.GetProperties() 
      .Where(p => GetValidations(p).Length != 0) 
      .ToDictionary(p => p.Name, CreatePropertyGetter); 

      PropertyLookupCache[key] = o; 
      return o; 
     } 
     return (Dictionary<string, Func<object, object>>)PropertyLookupCache[key]; 
    } 

    private static Func<object, object> CreatePropertyGetter(PropertyInfo propertyInfo) 
    { 
     var instanceParameter = Expression.Parameter(typeof(object), "instance"); 

     var expression = Expression.Lambda<Func<object, object>>(
      Expression.ConvertChecked(
       Expression.MakeMemberAccess(
        Expression.ConvertChecked(instanceParameter, propertyInfo.DeclaringType), 
        propertyInfo), 
       typeof(object)), 
      instanceParameter); 

     var compiledExpression = expression.Compile(); 

     return compiledExpression; 
    } 

    private static ValidationAttribute[] GetValidations(PropertyInfo property) 
    { 
     return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true); 
    } 


} 
+0

你是否在为你的实体使用poco类? – dnndeveloper 2010-10-02 21:30:11

+0

我想问哪个是这个问题的原因,在这个解决方案中,我的实体必须扩展DataErrorInfo,但是否则它们会是POCO。 – TheCodeKing 2011-08-27 23:33:42

2

当然,我不知道你的整个方案,但我相信使用视图模型包裹业务实体(或模型)是MVVM模式的很大一部分,特别是如果你没有可绑定的模型(你可以直接绑定的模型)。包装可以包括本场景中的错误管理信息或其他事物,如定制模型显示等。

也就是说,您可以看看Prism的v4.0 MVVM RI,它使用INotifyDataErrorInfo进行验证,并且应该提供有关验证方法的有趣见解。

我希望这会有所帮助。

感谢, 达米安

+0

感谢您的意见达米安。我回头调查了INotifyDataErrorInfo,但不幸的是它在WPF中不受支持。我已经在使用标准的MVVM,唯一的问题就是向我的实体添加验证逻辑,这让我很讨厌。然而,它似乎是让事情优雅地工作的最佳折衷方案。 – TheCodeKing 2010-09-20 12:10:40