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

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

本文主要是展示在 WPF 中使用 ValidationRule 自定义验证规则,然后通过 Behavior 传递到 ViewModel 中,在 ViewModel 中对错误信息统一响应。

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 中默认的效果。效果如下图:

ValidationRule01

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 + "\r\n");
                                           }
                                           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 + "\r\n");
                }
                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、效果展示

ValidationRule02

posted @ 2021-02-04 18:57  深秋无痕  阅读(1401)  评论(0编辑  收藏  举报