Validation客户端同步数据验证

最后更新于:2022-04-01 14:21:01

![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eb67c868925.jpg) 前文介绍过Silverlight Validation中两个数据验证机制,[ValidatesOnExceptions异常捕获验证机制](http://www.cnblogs.com/jv9/archive/2010/09/09/1821891.html)和[DataAnnotation验证机制](http://www.cnblogs.com/jv9/archive/2010/09/10/1822910.html),这两种验证机制,是在Silverlight 3 Validation Framework推出的,其运行方式类似,都是当异常抛出后,应用对异常信息进行捕获,并显示在客户端。在Silverlight 4中,Silverlight Validation有相对的改进,本篇将介绍Silverlight 4中新加入的验证机制功能,IDataErrorInfo客户端同步验证机制。 **Silverlight 4 IDataErrorInfo接口概述** 相信熟悉WPF的开发人员都知道,WPF也具有IdataErrorInfo接口,其接口可以无需抛出任何异常,即可对数据进行验证。而Silverlight的IDataErrorInfo接口就是从WPF中转化来的。与前面两种验证机制相比,Silverlight 4 IDataErrorInfo提供的同步验证方式,不再需要基于异常抛出的基础上来激活验证,简单的理解,就是当值验证失败的时候,不会抛出任何异常信息。这种验证机制,相对前两种验证机制来说更加灵活。 **Silverlight 4 IDataErrorInfo接口类成员** [IDataErrorInfo接口](http://msdn.microsoft.com/en-us/library/system.componentmodel.idataerrorinfo.aspx),位于System.ComponentModel命名空间。该接口主要被应用在需要对其进行验证的数据成员类。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-11-02_56374111ddec5.gif) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b79c3e.gif) 代码 ~~~           #region IDataErrorInfo Members           public string Error           {               get { throw new NotImplementedException(); }           }           public string this[string columnName]  9         {              get { throw new NotImplementedException(); }         }          #endregion ~~~ IDataErrorInfo接口具有两个**属性**: 1. Error: 该属性为验证设置属性错误提示信息; 2. Item: 该属性为错误信息集合,其中索引值为属性名,将其对应的错误信息,设置到指定的被验证控件中; 从MSDN对IdataErrorInfo接口的解释中可以看到,IDataErrorInfo接口,没有任何事件方法被预先定义。也就是说,IDataErrorInfo接口本身不具备自动激活验证功能。简单的可以理解成为,当验证错误产生时,该错误信息不会自动捕获,然后显示到用户客户端上。解决该问题,需要引入另外一个接口[INotifyPropertyChanged](http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx)。对Silverlight早期版本熟悉的开发人员,应该对[INotifyPropertyChanged](http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx)接口并不陌生,该接口主要功能是当数据成员改变时发出通知到客户端,是Silverlight中最常用的一个接口,这里对该接口不再赘述。 ~~~ public class Customer  : INotifyPropertyChanged     public event PropertyChangedEventHandler PropertyChanged;     private void NotifyPropertyChanged(String info)     {         if (PropertyChanged != null)         {             PropertyChanged(this, new PropertyChangedEventArgs(info));         }     }     public string CustomerName     {         get         {             return this.customerNameValue;         }         set         {             if (value != this.customerNameValue)             {                 this.customerNameValue = value;                 NotifyPropertyChanged("CustomerName");             }         }     } ~~~ 在使用了[INotifyPropertyChanged](http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx)接口后,每当数据成员验证错误产生时,都会执行IDataErrorInfo接口,检测Error和Item属性。但是仅仅使用[INotifyPropertyChanged](http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx)接口,还无法正常将错误信息显示在客户端,同时也需要在客户端添加新的绑定验证属性[ValidatesOnDataErrors](http://msdn.microsoft.com/en-us/library/system.windows.data.binding.validatesondataerrors.aspx), 该属性在默认绑定情况下是False,如果使用IDataErrorInfo接口,则需要使用True,这时,客户端才会显示IDataErrorInfo接口被执行后产生的验证错误信息。 <TextBox Text="{Binding CustomerName,Mode=TwoWay,ValidatesOnDataErrors=true}" /> 讲到这里,我们可以结合前两篇讲解的绑定验证属性,来理解IDataErrorInfo接口。 使用IDataErrorInfo接口,必须结合INotifyPropertyChanged接口使用,否则UI无法获取到相对应属性的错误关联信息。 而使用IDataErrorInfo接口绑定验证属性时,在客户端控件内容绑定中使用ValidatesOnDataErrors = True, 另外,如果需要使用BindingValidationError事件,在客户端控件内容中,需要再绑定NotifyOnValidationError = True, 如果需要对异常进行捕获相应,同时,也需要绑定ValidatesOnExceptions = True。 **IDataErrorInfo接口使用实例演示** 本实例仍旧使用上一篇的实例代码,在其基础上添加对IDataErrorInfo接口的调用, 首先在User数据成员类中,执行IDataErrorInfo和INotifyPropertyChanged接口 ~~~        #region IDataErrorInfo Members         private string _dataError = string.Empty;         public string Error         {             get { return _dataError; }         }         private Dictionary<string, string> _dataErrors = new Dictionary<string, string>();         public string this[string columnName]         {             get              {                 if (_dataErrors.ContainsKey(columnName))                     return _dataErrors[columnName];                 else                     return null;             }         }         #endregion         #region INotifyPropertyChanged Members         public event PropertyChangedEventHandler PropertyChanged;         protected void NotifyPropertyChanged(string propertyName)         {             if (PropertyChanged != null)                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));         }         #endregion ~~~ 其中IDataErrorInfo中定义的Dictionary是验证错误集合。而INotifyPropertyChanged是最简单的当数据成员改变时返回通知到客户端,其中没有过多的逻辑代码。 我们使用最简单的数据成员生成一个验证错误,IDataErrorInfo捕获显示作为演示,  在User类中,添加一个Address地址数据成员, ~~~         private string _address;         public string address         {             get             {                 return _address;             }             set             {                 _address = value;             }         } ~~~ 然后在set中添加简单的逻辑代码, ~~~         private string _address;         public string address         {             get             {                 return _address;             }             set             {                 if (string.IsNullOrEmpty(value))                     _dataErrors["address"] = "地址必须填写";                 else if (value.Trim().Length < 6)                     _dataErrors["address"] = "地址至少6个字";                 else                     if (_dataErrors.ContainsKey("address"))                         _dataErrors.Remove("address");                 _address = value;                 NotifyPropertyChanged("address");             }         } ~~~ 修改前台,添加新的输入框,另外绑定输入框内容到address, ~~~ <StackPanel Orientation="Horizontal" Margin="5">                 <TextBlock Text="地   址: " VerticalAlignment="Center"/>                 <TextBox x:Name="txtAddress" Width="200" DataContext="{Binding Source={StaticResource UserDataContext}}" Text="{Binding Path=address, Mode=TwoWay, ValidatesOnDataErrors=True}" /> </StackPanel> ~~~ 其运行结果为: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eb67cd73e9d.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eb67cd826ed.jpg) 下面,我们做一个较为复杂一些的数据验证, 学生成绩对应表 A   -  90-100分 B   -  80-89分 C   -  70-79分 D   -  60-69分 F   -  0-59分 在这个实例中,我们对多个数据成员进行客户端数据验证。 添加两个新数据成员,gradelevel和graderange,其中使用独立的验证函数ValidateGradeLevelandRange进行数据验证, ~~~         private string _gradelevel;         public string gradelevel         {             get { return _gradelevel; }             set             {                 if (ValidateGradeLevelandRange(value,graderange))                 {                     _gradelevel = value;                     NotifyPropertyChanged("gradelevel");                 }             }         }         private decimal _graderange;         public decimal graderange         {             get { return _graderange; }             set             {                 if (ValidateGradeLevelandRange(gradelevel, value))                 {                     _graderange = value;                     NotifyPropertyChanged("graderange");                 }             }         } ~~~ ~~~ #region Customize Validation         private bool ValidateGradeLevelandRange(string level, decimal range)         {             bool isValid = false;             if (level == null)             {                 _dataErrors["gradelevel"] = "成绩等级必须是A,B,C,D,F";                 return false;             }             else             {                 switch (level.ToUpper())                 {                     case "A":                         isValid = (range >= 90 && range <= 100);                         break;                     case "B":                         isValid = (range >= 80 && range <= 89);                         break;                     case "C":                         isValid = (range >= 70 && range <= 79);                         break;                     case "D":                         isValid = (range >= 60 && range <= 69);                         break;                     case "F":                         isValid = (range >= 0 && range <= 59);                         break;                     default:                         _dataErrors["gradelevel"] = "成绩等级必须是A,B,C,D,F";                         return false;                 }             }             if (isValid)             {                 if (_dataErrors.ContainsKey("gradelevel"))                     _dataErrors.Remove("gradelevel");                 if (_dataErrors.ContainsKey("graderange"))                     _dataErrors.Remove("graderange");             }             else             {                 _dataErrors["gradelevel"] = "成绩等级和成绩范围不符合";                 _dataErrors["graderange"] = "成绩范围和成绩等级不符合";             }             return isValid;         }         #endregion ~~~ 在前台添加两个成绩输入框, ~~~~             <StackPanel Orientation="Horizontal" Margin="5">                 <TextBlock Text="成绩等级: " VerticalAlignment="Center"/>                 <TextBox x:Name="txtGradeLevel" Width="200" DataContext="{Binding Source={StaticResource UserDataContext}}" Text="{Binding Path=gradelevel, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=False, ValidatesOnExceptions=True}" />             </StackPanel>             <StackPanel Orientation="Horizontal" Margin="5">                 <TextBlock Text="成绩范围: " VerticalAlignment="Center"/>                 <TextBox x:Name="txtGradeRange" Width="200" DataContext="{Binding Source={StaticResource UserDataContext}}" Text="{Binding Path=graderange, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=False, ValidatesOnExceptions=True}" />             </StackPanel> ~~~ 其最终运行结果如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eb67cd90d76.jpg) 今天内容讲到这里了。 [源代码下载](http://silverlightchina.net/uploads/soft/100913/1_1645248331.rar) 欢迎大家加入"专注Silverlight" 技术讨论群: 32679955(六群) 23413513(五群) 32679922(四群) 100844510(三群) 37891947(二群) 22308706(一群)    
';