前言
WPF中的TextBox是最常使用的控件,一个友好的界面应该加入验证的功能,如下图所示
当输入年龄不合适时,及时给出友好的提醒。这种功能很常见,也很实用。实现的方式也有很多种。这篇文章介绍使用ExceptionValidationRule,即绑定的属性数据发生异常时,触发TextBox自身的Validation机制。下面先看看主要代码
实现
前台
<TextBox Name="txtAge"> <TextBox.Text> <Binding Path="Age" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <ExceptionValidationRule/> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
或采用如下更简洁的方式
<TextBox Text="{Binding Age,UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" />
说明:UpdateSourceTrigger="PropertyChanged",验证规则采用ExceptionValidationRule。
后台
class Student { private int age; /// <summary> /// 姓名 /// </summary> public string Name { get; set; } ///<summary> /// 年龄 /// </summary> public int Age { get { return age; } set { if (value >=0 && value <= 150) { age = value; } else { throw new Exception("Age should between 0 and 150"); } } } }
说明:这里为了简化代码,Student类并未继承INotifyPropertyChanged接口,如果需要实现双向绑定,必须继承并实现INotifyPropertyChanged接口
运行的效果如下图所示,当输入200时,TextBox的Border为红色,但是界面中并未出现“Age should between 0 and 150”字眼。
其实,此刻已经成功了一半了,至于没有出现错误提示信息,是TextBox模板的问题,修改下TextBox模板即可
<Style TargetType="TextBox"> <Setter Property="MinHeight" Value="30"/> <Setter Property="Validation.ErrorTemplate"> <Setter.Value> <ControlTemplate> <StackPanel Orientation="Horizontal" > <Border > <Grid> <AdornedElementPlaceholder x:Name="adorner" /> </Grid> </Border> <Grid Width="10"/> <Popup Name="popup" AllowsTransparency="True" Placement="Right"> <Border x:Name="errorBorder" Background="#ffdc000c" Opacity="0" MinHeight="30" > <TextBlock Margin="5,0" Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}" Foreground="White" TextWrapping="Wrap" VerticalAlignment="Center"/> </Border> </Popup> </StackPanel> <ControlTemplate.Triggers> <DataTrigger Value="True"> <DataTrigger.Binding> <Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" /> </DataTrigger.Binding> <DataTrigger.Setters> <Setter Property="IsOpen" TargetName="popup" Value="true"/> </DataTrigger.Setters> <DataTrigger.EnterActions> <BeginStoryboard x:Name="fadeInStoryboard"> <Storyboard> <DoubleAnimation Duration="00:00:00.15" Storyboard.TargetName="errorBorder" Storyboard.TargetProperty="Opacity" To="1"/> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <StopStoryboard BeginStoryboardName="fadeInStoryboard"/> <BeginStoryboard x:Name="fadeOutStoryBoard"> <Storyboard> <DoubleAnimation Duration="00:00:00" Storyboard.TargetName="errorBorder" Storyboard.TargetProperty="Opacity" To="0"/> </Storyboard> </BeginStoryboard> </DataTrigger.ExitActions> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TextBox"> <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True"> <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" /> </Border> <ControlTemplate.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="BorderBrush" Value="#ffdc000c"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
说明:最关键的代码是
Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"
有些文档使用如下代码,本人不推荐这种写法,因为不是所有的TextBox绑定都有验证消息,如果缺失,可能导致index异常。
Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
补充
走到这一步已经能实现输入验证功能了,但是新的需求来了,如何在“Submit”的时候判断是否所有的数据都满足要求。
方法一:判断每一个TextBox是否都满足条件
private void btnSubmit_Click(object sender, RoutedEventArgs e) { if(!Validation.GetHasError(txtName) && !Validation.GetHasError(txtAge)) { // TODO } else { MessageBox.Show("Please input correct data in red textbox"); } }
很明显这种方式存在一个问题,每个需要验证的TextBox都需要命名,然后在btnSubmit_Click下进行判断。对于数据项少的页面还好,数据项较多的,就比较繁琐而且容易遗漏。例如一些工控软件的数据项,可能有上百项。
方法二:
在Student类添加一个IsValidated属性,默认值为true,在每个异常项加入IsValidated = false。btnSubmit_Click只需要判断这个属性即可,主要代码如下
using System; using System.ComponentModel; class ViewMode: INotifyPropertyChanged { private bool isValidated = true; // 默认为全部通过验证 public event PropertyChangedEventHandler PropertyChanged; protected void InvokePropertyChanged(string property) { if (this.PropertyChanged != null) { this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property)); } } public bool IsValidated { get { return isValidated; } protected set { isValidated = value; } } } class Student:ViewMode { private string name; private double age; ///<summary> /// 姓名 /// </summary> public string Name { get { return name; } set { if (!string.IsNullOrEmpty(value)) { name = value; InvokePropertyChanged("Name"); } else { IsValidated = false; throw new Exception("Name cannot be empty"); } } } ///<summary> /// 年龄 /// </summary> public double Age { get { return age; } set { if (value >=0 && value <= 150) { age = value; InvokePropertyChanged("Age"); } else { IsValidated = false; throw new Exception("Age should between 0 and 150"); } } } } private void btnSubmit_Click(object sender, RoutedEventArgs e) { if(!stu.IsValidated) { MessageBox.Show("Please input correct data in red textbox"); } }
源码下载
立即下载
文章评论