首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > Ruby Rails >

【WPF】MVVM模式上的输入校验(IDataErrorInfo + DataAnnotations)

2012-11-22 
【WPF】MVVM模式下的输入校验(IDataErrorInfo + DataAnnotations)【前言】Windows Presentation Foundation (W

【WPF】MVVM模式下的输入校验(IDataErrorInfo + DataAnnotations)
【前言】
Windows Presentation Foundation (WPF) 具有一个丰富数据绑定系统。除了作为通过 Model-View-ViewModel (MVVM) 模式从支持逻辑和数据对 UI 定义进行松散耦合的关键推动力之外,数据绑定系统还为业务数据验证方案提供强大而灵活的支持。WPF 中的数据绑定机制包括多个选项,可用于在创建可编辑视图时校验输入数据的有效性。

验证机制说明异常通过在某个 Binding 对象上设置 ValidatesOnExceptions 属性,如果在尝试对源对象属性设置已修改的值的过程中引发异常,则将为该 Binding 设置验证错误。ValidationRulesBinding 类具有一个用于提供 ValidationRule 派生类实例的集合的属性。这些 ValidationRules 需要覆盖某个 Validate 方法,该方法由 Binding 在每次绑定控件中的数据发生更改时进行调用。如果 Validate 方法返回无效的 ValidationResult 对象,则将为该 Binding 设置验证错误。IDataErrorInfo通过在绑定数据源对象上实现 IDataErrorInfo 接口并在 Binding 对象上设置 ValidatesOnDataErrors 属性,Binding 将调用从绑定数据源对象公开的 IDataErrorInfo API。如果从这些属性调用返回非 null 或非空字符串,则将为该 Binding 设置验证错误。
在使用 WPF 中的数据绑定来呈现业务数据时,通常应使用 Binding 对象在目标控件的单个属性与数据源对象属性之间提供数据管道。
若要使绑定验证有效,首先需要进行 TwoWay 数据绑定。这意味着,除了从源属性流向目标属性以进行显示的数据之外,编辑过的数据也会从目标流向源。
当 TwoWay 数据绑定中输入或修改数据时,将启动以下工作流:
用户通过击键、鼠标、触摸或与各元素间的手写笔交互来输入或修改数据,从而更改元素的属性。如果需要,可将数据转换为数据源属性类型。设置源属性值。触发 Binding.SourceUpdated 附加事件。如果数据源属性上的 setter 引发异常,则异常会由 Binding 捕获,并可用于指示验证错误。如果实现了 IDataErrorInfo 接口,则会对数据源对象调用该接口的方法获得该属性的错误信息。向用户呈现验证错误指示,并触发 Validation.Error 附加事件。
本篇主要介绍MVVM模式下, IDataErrorInfo 的校验以及结合 DataAnnotation 的实现方法。比较来说,在 set 里直接 throw Exception 实现最为简单,但不适合组合校验且Model里需要编写过重的校验代码;ValidationRules 更适合在用户控件或者自定义控件场合使用。IDataErrorInfo 则是比较普遍且灵活的校验实现方式。

【实例分析】
我们先来看看 IDataErrorInfo 的实现方法:Error 属性用于指示整个对象的错误,而索引器用于指示单个属性级别的错误。
两者的工作原理相同:如果返回非 null 或非空字符串,则表示存在验证错误。否则,返回的字符串用于向用户显示错误。

Person.cs 的 Age 属性进行了校验。



利用 IDataErrorInfo 的好处是它可用于轻松地处理交叉耦合属性。但也具有一个很大的弊端:
索引器的实现通常会导致较大的 switch-case 语句(对象中的每个属性名称都对应于一种情况),
必须基于字符串进行切换和匹配,并返回指示错误的字符串。而且,在对象上设置属性值之前,不会调用 IDataErrorInfo 的实现。

为了避免出现大量的 switch-case,并且将校验逻辑进行分离提高代码复用,于是 DataAnnotations 华丽登场。
改造下上面的 Person 类,加上 [Range]  ValidationAttribute:(需要添加 System.ComponentModel.DataAnnotations.dll)


(2) 利用 CustomerValidationAttribute
先实现一个 public static 的校验方法(必须返回 ValidationResult )


在实际开发中,我们还经常使用 EF 等 ORM 来做数据访问层,Model 通常会由这个中间件自动生成(利用T4等代码生成工具)。而他们通常是 POCO 数据类型,这时候如何能把属性的校验特性加入其中呢。这时候, TypeDescriptor.AddProviderTransparent + AssociatedMetadataTypeTypeDescriptionProvider 可以派上用场,它可以实现在另一个类中增加对应校验特性来增强对原类型的元数据描述。按照这种思路,将上面的 Person 类分离成两个文件:第一个分离类,可以想象是中间件自动生成的 Model 类。第二个分离类中实现 IDataErrorInfo,并定义一个Metadata 类来增加校验特性。(EF CodeFirst 也可以使用这一思路)

Person.cs (原生的 Person 类,没有校验特性)
 public static class ValidationExtension {     public static string ValidateProperty(this object obj, string propertyName)     {         if (string.IsNullOrEmpty(propertyName))             return string.Empty;         var targetType = obj.GetType();         //你也可以利用 MetadataType 在分离类上声明         //var targetMetadataAttr = targetType.GetCustomAttributes(false)         //    .FirstOrDefault(a => a.GetType() == typeof(MetadataTypeAttribute)) as MetadataTypeAttribute;         //if (targetMetadataAttr != null && targetType != targetMetadataAttr.MetadataClassType)         //{         //    TypeDescriptor.AddProviderTransparent(         //           new AssociatedMetadataTypeTypeDescriptionProvider(targetType, targetMetadataAttr.MetadataClassType), targetType);         //}         if (targetType != typeof(MetadataType))         {             TypeDescriptor.AddProviderTransparent(                    new AssociatedMetadataTypeTypeDescriptionProvider(targetType, typeof(MetadataType)), targetType);         }         var propertyValue = targetType.GetProperty(propertyName).GetValue(obj, null);         var validationContext = new ValidationContext(obj, null, null);         validationContext.MemberName = propertyName;         var validationResults = new List();         Validator.TryValidateProperty(propertyValue, validationContext, validationResults);         if (validationResults.Count > 0)         {             return validationResults.First().ErrorMessage;         }         return string.Empty;     }}
利用这一思路,可以很容易的实现各种 POCO Model 类型的校验逻辑分离,这对于开发分层架构的应用框架时非常有用。




热点排行