接上一篇文章《WPF的TextBox输入验证之Exception验证》,该文章继续讲述TextBox输入验证的方法,利用ValidationRule验证。
效果
初步方案
// 第一步,修改TextBox的Validation.ErrorTemplate // 修改方法参考文章《WPF的TextBox输入验证之Exception验证》或下载源码 // 第二步,写验证规则 using System.Windows.Controls; class AgeRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { int age; if (!int.TryParse(value.ToString(), out age)) { return new ValidationResult(false, "Invalid number format"); } if (age > 150 || age < 0) { return new ValidationResult(false, "Age should between 0 and 150"); } return ValidationResult.ValidResult; } } // 第三步,写XAML布局 <TextBox Grid.Row="1" Grid.Column="1" Name="txtAge" > <TextBox.Text> <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" > <Binding.ValidationRules> <local:NumberRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> // 第四步,提交时验证所有控件是否满足要求 private void btnSubmit_Click(object sender, RoutedEventArgs e) { if (!Validation.GetHasError(txtName) && !Validation.GetHasError(txtAge)) { MessageBox.Show("Verificate successfully."); } else { MessageBox.Show("Please input correct data in red textbox","Warning", MessageBoxButton.OK, MessageBoxImage.Warning); } }
该方案有两个的缺点:一、提交表单时需要验证所有控件是否满足要求,所以所有TextBox都需要命名;二、每个属性都需要写一个验证类,工作量较大。针对第一点我们查找出页面中所有的TextBox控件,然后进行遍历判断,代码如下:
private void Submit() { List<TextBox> texboxs = new List<TextBox>(); GetChildrenObject(this, ref texboxs, true); foreach (var texbox in texboxs) { if (Validation.GetHasError(texbox)) { MessageBox.Show("Please input correct data in red textbox", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); return; } } MessageBox.Show("Verificate successfully."); } private void GetChildrenObject<T>(DependencyObject obj, ref List<T> list, bool isDeepSearch) where T : FrameworkElement { DependencyObject child = null; for (var i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++) { child = VisualTreeHelper.GetChild(obj, i); if (child is T) { list.Add((T)child); } else { if (isDeepSearch) { GetChildrenObject(child, ref list, true); } } } }
针对第二点,我们可以抽象出一个通用的验证类,例如年龄的范围是0-150岁,身高的的范围是40-300cm,那么我们抽象出一个数据范围验证类,在Xaml中设置最大值和最小值,改进方案如下。
改进方案
<TextBox Grid.Row="1" Grid.Column="1" Name="txtAge" > <TextBox.Text> <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" > <Binding.ValidationRules> <local:NumberRule Minimum="0" Maximum="150"/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <TextBox Grid.Row="1" Grid.Column="1" Name="txtHeight" > <TextBox.Text> <Binding Path="Height" UpdateSourceTrigger="PropertyChanged" > <Binding.ValidationRules> <local:NumberRule Minimum="40" Maximum="300" ErrorMessage="身高范围[40,300]cm"/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> // 数字验证类 class NumberRule : ValidationRule { // 添加ErrorMessage字段,用于自定义错误提示 public string ErrorMessage { get; set; } public double Minimum { get; set; } public double Maximum { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { double number; if (!double.TryParse(value.ToString(), out number)) { return new ValidationResult(false, "Invalid number format"); } if (number > Maximum || number < Minimum) { if(!string.IsNullOrEmpty(ErrorMessage)) { return new ValidationResult(false, ErrorMessage); } else { return new ValidationResult(false, string.Format("Value should between {0} and {1}", Minimum, Maximum)); } } return ValidationResult.ValidResult; } }
完美主义者
经过改进后的方案可以说是相当完美了,但是还是发现几个奇怪的现象:
1、TextBox代码比较臃肿,每个TextBox至少需要9行代码,而且还无法使用模板解决
2、属性的范围及错误提示都在xaml中
3、Submit时,是对Texbox进行验证,而非属性本身
针对第一点了其实还能接受,至于第二点,如果数据的验证范规则或者范围改变,需要修改前端代码,更多人倾向于在后端写验证规则,前端调用。所以有没有一种方式是后端验证,将验证的结果通知到UI呢?这个就是我们下一篇文章要介绍的。《WPF的TextBox输入验证之IDataErrorInfo验证》。
源码下载
文章评论