王彦为

聚沙成塔
  1. 首页
  2. WPF
  3. 正文

WPF的TextBox输入验证之Exception验证

2016-12-20 12587点热度 38人点赞 0条评论

前言


WPF中的TextBox是最常使用的控件,一个友好的界面应该加入验证的功能,如下图所示
textboxvalidation
当输入年龄不合适时,及时给出友好的提醒。这种功能很常见,也很实用。实现的方式也有很多种。这篇文章介绍使用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”字眼。
textboxvalidation1
其实,此刻已经成功了一半了,至于没有出现错误提示信息,是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");
    }
}

源码下载


立即下载

标签: 暂无
最后更新:2020-09-25

王彦为

新生代农民工,十年医疗器械行业从业经验,现居苏州。爱生活,爱做梦。

打赏 点赞
< 上一篇
下一篇 >

文章评论

取消回复

COPYRIGHT © 2022 王彦为. ALL RIGHTS RESERVED.

苏ICP备16063331号-1

苏公网安备32050702011313号