qouoww

质量管理+软件开发=聚焦管理软件的开发与应用

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

对无效数据的输入进行验证和必要的提示是商业应用程序必备的功能之一。Silverlight使用Data Annotation特性标记来进行数据的验证,对用户输入数据的合法性进行判断,阻止非法数据提交到服务器,并给出修改提示。

1、验证错误信息的显示

1)数据输入控件:

image

特征:Label控件变红,输入框变红,在输入框右侧显示红底白色的提示语;

2)ValidationSummary控件

只需要将该控件放在视图中即可:

<sdk:ValidationSummary />

另外还需要将各个输入控件的NotifyOnValidationError属性设置为True;

image

显示特征:在顶端显示错误个数;在下边列出错误字段及错误信息;直到所有错误排除后才消失有关提示信息;

 

3)DataForm控件

image

显示特征:组合式验证信息显示;

如果AutoCommit属性设置为False,在允许用户提交更新之前,DataForm控件会确保被绑定的实体或对象是有效的;如果AutoCommit属性设置为True,则DataForm控件会阻止用户从当前记录中移出,直到验证错误全部纠正为止。

 

4)DataGrid控件:

image

特征:组合式验证错误信息显示;

如果错误未修改将无法添加新行;如果绑定的对象实现了IEditableObject接口,也可以按Esc键退出编辑状态,从而消除错误信息;

 

2、数据验证的类型

  • 属性级验证
  • 对象级验证:比如结束日期应大于开始日期,这两个属性在同一个对象/实体中;
  • 领域级验证:比如提交订单前应确保库存满足订单的需要,否则无法提交订单;

1)定义验证规则:

对于属性级验证,不管是在实体类还是在ViewModels类中,都可以使用System.ComponentModel.DataAnnotations名称空间下的特性标记进行验证规则定义:

[Required] 
[StringLength(50)] 
[RegularExpression(@"(?<user>[^@]+)@(?<host>.+)")] 
public string EmailAddress; 
 
[Range(0, 150)] 
public int Age; 

而定义对象级的验证规则,应继承ValidationAttribute类,然后在相应的类上使用该特类生成的特性标识。

public class SellDatesValidationAttribute : ValidationAttribute 
{ 
    protected override ValidationResult IsValid(object value,  
                                        ValidationContext validationContext) 
    { 
        Product product = value as Product; 
        return product.SellEndDate > product.SellStartDate ?  
            ValidationResult.Success : new ValidationResult( 
                "The sell end date must be greater than the sell start date"); 
    } 
} 

使用方法:
[SellDatesValidation] 
public class Product 
 

2)从对象报告验证错语信息

在Silverlgiht中有几种方式用于从实体/对象报告数据验证信息:

  • 抛出异常
  • 实现IDataErrorInfo接口及相应的行为
  • 实现INotifyDataErrorInfo接口及相应的行为(RIA服务暴露的实体类继承自Entity类,已经实现了INotifyDataErrorInfo接口。)

a)捕捉异常

public string Name 
{ 
    get { return _name; } 
    set 
    { 
        _name = value; 
 
        // Check validation rule  
        if (string.IsNullOrEmpty(value)) 
            throw new ValidationException("You must enter a name for the product"); 
    } 
} 
<TextBox Text="{Binding Name, Mode=TwoWay, ValidatesOnExceptions=True}" /> 

如果将 ValidatesOnExceptions 设置为 true,则告知绑定引擎捕捉当从 TwoWay 绑定中的目标更新源对象时发生的异常。 这些异常以两种方式发生:

a). 从源对象的 setter 函数引发。

b). 由类型转换器引发。

为了收到已发生这些异常的通知,必须将 NotifyOnValidationError 属性设置为 true。

如果将此属性设置为 false,则该应用程序将在发生验证异常时失败而不给出任何提示。

 

b)IDataErrorInfo接口

此接口使数据实体类能够实现自定义验证规则并公开用户界面的验证结果。 通常实现此接口来提供相对简单的客户端验证逻辑。

IDataErrorInfo.Item 属性为整个对象返回一条验证错误消息。 Error 属性为整个对象返回一条错误消息。 这些单一消息中的每条消息都可表示多个错误。

使用方法

将控件绑定到实现 IDataErrorInfo 的实体的属性上,并将 Binding.ValidatesOnDataErrors 属性设置为true。 然后,每当绑定实体的属性更改值时,绑定引擎都会通过将属性名传递到 IDataErrorInfo.Item 验证新值。 绑定引擎使用这些验证结果,为该绑定更新 Validation.Errors 集合。 首先,绑定引擎为来自于异常或 IDataErrorInfo 验证的绑定属性,移除任何现有的错误。 然后,如果新值是无效,绑定引擎会为此属性添加一个新错误。

可以同时使用 ValidatesOnExceptionsValidatesOnDataErrors;但是,发生的任何异常都会阻止 IDataErrorInfo 验证。

实现示例:

http://msdn.microsoft.com/zh-cn/library/system.componentmodel.idataerrorinfo(v=vs.95).aspx

综合来讲,当实现该接口的对象绑定到控件上以后,控件就会根据验证的结果实时显示出错误的信息,但是其灵活性较差,所以该接口一般不予应用。

c)INotifyDataErrorInfo接口

此接口使数据实体类能够实现自定义验证规则并公开用户界面的验证结果。 通常实现此接口来提供服务器端验证逻辑等异步验证逻辑。 此接口还支持自定义错误对象、每个属性的多个错误、跨属性错误和实体级错误。

跨属性错误是影响多个属性的错误。 您可以将这些错误与一个或所有受影响的属性关联,或者您也可以将它们视为实体级错误。 实体级别错误为影响多个属性或影响整个实体的错误,但不影响特定属性。

 

IDataErrorInfo 成员

INotifyDataErrorInfo.HasErrors 属性为实体显示当前是否存在任何验证错误。 GetErrors 方法返回含有固定属性或整个实体的验证错误的 IEnumerable。 您可以实现此方法来为每个受影响的属性在实体级报告跨属性错误,它取决于您的报表需求。

异步验证规则完成处理时,属性或实体的错误 IEnumerable 可能改变或被替换。 当 ErrorsChanged 更改时,一定会发生 IEnumerable 事件。 事件处理程序可以检索新的HasErrorsGetErrors 值,以便为受影响的属性或实体更新用户界面验证反馈。

GetErrors 方法返回的验证错误可以是任何类型。 但是,如果您要实现自定义错误类型,一定要重写 ToString 方法来返回一条错误消息。 Silverlight 在其默认错误报告中使用此字符串。

当您在用户界面提供自定义错误报告时,自定义错误对象十分有用。 例如,您可以创建报告 ToolTip 的模板,以便绑定 ErrorLevel 属性,并以黄色显示警告信息和以红色显示严重错误。

数据绑定支持。

Silverlight 绑定引擎为 INotifyDataErrorInfo 提供内置支持。 若要实现这种支持,只需将控件绑定到实现 INotifyDataErrorInfo 实体的属性上,或绑定空间到实体本身上。 默认情况下,Binding.ValidatesOnNotifyDataErrors 属性必须为 true。

绑定引擎调用 GetErrors 为绑定属性或实体检索一切初始化错误。 如果 Binding.Mode 属性值不是 OneTime,则绑定引擎会将 ErrorsChanged 事件交给监视器以进行更新。 每当绑定属性或实体错误改变,绑定引擎都将调用 GetErrors 来检索已更新的错误。 尽管您可以在自定义错误报告中使用它,请注意绑定引擎永远不会使用 HasErrors 属性。

绑定引擎使用这些验证结果,为该绑定更新 Validation.Errors 集合。 首先,绑定引擎为来自于异常或 INotifyDataErrorInfo 验证的绑定属性,移除任何现有的错误。 然后,如果新值是无效,绑定引擎会为由 GetErrors 方法返回的每个错误添加一个新错误。

INotifyDataErrorInfo 错误不会干扰源自 IDataErrorInfo 的其他错误。 但是,当基于异常的错误处于活动状态时,绑定引擎将不会公开 INotifyDataErrorInfo 错误。

错误报告

Validation.Errors 集合在用户界面中为错误报告提供绑定源。 多个控件从其内置错误的模板绑定到此集合中。

若要提供自定义错误报告,您可以替换或修改已有的错误模板。 或者,您可以处理 ErrorsChanged 事件并直接访问 INotifyDataErrorInfo 成员。

您也可以将 Binding.NotifyOnValidationError 属性设置为 true 并处理 FrameworkElement.BindingValidationError事件。 每次为特定的绑定添加或删除 Validation.Errors 时都会发生此事件。 有些控件,例如 ValidationSummary,需要这种机制,而不是使用 Validation.Errors 集合。

示例:见http://msdn.microsoft.com/zh-cn/library/system.componentmodel.inotifydataerrorinfo(v=vs.95).aspx

相关示例代码随附下载。

 

3)在视图的后置代码中处理验证错误

按如下步骤进行:

a)设置binding的NotifyOnValidationError和ValidatesOnDataErrors属性为True:

<TextBox Text="{Binding Name, Mode=TwoWay, NotifyOnValidationError=True,  
                        ValidatesOnDataErrors=True}"  
         BindingValidationError="NameTextBox_BindingValidationError" /> 

b)在后置代码中处理BindingValidationError事件。

private void NameTextBox_BindingValidationError(object sender,  
                                                ValidationErrorEventArgs e) 
{ 
    if (e.Action == ValidationErrorEventAction.Added) 
    { 
        // Do something with the error 
        string errorMessage = e.Error.ErrorContent.ToString(); 
    } 
} 
 

4)使用特性标记验证非RIA元数据类

通过RIA服务暴露的实体可以使用特性标记来进行验证,很方便,但是常规的元数据类如果使用这种标记则不会正常实现验证功能,需要做一些设置.Silverlight的DataAnnotation验证机制,在添加验证属性后,不需要在Setter中进行验证判断,仅需要在Setter中激活该验证属性即可,而要实现激活验证,则需要使用ValidationContext和Validator类。为了更好的理解Silverlight DataAnnotation验证机制,我们来对这两个类进行简单的讲解.

首先说说Validator类,该类是一个静态类,主要用来当数据成员被指定验证元数据属性时,验证对象,属性和方法。简单的理解就是包含了各种具体验证方法的类。例如Require验证属性,Validator类将会根据该验证属性执行对应的验证方法,对目标值进行判断。在该类中,包含ValidateProperty方法(验证错误返回异常)和TryValidateProperty方法(验证错误返回False),可以分别对当前属性进行验证操作。

ValiationContext类,该类是对当前执行的数据验证提供上下文描述的。简单的理解,也就是为验证提供数据传输,属性标识等任务。

属性验证:

private string _email;
[Required(ErrorMessage = "必填选项")]
public string email
{
        get{ return _email ;}
        set
          {
                var tempValidator=new ValidationContext(this, null ,null );
                tempValidator.MebmerName="email";
                Validator.ValidateProperty(value,tempValidator);
                _email = value;
          }

在上文代码中,我们定义一个ValidationContext实例,该实例中包含了需要验证对象的引用,并且,我们定义了验证对象的MemberName,通过调用Validator.ValidateProperty静态方法,检查目标数据是否符合当前验证属性,如果返回False,则抛出一个ValidationException。

上面代码也可简写为:

Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "email" });

再比如:

      private string _password;
         [StringLength(6, ErrorMessage="密码不能超过6个字符")]
         public string password
         {
             get { return _password; }
             set 
             {
                 Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "password" });
                 _password = value; 
            }
        }

可以看到,除了实现上稍有麻烦,使用方法都是类似的。

对象验证

使用Validator类进行对象验证通常都在IEditableObject接口的EndEdit方法中进行,在该方法中可以调用下列返回值为Boolean的方法,如果返回True则正常更新,如果返回值为False,则处理验证结果:

public bool Validate() 
{ 
    var validationContext = new ValidationContext(this, null, null); 
    var validationResults = new List<ValidationResult>(); 
 
    bool isValid = Validator.TryValidateObject(this, validationContext,  
                                               validationResults, true); 
 
    _validationErrors.Clear(); 
 
    foreach (ValidationResult result in validationResults) 
    { 
        foreach (string propertyName in result.MemberNames) 
        { 
            List<string> errors = null; 
 
            if (_validationErrors.ContainsKey(propertyName)) 
            { 
                errors = _validationErrors[propertyName]; 
            } 
            else 
            { 
                errors = new List<string>(); 
                _validationErrors[propertyName] = errors; 
            } 
 
            errors.Add(result.ErrorMessage); 
 
            if (ErrorsChanged != null) 
                ErrorsChanged(this,  
                new DataErrorsChangedEventArgs(propertyName)); 
        } 
    } 
 
    return isValid; 
}
示例文件下载地址:/Files/qouoww/ValidationDemo.zip
posted on 2012-05-13 16:40  qouoww  阅读(1982)  评论(3编辑  收藏  举报