[WPF 基础知识系列] —— 绑定中的数据校验Vaildation
前言:
只要是有表單存在,那么就有可能有對(duì)數(shù)據(jù)的校驗(yàn)需求。如:判斷是否為整數(shù)、判斷電子郵件格式等等。
WPF采用一種全新的方式 - Binding,來(lái)實(shí)現(xiàn)前臺(tái)顯示與后臺(tái)數(shù)據(jù)進(jìn)行交互,當(dāng)然數(shù)據(jù)校驗(yàn)方式也不一樣了。
本專題全面介紹一下WPF中4種Validate方法,幫助你了解如何在WPF中對(duì)binding的數(shù)據(jù)進(jìn)行校驗(yàn),并處理錯(cuò)誤顯示。
?
一、簡(jiǎn)介
正常情況下,只要是綁定過(guò)程中出現(xiàn)異常或者在converter中出現(xiàn)異常,都會(huì)造成綁定失敗。
但是WPF不會(huì)出現(xiàn)任何異常,只會(huì)顯示一片空白(當(dāng)然有些Converter中的異常會(huì)造成程序崩潰)。
這是因?yàn)槟J(rèn)情況下,Binding.ValidatesOnException為false,所以WPF忽視了這些綁定錯(cuò)誤。
但是如果我們把Binding.ValidatesOnException為true,那么WPF會(huì)對(duì)錯(cuò)誤做出以下反應(yīng):
我們的Binding對(duì)象,維護(hù)著一個(gè)ValidationRule的集合,當(dāng)設(shè)置ValidatesOnException為true時(shí),
默認(rèn)會(huì)添加一個(gè)ExceptionValidationRule到這個(gè)集合當(dāng)中。
PS:對(duì)于綁定的校驗(yàn)只在Binding.Mode 為TwoWay和OneWayToSource才有效,
即當(dāng)需要從target控件將值傳到source屬性時(shí),很容易理解,當(dāng)你的值不需要被別人使用時(shí),就很可能校驗(yàn)也沒(méi)必要。
?
二、四種實(shí)現(xiàn)方法
1、在Setter方法中進(jìn)行判斷
直接在Setter方法中,對(duì)value進(jìn)行校驗(yàn),如果不符合規(guī)則,那么就拋出異常。然后修改XAML不忽視異常。
public class PersonValidateInSetter : ObservableObject{private string name;private int age;public string Name{get { return this.name; }set{if (string.IsNullOrWhiteSpace(value)){throw new ArgumentException("Name cannot be empty!");}if (value.Length < 4){throw new ArgumentException("Name must have more than 4 char!");}this.name = value;this.OnPropertyChanged(() => this.Name);}}public int Age{get{ return this.age; }set{if (value < 18){throw new ArgumentException("You must be an adult!");}this.age = value;this.OnPropertyChanged(() => this.Age);}}}?
<Grid DataContext="{Binding PersonValidateInSetter}"><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="Auto" /><ColumnDefinition /></Grid.ColumnDefinitions><TextBlock Text="Name:" /><TextBox Grid.Column="1"Margin="1"Text="{Binding Name,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" /><TextBlock Grid.Row="1" Text="Age:" /><TextBox Grid.Row="1"Grid.Column="1"Margin="1"Text="{Binding Age,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" /></Grid>?
當(dāng)輸入的值,在setter方法中校驗(yàn)時(shí)出現(xiàn)錯(cuò)誤,就會(huì)出現(xiàn)一個(gè)紅色的錯(cuò)誤框。
關(guān)鍵代碼:ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged。
PS:這種方式有一個(gè)BUG,首次加載時(shí)不會(huì)對(duì)默認(rèn)數(shù)據(jù)進(jìn)行檢驗(yàn)。
?
2、繼承IDataErrorInfo接口
使Model對(duì)象繼承IDataErrorInfo接口,并實(shí)現(xiàn)一個(gè)索引進(jìn)行校驗(yàn)。如果索引返回空表示沒(méi)有錯(cuò)誤,如果返回不為空,
表示有錯(cuò)誤。另外一個(gè)Erro屬性,但是在WPF中沒(méi)有被用到。
public class PersonDerivedFromIDataErrorInfo : ObservableObject, IDataErrorInfo{private string name;private int age;public string Name{get{return this.name;}set{this.name = value;this.OnPropertyChanged(() => this.Name);}}public int Age{get{return this.age;}set{this.age = value;this.OnPropertyChanged(() => this.Age);}}// never called by WPFpublic string Error{get{return null;}}public string this[string propertyName]{get{switch (propertyName){case "Name":if (string.IsNullOrWhiteSpace(this.Name)){return "Name cannot be empty!";}if (this.Name.Length < 4){return "Name must have more than 4 char!";}break;case "Age":if (this.Age < 18){return "You must be an adult!";}break;}return null;}}} <Grid? DataContext="{Binding PersonDerivedFromIDataErrorInfo}"><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="Auto" /><ColumnDefinition /></Grid.ColumnDefinitions><TextBlock Text="Name:" /><TextBox Grid.Column="1"Margin="1"Text="{Binding Name,NotifyOnValidationError=True,ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}" /><TextBlock Grid.Row="1" Text="Age:" /><TextBox Grid.Row="1"Grid.Column="1"Margin="1"Text="{Binding Age,NotifyOnValidationError=True,ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}" />?
PS:這種方式,沒(méi)有了第一種方法的BUG,但是相對(duì)很麻煩,既需要繼承接口,又需要添加一個(gè)索引,如果遺留代碼,那么這種方式就不太好。
?
3、自定義校驗(yàn)規(guī)則
一個(gè)數(shù)據(jù)對(duì)象或許不能包含一個(gè)應(yīng)用要求的所有不同驗(yàn)證規(guī)則,但是通過(guò)自定義驗(yàn)證規(guī)則就可以解決這個(gè)問(wèn)題。
在需要的地方,添加我們創(chuàng)建的規(guī)則,并進(jìn)行檢測(cè)。
通過(guò)繼承ValidationRule抽象類,并實(shí)現(xiàn)Validate方法,并添加到綁定元素的Binding.ValidationRules中。
public class MinAgeValidation : ValidationRule{public int MinAge { get; set; }public override ValidationResult Validate(object value, CultureInfo cultureInfo){ValidationResult result = null;if (value != null){int age;if (int.TryParse(value.ToString(), out age)){if (age < this.MinAge){result = new ValidationResult(false, "Age must large than " + this.MinAge.ToString(CultureInfo.InvariantCulture));}}else{result = new ValidationResult(false, "Age must be a number!");}}else{result = new ValidationResult(false, "Age must not be null!");}return new ValidationResult(true, null);}} <Grid><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="Auto" /><ColumnDefinition /></Grid.ColumnDefinitions><TextBlock Text="Name:" /><TextBox Grid.Column="1" Margin="1" Text="{Binding Name}"></TextBox><TextBlock Grid.Row="1" Text="Age:" /><TextBox Grid.Row="1"Grid.Column="1"Margin="1"><TextBox.Text><Binding Path="Age"UpdateSourceTrigger="PropertyChanged"ValidatesOnDataErrors="True"><Binding.ValidationRules><validations:MinAgeValidation MinAge="18" /></Binding.ValidationRules></Binding></TextBox.Text></TextBox></Grid>這種方式,也會(huì)有第一種方法的BUG,暫時(shí)還不知道如何解決,但是這個(gè)能夠靈活的實(shí)現(xiàn)校驗(yàn),并且能傳參數(shù)。
效果圖:
?
4、使用數(shù)據(jù)注解(特性方式)
在System.ComponentModel.DataAnnotaions命名空間中定義了很多特性,
它們可以被放置在屬性前面,顯示驗(yàn)證的具體需要。放置了這些特性之后,
屬性中的Setter方法就可以使用Validator靜態(tài)類了,來(lái)用于驗(yàn)證數(shù)據(jù)。
public class PersonUseDataAnnotation : ObservableObject{private int age;private string name;[Range(18, 120, ErrorMessage = "Age must be a positive integer")]public int Age{get{return this.age;}set{this.ValidateProperty(value, "Age");this.SetProperty(ref this.age, value, () => this.Age);}}[Required(ErrorMessage = "A name is required")][StringLength(100, MinimumLength = 3, ErrorMessage = "Name must have at least 3 characters")]public string Name{get{return this.name;}set{this.ValidateProperty(value, "Name");this.SetProperty(ref this.name, value, () => this.Name);}}protected void ValidateProperty<T>(T value, string propertyName){Validator.ValidateProperty(value,new ValidationContext(this, null, null) { MemberName = propertyName });
}
} <Grid><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="Auto" /><ColumnDefinition /></Grid.ColumnDefinitions><TextBlock Text="Name:" /><TextBox Grid.Column="1"Margin="1" Text="{Binding Name,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" /><TextBlock Grid.Row="1" Text="Age:" /><TextBox Grid.Row="1"Grid.Column="1"Margin="1"Text="{Binding Age,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" /></Grid>
使用特性的方式,能夠很自由的使用自定義的規(guī)則,而且在.Net4.5中新增了很多特性,可以很方便的對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)。
例如:EmailAddress, Phone, and Url等。
?
三、自定義錯(cuò)誤顯示模板
在上面的例子中,我們可以看到當(dāng)出現(xiàn)驗(yàn)證不正確時(shí),綁定控件會(huì)被一圈紅色錯(cuò)誤線包裹住。
這種方式一般不能夠正確的展示出,錯(cuò)誤的原因等信息,所以有可能需要自己的錯(cuò)誤顯示方式。
前面,我們已經(jīng)講過(guò)了。當(dāng)在檢測(cè)過(guò)程中,出現(xiàn)錯(cuò)誤時(shí),WPF會(huì)把錯(cuò)誤信息封裝為一個(gè)ValidationError對(duì)象,
并添加到Validation.Errors中,所以我們可以取出錯(cuò)誤詳細(xì)信息,并顯示出來(lái)。
1、為控件創(chuàng)建ErrorTemplate
下面就是一個(gè)簡(jiǎn)單的例子,每次都把錯(cuò)誤信息以紅色展示在空間上面。這里的AdornedElementPlaceholder相當(dāng)于
控件的占位符,表示控件的真實(shí)位置。這個(gè)例子是在書(shū)上直接拿過(guò)來(lái)的,只能做基本展示用。
<ControlTemplate x:Key="ErrorTemplate"><Border BorderBrush="Red" BorderThickness="2"><Grid><AdornedElementPlaceholder x:Name="_el" /><TextBlock Margin="0,0,6,0"HorizontalAlignment="Right"VerticalAlignment="Center"Foreground="Red"Text="{Binding [0].ErrorContent}" /></Grid></Border></ControlTemplate> <TextBox x:Name="AgeTextBox"Grid.Row="1"Grid.Column="1"Margin="1" Validation.ErrorTemplate="{StaticResource ErrorTemplate}" >使用方式非常簡(jiǎn)單,將上面的模板作為邏輯資源加入項(xiàng)目中,然后像上面一樣引用即可。
效果圖:
對(duì)知識(shí)梳理總結(jié),希望對(duì)大家有幫助!
posted on 2018-09-21 10:24 NET未來(lái)之路 閱讀(...) 評(píng)論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/9685193.html
總結(jié)
以上是生活随笔為你收集整理的[WPF 基础知识系列] —— 绑定中的数据校验Vaildation的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 光大本来生活联名信用卡年费多少?怎么免年
- 下一篇: .Net Core应用框架Util介绍(