在软件UI界面录入数据时候,为了保证数据的正确、有效、规范性,很多情况下都要验证数据的有效性。比如录入某个人的信息,那么我们就需要对这个人的姓名验证是否为空字符串,年龄是否为有效数字,性别是否为男或女等等。
在WPF开发中,数据的验证和数据绑定结合在一起,实现起来很优雅。我们可以让数据Model(模型)实现IDataErrorInfo接口来实现每个属性的验证逻辑。或者自定义一个继承自ValidationRule的类来实现验证逻辑。然后在Xaml中提供控件的ErrorTemplate模板,就可以得到很好的验证效果。下面提供一个样例代码:
后台数据
1 public class Person : IDataErrorInfo 2 { 3 public string Name { get; set; } 4 5 public int Age { get; set; } 6 7 public string Sex { get; set; } 8 9 public string Error 10 { 11 get { return "输入字符串无效"; } 12 } 13 14 public string this[string columnName] 15 { 16 get 17 { 18 string msg = string.Empty; 19 switch (columnName) 20 { 21 case "Name": 22 { 23 if (string.IsNullOrEmpty(this.Name.Trim())) 24 { 25 msg = "*请完善姓名内容"; 26 } 27 break; 28 } 29 case "Age": 30 { 31 if (this.Age < 0 || this.Age > 120) 32 { 33 msg = "*年龄范围为{0-120}之间"; 34 } 35 break; 36 } 37 case "Sex": 38 { 39 if (string.IsNullOrEmpty(this.Sex.Trim()) || 40 !(this.Sex.Trim() == "Boy" || this.Sex.Trim() == "Girl")) 41 { 42 msg = "*你确定你既不是Boy也不是Girl吗?"; 43 } 44 break; 45 } 46 } 47 return msg; 48 } 49 } 50 } 51 52 public class AgeValidate : ValidationRule 53 { 54 private int minAge; 55 public int MinAge 56 { 57 get { return minAge; } 58 set { minAge = value; } 59 } 60 61 private int maxAge; 62 public int MaxAge 63 { 64 get { return maxAge; } 65 set { maxAge = value; } 66 } 67 68 public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 69 { 70 int age; 71 string msg = string.Format("请输入大于{0}小于{1}的数字", minAge, maxAge); 72 if (int.TryParse(value.ToString(), out age)) 73 { 74 if (age < minAge || age > maxAge) 75 return new ValidationResult(false, msg); 76 else 77 return new ValidationResult(true, null); 78 } 79 else 80 { 81 return new ValidationResult(false, "请输入数字"); 82 } 83 } 84 } 85 86 public MainWindow() 87 { 88 InitializeComponent(); 89 90 Person ZhangSan = new Person() 91 { 92 Age = 20, 93 Name = "ZhangSan", 94 Sex = "Boy" 95 }; 96 this.DataContext = ZhanSan; 97 98 }
UI代码
1 <Window.Resources> 2 <local:Converter x:Key="MarginConverter"></local:Converter> 3 <Style TargetType="TextBox"> 4 <Setter Property="Width" Value="120"></Setter> 5 <Setter Property="Margin" Value="5"></Setter> 6 <Setter Property="VerticalAlignment" Value="Center"></Setter> 7 </Style> 8 <Style TargetType="TextBlock"> 9 <Setter Property="MinWidth" Value="50"></Setter> 10 <Setter Property="Margin" Value="5"></Setter> 11 <Setter Property="VerticalAlignment" Value="Center"></Setter> 12 </Style> 13 <ControlTemplate x:Key="ErrorTemplate"> 14 <Canvas Panel.ZIndex="100"> 15 <Border BorderBrush="Red" BorderThickness="1"> 16 <AdornedElementPlaceholder Name="MyAdorner"/> 17 </Border> 18 <TextBlock Foreground="Red" FontSize="12" 19 Margin="{Binding ElementName=MyAdorner, Path=ActualWidth, Converter={StaticResource MarginConverter}}" 20 Text="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"> 21 </TextBlock> 22 </Canvas> 23 </ControlTemplate> 24 </Window.Resources> 25 26 <StackPanel> 27 <WrapPanel> 28 <TextBlock>Name:</TextBlock> 29 <TextBox Text="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" 30 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"></TextBox> 31 <TextBlock>Age:</TextBlock> 32 <TextBox Validation.ErrorTemplate="{StaticResource ErrorTemplate}"> 33 <TextBox.Text> 34 <Binding Path="Age" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged" 35 NotifyOnValidationError="True"> 36 <Binding.ValidationRules> 37 <local:AgeValidate MaxAge="99" MinAge="1"></local:AgeValidate> 38 </Binding.ValidationRules> 39 </Binding> 40 </TextBox.Text> 41 </TextBox> 42 </WrapPanel> 43 <WrapPanel> 44 <TextBlock>Sex:</TextBlock> 45 <TextBox Text="{Binding Path=Sex, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" 46 Validation.ErrorTemplate="{StaticResource ErrorTemplate}"></TextBox> 47 </WrapPanel> 48 <WrapPanel> 49 <TextBlock>Age:</TextBlock> 50 <TextBox Validation.ErrorTemplate="{StaticResource ErrorTemplate}"> 51 <TextBox.Text> 52 <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"> 53 </Binding> 54 </TextBox.Text> 55 </TextBox> 56 </WrapPanel> 57 </StackPanel>
Converter Class
1 public class Converter : IValueConverter 2 { 3 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 4 { 5 if (value is double) 6 { 7 return new Thickness((double)value + 5, 3, 5, 5); 8 } 9 else return value; 10 } 11 12 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 13 { 14 throw new NotImplementedException(); 15 } 16 }
注意:1. xaml中验证时机 UpdateSourceTrigger="PropertyChanged"表示属性修改后马上验证;
2. ErrorTemplate 中TextBlock的 Text="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">的实现,这里Path=AdornedElement指的就是MyAdorner所装饰的元素,也就是我们需要验证的控件。因此获取的是该控件的(Validation.Errors)[0].ErrorContent 验证错误信息。