【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、在前端页面中添加验证
在前端页面中需要进行以下操作:
-
添加自定义的 ValidationRule 所有的命名空间;
xmlns:valRules="clr-namespace:MyValidationRuleDemo.MyValidationRules"
-
在需要验证的控件上的 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 中默认的效果。效果如下图:
3、使用 Behavior 自定义响应效果
上面虽然已经在页面上有了基本的错误响应效果,但是效果过于单一。这里我们在这里使用 Behavior 监听 Validation.Error 事件,并将错误信息传递到 ViewModel 中进行统一进行错误提醒。同时,在 MVMM 架构中将错误信息传递到 ViewModel 中进行统一处理,在有需要的时候也有利于业务逻辑处理。
3.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>
-
通过自定义的 ValidationExceptionBehavior 继承于 Behavior,用于监听 Validation.Error 的错误事件。
protected override void OnAttached() { //添加 Validation.Error 事件监听 this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(OnValidationError)); }
-
View 中添加 Behavior;
<i:Interaction.Behaviors> <local:ValidationExceptionBehavior /> </i:Interaction.Behaviors>
-
在 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()); } }
-
在 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()); }); }
-
开启 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(); }
}
}
}