精华内容
下载资源
问答
  • WPF 数据验证

    2011-12-15 14:02:49
    WPF 数据验证,下载即可作用;界面写入提示信息;自动判断信息的种类;在鼠标放在控件上自动显示提示信息。
  • WPF数据验证

    2019-07-08 11:27:17
    当填写表单时,需要对填写的内容进行验证,检查数据是否符合要求,比如字符串的长度、日期的格式、数字等。WPF支持自定义验证规则,并提供可视化反馈,以便在输入无效值时向用户发出通知。 下面的示例将演示一个模拟...

    当填写表单时,需要对填写的内容进行验证,检查数据是否符合要求,比如字符串的长度、日期的格式、数字等。WPF支持自定义验证规则,并提供可视化反馈,以便在输入无效值时向用户发出通知。

    下面的示例将演示一个模拟员工信息录入的过程,如果年龄不再给定的输入范围内,将在文本框的后面显示一个红色的叹号,当鼠标移至错误文本框时,显示提示消息,提示用户正确的输入格式。

    主要内容为:自定义验证规则、定义输入错误时控件的外观、定义样式触发器显示错误提示

    自定义验证规则

    可以继承ValidationRule类,重写Validate方法,实现自定义的验证规则

    下面是年龄的验证规则的实现:

        public class AgeRangeRule : ValidationRule
        {
            private int _min;
            private int _max;
    
            public AgeRangeRule()
            {
    
            }
    
            public int Min
            {
                get { return _min; }
                set { _min = value; }
            }
    
            public int Max
            {
                get { return _max; }
                set { _max = value; }
            }
    
            public override ValidationResult Validate(object value, CultureInfo cultureInfo)
            {
                int age = 0;
                try
                {
                    if (((string)value).Length > 0)
                        age = Int32.Parse((String)value);
                }
                catch (Exception e)
                {
                    return new ValidationResult(false, "输入的数字无效!");
                }
    
                if ((age < Min) || (age > Max))
                {
                    return new ValidationResult(false,
                      "输入的年龄范围必须在: " + Min + " - " + Max + "之间");
                }
                else
                {
                    return new ValidationResult(true, null);
                }
            }
        }
    View Code

    定义输入错误时控件的外观

    自定义的 ControlTemplate,它用于创建一个红色感叹号,以通知用户验证错误。 控件模板用于重新定义控件的外观。

    <ControlTemplate x:Key="validationTemplate">
      <DockPanel>
        <AdornedElementPlaceholder/>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
      </DockPanel>
    </ControlTemplate>

    定义样式触发器显示错误提示

    显示错误消息的 ToolTip 是使用名为 textBoxInError 的样式创建的。 如果 HasError 的值是 true,则触发器将当前 TextBox 的工具提示设置为其第一个验证错误。 RelativeSource 设置为 Self,以引用当前元素。

    <Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
          <Setter Property="ToolTip"
            Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                            Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
      </Style.Triggers>
    </Style>

     

    控件绑定相应的模板和样式

    <TextBox Name="textBox1" Width="50" FontSize="15"
             Validation.ErrorTemplate="{StaticResource validationTemplate}"
             Style="{StaticResource textBoxInError}"
             Grid.Row="1" Grid.Column="1" Margin="2">
      <TextBox.Text>
        <Binding Path="Age" Source="{StaticResource employee}"
                 UpdateSourceTrigger="PropertyChanged" >
          <Binding.ValidationRules>
            <c:AgeRangeRule Min="21" Max="130"/>
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>

    当输入的年龄不在定义范围内时,控件的显示方式如下:

    SNAGHTML18d97c4

    注:当数据绑定验证时,如果验证规则的ValidationStep属性设置为RawProposedValue或ConvertedProposedValue值时,在验证失败的情况下,数据不会更新到源,这也在情理之中,不合格的数据我们当然不接受。

    转载于:https://www.cnblogs.com/guofeiji/p/5517094.html

    展开全文
  • WPF数据验证方式

    2018-03-30 08:40:20
    WPF有两种数据验证的方式:1 在数据对象上进行验证:普通属性验证或者实现IDataErrorInfo接口2 可以再绑定规则上进行验证:ExceptionValidationRule异常验证规则 DataErrorValidationRule数据错误验证规则...

    WPF有两种数据验证的方式:

    1 在数据对象上进行验证:普通属性验证或者实现IDataErrorInfo接口

    2 可以再绑定规则上进行验证:ExceptionValidationRule异常验证规则 DataErrorValidationRule数据错误验证规则

    展开全文
  • 虽然标题是wpf数据验证,但并不是对IDataErrorInfo、ValidationRule、属性中throwException这几种验证方式的介绍; 之前做项目时(例如员工工资管理),根据员工编号和年度月份验证 当月数据的唯一性,因为第一次开发...

    虽然标题是wpf数据验证,但并不是对IDataErrorInfo、ValidationRule、属性中throw Exception这几种验证方式的介绍;

    之前做项目时(例如员工工资管理),根据员工编号和年度月份验证 当月数据的唯一性,因为第一次开发wpf经验不足,所以用过几种不同的方式,并且现在用的这种方式也不是很满意,如果哪位大神有更好的办法 麻烦发个链接。

    文章最后会把验证、列表的样式和验证中常使用的一些方法贴出来,方便大家使用;

    列表页面 员工编号和年度月份验证

    添加修改页面 填写编号选择月份后,验证不通过都是在编号处提示

     

    一、言归正传,逐步写一下我当时的思路

    1、为了实现这种需求的验证,最先想到的就是实现了ValidationRule的自定义验证类(ValidateExistLogName)能有一个属性(ValiByProperty) binding上月份中选择的值,关联月份和当前输入的员工编号来验证当月是否存在;

    1
    2
    3
    4
    5
    <Binding.ValidationRules>
     
    <tool:ValidateExistLogName ValiByProperty="{Binding CurMonth}"/>
     
    </Binding.ValidationRules>

      但是只有DependencyObject派生类的DependencyProperty属性才能进行binding,于是我找到了给ValidationRule派生类的属性上binding的办法

    参考链接:http://www.codeproject.com/Articles/18678/Attaching-a-Virtual-Branch-to-the-Logical-Tree-in

         https://social.msdn.microsoft.com/Forums/vstudio/en-US/982e2fcf-780f-4f1c-9730-cedcd4e24320/binding-validationrules-property?forum=wpf

      这种方式可能添加页面比较好实现,但是对于列表DataGrid恐怕binding起来就,也许有人说可以DataGrid的IsReadOnly=false,但是我的需求是修改页面修改的同时支持列表直接修改。

     

    2、对实体类添加PropertyChangedEventHandler事件,这种方式可以实现,但是却不是在ValidationRule中验证,而且事件中的逻辑代码也稍较麻烦,因为e.PropertyName绑定的是datepicker控件时,需throw new Exception才能显示出来错误

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    列表中 初始化列表时遍历datagrid中的绑定源数据:
     foreach (var item in data)
                {
                    //为新加数据也加入事件
                    item.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(source_PropertyChanged);
                    item.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(source_PropertyChanged);
                }
     
    添加或者修改直接给绑定的实体类添加事件:
    source.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(source_PropertyChanged);

     

    3、期间还尝试了别的,但改动不大 基本记不清楚了,最后还是在派生自ValidationRule的类中添加需要验证的实体属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    public class ValidateExistCurMonthOrLogName : ValidationRule
    {
        public object Entity { getset; }
        public int ValiPropertyType { getset; }//1验证LogName,2验证CurMonth
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
          if(this.ValiPropertyType==1)
              {
                    int tmp;
                    if (string.IsNullOrEmpty(value as string) || string.IsNullOrWhiteSpace(value as string))
                    {
                        return new ValidationResult(false"不能为空!");
                    }
                    else if (!int.TryParse(value as stringout tmp))
                    {
                        return new ValidationResult(false"请输入正确的数字!");
                    }
                    else if(...验证是否已存在)
             .........
          }
                    .........
     
        
    }
     
     
    1、DataGrid列表
    //在单元格开始编辑的时候,把要验证的实体赋值
    dataGrid.PreparingCellForEdit += delegate(object sender, DataGridPreparingCellForEditEventArgs e)
    {
                  //记录原始状态
                    AllowanceData model = e.Row.Item as AllowanceData;
                    allowanceDataHelper.SourceToModel(model, originalModel);
                    //获取cell
                    DataGridCell cell = OperateControlHelper.GetCell(dataGrid, e.Row.GetIndex(), e.Column.DisplayIndex);<br>                //判断当前编辑的是TextBox还是DatePicker
                    DatePicker dp = OperateControlHelper.GetVisualChild<DatePicker>(cell);
                    TextBox txb = OperateControlHelper.GetVisualChild<TextBox>(cell);
                    FrameworkElement node;
                    DependencyProperty depenPro;
                    if (dp != null)
                    {
                        node = dp;
                        depenPro = DatePicker.TextProperty;
                    }
                    else if (txb != null)
                    {
                        node = txb;
                        depenPro = TextBox.TextProperty;
                    }
                    else
                    {
                        throw new Exception("...");
                    }
                    InitValidateExistCurMonthOrLogName(node, new ValidateExistCurMonthOrLogName() { Entity = originalModel });
    }
     
    2、添加或修改页面直接调用
    InitValidateExistCurMonthOrLogName(txbLogName, new ValidateExistCurMonthOrLogName() { Entity = source });
    InitValidateExistCurMonthOrLogName(dpCurMonth, new ValidateExistCurMonthOrLogName() { Entity = source });
     
    //调用
    void InitValidateExistCurMonthOrLogName(FrameworkElement node, ValidateExistCurMonthOrLogName modelArgs)
            {
                //获取类型
                DependencyProperty depenPro;
                if (node is DatePicker)
                {
                    depenPro = DatePicker.TextProperty;
                }
                else
                {
                    depenPro = TextBox.TextProperty;
                }
                //获取自定义验证
                ValidateExistCurMonthOrLogName validateLogNameOrCurMonth = node.GetBindingExpression(depenPro).ParentBinding.ValidationRules.Select(v =>
                {
                    if (v is ValidateExistCurMonthOrLogName)
                        return v;
                    return null;
                }).FirstOrDefault() as ValidateExistCurMonthOrLogName;
                if (validateLogNameOrCurMonth != null)
                {
                    validateLogNameOrCurMonth.Entity = modelArgs.Entity;
                }
            }

     

    二、styel

    1、列表的样式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    <Style TargetType="DataGrid">
                <Setter Property="AutoGenerateColumns" Value="False"/>
                <!--<Setter Property="IsReadOnly" Value="True"/>-->
                <Setter Property="VerticalAlignment" Value="Top"/>
                <Setter Property="CanUserSortColumns" Value="False"/>
                <Setter Property="CanUserResizeColumns" Value="False"/>
                <Setter Property="CanUserResizeRows" Value="False"/>
                <Setter Property="SelectionMode" Value="Extended"/>
                <Setter Property="SelectionUnit" Value="FullRow"/>
                <Setter Property="CanUserReorderColumns" Value="False"/>
                <Setter Property="AlternationCount" Value="2"/>
                <Setter Property="RowHeaderWidth" Value="0"/>
                <Setter Property="CanUserAddRows" Value="False"/>
     
                <Setter Property="CanUserResizeColumns" Value="false"/>
                <Setter Property="Background" Value="#b7e9fe" />
                <Setter Property="BorderBrush" Value="gray" />
                <Setter Property="HorizontalGridLinesBrush">
                    <Setter.Value>
                        <SolidColorBrush Color="#85cfee"/>
                    </Setter.Value>
                </Setter>
                <Setter Property="VerticalGridLinesBrush">
                    <Setter.Value>
                        <SolidColorBrush Color="#85cfee"/>
                    </Setter.Value>
                </Setter>
            </Style>
     
            <Style TargetType="DataGridColumnHeader">
                <Setter Property="SnapsToDevicePixels" Value="True" />
                <Setter Property="MinWidth" Value="0" />
                <Setter Property="MinHeight" Value="28" />
                <Setter Property="Foreground" Value="#07638a" />
                <Setter Property="FontWeight" Value="Bold"/>
                <Setter Property="FontSize" Value="12" />
                <Setter Property="Cursor" Value="Hand" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="DataGridColumnHeader">
                            <Border x:Name="BackgroundBorder" BorderThickness="0,1,0,1"
                                 BorderBrush="#85cfee"
                                  Width="Auto">
                                <Grid >
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <ContentPresenter  Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                                    <Path x:Name="SortArrow" Visibility="Collapsed" Data="M0,0 L1,0 0.5,1 z" Stretch="Fill"  Grid.Column="2" Width="8" Height="6" Fill="White" Margin="0,0,50,0"
                                VerticalAlignment="Center" RenderTransformOrigin="1,1" />
                                    <Rectangle Width="1" Fill="#85cfee" HorizontalAlignment="Right" Grid.ColumnSpan="1" />
                                </Grid>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="Height" Value="25"/>
            </Style>
     
            <Style  TargetType="DataGridRow">
                <Setter Property="Background" Value="#FFFFFF" />
                <Setter Property="Height" Value="25"/>
                <Setter Property="Foreground" Value="#07638a" />
                <Style.Triggers>
                    <Trigger Property="AlternationIndex" Value="0" >
                        <Setter Property="Background" Value="#FFFFFF" />
                    </Trigger>
                    <Trigger Property="AlternationIndex" Value="1" >
                        <Setter Property="Background" Value="#e1f5fd" />
                    </Trigger>
     
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="LightGray"/>
                    </Trigger>
     
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Foreground" Value="Black"/>
                    </Trigger>
                </Style.Triggers>
            </Style>

    2、DataGrid的ErrorTemplate

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <Style x:Key="textBoxErrorTemplateInDataGrid" TargetType="{x:Type TextBox}">
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="BorderBrush" Value="#6bc4e9"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="MinWidth" Value="80"/>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="Validation.ErrorTemplate">
                        <Setter.Value>
                            <ControlTemplate>
                                <DockPanel LastChildFill="True">
                                    <Ellipse DockPanel.Dock="Right"
    Width="15" Height="15" Margin="-25,0,0,0" StrokeThickness="1" Fill="Red" >
                                        <Ellipse.Stroke>
                                            <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
                                                <GradientStop Color="#FFFA0404" Offset="0"/>
                                                <GradientStop Color="#FFC9C7C7" Offset="1"/>
                                            </LinearGradientBrush>
                                        </Ellipse.Stroke>
                                    </Ellipse>
                                    <TextBlock DockPanel.Dock="Right" ToolTip="{Binding ElementName=errorHint,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
    Foreground="White" FontSize="11pt" Margin="-15,5,0,0" FontWeight="Bold">!
    <TextBlock.Triggers>
    </TextBlock.Triggers>
                                    </TextBlock>
                                    <Border BorderBrush="Red" BorderThickness="1">
                                        <AdornedElementPlaceholder Name="errorHint" />
                                    </Border>
                                </DockPanel>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>

    3、添加修改的ErrorTemplate

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <Style x:Key="datePickerErrorTemplate" TargetType="{x:Type DatePicker}">
           <Setter Property="VerticalAlignment" Value="Center"/>
           <Setter Property="HorizontalAlignment" Value="Left"/>
           <Setter Property="BorderBrush" Value="#6bc4e9"/>
           <Setter Property="Foreground" Value="#07638a"/>
           <Setter Property="Margin" Value="5, 10, 0, 0"/>
           <Setter Property="Width" Value="120"/>
           <Setter Property="Height" Value="23"/>
           <Setter Property="BorderThickness" Value="1"/>
           <Style.Triggers>
               <Trigger Property="Validation.HasError" Value="true">
                   <Setter Property="Validation.ErrorTemplate">
                       <Setter.Value>
                           <ControlTemplate>
                               <DockPanel LastChildFill="True">
                                   <TextBlock DockPanel.Dock="Right" Margin="5,0,0,0" VerticalAlignment="Center" Foreground="Red" FontSize="12" 
                                   Text="{Binding ElementName=errorHint, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                                   </TextBlock>
                                   <Border BorderBrush="Red" BorderThickness="1">
                                       <AdornedElementPlaceholder Name="errorHint" />
                                   </Border>
                               </DockPanel>
                           </ControlTemplate>
                       </Setter.Value>
                   </Setter>
               </Trigger>
           </Style.Triggers>
       </Style>

      

    三、wpf验证中常用的方法

    1、获取自定义验证类

    1
    public YourValidationRule GetValidationRule(FrameworkElement node,DependencyProperty depenPro)<br>{
    1
    2
    3
    4
    5
    <em id="__mceDel">YourValidationRule vr = node.GetBindingExpression(depenPro).ParentBinding.ValidationRules.Select(v =>
                {
                    if (v is YourValidationRule )
             return v; return null;
               }).FirstOrDefault() as YourValidationRule ;<br>return vr;<br>}</em>

    2、递归判断是否有未通过验证的控件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public static bool IsHasError(DependencyObject node, out string errorMsg)
    {
        errorMsg = string.Empty;
        if (node != null)
        {
            bool isValid = !Validation.GetHasError(node);
            if (!isValid)
            {
                if (node is IInputElement)
                    if (((IInputElement)node).IsEnabled == true)
                    {
                        ValidationError ve = Validation.GetErrors(node).FirstOrDefault();
                        if (ve != null)
                        {
                            errorMsg = ve.ErrorContent.ToString();
                        }
                        Keyboard.Focus((IInputElement)node);
                        return false;
                    }
            }
        }
        foreach (object subnode in LogicalTreeHelper.GetChildren(node))
        {
            if (subnode is DependencyObject)
            {
                if (IsHasError((DependencyObject)subnode, out errorMsg) == falsereturn false;
            }
        }
        return true;
    }

    3、向控件中添加错误验证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void AddValidationError<T>(FrameworkElement fe, DependencyProperty dp, string errorMsg) where T : ValidationRule, new()
            {
                ValidationError validationError =
    new ValidationError(new NotConvertInt(),
    fe.GetBindingExpression(dp));
     
                validationError.ErrorContent = "该用户在本月已存在数据!";
     
                Validation.MarkInvalid(
                    fe.GetBindingExpression(dp),
                    validationError);
            }

    4、清空控件中的错误验证

    1
    2
    3
    4
    public static void ClearValidationError(FrameworkElement fe, DependencyProperty dp)
            {
                Validation.ClearInvalid(fe.GetBindingExpression(dp));
            }

    5、从DataGrid获得Cell

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public static DataGridCell GetCell(DataGrid dataGrid, int row, int column)
            {
                DataGridRow rowContainer = GetRow(dataGrid, row);
                if (rowContainer != null)
                {
                    DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
                    if (presenter == null)
                    {
                        dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
                        presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
                    }
                    DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
                    return cell;
                }
                return null;
            }

    6、从DataGrid获得Row

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public static DataGridRow GetRow(DataGrid dataGrid, int index)
           {
               DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
               if (row == null)
               {
                   dataGrid.UpdateLayout();
                   dataGrid.ScrollIntoView(dataGrid.Items[index]);
                   row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
               }
               return row;
           }

    7、获取指定索引项的元素

    1
    2
    3
    4
    5
    6
    7
    public static TContainer GetContainerFromIndex<TContainer>
      (ItemsControl itemsControl, int index)
        where TContainer : DependencyObject
            {
                return (TContainer)
                  itemsControl.ItemContainerGenerator.ContainerFromIndex(index);
            }

    8、从DataGrid中获取正在编辑的Row

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public static DataGridRow GetEditingRow(DataGrid dataGrid)
            {
                var sIndex = dataGrid.SelectedIndex;
                if (sIndex >= 0)
                {
                    var selected = GetContainerFromIndex<DataGridRow>(dataGrid, sIndex);
                    if (selected.IsEditing) return selected;
                }
     
                for (int i = 0; i < dataGrid.Items.Count; i++)
                {
                    if (i == sIndex) continue;
                    var item = GetContainerFromIndex<DataGridRow>(dataGrid, i);
                    if (item.IsEditing) return item;
                }
     
                return null;
            }

    转载于:https://www.cnblogs.com/sjqq/p/7978370.html

    展开全文
  • 深入剖析WPF数据验证

    2019-04-16 02:43:35
    1、WPF包含的验证方式 方式 父类 对应接口 处理方式 简便写法 默认值 ExceptionValidationRule ValidationRule 无 ...

    1、WPF包含的验证方式

    方式

    父类

    对应接口

    处理方式

    简便写法

    默认值

    ExceptionValidationRule 

    ValidationRule

    检查在更新数据源(Source)时抛出的异常

    ValidatesOnExceptions=True

    false

    DataErrorValidationRule

    ValidationRule

    IDataErrorInfo

    检查实现了IDataError接口的对象生成的错误

    ValidatesOnDataErrors=True

    false

    NotifyDataErrorValidationRule

    ValidationRule

    INotifyDataErrorInfo

    检查实现了INotifyDataErrorInfo接口的对象生成的错误

    ValidatesOnNotifyDataErrors=True

    true

    说明:

    1、如果一个Binding的ValidationRules设置了ExceptionValidationRule或在Binding中直接指定ValidateOnException=true,那么它捕获属性中抛出的异常,参照下面的详细示例

    2、对应的如果一个Binding的ValidationRules设置了DataErrorValidationRule或NotifyDataErrorValidationRule,或直接在Binding中指定ValidateOnDataError或ValidateOnNotifyDataErrors,那么它捕获实现对应接口的错误

    2、ValidationStep属性,默认是:RawProposedValue,对于DataErrorValidationRule来说,默认是UpdatedValue

    <ExceptionValidationRule ValidationStep="RawProposedValue" />
        //
        // Summary:
        //     确定何时执行ValidationRule
        public enum ValidationStep
        {
            //
            // Summary:
            //     在数据没有转换之前(应用Converter)之前执行ValidationRule
            RawProposedValue = 0,
            //
            // Summary:
            //     在数据转换发生之后,赋值给source之前执行ValidationRule
            ConvertedProposedValue = 1,
            //
            // Summary:
            //     在source的值更新之后执行ValidationRule
            UpdatedValue = 2,
            //
            // Summary:
            //     在数据提交之后执行ValidationRule
            CommittedValue = 3
        }

    3、在UI上显示错误信息

    • 指定Validation.ErrorTemplate(AdornedElementPlaceholder是输入控件,如TextBox的占位符)

            <ControlTemplate x:Key="validationTemplate">
                <DockPanel>
                    <TextBlock Foreground="Red"  FontSize="20">!</TextBlock>
                    <AdornedElementPlaceholder/>
                </DockPanel>
            </ControlTemplate>
    • 使用Style指定错误样式
            <Style x:Key="textStyleTextBox" TargetType="TextBox">
                <Setter Property="Foreground" Value="#333333" />
                <Setter Property="MaxLength" Value="40" />
                <Setter Property="Width" Value="392" />
                <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>

    4、Validation处理过程

    数据验证发生在TwoWay和OneWayToSource的Binding中

    验证流程(如果有一个错误发生,验证流程终止)

    1. 绑定引擎(Binding engine)检查ValidationStep设置为RawProposedValue的任意自定义ValidationRule

    2. 绑定引擎调用Converter(如果有Converter)

    3. 如果转换成功,绑定引擎检查ValidationStep设置为ConvertedProposedValue的任意自定义ValidationRule

    4. 绑定引擎设置source属性

    5. 绑定引擎调用Validate方法,检查ValidationStep设置为UpdatedValue的任意自定义ValidaRule。

      1. 如果Binding中包含DataErrorValidationRule,并且ValidationStep设置为Default(UpdatedValue),此时检查DataErrorValidationRule

      2. 此时也是ValidatesOnDataErrors设置为true的默认检查时机

    6. 绑定引擎调用Validate方法检查ValidationStep设置为CommittedValue的ValidationRule

     

    5、说明:

    • 如果一个ValidationRule在某一个时机/步骤没有检查通过,绑定引擎创建一个ValidationError对象,并添加到绑定元素的Errors集合,在此之前,绑定引擎会移除之前此步骤添加的ValidationError对象。例如:如果一个ValidationRule在UpdatedValue步骤没有检查通过,那么下次检查UpdatedValue时机的任意ValidationRule之前,会把之前添加的ValidationError对象移除

    • Errros不为空时,元素的附加属性HasError会设置为true,如果Binding的NotifyOnValidationError=true,那么绑定引擎会触发元素的附加事件Validation.Error

    • 如果一个验证通过的值从source传递到target(或从target传递到source)时,Errors集合会清空

    6、ValidationRule.ValidatesOnTargetUpdated属性(默认false)

    <local:ValueIsNotNullValidationRule  ValidatesOnTargetUpdated="True" />
            private RelayCommand loadCommand;
            /// <summary>
            /// Gets the LoadCommand.
            /// </summary>
            public RelayCommand LoadCommand
            {
                get
                {
                    return loadCommand
                        ?? (loadCommand = new RelayCommand(
                        () =>
                        {
                            UserName = null;
                        }));
                }
            }

    如果直接给source赋值时(更新到target),会触发检查ValidatioRule。

     

    7、当使用ExceptionValidationRule或ValidateOnExceptions=true时,你可以使用 UpdateSourceExceptionFilter处理验证发出的异常,如果没有 UpdateSourceExceptionFilter,Binding引擎会创建一个ValidationError。

    示例:

    BindingExpression myBindingExpression =  textBox.GetBindingExpression(TextBox.TextProperty);
    Binding myBinding =  myBindingExpression.ParentBinding;
    myBinding.UpdateSourceExceptionFilter = new  UpdateSourceExceptionFilterCallback(ReturnExceptionHandler);
    myBindingExpression.UpdateSource();
            object ReturnExceptionHandler(object bindingExpression,  Exception exception)
            {
                return "This is from the  UpdateSourceExceptionFilterCallBack.";
            }

    8、ExceptionValidationRule 详细示例:它的简便写法是直接在binding设置ValidatesOnExceptions为True,即:下面两种写法效果相同

            <TextBox
                Grid.Column="1"
                Width="200"
                HorizontalAlignment="Left">
                <TextBox.Text>
                    <Binding
                        Mode="TwoWay"
                        Path="UserName"
                        UpdateSourceTrigger="PropertyChanged">
                        <Binding.ValidationRules>
                            <ExceptionValidationRule />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>
            </TextBox>
             <TextBox
                Grid.Row="1"
                Grid.Column="1"
                Width="200"
                HorizontalAlignment="Left"
                Text="{Binding Email, Mode=TwoWay,  ValidatesOnExceptions=True,  UpdateSourceTrigger=PropertyChanged}" />

    详细示例:

            <Style x:Key="validationTextBoxStyle"  TargetType="TextBox">
                <Setter Property="VerticalAlignment" Value="Center"  />
                <Style.Triggers>
                    <Trigger Property="Validation.HasError"  Value="true">
                        <Setter Property="ToolTip" Value="{Binding  (Validation.Errors)[0].ErrorContent,  RelativeSource={RelativeSource Mode=Self}}" />
                    </Trigger>
                </Style.Triggers>
            </Style>
            <TextBox
                x:Name="tbUserName"
                Grid.Column="1"
                Width="200"
                HorizontalAlignment="Left"
                Style="{DynamicResource validationTextBoxStyle}">
                <TextBox.Text>
                    <Binding
                        Mode="TwoWay"
                        Path="UserName"
                        UpdateSourceTrigger="PropertyChanged">
                        <Binding.ValidationRules>
                            <ExceptionValidationRule  ValidationStep="RawProposedValue" />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>
            </TextBox>
            private string userName;
            public string UserName
            {
                get { return userName; }
                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        throw new Exception("用户名不能为空");
                    }
                    if (value.Length < 5)
                    {
                        throw new Exception("用户名长度不能小于5个字符");
                    }
                    if (value != userName)
                    {
                        userName = value;
                        RaisePropertyChanged(() => this.UserName);
                    }
                }
            }

    9、IDataErrorInfo示例

        public class BaseViewModel : ViewModelBase, IDataErrorInfo
        {
            protected Dictionary<string, string> errorList = new  Dictionary<string, string>();
            public string this[string columnName] =>  errorList.ContainsKey(columnName) ? errorList[columnName] :  string.Empty;
            public string Error => errorList.Count > 0 ?  errorList.FirstOrDefault().Value : string.Empty;
        }
            private string email;
            public string Email
            {
                get { return email; }
                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        errorList.Add("Email", "Email不能为空");
                        return;
                    }
                    if (value != email)
                    {
                        email = value;
                        RaisePropertyChanged(() => this.Email);
                    }
                }
            }
            <TextBox
                Grid.Row="1"
                Grid.Column="1"
                Width="200"
                HorizontalAlignment="Left"
                Style="{DynamicResource validationTextBoxStyle} "
                Text="{Binding Email, Mode=TwoWay,  ValidatesOnDataErrors=True,  UpdateSourceTrigger=PropertyChanged}" />

    10、INotifyDataErrorInfo示例

        public class BaseViewModel1 : ViewModelBase,  INotifyDataErrorInfo
        {
            protected Dictionary<string, string> errorList = new  Dictionary<string, string>();
            public bool HasErrors => errorList.Count > 0;
            public event EventHandler<DataErrorsChangedEventArgs>  ErrorsChanged;
            public IEnumerable GetErrors(string propertyName)
            {
                if (errorList.ContainsKey(propertyName))
                {
                    yield return errorList[propertyName];
                }
            }
        }

     

            private string email;
            public string Email
            {
                get { return email; }
                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        errorList.Add("Email", "Email不能为空");
                        return;
                    }
                    if (value != email)
                    {
                        email = value;
                        RaisePropertyChanged(() => this.Email);
                    }
                }
            }
            <TextBox
                Grid.Row="1"
                Grid.Column="1"
                Width="200"
                HorizontalAlignment="Left"
                Style="{DynamicResource validationTextBoxStyle}"
                Text="{Binding Email, Mode=TwoWay,  ValidatesOnNotifyDataErrors=True,  UpdateSourceTrigger=PropertyChanged}" />

    11、INotifyDataErrorInfo高级示例(使用特性——Attribute实现验证)

            <TextBox
                Grid.Row="1"
                Grid.Column="1"
                Width="200"
                HorizontalAlignment="Left"
                Style="{DynamicResource validationTextBoxStyle}"
                Text="{Binding Email, Mode=TwoWay,  ValidatesOnNotifyDataErrors=True,  UpdateSourceTrigger=PropertyChanged}" />

    其实ValidatesOnNotifyDataErrors默认是true,不用设置也可以,下面的RequiredLooseAttribute继承自:ValidationAttribute,在

    System.ComponentModel.DataAnnotations命名空间下,需要引用该dll。
    
        /// <summary>
        /// 默认允许为null和string.empty,不允许为空白字符
        /// </summary>
        [AttributeUsage(AttributeTargets.Property, AllowMultiple =  false)]
        public class EmailAddressEmptyAttribute :  RequiredLooseAttribute
        {
            protected override bool IsValidString(string  inputString)
            {
                string regPattern =  @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
                Regex reg = new Regex(regPattern);
                return reg.IsMatch(inputString);
            }
        }
    
    
        public class ExceptionValidationViewModel :  BaseViewModelWithValidation
        {
            private string email = "11";
            [EmailAddressEmpty(AllowNullOrEmptyString = false,  AllowWhiteSpace = false, ErrorMessage = "请输入合法的Email")]
            public string Email
            {
                get { return email; }
                set
                {
                    if (value != email)
                    {
                        email = value;
                        RaisePropertyChanged(() => this.Email);
                    }
                }
            }
        }
        public abstract class BaseViewModelWithValidation :  BaseViewModel, INotifyDataErrorInfo
        {
            private Dictionary<string, List<string>> allErrorList =  new Dictionary<string, List<string>>();
            public bool HasErrors => allErrorList != null &&  allErrorList.Count > 0;
            public event EventHandler<DataErrorsChangedEventArgs>  ErrorsChanged;
            protected void NotifyErrorsChanged(string propName)
            {
                ErrorsChanged?.Invoke(this, new  DataErrorsChangedEventArgs(propName));
            }
    
            public IEnumerable GetErrors(string propertyName)
            {
                if (allErrorList != null &&  allErrorList.ContainsKey(propertyName))
                {
                    List<string> errors = allErrorList[propertyName];
                    foreach (var item in errors)
                    {
                        yield return item;
                    }
                }
                yield break;
            }
    
            public override void  RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
            {
                base.RaisePropertyChanged(propertyExpression);
                ValidateProperty(propertyExpression);
            }
    
            protected bool ValidateProperty<T>(Expression<Func<T>>  propExpression)
            {
                string propName = GetPropertyName(propExpression);
                object propValue =  GetType().GetProperty(propName).GetValue(this);
                bool isValid = ValidateProperty(propName, propValue);
                return isValid;
            }
    
            protected bool ValidateProperty(string propName, object  value)
            {
                var validationResults = new List<ValidationResult>();
                ValidationContext context = new  ValidationContext(this) { MemberName = propName };
                bool isValid = Validator.TryValidateProperty(value,  context, validationResults);
                if (isValid)
                {
                    RemoveErrorsForProperty(propName);//移除所有的Error
                }
                else
                {
                    AddErrorsForProperty(propName,  validationResults);//添加Error
                }
                return isValid;
            }
        }

    12、总结

    通过三种验证方式的比较,实际项目中用的最多的就是后两种,IDataErrorInfo和INotifyDataErrorInfo,即实现好通用的ViewModel基类之后,使用特性Attribute来给Property校验。

    值得一提的是,IDataErrorInfo也可以通过自定义Attribute来实现验证,通过实现IDataErrorInfo的BaseViewModel中通过反射获取属性的Attribute实现

    var attr = Attribute.GetCustomAttributes(propInfo,  typeof(ValidationBaseAttribute))
    string error = attr.Validate(value);
        [AttributeUsage(AttributeTargets.Property, AllowMultiple  =true)]
        public class ValidationBaseAttribute : Attribute
        {
            public ValidationBaseAttribute()
            {
                
            }
            public string ErrorMessage{ get; set; }
            
            public virtual string Validate(object value)
            {
                return string.Empty;
            }
        }

    因为Exception方式要对每个属性都写单独的验证代码(set方法中或者写ValidationRule),而且抛出异常本身就会影响性能,加剧WPF程序的性能问题(本身就够臃肿了……)

    展开全文
  • 文章目录一、前言二、方法及步骤2.1 自定义验证规则2.2 数据...后端数据与前端数据绑定后可以实现数据的双向传递,同时在传递过程中,也能对数据进行一些操作,例如数据验证和数据转换,本篇文章重点介绍数据验证。 .
  • WPF 数据验证实现步骤

    千次阅读 2010-11-10 16:05:00
      --- 数据验证   1、使用ValidationRule验证 需实现其子类,自定义验证规则,即重写Validate(object value, System.Globalization.CultureInfo cultureInfo)方法   2、需验证的...
  • 最近发现在TabControl 中使用数据验证有个特别闹心的情况,就是在切换TabItem后验证效果居然没有了!!! 关键是红色的框框也没有了!!! TabItem01 块代码是优化后的,使用AdornerDecorator 作为TabItem顶层装饰...
  • 下面代码演示了一个专门查找 TextBox 对象中非法数据的示例:   [csharp]   view plain   copy   private  void GetErrors(StringBuilder sb, DependencyObject obj){    foreach ...
  • ExceptionValidationRule 是预先构建的验证规则,它向WPF报告所以的异常。要使用 ExceptionValidationRule 验证规则,必须将它添加到 Binding.ValidationRules 集合中: Xaml: xmlns="http://sche
  • wpf数据验证(转)

    千次阅读 2010-08-16 22:15:00
    :Beth Massi [原文链接]:Displaying Data Validation Messages in WPF   以下是我特别关注的内容:   设置自定义有效性验证Style  我们显然希望能让用户知道这里的问题怎么解决....
  • 应用自定义验证规则的方法和应用自定义转换器的方法类似。该方法定义了一个 ValidationRule 的类,并且为了执行验证重写 Validate 方法。 public class PositivePriceRule : ValidationRule{ public override ...
  • <StyleTargetType="{x:TypeTextBox}"><SetterProperty="Validation.ErrorTemplate"><Setter.Value><ControlTemplate>...
  • 为了告知WPF使用 IDataErrorInfo 接口,并且当修改了一个属性后使用该接口检查错误,必须为 Binding.ValidationRules 集合添加 DataErrorValidationRule 验证规则: xmlns=...
  • 前面介绍过居于 IDataErrorInfo 接口的数据验证。利用 IDataErrorInfo 的好处是它可用于轻松地处理交叉耦合属性。但也具有一个很大的弊端:索引器的实现通常会导致较大的 switch-case 语句(对象中的每个属性名称都...
  • Text="{Binding ElementName=adorned,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
  • WPF数据验证(5)—— 错误模板

    千次阅读 2013-02-07 20:02:40
    Text="{Binding ElementName=adorned,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
  • WPF数据验证

    2019-04-18 15:46:00
    WPF数据验证 原文:【WPF数据验证引言 数据验证在任何用户界面程序中都是不可缺少的一部分.在WPF中,数据验证更是和绑定紧紧联系在一起,下面简单介绍MVVM模式下常用的几种验证方式. 错误信息...
  • wpf数据验证实例及常用方法小结

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 373
精华内容 149
关键字:

wpf数据验证