首页 > 其他分享 >WPF表单验证

WPF表单验证

时间:2022-11-24 00:34:30浏览次数:72  
标签:return string 验证 dataErrors 表单 columnName WPF public

摘要

WPF表单验证是WPF重要基础设施之一,依靠MVVM的数据绑定机制及微软的有力封装,使得我们在处理实体表单验证等可以快捷高效的灵活处理。常见的表单验证实现大概有ExceptionValidationRuleIDataErrorInfo ,而本文则是通过IDataErrorInfo来实现表单验证功能

代码实现

1、封装验证模型

既然是直接对实体进行验证,那首先肯定是从实体对象模型着手,为了方便复用性,建议抽出一个公共的验证模型

public class ValidateModelBase : BindableBase, IDataErrorInfo
    {
        public Dictionary<string, string> dataErrors = new Dictionary<string, string>();  //错误信息集合

        public bool IsValidated
        {
            get
            {
                return !dataErrors.Any();
            }
        }

        public string this[string columnName]
        {
            get
            {
                ValidationContext vc = new ValidationContext(this, null, null)
                {
                    MemberName = columnName
                };
                var res = new List<ValidationResult>();
                var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res);
                if (res.Count > 0)
                {
                    string errorInfo = string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
                    AddError(dataErrors, columnName, errorInfo);
                    return errorInfo;
                }
                RemoveError(dataErrors, columnName);
                return null;
            }
        }

        /// <summary>
        /// 移除错误信息
        /// </summary>
        /// <param name="dataErrors"></param>
        /// <param name="columnName"></param>
        private void RemoveError(Dictionary<string, string> dataErrors, string columnName)
        {
            dataErrors.Remove(columnName);
        }

        /// <summary>
        /// 添加错误信息
        /// </summary>
        /// <param name="dataErrors"></param>
        /// <param name="columnName"></param>
        /// <param name="errorInfo"></param>
        private void AddError(Dictionary<string, string> dataErrors, string columnName, string errorInfo)
        {
            if (!dataErrors.ContainsKey(columnName))
            {
                dataErrors.Add(columnName, errorInfo);
            }
        }

        public string Error { get; set; }

        private bool isFormValid;
        /// <summary>
        /// 是否全局验证
        /// </summary>
        public bool IsFormValid
        {
            get { return isFormValid; }
            set { isFormValid = value; RaisePropertyChanged(); }
        }
    }

原理就是使用索引器获取页面上的错误信息添加到一个字典集合中,公开一个是否验证成功的属性,方便外部验证数据是否正确,这里直接使用字典的一个扩展方法Enumerable.Any ,如果字典中包含元素则返回True,否则False

2、准备实体对象并继承验证模型
public class StudentModel : ValidateModelBase
    {
        private string _StudentName;
        /// <summary>
        /// 学生姓名
        /// </summary>
        [Required(ErrorMessage = "学生姓名不允许为空")]
        [MinLength(2,ErrorMessage = "学生姓名不能少于两个字符")]
        public string StudentName
        {
            get { return _StudentName; }
            set { _StudentName = value; RaisePropertyChanged(); }
        }


        private int _StudentAge;
        /// <summary>
        /// 学生年龄
        /// </summary>
        [Required(ErrorMessage = "学生年龄不允许为空")]
        [Range(3, 120, ErrorMessage = "学生年龄范围需在1-120岁之间")]
        public int StudentAge
        {
            get { return _StudentAge; }
            set { _StudentAge = value; RaisePropertyChanged(); }
        }


        private string _StudentEmail;
        /// <summary>
        /// 学生邮箱
        /// </summary>
        [EmailAddress(ErrorMessage = "邮箱地址不合法")]
        public string StudentEmail
        {
            get { return _StudentEmail; }
            set { _StudentEmail = value; RaisePropertyChanged(); }
        }


        private string _StudentPhoneNumber;
        /// <summary>
        /// 学生手机号
        /// </summary>
        [Required(ErrorMessage = "学生手机号不允许为空")]
        [RegularExpression(@"^1[3-9]\d{9}$", ErrorMessage = "手机号不正确")]
        public string StudentPhoneNumber
        {
            get { return _StudentPhoneNumber; }
            set { _StudentPhoneNumber = value; }
        }

    }

因其微软早已对验证进行其封装为注解(特性),相信做Web的童鞋并不陌生,不太了解的前往微软文档查阅下==>DataType 枚举

3、使用Adorned装饰器对文本框进行错误模板重写
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="VaildationTextBoxStyle" TargetType="{x:Type TextBox}">
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="Padding" Value="2,1,1,1" />
        <Setter Property="AllowDrop" Value="true" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
        <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Border
                            x:Name="adornerborder"
                            VerticalAlignment="Top"
                            BorderThickness="1">
                            <Grid>
                                <AdornedElementPlaceholder x:Name="adorner" Margin="-1" />
                            </Grid>
                        </Border>
                        <Border
                            x:Name="errorBorder"
                            MinHeight="24"
                            Margin="8,0,0,0"
                            Background="#FFdc000c"
                            CornerRadius="0"
                            IsHitTestVisible="False"
                            Opacity="0">
                            <TextBlock
                                Margin="8,2,8,3"
                                VerticalAlignment="Center"
                                Foreground="White"
                                Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
                                TextWrapping="Wrap" />
                        </Border>
                    </StackPanel>

                    <ControlTemplate.Triggers>
                        <DataTrigger Value="True">
                            <DataTrigger.Binding>
                                <Binding ElementName="adorner" Path="AdornedElement.Tag" />
                            </DataTrigger.Binding>
                            <DataTrigger.EnterActions>
                                <BeginStoryboard x:Name="fadeInStoryboard1">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Storyboard.TargetName="errorBorder"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1"
                                            Duration="00:00:00.15" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.Setters>
                                <Setter TargetName="adornerborder" Property="BorderBrush" Value="#FFdc000c" />
                            </DataTrigger.Setters>
                        </DataTrigger>

                        <DataTrigger Value="True">
                            <DataTrigger.Binding>
                                <Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" />
                            </DataTrigger.Binding>
                            <DataTrigger.EnterActions>
                                <BeginStoryboard x:Name="fadeInStoryboard">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Storyboard.TargetName="errorBorder"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1"
                                            Duration="00:00:00.15" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <!--  是否保留异常直到数据正常:如果不需要则放开下列验证  -->
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="fadeInStoryboard" />
                                <BeginStoryboard x:Name="fadeOutStoryBoard">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Storyboard.TargetName="errorBorder"
                                            Storyboard.TargetProperty="Opacity"
                                            To="0"
                                            Duration="00:00:00" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.ExitActions>
                            <DataTrigger.Setters>
                                <Setter TargetName="adornerborder" Property="BorderBrush" Value="#FFdc000c" />
                            </DataTrigger.Setters>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style BasedOn="{StaticResource VaildationTextBoxStyle}" TargetType="{x:Type TextBox}" />
</ResourceDictionary>

这里直接定义为一个资源字典了,其中比较核心的是Validation.ErrorTemplate,取验证器错误信息集合中的索引为0的一条进行显示

Tips:如果要全局引用,建议定义一个无Key的Style,并放置在App.xaml

<Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/WPF_FormValidation_Demo;component/Resources/VaildationTextBoxStyle.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
4、最后就是在页面上使用
<StackPanel Margin="5" Orientation="Horizontal">
                    <TextBlock Width="70" TextAlignment="Right">
                        <Run Foreground="Red" Text="*" />
                        <Run Text="学生姓名:" />
                    </TextBlock>
                    <TextBox
                        Width="150"
                        Tag="{Binding StudentInfo.IsFormValid, UpdateSourceTrigger=PropertyChanged}"
                        Text="{Binding StudentInfo.StudentName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
                </StackPanel>

注意更新源触发模式一定要指定为PropertyChanged,还要将ValidatesOnDataErrors启用。

如果想要在没有验证通过的情况下禁用提交按钮,可将IsValidated绑定到按钮的IsEnabled属性上

<Button
                    Width="150"
                    Height="26"
                    Margin="76,5"
                    IsEnabled="{Binding IsValidated}"
                    HorizontalAlignment="Left"
                    Command="{Binding SubmitCommand}"
                    Content="新生注册" />

如果想要统一验证并显示错误信息,可将IsFormValid启用

private void Submit()
        {
            if (!StudentInfo.IsValidated)
            {
                StudentInfo.IsFormValid = true;
                MessageBox.Show("新生注册失败,请检查录入的新生信息是否正确!");
                return;
            }
            MessageBox.Show("新生注册成功!");
        }

别看我写在提交的Command方法里了,写在构造函数里也可以的,看业务需要在什么时候进行统一验证,自己灵活处理即可。

效果

  • 全局统一验证

    image-20221123235048833

  • 单一光标聚焦验证

    image-20221123235121965

  • 动效演示

    1

源代码

https://github.com/luchong0813/WPF_FormValidation_Demo

标签:return,string,验证,dataErrors,表单,columnName,WPF,public
From: https://www.cnblogs.com/chonglu/p/16920611.html

相关文章

  • 【Abp笔记】验证 Validation
    验证Validation官方文档https://docs.abp.io/zh-Hans/abp/latest/Validation哪里可以验证?哪里都行怎么验证?1.需要验证的类型(实体、DTO、Options之类的)实现IValid......
  • JS验证及错误调试
    /* try语句测试代码块的错误。 catch语句处理错误。 throw语句创建自定义错误。 finally语句在try和catch语句之后,无论是否有触发异常,该语句都会执行......
  • js012-提交表单的两种方式
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>提交表单</title></head><body><formid="i1"action="https://www.baidu.com">......
  • Pig4Cloud之登陆验证(一)客户端认证处理
    前端登陆handleLogin(){this.$refs.loginForm.validate(valid=>{if(valid){this.$store.dispatch("LoginByUsername",t......
  • 【转】WPF 中双向绑定通知机制之OBSERVABLECOLLECTION使用
    WPF中双向绑定通知机制之ObservableCollection使用msdn中  ObservableCollection<T> 类  表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将......
  • vue 使用i18n 实现中英文切换 表单校验提示不更新问题
    在用i18n实现中英文切换的时候,出现了在表单中校验不更新的问题,尝试多种方式无果,下面这个方法值得一试。可轻松解决此问题。data(){ruleInline:{userName:......
  • 第三章. 业务功能开发--登录验证(使用拦截器)
    第三章.业务功能开发--登录验证(使用拦截器)需求登录验证. 用户访问任何业务资源,都需要进行登录验证. *只有登录成功的用户才能访问业务资源 *没有登录成功的用户访......
  • wpf 子页面调用父窗口方法
     参考:http://www.360doc.com/content/17/1113/11/24811_703389993.shtml//1)子页面后台定义委托(namespace下class外)//定义委托internaldelegatevoidColorChange(o......
  • wpf 后台添加控件
    音乐播放列表,鼠标右键点击列表时,出现添加到歌单,做成每次右键点击时,后台新增选项了(也可以前台绑定数据源每次增删改后更新数据源)TextBlocktext=newTextBlock();text.......
  • wpf 后台修改控件颜色(颜色Brush类型新建)
    16进制、rgb格式、提供的颜色示例:xxx.Foreground=newSolidColorBrush((Color)ColorConverter.ConvertFromString("#FFFFFF"));xxx.Background=newSolidColorBrus......