
在學習了[Silverlight Validation數據驗證基礎屬性和事件](http://www.cnblogs.com/jv9/archive/2010/09/09/1821891.html)后,大家對Silverlight數據驗證應該有了一個簡單明了的認識。今天,我將繼續介紹另外一種Silverlight的Validation驗證機制,DataAnnotation。
在文章開始前,我想首先介紹一下Visual Studio中如何調試Silverlight的Validation代碼。
Visual Studio 2010調試Silverlight Validation設置技巧*
相信大家在運行上一篇的代碼時會發現,在異常出現時,Visual Studio會自動中斷和獲取當前異常錯誤信息,這為調試帶來了一些不便。

針對以上問題,我們可以在Visual Studio中進行簡單設置,暫時取消在Debug模式下對異常的捕獲,方法如下:
首先到Debug菜單,選擇Exceptions菜單,也可以使用“Ctrl+Alt+E”,激活Exception窗口


點擊“Find”查找以下選項,
System.Exception,將其后面的CheckBox取消選中,

這樣就實現了當數據驗證時,Visual Studio不再捕獲異常錯誤。
對于本篇,我們將使用DataAnnotation驗證機制,該驗證機制與[上一篇](http://www.cnblogs.com/jv9/archive/2010/09/09/1821891.html)略有不同,所以,如果要實現Visual Studio忽略捕獲異常,要另外在Exception窗口搜索“System.ComponentModel.DataAnnotations.ValidationException",同樣將其后面的CheckBox取消選中。

Visual Studio 2008和Silverlight 3開發環境中,默認情況下Exception沒有System.ComponentModel.DataAnnotations.ValidationException選項,開發人員可以自行添加一個新的異常即可,點擊“Add”按鈕,

在完成以上的操作后,再次執行Silverlight應用調試Validation時,Visual Studio不再出現異常捕獲,相對方便很多。
[Silverlight DataAnnotation](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx)驗證機制
**驗證Metadata屬性**
Silverlight的DataAnnotation驗證機制是Silverlight 3發布WCF RIA Services測試版是推出的客戶端驗證機制,對于DataAnnotation的翻譯,可以理解為“數據元素注釋”驗證法。該驗證機制,使用了System.ComponentModel.DataAnnotations命名空間中的屬性類,通過對DataMember數據成員設置Metadata元數據屬性,對其驗證值進行判斷是否符合當前屬性條件,以達到Validation的效果。該驗證機制,多數運用于WCF RIA Services應用中.
~~~
??????????private?string?_email;
??????????[Required(ErrorMessage?=?"必填選項")]
??????????public?string?email
??????????{
??????????????get?{?return?_email;?}
??????????????set?
??????????????{
??????????????????_email?=?value;?
??????????????}
?????????}
~~~
從上面代碼可以看到屬性上面的注釋?[Required(ErrorMessage?=?"必填選項")],該注釋就是DataAnnotations類中的固有屬性,其結果是判斷該控件內容是否為空,如果是,則彈出異常。目前常用的DataAnnotation屬性如下列表:
<table class="MsoTableGrid" style="border-collapse: collapse; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-yfti-tbllook: 1184; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt;" border="1" cellspacing="0" cellpadding="0"><tbody><tr style="height: 29.55pt; mso-yfti-irow: 0; mso-yfti-firstrow: yes;"><td style="padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 92.8pt; padding-right: 5.4pt; height: 29.55pt; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; border: black 1pt solid;" width="124"><p class="MsoNormal" style="text-align: center; line-height: normal; margin: 0cm 0cm 0pt;" align="center"><strong style="mso-bidi-font-weight: normal;"><span style="font-family: 宋體; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋體; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin;" lang="ZH-CN">屬性名稱</span><span lang="EN-US"/></strong></p></td><td style="border-bottom: black 1pt solid; border-left: #f0f0f0; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 244.7pt; padding-right: 5.4pt; height: 29.55pt; border-top: black 1pt solid; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-left-alt: solid black .5pt; mso-border-left-themecolor: text1;" width="326"><p class="MsoNormal" style="text-align: center; line-height: normal; margin: 0cm 0cm 0pt;" align="center"><strong style="mso-bidi-font-weight: normal;"><span style="font-family: 宋體; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋體; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin;" lang="ZH-CN">描述</span><span lang="EN-US"/></strong></p></td></tr><tr style="height: 29.55pt; mso-yfti-irow: 1;"><td style="border-bottom: black 1pt solid; border-left: black 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 92.8pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1;" width="124"><p class="MsoNormal" style="text-align: center; line-height: normal; margin: 0cm 0cm 0pt;" align="center"><span lang="EN-US"><span style="font-family: Calibri;">Required</span></span></p></td><td style="border-bottom: black 1pt solid; border-left: #f0f0f0; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 244.7pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-left-alt: solid black .5pt; mso-border-left-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1; mso-border-bottom-themecolor: text1; mso-border-right-themecolor: text1;" width="326"><p class="MsoNormal" style="line-height: normal; margin: 0cm 0cm 0pt;"><span style="font-family: 宋體; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋體; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin;" lang="ZH-CN">標識該屬性為必需參數,不能為空</span><span lang="EN-US"/></p></td></tr><tr style="height: 29.55pt; mso-yfti-irow: 2;"><td style="border-bottom: black 1pt solid; border-left: black 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 92.8pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1;" width="124"><p class="MsoNormal" style="text-align: center; line-height: normal; margin: 0cm 0cm 0pt;" align="center"><span lang="EN-US"><span style="font-family: Calibri;">StringLength</span></span></p></td><td style="border-bottom: black 1pt solid; border-left: #f0f0f0; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 244.7pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-left-alt: solid black .5pt; mso-border-left-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1; mso-border-bottom-themecolor: text1; mso-border-right-themecolor: text1;" width="326"><p class="MsoNormal" style="line-height: normal; margin: 0cm 0cm 0pt;"><span style="font-family: 宋體; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋體; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin;" lang="ZH-CN">標識該字符串有長度限制,可以限制最小或最大長度</span><span lang="EN-US"/></p></td></tr><tr style="height: 29.55pt; mso-yfti-irow: 3;"><td style="border-bottom: black 1pt solid; border-left: black 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 92.8pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1;" width="124"><p class="MsoNormal" style="text-align: center; line-height: normal; margin: 0cm 0cm 0pt;" align="center"><span lang="EN-US"><span style="font-family: Calibri;">Range</span></span></p></td><td style="border-bottom: black 1pt solid; border-left: #f0f0f0; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 244.7pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-left-alt: solid black .5pt; mso-border-left-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1; mso-border-bottom-themecolor: text1; mso-border-right-themecolor: text1;" width="326"><p class="MsoNormal" style="line-height: normal; margin: 0cm 0cm 0pt;"><span style="font-family: 宋體; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋體; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin;" lang="ZH-CN">標識該屬性值范圍,通常被用在數值型和日期型</span><span lang="EN-US"/></p></td></tr><tr style="height: 29.55pt; mso-yfti-irow: 4;"><td style="border-bottom: black 1pt solid; border-left: black 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 92.8pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1;" width="124"><p class="MsoNormal" style="text-align: center; line-height: normal; margin: 0cm 0cm 0pt;" align="center"><span lang="EN-US"><span style="font-family: Calibri;">RegularExpression</span></span></p></td><td style="border-bottom: black 1pt solid; border-left: #f0f0f0; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 244.7pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-left-alt: solid black .5pt; mso-border-left-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1; mso-border-bottom-themecolor: text1; mso-border-right-themecolor: text1;" width="326"><p class="MsoNormal" style="line-height: normal; margin: 0cm 0cm 0pt;"><span style="font-family: 宋體; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋體; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin;" lang="ZH-CN">標識該屬性將根據提供的正則表達式進行對比驗證</span><span lang="EN-US"/></p></td></tr><tr style="height: 29.55pt; mso-yfti-irow: 5; mso-yfti-lastrow: yes;"><td style="border-bottom: black 1pt solid; border-left: black 1pt solid; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 92.8pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1;" width="124"><p class="MsoNormal" style="text-align: center; line-height: normal; margin: 0cm 0cm 0pt;" align="center"><span lang="EN-US"><span style="font-family: Calibri;">CustomValidation</span></span></p></td><td style="border-bottom: black 1pt solid; border-left: #f0f0f0; padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 244.7pt; padding-right: 5.4pt; height: 29.55pt; border-top: #f0f0f0; border-right: black 1pt solid; padding-top: 0cm; mso-border-alt: solid black .5pt; mso-border-themecolor: text1; mso-border-left-alt: solid black .5pt; mso-border-left-themecolor: text1; mso-border-top-alt: solid black .5pt; mso-border-top-themecolor: text1; mso-border-bottom-themecolor: text1; mso-border-right-themecolor: text1;" width="326"><p class="MsoNormal" style="line-height: normal; margin: 0cm 0cm 0pt;"><span style="font-family: 宋體; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋體; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin;" lang="ZH-CN">標識該屬性將按照用戶提供的自定義驗證方法,進行數值驗證</span><span lang="EN-US"/></p></td></tr></tbody></table>
在隨后的實例中,我們將一一演示這些屬性的使用方法。
**ValidationContext和Validator類**
閱讀過上一篇Silverlight Validation基礎的朋友應該知道,Silverlight的數據驗證,可以在數據成員的Setter中設置條件驗證,根據其驗證結果判斷是否符合驗證。例如:
~~~
??????????private?int?_age;
??????????public?int?Age
??????????{
??????????????get?{?return?_age;?}
??????????????set
??????????????{
??????????????????if?(value?>?100?||?value?<?0)
??????????????????{
??????????????????????throw?new?Exception("請輸入年齡值在0?-?100之間.");
?????????????????}
?????????????????_age?=?value;
?????????????}
?????????}
~~~
在set中,判斷年齡值是否超過100歲或者低于0歲,如果不符合條件,則拋出異常,該異常將被Validation機制捕獲,并顯示到UI。
而Silverlight的DataAnnotation機制,與上面驗證方法不同。Silverlight的DataAnnotation驗證機制,在添加驗證屬性后,不需要在Setter中進行驗證判斷,僅需要在Setter中激活該驗證屬性即可,而要實現激活驗證,則需要使用ValidationContext和Validator類。為了更好的理解Silverlight DataAnnotation驗證機制,我們來對這兩個類進行簡單的講解,
首先說說[Validator類](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validator.aspx),該類是一個靜態類,主要用來當數據成員被指定驗證元數據屬性時,驗證對象,屬性和方法。簡單的理解就是包含了各種具體驗證方法的類。例如上文代碼,我們使用了Require驗證屬性,Validator類將會根據該驗證屬性執行對應的驗證方法,對目標值進行判斷。在該類中,包含ValidateProperty方法和TryValidateProperty方法,可以分別對當前屬性進行驗證操作。
而[ValiationContext類](http://msdn.microsoft.com/en-us/library/dd382177.aspx),該類是對當前執行的數據驗證提供上下文描述的。簡單的理解,也就是為驗證提供數據傳輸,屬性標識等任務。
我們對email屬性,進行簡單的修改,添加以上兩個類,讓該屬性Silverlight的DataAnnotation機制生效。
~~~
??????????private?string?_email;
??????????[Required(ErrorMessage?=?"必填選項")]
??????????public?string?email
??????????{
??????????????get?{?return?_email;?}
??????????????set?
??????????????{
??????????????????var?tmpValidator?=?new?ValidationContext(this,?null,?null);
??????????????????tmpValidator.MemberName?=?"email";
?????????????????Validator.ValidateProperty(value,?tmpValidator); ?????????????????_email?=?value;?
?????????????}
?????????}
~~~
在上文代碼中,我們定義一個ValidationContext實例,該實例中包含了需要驗證對象的引用,并且,我們定義了驗證對象的MemberName,通過調用Validator.ValidateProperty靜態方法,檢查目標數據是否符合當前驗證屬性,如果返回False,則拋出一個ValidationException。
上面代碼也可簡寫為:
~~~
Validator.ValidateProperty(value,?new?ValidationContext(this,?null,?null)?{?MemberName?=?"email"?});
~~~
當運行實例后,輸入空格在郵件文本框中,Silverlight的DataAnnotation驗證機制將被激活,生成如下效果:

在理解了上面的Silverlight的DataAnnotation驗證機制的基本類和屬性后,我們可以做幾個簡單的實例,來加深理解。
1. StringLength,定義Password密碼框最大可輸入6個字符,
~~~
?????????????<StackPanel?Orientation="Horizontal"?Margin="5">
?????????????????<TextBlock?Text="密???碼:?"?VerticalAlignment="Center"/>
?????????????????<TextBox?x:Name="txtPassword"?Width="200"?DataContext="{Binding?Source={StaticResource?UserDataContext}}"?Text="{Binding?Path=password,?Mode=TwoWay,ValidatesOnExceptions=True,?NotifyOnValidationError=True}"?/>
?????????????</StackPanel>
~~~
~~~
??????????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;?
?????????????}
?????????}
~~~

2. Range,我們使用Range屬性設置Age年齡的有效范圍,
~~~
?<StackPanel?Orientation="Horizontal"?Margin="5"> ?????????????????<TextBlock?Text="年???齡:?"?VerticalAlignment="Center"/>
?????????????????<TextBox?x:Name="txtAge"?Width="200"?DataContext="{Binding?Source={StaticResource?UserDataContext}}"?Text="{Binding?Path=Age,?Mode=TwoWay,?NotifyOnValidationError=True,?ValidatesOnExceptions=True}"?/>
?????????????</StackPanel>
~~~
~~~
??????????private?int?_age;
??????????[Range(0,?100,?ErrorMessage?=?"請輸入年齡值在0?-?100之間")]
??????????public?int?Age
??????????{
??????????????get?{?return?_age;?}
??????????????set
??????????????{
??????????????????Validator.ValidateProperty(value,?new?ValidationContext(this,?null,?null)?{?MemberName?=?"Age"?});
?????????????????_age?=?value;
?????????????}
?????????}
~~~

3. RegularExpression,我們使用正則表達式屬性,驗證郵件輸入框。
~~~
?<StackPanel?Orientation="Horizontal"?Margin="5">
?????????????????<TextBlock?Text="郵???件:?"?VerticalAlignment="Center"/>
?????????????????<TextBox?x:Name="txtEmail"?Width="200"?DataContext="{Binding?Source={StaticResource?UserDataContext}}"?Text="{Binding?Path=email,?Mode=TwoWay,?ValidatesOnNotifyDataErrors=False,?NotifyOnValidationError=True,?ValidatesOnExceptions=True}"?/>
?????????????</StackPanel>
~~~
~~~
??????????private?string?_email;
??????????[Required(ErrorMessage?=?"必填選項")]
??????????[RegularExpression(@"^([0-9a-zA-Z]([-./w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-/w]*[0-9a-zA-Z]/.)+[a-zA-Z]{2,9})$",ErrorMessage="請輸入正確的Email格式")]
??????????public?string?email
??????????{
??????????????get?{?return?_email;?}
??????????????set?
??????????????{
??????????????????var?tmpValidator?=?new?ValidationContext(this,?null,?null);
?????????????????tmpValidator.MemberName?=?"email";
?????????????????Validator.ValidateProperty(value,?tmpValidator); ?????????????????_email?=?value;?
?????????????}
?????????}
~~~

4. 自定義驗證方法的應用
我們創建一個簡單的自定義驗證方法,驗證用戶名是否為jv9.
首先創建自定義驗證類,CustomizeValidation,在類中添加引用,using System.ComponentModel.DataAnnotations;
然后繼承ValidationAttribute類,使其預定義該類為自定義驗證屬性,
完成上面的設置后,即可創建自定義驗證方法。
~~~
?????public?class?CustomizeValidation?:?ValidationAttribute
?????{
?????????protected?override?ValidationResult?IsValid(object?value,?ValidationContext?validationContext)
?????????{
?????????????String?checkName?=?value.ToString();
?
?????????????return?checkName?==?"jv9"???ValidationResult.Success?:?new?ValidationResult("請使用指定用戶名");
?????????}
?????}
~~~
在Name屬性中,進行調用,
~~~
??????????private?string?_name;
??????????[CustomizeValidation]
??????????public?string?Name
??????????{
??????????????get?{?return?_name;?}
??????????????set?
??????????????{
??????????????????Validator.ValidateProperty(value,?new?ValidationContext(this,?null,?null)?{?MemberName?=?"Name"?});
??????????????????if?(string.IsNullOrEmpty(value))
?????????????????{
?????????????????????throw?new?Exception("用戶名不能為空.");
?????????????????}
?????????????????_name?=?value;?
?????????????}
?????????}
~~~

對于Silverlight的DataAnnotation驗證機制,相比其他驗證機制使用起來較為簡單,但是其本身具有一定局限性。特別是當數據成員來自服務器端,會因為類庫無法共享使用造成無法正常驗證。前文曾提及,該驗證機制多數用在WCF RIA Services,因為WCF RIA應用提供的數據層,可生成對應客戶端代碼,即可實現在客戶端的DataAnnotation驗證。在隨后的實例中,我將演示一套WCF RIA服務下的驗證實例。
今天講到這里,希望大家能夠有所收獲。
[源代碼下載](http://files.cnblogs.com/jv9/SilverlightValidationDemo.rar)
歡迎大家加入"專注Silverlight" 技術討論群:
32679955(六群)
23413513(五群)
32679922(四群)
100844510(三群)
37891947(二群)
22308706(一群)
- 前言
- Out of Browser開篇
- Out of Browser配置,安裝和卸載
- Out of Browser的自定義應用
- Out of Browser存取本地文件系統
- Out of Browser與COM的交互基礎
- Out of Browser與Office的互操作
- Out of Browser的Debug和Notifications窗口
- Out of Browser音樂播放器
- Out of Browser與COM互操作實例
- Out of Browser在線更新和Silent安裝
- Validation數據驗證開篇
- Validation數據驗證基礎屬性和事件
- Validation數據驗證DataAnnotation機制和調試技巧
- Validation服務器端異步數據驗證
- Validation客戶端同步數據驗證
- Validation用戶提交數據驗證捕獲
- Datagrid,Dataform數據驗證和ValidationSummary
- 自定義擴展Validation類,驗證框架的總結和建議
- Navigation導航框架開篇
- 理解Navigation導航框架Frame類
- 理解Navigation導航框架Page類
- Navigation導航框架URI映射機制
- Navigation導航框架傳遞參數