Validation ValidationRule iDataErrorInfo+DataAnimation 的用法
//Validation:依賴屬性用(在驗證回調中返回false)
//ValidationRule:依賴屬性和普通類的屬性都能用
//IdataErrorInfo:依賴屬性和普通類的屬性都能用,誰想用的話 就繼承iDataErrorInfo
區別?
ValidationRule可以自定義錯誤信息,Validation和ValidationRule在xaml中的用法一樣,比較繁瑣
iDataErrorErrorInfo可以使用屬性索引器,在xaml中只需咋binding表達式中打開ValidatesOnDataErrors=True即可,xaml代碼比較少
ValidationRule和iDataErrorInfo的錯誤信息都是被全局靜態對象Validation接收的
1.Validation 依賴屬性的驗證
界面上有2個控件,一個textbox綁定一個自定義的依賴屬性,一個textblock綁定前面依賴屬性的驗證結果
依賴屬性都有一個private static bool ValidateValueCallback(object value)驗證回調函數,返回值是驗證的結果bool類型的,value是依賴屬性的數據,在驗證回調中做一下判斷,當不滿足條件時,返回一個false,
這時候全局靜態對象Validation就能捕獲到這個驗證結果
<TextBox Name="tb"> <TextBox.Text> <Binding Path="MyProperty" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <ExceptionValidationRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <TextBox Text="{Binding Path=(Validation.Errors)[0].ErrorContent,ElementName=tb}"/>-->
在依賴屬性的驗證回調中判斷,
運行結果
2.ValidationRule 依賴屬性的驗證
上面的驗證信息(“123456”不是屬性“Myproperty”的有效值)是由ExceptionValidationRule默認提供的錯誤提示消息
錯誤信息能不能自定義呢?這時候要自己寫一個ValidationRule
可以看到ExceptionValidationRule也是繼承自ValidationRule的
新建一個類繼承自ValidationRule,重寫ValidationResult
那怎么用自己定義的去代替ExceptionValidationRule?
運行結果:
3.iDataErrorInfo和DataAnnotations特性
普通屬性所在的類繼承iDataErrorInfo,並實現iDataErrorInfo提供的接口和索引器
public class Person : INotifyPropertyChanged, IDataErrorInfo { private int _id; private string _name; public string Name { get { return _name; } set { _name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } } public int ID { get { return _id; } set { _id = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ID")); } } //INotifyPropertyChanged的接口 public event PropertyChangedEventHandler? PropertyChanged; //IDataErrorInfo的接口 public string Error { get { return ""; } } public string this[string columnName] { get { if (columnName == "ID") { if (_id >18) { return ">18"; } } if (columnName == "Name") { if (Name.Length > 3) return "leng>3"; } return string.Empty; } } }
Person類提供了2個屬性,繼承了iDataErrorInfo,在索引器中判斷每個屬性的值是否符合要求,在索引器中的return 返回的值會被全局靜態對象Validation接受,這是binding表達式提供的功能,會在綁定的目標和數據源之間檢測是否存在檢驗信息,在控件上如何獲取呢?
只需要在需要接受驗證的對象上打開
ValidatesOnDataErrors=True,ValidatesOnExceptions=True
xaml代碼:
<Grid> <StackPanel> <Label Content="ID"/> <TextBox x:Name="tb" Text="{Binding ID, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True}"/> <TextBox Text="{Binding Path=(Validation.Errors)[0].ErrorContent,ElementName=tb}"/> <Label Content="Name"/> <TextBox x:Name="tbname" Text="{Binding Name,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True}"/> <TextBox Text="{Binding Path=(Validation.Errors)[0].ErrorContent,ElementName=tbname}"/> </StackPanel> </Grid>
運行結果:
當使用IdataErrorInfo時,需要在索引器中以此判斷每個屬性和屬性的值,為了簡化索引器中的代碼,通過反射去獲取每個屬性,並通過添加特性的方法去完成驗證
public class Person : INotifyPropertyChanged, IDataErrorInfo { private int _id; private string _name; [Required] [MyArrtibute ] public string Name { get { return _name; } set { _name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } } [Range(0,18,ErrorMessage ="超出范圍了")] [Required] public int ID { get { return _id; } set { _id = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ID")); } } //INotifyPropertyChanged的接口 public event PropertyChangedEventHandler? PropertyChanged; //IDataErrorInfo的接口 public string Error { get { return ""; } } public string this[string columnName] { get { //引入命名空間using System.ComponentModel.DataAnnotations; var vc = new ValidationContext(this, null, null); vc.MemberName = columnName; var res = new List<ValidationResult>(); var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res); if (res.Count > 0) { return string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray()); } return string.Empty; } } }
一個類繼承了IdataErrorInfi后,又使用DataAnnotations去改善索引器后,屬性的驗證邏輯需要通過特性的方式去完成,特性分為系統自帶的和自定義的,新建特性的方法,新建一個類並繼承ValidationAttribute,在該類中返回一個ValidationResult即可。
新建特性代碼:
public class MyArrtibute : ValidationAttribute { protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { if (value.ToString().Length > 10) return new ValidationResult("名字長度大於10了"); return base.IsValid(value, validationContext); } }
第一個參數value就是在binding表達式,從數據源到目標的值,這里的return返回的值依舊是被全局靜態對象Validation接受,這是由binding功能提供的
特性的使用方法:
[Required] [MyArrtibute ] public string Name { get { return _name; } set { _name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } }
在需要驗證的屬性前加上特性即可
運行結果: