1、ValidationRule 驗證
ValidationRule:是通過ValidationRule中的的Validate方法來驗證我們綁定的屬性。所以我們的用法是繼承ValidationRule,重寫他的Validate方法。示例
public class RequiredRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { if (value == null) return new ValidationResult(false, "不能為空值!"); if (string.IsNullOrEmpty(value.ToString())) return new ValidationResult(false, "不能為空字符串!"); return new ValidationResult(true, null); } } 
         而XAML中需要把錯誤信息顯示出來。
<Window.Resources>
        <ControlTemplate x:Key="ErrorTemplate"> <Border BorderBrush="Red" BorderThickness="1"> <AdornedElementPlaceholder/> </Border> </ControlTemplate> <Style TargetType="TextBox"> <Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorTemplate}"> </Setter> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/> </Trigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <TextBlock Text="姓名"/> <TextBox> <TextBox.Text> <Binding Path="Name" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"> <Binding.ValidationRules> <ValidationRules:RequiredRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <TextBlock Text="年齡"/> <TextBox > <TextBox.Text> <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"> <Binding.ValidationRules> <ValidationRules:GreaterThanRule Number="10"/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> </StackPanel> 
         這樣顯示的錯誤信息就會以 ToolTip和紅色邊框的形式顯示出來。但這邊如果又在TextBox里面設置ToolTip那么就會優先選擇TextBox里的,也就是Style中的ToolTip遇到錯誤信息是不會顯示出來的,而是顯示TextBox中的ToolTip。所以我們可以改善一下顯示的模版來解決這個問題。
<ControlTemplate x:Key="ErrorTemplate"> <DockPanel LastChildFill="true"> <Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10" ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"> <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white"> </TextBlock> </Border> <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" > <Border BorderBrush="red" BorderThickness="1" /> </AdornedElementPlaceholder> </DockPanel> </ControlTemplate> 
         2、Exception 驗證
Exception :我們xaml中綁定的對象是屬性。所以Exception驗證,就是通過屬性的改變來判斷是否正常。如:
 public int Age { get { return _age; } set { if (value > 200) { throw new Exception("年齡不能大於200"); } _age = value; } } 
         同樣跑出的異常在Xaml中也要顯示下。XAML同上。這種方式就會破壞POCO的設計原則。
3、IDataErrorInfo 驗證
IDataErrorInfo:這個驗證是通過我們的實體對象繼承IDataErrorInfo來實現的。這里聲明的this索引器來訪問類的成員。
 public class BaseDataErrorInfo : IDataErrorInfo { private string _error; public string this[string columnName] { get { return GetErrorFor(columnName); } } public string Error { get { return _error; } set { _error = value; } } public virtual string GetErrorFor(string columnName) { return string.Empty; } } 
         public class Person : BaseDataErrorInfo { public string Name { get; set; } public override string GetErrorFor(string columnName) { if (columnName == "Name") if (string.IsNullOrEmpty(Name)) return "Name 不能為空"; return base.GetErrorFor(columnName); } } 
         XAML同上。
4、Custom Control 驗證
這里我即不想污染實體類,又想實現一個通用的Validate。我想通過我xaml綁定的屬性和它所屬的控件。來顯示ToolTip。
  public abstract class Validator : FrameworkElement { static Validator() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Validator), new FrameworkPropertyMetadata(typeof(Validator))); } public virtual string ErrorMessage { get { return string.Empty; } } public abstract bool InitialValidation(); public FrameworkElement ElementName { get { return (FrameworkElement)GetValue(ElementNameProperty); } set { SetValue(ElementNameProperty, value); } } // Using a DependencyProperty as the backing store for ElementName. This enables animation, styling, binding, etc... public static readonly DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName", typeof(FrameworkElement), typeof(Validator), new PropertyMetadata(null)); public object Source { get { return (object)GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } // Using a DependencyProperty as the backing store for Source. This enables animation, styling, binding, etc... public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(object), typeof(Validator), new UIPropertyMetadata(new PropertyChangedCallback(ValidPropertyPropertyChanged))); private static void ValidPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var validator = d as Validator; if (validator != null) validator.SetSourceFromProperty(); if (string.IsNullOrEmpty(e.NewValue.ToString())) { if (validator != null) { validator.IsValid = validator.InitialValidation(); if (validator.ElementName.DataContext != null) validator.ShowToolTip(); validator.IsValid = false; } } } private void ShowToolTip() { if (IsValid) { timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromSeconds(1.5); _toolTip = new ToolTip(); _toolTip.StaysOpen = true; _toolTip.PlacementTarget = ElementName; _toolTip.Placement = PlacementMode.Right; _toolTip.Content = ErrorMessage; _toolTip.IsOpen = true; timer.Tick += (sender, args) => { _toolTip.IsOpen = false; timer.Stop(); }; timer.Start(); } } private void SetSourceFromProperty() { var expression = this.GetBindingExpression(SourceProperty); if (expression != null && this.ElementName == null) this.SetValue(Validator.ElementNameProperty, expression.DataItem as FrameworkElement); } private ToolTip _toolTip; private DispatcherTimer timer; public bool IsValid { get; set; } } 
         這是一個簡單的Validate基類。提供思想。功能不完善。
然后繼承這個Validator
public class RequiredValidator : Validator { public override string ErrorMessage { get { return "不能為空值"; } } public override bool InitialValidation() { if (Source == null) return false; return string.IsNullOrEmpty(Source.ToString()); } } 
         這里ErrorMessage是顯示錯誤信息。
InitialValidation方法是我們要驗證的規則。
關於使用的小例子:
一、通過代碼實現數據綁定
通過代碼實現數據綁定,使用的是System.Windows.Data命名空間的Binding類,主要使用Binding類的如下的屬性:
- Source屬性:綁定到的數據源
 - Mode屬性:綁定的模式(OneTime、OneWay、TwoWay、OneWayToSource或Default)
 - Path屬性:綁定到的數據源的屬性
 - Converter屬性:綁定時所使用的類型轉換器
 
在綁定目標控件上使用SetBinding方法添加數據綁定。例如將MyData的Name屬性綁定到txtName控件的Text屬性上,使用MyColorConverter轉換器將MyBindingColor的ColorObject屬性綁定到rec控件的Fill屬性上:
   1: MyData data = new MyData(); 
              2:   
              3: Binding binding1 = new Binding(); 
              4: binding1.Source = data; 
              5: binding1.Mode = BindingMode.OneWay; 
              6: binding1.Path = new PropertyPath("Name"); 
              7:   
              8: txtName.SetBinding(TextBox.TextProperty, binding1); 
              9:   
             10:   
             11: MyBindingColor color = new MyBindingColor(); 
             12:   
             13: Binding binding2 = new Binding(); 
             14: binding2.Source = color; 
             15: binding2.Mode = BindingMode.OneWay; 
             16: binding2.Path = new PropertyPath("ColorObject"); 
             17: binding2.Converter = new MyColorConverter(); 
             18:   
             19: rec.SetBinding(Rectangle.FillProperty, binding2); 
          二、實現綁定數據的驗證:
對於綁定數據的驗證,系統采用如下的機制:

使用 WPF 數據綁定模型可以將 ValidationRules 與 Binding 對象相關聯。當綁定目標的屬性向綁定源屬性傳遞屬性值時(僅限TwoWay模式或OneWayToSource模式),執行ValidationRule中的Validate方法,實現對界面輸入數據的驗證。
定義驗證可以采用以下三種:
- DataErrorValidationRule:檢查由源對象的 IDataErrorInfo 實現所引發的錯誤,要求數據源對象實現System.ComponentModel命名空間的IDataErrorInfo接口。
 
例如,定義一個學生信息類,要求其學生成績在0到100間,學生姓名的長度在2到10個字符間:
   1: public class StudentInfoWithValidation : IDataErrorInfo 
              2: { 
              3:     #region 構造方法 
              4:     public StudentInfoWithValidation() 
              5:     { 
              6:         StudentName = "Tom"; 
              7:         Score = 90; 
              8:     } 
              9:     public StudentInfoWithValidation(string m_StudentName,double m_Score) 
             10:     { 
             11:         StudentName = m_StudentName; 
             12:         Score = m_Score; 
             13:     } 
             14:     #endregion 
             15:   
             16:     #region 屬性 
             17:     public string StudentName 
             18:     { 
             19:         get; set; 
             20:     } 
             21:     public double Score 
             22:     { 
             23:         get; set; 
             24:     } 
             25:     #endregion 
             26:   
             27:     #region 實現IDataErrorInfo接口的成員 
             28:     public string Error 
             29:     { 
             30:         get  
             31:         { 
             32:             return null; 
             33:         } 
             34:     } 
             35:   
             36:     public string this[string columnName] 
             37:     { 
             38:         get 
             39:         { 
             40:             string result = null; 
             41:   
             42:             switch (columnName) 
             43:             { 
             44:                 case "StudentName": 
             45:                     // 設置StudentName屬性的驗證規則 
             46:                     int len = StudentName.Length; 
             47:                     if (len < 2 || len > 10) 
             48:                     { 
             49:                         result = "StudentName length must between 2 and 10"; 
             50:                     } 
             51:                     break; 
             52:                 case "Score": 
             53:                     // 設置Score屬性的驗證規則 
             54:                     if (Score < 0 || Score > 100) 
             55:                     { 
             56:                         result = "Score must between 0 and 100"; 
             57:                     } 
             58:                     break; 
             59:             } 
             60:   
             61:             return result; 
             62:         } 
             63:     } 
             64:     #endregion 
             65: } 
          在界面上,定義兩個TextBox綁定到StudentName和Score兩個屬性上,並設置其采用DataErrorValidationRule:
   1: <Window x:Class="WPFDataBindingDemo.WinDataErrorValidationRuleDemo" 
              2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
              3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
              4:     xmlns:local="clr-namespace:WPFDataBindingDemo" 
              5:     Title="WinDataErrorValidationRuleDemo" Height="154" Width="300"> 
              6:     <Canvas Height="116" x:Name="mainCanvas"> 
              7:         <Canvas.Resources> 
              8:             <local:StudentInfoWithValidation x:Key="myData" /> 
              9:         </Canvas.Resources> 
             10:         <Canvas.DataContext> 
             11:             <Binding Source="{StaticResource myData}" /> 
             12:         </Canvas.DataContext> 
             13:         <Label Canvas.Left="10" Canvas.Top="10" Height="28" Name="label1" Width="120">StudentName:</Label> 
             14:         <Label Canvas.Left="10" Canvas.Top="36" Height="28" Name="label2" Width="120">Score:</Label> 
             15:         <TextBox Canvas.Left="136" Canvas.Top="12" Height="23" Name="textBox1" Width="120"> 
             16:             <TextBox.Text> 
             17:                 <Binding Path="StudentName"  
             18:                          Mode="TwoWay"  
             19:                          UpdateSourceTrigger="PropertyChanged" 
             20:                          ValidatesOnDataErrors="True" /> 
             21:             </TextBox.Text> 
             22:         </TextBox> 
             23:         <TextBox Canvas.Left="136" Canvas.Top="41" Height="23" Name="textBox2" Width="120"> 
             24:             <TextBox.Text> 
             25:                 <Binding Path="Score" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"> 
             26:                     <!--與上一個TextBox控件的寫法作用相同--> 
             27:                     <Binding.ValidationRules> 
             28:                         <DataErrorValidationRule /> 
             29:                     </Binding.ValidationRules> 
             30:                 </Binding> 
             31:             </TextBox.Text> 
             32:         </TextBox> 
             33:         <Button Canvas.Left="12" Canvas.Top="79" Height="23" Name="button1" Width="118" Click="button1_Click">Get Student Info</Button> 
             34:         <Button Canvas.Left="136" Canvas.Top="79" Height="23" Name="button2" Width="118" Click="button2_Click">Get Validate State</Button> 
             35:     </Canvas> 
             36: </Window> 
          
 
從執行的結果上來看,當驗證出現錯誤(即索引器屬性返回的字符串不為空時),系統默認給出一種驗證錯誤的顯示方式(控件以紅色邊框包圍),但是需注意兩點:
- 產生驗證錯誤,驗證后的數據仍然會更改數據源的值
 - 如果系統出現異常,如成績值輸入 “90d”,則系統不會顯示錯誤,控件上的輸入值也不賦值到數據源。這種情況下,需要使用ExceptionValidationRule。
 - ExceptionValidationRule:即當綁定目標的屬性值向綁定源的屬性值賦值時引發異常所產生的驗證。此種方式若實現自定義的邏輯驗證,通常設置數據源的屬性的Set訪問器,在Set訪問器中,根據輸入的值結合邏輯,使用throw拋出相應的異常。
 
例如上例中,對於Score對應的TextBox,再加入ExceptionValidationRule:
   1: <TextBox Canvas.Left="136" Canvas.Top="41" Height="23" Name="textBox2" Width="120"> 
              2:     <TextBox.Text> 
              3:         <Binding Path="Score" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"> 
              4:             <!--與上一個TextBox控件的寫法作用相同--> 
              5:             <Binding.ValidationRules> 
              6:                 <DataErrorValidationRule /> 
              7:                 <ExceptionValidationRule /> 
              8:             </Binding.ValidationRules> 
              9:         </Binding> 
             10:     </TextBox.Text> 
             11: </TextBox> 
          
- 自定義驗證規則:定義一個類,繼承ValidationRule抽象類,實現其Validate方法,驗證某一輸入。
 
例如,定義一個類,用來驗證輸入的Email地址是否合法(驗證的Email允許為字符串的空值String.Empty,但有輸入必須符合Email的格式要求)
在學生類中添加Email可讀可寫屬性(並不做相應的驗證,忽略其他重復代碼):
   1: public string Email 
              2: { 
              3:     set; get; 
              4: } 
          定義一個類,實現Email格式驗證:
   1: using System.Globalization; 
              2: using System.Text.RegularExpressions; 
              3: using System.Windows.Controls; 
              4:   
              5: namespace WPFDataBindingDemo 
              6: { 
              7:     public class EmailValidationRule : ValidationRule 
              8:     { 
              9:         public override ValidationResult Validate(object value, CultureInfo cultureInfo) 
             10:         { 
             11:             bool isValid = false; 
             12:             string message = null; 
             13:   
             14:             // 檢查輸入值不為空,且是字符串 
             15:             if (value != null && value is string) 
             16:             { 
             17:                 string email = value.ToString(); 
             18:   
             19:                 // 檢查輸入的字符串是否為String.Empty 
             20:                 if (email != string.Empty) 
             21:                 { 
             22:                     string emailFormartRegex = 
             23:                         @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|" + 
             24:                         @"(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"; 
             25:                      
             26:                     // 檢查輸入的字符串是否符合Email格式 
             27:                     isValid = Regex.IsMatch(email, emailFormartRegex); 
             28:   
             29:                     if (! isValid) 
             30:                     { 
             31:                         message = "Input string not match Email Format"; 
             32:                     } 
             33:                 } 
             34:                 else 
             35:                 { 
             36:                     // 輸入的字符串為字符串空值時,認為驗證通過 
             37:                     isValid = true; 
             38:                 } 
             39:             } 
             40:             else 
             41:             { 
             42:                 message = "Input value is NULL or is not string."; 
             43:             } 
             44:   
             45:             // 返回驗證結果(ValidationResult對象) 
             46:             return new ValidationResult(isValid,message); 
             47:         } 
             48:     } 
             49: } 
          在界面上:
   1: <TextBox Canvas.Left="104" Canvas.Top="70" Height="23" Name="textBox3" Width="152"> 
              2:     <Binding Mode="TwoWay" Path="Email" UpdateSourceTrigger="PropertyChanged"> 
              3:         <Binding.ValidationRules> 
              4:             <local:EmailValidationRule /> 
              5:         </Binding.ValidationRules> 
              6:     </Binding> 
              7: </TextBox> 
          
三、為數據驗證提供視覺效果
在數據驗證錯誤后,可以通過以下兩種方式提供相應的視覺效果:
- 定義Style及相應的觸發器
 
如果要使輸入的控件的外觀發生變化,可以使用Style。例如上例中出錯,使輸入的文本框的背景顏色和字體顏色發生變化,並提供ToolTip顯示錯誤信息,可以定義如下的Style:
   1: <Style TargetType="TextBox"> 
              2:     <Setter Property="Background" Value="White" /> 
              3:     <Setter Property="Foreground" Value="Black" /> 
              4:     <Style.Triggers> 
              5:         <Trigger Property="Validation.HasError" Value="True"> 
              6:             <Setter Property="Background" Value="#DDD" /> 
              7:             <Setter Property="Foreground" Value="Red" /> 
              8:             <Setter Property="ToolTip" 
              9:                     Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/> 
             10:         </Trigger> 
             11:     </Style.Triggers> 
             12: </Style> 
          效果如下:
 
- 定義控件模板
 
如果要為相應的控件添加一些輔助的控件,可以使用控件模板,如出現驗證錯誤時,不使用系統默認的紅色邊框,而是在文本框后添加一個紅色的星號:
   1: <ControlTemplate x:Key="validErrorTextBoxTemplate"> 
              2:     <DockPanel> 
              3:         <AdornedElementPlaceholder/> 
              4:         <TextBlock Foreground="Red" FontSize="20">*</TextBlock> 
              5:     </DockPanel> 
              6: </ControlTemplate> 
          並在每一個輸入的TextBox中添加:
   1: Validation.ErrorTemplate="{StaticResource validErrorTextBoxTemplate}" 
          

 