【WPF学习笔记】WPF中使用ValidationRule自定义验证规则

  • A+
所属分类:.NET技术
摘要

本文主要是展示在 WPF 中使用 ValidationRule 自定义验证规则,同时展示两种错误响应方式。一种是通过 Behavior 传递到 ViewModel 中,然后进行错误信息响应;一种是直接在 View 中遍历当前也的所有错误元素,在页面中通过事件统一响应。


WPF中使用ValidationRule自定义验证规则

本文主要是展示在 WPF 中使用 ValidationRule 自定义验证规则,同时展示两种错误响应方式。一种是通过 Behavior 传递到 ViewModel 中,然后进行错误信息响应;一种是直接在 View 中遍历当前也的所有错误元素,在页面中通过事件统一响应。

1、自定义验证规则类

这里自定义两个验证规则类,分别用于验证 “用户名”输入不可为空、“邮箱”输入值需满足格式要求。

两个类需要继承 ValidationRule 类。ValidationRule 是抽象类,需要具体实现 Validate 方法。代码如下:

NotEmptyValidationRule.cs

using System.Globalization; using System.Windows.Controls;  namespace MyValidationRuleDemo.MyValidationRules {     public class NotEmptyValidationRule : ValidationRule     {         public override ValidationResult Validate(object value, CultureInfo cultureInfo)         {             return string.IsNullOrWhiteSpace((value ?? "").ToString()) ?                 new ValidationResult(false, "不能为空") : new ValidationResult(true, null);         }     } } 

EmailValidationRule.cs

using System.Globalization; using System.Text.RegularExpressions; using System.Windows.Controls;  namespace MyValidationRuleDemo.MyValidationRules {     public class EmailValidationRule : ValidationRule     {         public override ValidationResult Validate(object value, CultureInfo cultureInfo)         {             Regex emailRegex = new Regex("^\s*([A-Za-z0-9_-]+(\.\w+)*@(\w+\.)+\w{2,5})\s*$");             string str = (value ?? "").ToString();             if (!string.IsNullOrWhiteSpace(str))             {                 if (!emailRegex.IsMatch(str))                 {                     return new ValidationResult(false, "邮箱地址错误!");                 }             }             return new ValidationResult(true, null);         }     } } 

2、在前端页面中添加验证

在前端页面中需要进行以下操作:

  1. 添加自定义的 ValidationRule 所有的命名空间;

    xmlns:valRules="clr-namespace:MyValidationRuleDemo.MyValidationRules" 
  2. 在需要验证的控件上的 Binding 上对应的自定义验证规则类;

    <Binding.ValidationRules> 	<valRules:EmailValidationRule /> </Binding.ValidationRules> 

具体代码如下:

<Window     x:Class="MyValidationRuleDemo.MainWindow"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     xmlns:local="clr-namespace:MyValidationRuleDemo"     xmlns:valRules="clr-namespace:MyValidationRuleDemo.MyValidationRules"     Title="MainWindow"     Width="800"     Height="400"     mc:Ignorable="d">     <Grid>         <Grid.RowDefinitions>             <RowDefinition />             <RowDefinition />             <RowDefinition />         </Grid.RowDefinitions>         <StackPanel             HorizontalAlignment="Center"             VerticalAlignment="Center"             Orientation="Horizontal">             <Label                 Content="用户名:"                 Margin="0,0,10,0"                 FontSize="20" />             <TextBox Width="200" Height="30">                 <TextBox.Text>                     <Binding                         Path="UserName"                         UpdateSourceTrigger="PropertyChanged">                         <Binding.ValidationRules>                             <valRules:NotEmptyValidationRule />                         </Binding.ValidationRules>                     </Binding>                 </TextBox.Text>             </TextBox>         </StackPanel>         <StackPanel Grid.Row="1"             HorizontalAlignment="Center"             VerticalAlignment="Center"             Orientation="Horizontal">             <Label                 Content="邮箱:"                 Margin="0,0,10,0"                 FontSize="20" />             <TextBox Width="200" Height="30">                 <TextBox.Text>                     <Binding                         Path="UserEmail"                         UpdateSourceTrigger="PropertyChanged">                         <Binding.ValidationRules>                             <valRules:EmailValidationRule />                         </Binding.ValidationRules>                     </Binding>                 </TextBox.Text>             </TextBox>         </StackPanel>         <Button Grid.Row="2"             Content="提交"             Width="200"             Height="30"             Margin="0,20,0,0" />     </Grid> </Window> 

前端页面绑定的验证参数具体如下:

using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command;  namespace MyValidationRuleDemo.ViewModel {     public class MainViewModel : ViewModelBase     {         public MainViewModel()         {         }          private string userName;         /// <summary>         /// 用户名         /// </summary>         public string UserName         {             get { return userName; }             set { userName = value; RaisePropertyChanged(); }         }          private string userEmail;         /// <summary>         /// 用户邮件         /// </summary>         public string UserEmail         {             get { return userEmail; }             set { userEmail = value; RaisePropertyChanged(); }         }      } } 

此时,自定义的验证规则已经生效。当页面输入不符合规则时,会默认的红框进行标记。这是 WPF 中默认的效果。效果如下图:

【WPF学习笔记】WPF中使用ValidationRule自定义验证规则

3、使用 Behavior 自定义响应效果

上面虽然已经在页面上有了基本的错误响应效果,但是效果过于单一。这里我们在这里使用 Behavior 监听 Validation.Error 事件,并将错误信息传递到 ViewModel 中进行统一进行错误提醒。同时,在 MVMM 架构中将错误信息传递到 ViewModel 中进行统一处理,在有需要的时候也有利于业务逻辑处理。

3.1、实现步骤

进行以上操作需要进行以下步骤:

  1. 开启验证错误的通知属性 NotifyOnValidationError="True" 。这样就可以产生 Validation.Error 事件。

    <TextBox Width="200" Height="30">     <TextBox.Text>         <Binding                  Path="UserEmail"                  NotifyOnValidationError="True"                  UpdateSourceTrigger="PropertyChanged">             <Binding.ValidationRules>                 <valRules:EmailValidationRule />             </Binding.ValidationRules>         </Binding>     </TextBox.Text> </TextBox> 
  2. 通过自定义的 ValidationExceptionBehavior 继承于 Behavior,用于监听 Validation.Error 的错误事件。

    protected override void OnAttached() {     //添加 Validation.Error 事件监听     this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(OnValidationError)); } 
  3. View 中添加 Behavior;

    <i:Interaction.Behaviors>     <local:ValidationExceptionBehavior /> </i:Interaction.Behaviors> 
  4. 在 ValidationExceptionBehavior 中通过 AssociatedObject 的DataContext 获取到关联当前View的ViewModel。并将错误提示统一收集到 ViewModel 的 ErrorList 。

    private void OnValidationError(Object sender, ValidationErrorEventArgs e) {     MainViewModel mainModel = null;     if (AssociatedObject.DataContext is MainViewModel)     {         mainModel = this.AssociatedObject.DataContext as MainViewModel;     }     if (mainModel == null) return;      //OriginalSource 触发事件的元素     var element = e.OriginalSource as UIElement;     if (element == null) return;      //ValidationErrorEventAction.Added  表示新产生的行为     if (e.Action == ValidationErrorEventAction.Added)     {         mainModel.ErrorList.Add(e.Error.ErrorContent.ToString());     }     else if (e.Action == ValidationErrorEventAction.Removed) //ValidationErrorEventAction.Removed  该行为被移除,即代表验证通过     {         mainModel.ErrorList.Remove(e.Error.ErrorContent.ToString());     } } 
  5. 在 View 中按钮绑定的 Command 中统一处理 ErrorList;

    public RelayCommand SaveCommand { get; set; } public MainViewModel() {     SaveCommand = new RelayCommand(() =>                                    {                                        StringBuilder sb = new StringBuilder();                                        foreach (var error in ErrorList)                                        {                                            sb.Append(error + "rn");                                        }                                        MessageBox.Show(sb.ToString());                                    }); } 
  6. 开启 ValidationRule 的属性 ValidatesOnTargetUpdated="True",否则在加载页面后,文本框中未输入值则不会进行验证。

    <Binding.ValidationRules>     <valRules:EmailValidationRule ValidatesOnTargetUpdated="True" /> </Binding.ValidationRules> 

3.2、具体代码

完整代码如下:

ValidationExceptionBehavior.cs

using MyValidationRuleDemo.ViewModel; using System; using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity;  namespace MyValidationRuleDemo {     public class ValidationExceptionBehavior : Behavior<FrameworkElement>     {          protected override void OnAttached()         {             //添加 Validation.Error 事件监听             this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(OnValidationError));         }          private void OnValidationError(Object sender, ValidationErrorEventArgs e)         {             MainViewModel mainModel = null;             if (AssociatedObject.DataContext is MainViewModel)             {                 mainModel = this.AssociatedObject.DataContext as MainViewModel;             }             if (mainModel == null) return;              //OriginalSource 触发事件的元素             var element = e.OriginalSource as UIElement;             if (element == null) return;              //ValidationErrorEventAction.Added  表示新产生的行为             if (e.Action == ValidationErrorEventAction.Added)             {                 mainModel.ErrorList.Add(e.Error.ErrorContent.ToString());             }             else if (e.Action == ValidationErrorEventAction.Removed) //ValidationErrorEventAction.Removed  该行为被移除,即代表验证通过             {                 mainModel.ErrorList.Remove(e.Error.ErrorContent.ToString());             }         }          protected override void OnDetaching()         {             //移除 Validation.Error 事件监听             this.AssociatedObject.RemoveHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(OnValidationError));         }     } } 

MainView.xaml

<Window     x:Class="MyValidationRuleDemo.MainWindow"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"     xmlns:local="clr-namespace:MyValidationRuleDemo"     xmlns:valRules="clr-namespace:MyValidationRuleDemo.MyValidationRules"     xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"     Title="MainWindow"     Width="800"     Height="400"     mc:Ignorable="d">     <i:Interaction.Behaviors>         <local:ValidationExceptionBehavior />     </i:Interaction.Behaviors>     <Grid>         <Grid.RowDefinitions>             <RowDefinition />             <RowDefinition />             <RowDefinition />         </Grid.RowDefinitions>         <StackPanel             HorizontalAlignment="Center"             VerticalAlignment="Center"             Orientation="Horizontal">             <Label                 Content="用户名:"                 Margin="0,0,10,0"                 FontSize="20" />             <TextBox Width="200" Height="30">                 <TextBox.Text>                     <Binding                         Path="UserName"                         NotifyOnValidationError="True"                         UpdateSourceTrigger="PropertyChanged">                         <Binding.ValidationRules>                             <valRules:NotEmptyValidationRule ValidatesOnTargetUpdated="True" />                         </Binding.ValidationRules>                     </Binding>                 </TextBox.Text>             </TextBox>         </StackPanel>         <StackPanel Grid.Row="1"             HorizontalAlignment="Center"             VerticalAlignment="Center"             Orientation="Horizontal">             <Label                 Content="邮箱:"                 Margin="0,0,10,0"                 FontSize="20" />             <TextBox Width="200" Height="30">                 <TextBox.Text>                     <Binding                         Path="UserEmail"                         NotifyOnValidationError="True"                         UpdateSourceTrigger="PropertyChanged">                         <Binding.ValidationRules>                             <valRules:EmailValidationRule ValidatesOnTargetUpdated="True" />                         </Binding.ValidationRules>                     </Binding>                 </TextBox.Text>             </TextBox>         </StackPanel>         <Button Grid.Row="2"             Content="提交"             Command="{Binding SaveCommand}"             Width="200"             Height="30"             Margin="0,20,0,0" />     </Grid> </Window> 

MainViewModel.cs

using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using System.Collections.ObjectModel; using System.Text; using System.Windows;  namespace MyValidationRuleDemo.ViewModel {     public class MainViewModel : ViewModelBase     {         public MainViewModel()         {             SaveCommand = new RelayCommand(() =>             {                 StringBuilder sb = new StringBuilder();                 foreach (var error in ErrorList)                 {                     sb.Append(error + "rn");                 }                 MessageBox.Show(sb.ToString());             });         }          public RelayCommand SaveCommand { get; set; }          private string userName;         /// <summary>         /// 用户名         /// </summary>         public string UserName         {             get { return userName; }             set { userName = value; RaisePropertyChanged(); }         }          private string userEmail;         /// <summary>         /// 用户邮件         /// </summary>         public string UserEmail         {             get { return userEmail; }             set { userEmail = value; RaisePropertyChanged(); }         }          private ObservableCollection<string> errorList = new ObservableCollection<string>();         /// <summary>         /// 错误提示         /// </summary>         public ObservableCollection<string> ErrorList         {             get { return errorList; }             set { errorList = value; RaisePropertyChanged(); }         }      } } 

3.3、效果展示

【WPF学习笔记】WPF中使用ValidationRule自定义验证规则

4、直接在页面中进行错误响应

错误响应也可以直接在 View 中通过事件直接处理。但是这样处理就比较麻烦,在获取错误列表时,我们可能需要编写代码,从而在 View 中所有控件中查询有错误的控件。

4.1、单个错误响应

Error 事件是使用冒泡策略的路出事件,我们在 Binding 了 ValidationRule 的控件的父容器上关联处理程序,即可为多个控件处理 Error 事件。

在 View.xaml 中添加如下代码:

<Grid Validation.Error="Grid_Error">     <!--这里面是具体的页面内容--> </Grid> 

关联的事件处理程序如下:

private void Grid_Error(object sender, ValidationErrorEventArgs e) {     if (e.Action == ValidationErrorEventAction.Added)     {         MessageBox.Show(e.Error.ErrorContent.ToString());     } } 

4.2、获取错误列表

循环遍历整个 View 中的元素,并将存在的错误信息收集到一个字符串中,然后点击按钮时将该字符串中的信息展示出来。程序代码如下:

private void Button_Click(object sender, RoutedEventArgs e) {     string message;     if (HasErrors(out message))     {         MessageBox.Show(message);     } }  private bool HasErrors(out string message) {     StringBuilder sb = new StringBuilder();     GetError(sb, this.grid);     message = sb.ToString();     return !string.IsNullOrWhiteSpace(message); }  private void GetError(StringBuilder sb, DependencyObject obj) {     foreach (object child in LogicalTreeHelper.GetChildren(obj))     {         StackPanel element = child as StackPanel;         if (element == null) continue;          foreach (var ele in element.Children)         {             TextBox textBox = ele as TextBox;             if (textBox == null) continue;             if (Validation.GetHasError(textBox))             {                 foreach (ValidationError error in Validation.GetErrors(textBox))                 {                     sb.Append(error.ErrorContent.ToString() + "rn");                 }                 GetError(sb, element);             }          }     } }