zoukankan      html  css  js  c++  java
  • 关于WPF的验证

    引言

         数据验证在任何用户界面程序中都是不可缺少的一部分.在WPF中,数据验证更是和绑定紧紧联系在一起,下面简单介绍MVVM模式下常用的几种验证方式.

    错误信息显示

         在介绍数据验证之前,有必要介绍一下如何显示错误信息.方式很简单,定义一个样式触发器,将错误信息和 ToolTip绑定,如下:

    复制代码
               <Style TargetType="TextBox">
                    <Style.Triggers>
                        <Trigger Property="Validation.HasError" Value="true">
                            <Setter Property="ToolTip"
                                Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
    复制代码

     ValidatesOnExceptions验证规则

          ValidatesOnExceptions是WPF预定义的验证规则,它会捕捉任何位置上的异常,包括类型转换异常,属性设置器异常,值转换器异常等.捕捉到异常的时候,输入的边框会变成红色,当然也可以自定义错误的模板(Validation.ErrorTemplate).想要ValidatesOnExceptions生效,将绑定属性中的ValidatesOnExceptions设置为true即可.

    PS:无论设置为true或false,类型转换异常总会发生的,也就是总会有红色框. 

    数据对象中验证

        直接在数据对象中编写验证规则是最简单粗暴的方式,如下

    复制代码
            public int Price
            {
    
                get { return _price; }
                set
                {
                    if (Equals(value, _price)) return;
    
                    if (value < 0)
                    {
    
                        throw new ArgumentException("数值不能小于0");
    
                    }
                    else
                    {
                        _price = value;
                        RaisePropertyChanged(() => Price);
                    }
                }
            }
    复制代码

    如果小于0,程序不会抛错,文字提示也会显示在ToolTip上,前提是ValidatesOnExceptions=true.

    PS:这种方式能如期实现,是因为WPF的Binding 捕捉属性设置中的所有异常.但是,如果是代码设置负数的话,程序直接挂掉.

    自定义验证规则

           除了WPF预定义的验证规则外,我们还可以自定义验证规则,要继承ValidationRule,编写验证不能大于99的数值,代码如下:

    复制代码
        public class NumberRule : ValidationRule
        {
            public override System.Windows.Controls.ValidationResult Validate(object value, CultureInfo cultureInfo)
            {
                int i;
                //  int.TryParse(value.ToString(), out i);
                if (!int.TryParse(value.ToString(), out i))
                {
                    return new System.Windows.Controls.ValidationResult(false,
                           "字符串格式不对!");
                }
                if (i > 99)
                {
                    return new System.Windows.Controls.ValidationResult(false,
                            "数值不能大于99!");
                }
                else
                {
                    return new System.Windows.Controls.ValidationResult(true, null);
                }
    
            }
        }
    复制代码
    复制代码
           <TextBox  Height="25" Width="100" Margin="208,142,0,0"  VerticalAlignment="Top" HorizontalAlignment="Left" >
                <TextBox.Text>
                    <Binding Path="Price" Mode="TwoWay" ValidatesOnDataErrors="True">  
                        <Binding.ValidationRules>
                            <ExceptionValidationRule></ExceptionValidationRule>
                            <local:NumberRule></local:NumberRule>
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>           
            </TextBox>
    复制代码

    PS:自定义验证规则总是在ExceptionValidationRule之前进行,所以要在NumberRule加上转换类型的异常处理,不然会有抛错的可能性,当然上面的代码中如果有类型错误变量i总会返回0.

    PS:验证的执行顺序:自定义验证规则->值转换器->ExceptionValidationRule->数据对象验证.

     ValidatesOnDataErrors验证规则

         正常情况下,上面的几种方式都可以工作得很好,但是属性多达几十个的时候,写起来就不是那么的舒服了.这个时候我们可以通过继承接口IDataErrorInfo来将我们的验证规则统一起来,代码如下:

    复制代码
            public string Error
            { get { return ""; } }
    
            public string this[string propertyname]
            {
                get
                {
                    string result = null;
                    if (propertyname == "Price")
                    {
                        if (Price >99)
                        {
                            result = "数值不能大于99!!";
                        }
                    }
                    return result;
                }
            }
    复制代码
    Error在WPF中没作用,返回任意都可以.
    PS:记得把ValidatesOnDataErrors=true
    PS:验证的执行顺序:自定义验证规则->值转换器->ExceptionValidationRule->数据对象验证->ValidatesOnDataErrors.

    进阶 ValidatesOnDataErrors验证规则
    ValidatesOnDataErrors虽然能统一起来到一个地方,但是还免不了每一个属性单独写一个规则.所以我们需要一个更简便的方式,那就是DataAnnotations+IDataErrorInfo的方式,代码如下:
    复制代码
            private int _price;
            [Range(0, 99, ErrorMessage = "数值要在0到99之间")]
            public int Price
            {
    
                get { return _price; }
                set
                {
                    if (Equals(value, _price)) return;
    
                    if (value < 0)
                    {
    
                        throw new ArgumentException("数值不能小于0");
    
                    }
                    else
                    {
                        _price = value;
                        RaisePropertyChanged(() => Price);
                    }
                }
            }
    
            public string this[string propertyname]
            {
                get
                {
                    var vc = new ValidationContext(this, null, null);
                    vc.MemberName = propertyname;
                    var res = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
    
                    var result = Validator.TryValidateProperty(this.GetType().GetProperty(propertyname).GetValue(this, null), vc, res);
                    if (res.Count > 0)
                    {
                        return string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
                    }
                    return string.Empty;
                }
            }
    复制代码
    
    

     采用这种方式,开发的时候只需要简单的设置一下特性,就能如期望的显示我们的验证提示了,关于这种方式的详细用法,网上有一篇更好的文章:传送门.

     注意事项

           到这里,如果没有什么意外,相信大家都会采用DataAnnotations+IDataErrorInfo的方式,这种方式实现最简单,而且发生在viewmodel上,我们很容易地在保存环节得到所有异常信息,从而阻止保存数据的进行.但是WPF的数据验证中都有个通病,就是发生数据异常的时候,属性实际的值还是上次合法的值,和界面上显示的值有所不同.这个时候如果用户强行保存,我们就发现DataAnnotations+IDataErrorInfo的验证方式竟然通过了!这不符合我们的期望.这种情况我没发现有什么优雅的解决方案,暂时想到的只有在按钮的点击事件中遍历LogicalTreeHelper的输入控件,检查Validation.HasError属性,组合异常信息传给viewmodel,让viewmodel作出处理.其实最为彻底的方式是,封装数字输入控件等各类特定的控件,提高用户体验的同时,也让异常处理更简单.

    小结

        本文简单介绍了WPF数据验证的各种方式,而我们基本上都会采用 DataAnnotations+IDataErrorInfo的方式,如果您有更好的方式,请不吝指教,感激不尽!

    • 定义控件模板

    如果要为相应的控件添加一些辅助的控件,可以使用控件模板,如出现验证错误时,不使用系统默认的红色边框,而是在文本框后添加一个红色的星号:

       1: <ControlTemplate x:Key="validErrorTextBoxTemplate">
       2:     <DockPanel>
       3:         <AdornedElementPlaceholder/>
       4:         <TextBlock Foreground="Red" FontSize="20">*</TextBlock>
       5:     </DockPanel>
       6: </ControlTemplate>

    并在每一个输入的TextBox中添加:

       1: Validation.ErrorTemplate="{StaticResource validErrorTextBoxTemplate}"

    22-9

  • 相关阅读:
    【C++】未定义的标识符“_TCHAR*”
    【C++】编写动态库DLL
    【C++】使用SQLite步骤及示例
    【VS】如何查询该项目是什么样工程类型
    新概念英语第一册单词
    地道情景表达一
    日常学习经典口语一
    全国省市区sql脚本
    关于Object.defineProperty
    响应式原理
  • 原文地址:https://www.cnblogs.com/sjqq/p/8456894.html
Copyright © 2011-2022 走看看