2010-09-17 55 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