一、ValidationRule 驗證express
ValidationRule:是經過ValidationRule中的的Validate方法來驗證咱們綁定的屬性。因此咱們的用法是繼承ValidationRule,重寫他的Validate方法。示例ide
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中須要把錯誤信息顯示出來。post
<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>
Exception :咱們xaml中綁定的對象是屬性。因此Exception驗證,就是經過屬性的改變來判斷是否正常。如:ui
public int Age { get { return _age; } set { if (value > 200) { throw new Exception("年齡不能大於200"); } _age = value; } }
一樣跑出的異常在Xaml中也要顯示下。XAML同上。這種方式就會破壞POCO的設計原則。this
IDataErrorInfo:這個驗證是經過咱們的實體對象繼承IDataErrorInfo來實現的。這裏聲明的this索引器來訪問類的成員。spa
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同上。設計
這裏我即不想污染實體類,又想實現一個通用的Validate。我想經過我xaml綁定的屬性和它所屬的控件。來顯示ToolTip。3d
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基類。提供思想。功能不完善。code
而後繼承這個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類的以下的屬性:
在綁定目標控件上使用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方法,實現對界面輸入數據的驗證。
定義驗證能夠採用如下三種:
例如,定義一個學生信息類,要求其學生成績在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>
從執行的結果上來看,當驗證出現錯誤(即索引器屬性返回的字符串不爲空時),系統默認給出一種驗證錯誤的顯示方式(控件以紅色邊框包圍),可是需注意兩點:
例如上例中,對於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>
例如,定義一個類,用來驗證輸入的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。例如上例中出錯,使輸入的文本框的背景顏色和字體顏色發生變化,並提供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}"