绑定数据验证的方式:
验证类型 | 说明 |
Exception 验证 | 通过在某个 Binding 对象上设置 ValidatesOnExceptions 属性,如果源对象属性设置已修改的值的过程中引发异常,则抛出错误并为该 Binding 设置验证错误。 |
ValidationRule 验证 | Binding 类具有一个用于提供 ValidationRule 派生类实例的集合的属性。这些 ValidationRules 需要覆盖某个 Validate 方法,该方法由 Binding 在每次绑定控件中的数据发生更改时进行调用。 如果 Validate 方法返回无效的 ValidationResult 对象,则将为该 Binding 设置验证错误。 |
IDataErrorInfo 验证 | 通过在绑定数据源对象上实现 IDataErrorInfo 接口并在 Binding 对象上设置 ValidatesOnDataErrors 属性,Binding 将调用从绑定数据源对象公开的 IDataErrorInfo API。 如果从这些属性调用返回非 null 或非空字符串,则将为该 Binding 设置验证错误。 |
数据绑定呈现的流程:
1、 | 用户通过键盘、鼠标、手写板或者其他输入设备来输入或修改数据,从而改变绑定的目标信息 |
2、 | 设置源属性值。 |
3、 | 触发 Binding.SourceUpdated 事件。 |
4、 | 如果数据源属性上的 setter 引发异常,则异常会由 Binding 捕获,并可用于指示验证错误。 |
5、 | 如果实现了 IDataErrorInfo 接口,则会对数据源对象调用该接口的方法获得该属性的错误信息。 |
6、 | 向用户呈现验证错误指示,并触发 Validation.Error 附加事件。 |
数据验证:
1、Exception 验证
错误模板
12 <Setter Property="Validation.ErrorTemplate">
13 <Setter.Value>
14 <ControlTemplate>
15 <StackPanel Orientation="Horizontal">
16 <Border BorderThickness="1" BorderBrush="#FFdc000c" VerticalAlignment="Top">
17 <Grid>
18 <AdornedElementPlaceholder x:Name="adorner" Margin="-1"/>
19 </Grid>
20 </Border>
21 <Border x:Name="errorBorder" Background="#FFdc000c" Margin="8,0,0,0"
22 Opacity="0" CornerRadius="0"
23 IsHitTestVisible="False"
24 MinHeight="24" >
25 <TextBlock Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
26 Foreground="White" Margin="8,2,8,3" TextWrapping="Wrap" VerticalAlignment="Center"/>
27 </Border>
28 </StackPanel>
29 <ControlTemplate.Triggers>
30 <DataTrigger Value="True">
31 <DataTrigger.Binding>
32 <Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" />
33 </DataTrigger.Binding>
34 <DataTrigger.EnterActions>
35 <BeginStoryboard x:Name="fadeInStoryboard">
36 <Storyboard>
37 <DoubleAnimation Duration="00:00:00.15"
38 Storyboard.TargetName="errorBorder"
39 Storyboard.TargetProperty="Opacity"
40 To="1"/>
41 </Storyboard>
42 </BeginStoryboard>
43 </DataTrigger.EnterActions>
44 <DataTrigger.ExitActions>
45 <StopStoryboard BeginStoryboardName="fadeInStoryboard"/>
46 <BeginStoryboard x:Name="fadeOutStoryBoard">
47 <Storyboard>
48 <DoubleAnimation Duration="00:00:00"
49 Storyboard.TargetName="errorBorder"
50 Storyboard.TargetProperty="Opacity"
51 To="0"/>
52 </Storyboard>
53 </BeginStoryboard>
54 </DataTrigger.ExitActions>
55 </DataTrigger>
56 </ControlTemplate.Triggers>
57 </ControlTemplate>
58 </Setter.Value>
59 </Setter>
绑定验证
6 <TextBox.Text>
7 <Binding Path="UserNameEx" UpdateSourceTrigger="PropertyChanged">
8 <Binding.ValidationRules>
9 <ExceptionValidationRule></ExceptionValidationRule>
10 </Binding.ValidationRules>
11 </Binding>
12 </TextBox.Text>
VM 层抛异常
25 if (string.IsNullOrEmpty(value)) 26 { 27 throw new ApplicationException("该字段不能为空!"); 28 }
效果
ValidationRule 验证:
通过继承 ValidationRule 抽象类,并重写他的 Validate 方法来扩展编写我们需要的验证类。该验证类可以直接使用在我们需要验证的属性。
View 层
6 <TextBox.Text> 7 <Binding Path="UserName" UpdateSourceTrigger="PropertyChanged"> 8 <Binding.ValidationRules> 9 <app:RequiredRule /> 10 </Binding.ValidationRules> 11 </Binding> 12 </TextBox.Text>
重写验证规则
1 public class RequiredRule : ValidationRule 2 { 3 public override ValidationResult Validate(object value, CultureInfo cultureInfo) 4 { 5 if (value == null) 6 return new ValidationResult(false, "该字段不能为空值!"); 7 if (string.IsNullOrEmpty(value.ToString())) 8 return new ValidationResult(false, "该字段不能为空字符串!"); 9 return new ValidationResult(true, null); 10 } 11 }
VM层不需要抛异常
3、IDataErrorInfo 验证:
3.1、在 Binding 对象上设置 ValidatesOnDataErrors 属性
3.2、在绑定数据源对象上实现 IDataErrorInfo 接口
3.3、Binding 将调用从绑定数据源对象公开的 IDataErrorInfo API。如果从这些属性调用返回非 null 或非空字符串,则将为该 Binding 设置验证错误。
View 层:打开 ValidatesOnDataErrors
<GroupBox Header="IDataErrorInfo 验证" Margin="10 20 10 10" DataContext="{Binding Source={StaticResource Locator},Path=BindingForm}" >
<StackPanel x:Name="Form" Orientation="Vertical" Margin="0,20,0,0">
<StackPanel>
<Label Content="用户名" Target="{Binding ElementName=UserName}"/>
<TextBox Width="150"
Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" >
</TextBox>
</StackPanel>
<StackPanel>
<Label Content="性别" Target="{Binding ElementName=RadioGendeMale}"/>
<RadioButton Content="男" />
<RadioButton Content="女" Margin="8,0,0,0" />
</StackPanel>
<StackPanel>
<Label Content="生日" Target="{Binding ElementName=DateBirth}" />
<DatePicker x:Name="DateBirth" />
</StackPanel>
<StackPanel>
<Label Content="用户邮箱" Target="{Binding ElementName=UserEmail}"/>
<TextBox Width="150" Text="{Binding UserEmail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
</StackPanel>
<StackPanel>
<Label Content="用户电话" Target="{Binding ElementName=UserPhone}"/>
<TextBox Width="150" Text="{Binding UserPhone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
</StackPanel>
</StackPanel>
</GroupBox>
VM 层:继承 IDataErrorInfo 接口
public interface IDataErrorInfo
{
string this[string columnName] { get; }
string Error { get; }
}
public class BindingFormViewModel :ViewModelBase, IDataErrorInfo
{
public BindingFormViewModel()
{
}
#region 属性
private String userName;
/// <summary>
/// 用户名
/// </summary>
public String UserName
{
get { return userName; }
set { userName = value; }
}
private String userPhone;
/// <summary>
/// 用户电话
/// </summary>
public String UserPhone
{
get { return userPhone; }
set { userPhone = value; }
}
private String userEmail;
/// <summary>
/// 用户邮件
/// </summary>
public String UserEmail
{
get { return userEmail; }
set { userEmail = value; }
}
#endregion
public String Error
{
get { return null; }
}
public String this[string columnName]
{
get
{
Regex digitalReg = new Regex(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$");
Regex emailReg = new Regex("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$");
if (columnName == "UserName" && String.IsNullOrEmpty(this.UserName))
{
return "用户名不能为空";
}
if (columnName == "UserPhone" && !String.IsNullOrEmpty(this.UserPhone))
{
if (!digitalReg.IsMatch(this.UserPhone.ToString()))
{
return "用户电话必须为8-11位的数值!";
}
}
if (columnName == "UserEmail" && !String.IsNullOrEmpty(this.UserEmail))
{
if (!emailReg.IsMatch(this.UserEmail.ToString()))
{
return "用户邮箱地址不正确!";
}
}
return null;
}
}
}
可以考虑重写 ViewModelBase(封装 ValidateViewBase)
/// <summary>
/// 带有数据校验功能的通知基类
/// </summary>
public class ViewModelValidationBase : ViewModelBase, IDataErrorInfo
{
/// <summary>
/// 需要校验的属性集合
/// </summary>
private List<string> validationKeys = new List<string>();
/// <summary>
/// 错误信息字典
/// </summary>
private Dictionary<string, string> errorDic = new Dictionary<string, string>();
/// <summary>
/// 是否开始校验,以此控制IDataErrorInfo接口校验的时候界面刚初始化即开始校验
/// </summary>
public bool IsBeginValidation { get; set; }
public string this[string columnName]
{
get
{
if (!IsBeginValidation)
{
return string.Empty;
}
var vc = new ValidationContext(this, null, null);
vc.MemberName = columnName;
var res = new List<ValidationResult>();
var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res);
if (res.Count > 0)
{
string errorMessage = string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
AddErrorDic(columnName, errorMessage);
return errorMessage;
}
RemoveErrorDic(columnName);
return string.Empty;
}
}
public string Error => string.Join(Environment.NewLine, errorDic.Values.ToArray());
/// <summary>
/// 添加进行校验的属性集合
/// </summary>
/// <param name="keys"></param>
protected virtual void ValidationKey(string[] keys)
{
if (keys == null)
{
return;
}
foreach (var item in keys)
{
validationKeys.Add(item);
}
}
/// <summary>
/// 强制进行以此校验
/// </summary>
protected void Validation()
{
if (validationKeys != null)
{
foreach (var item in validationKeys)
{
RaisePropertyChanged(item);
}
}
}
private void AddErrorDic(string key, string value)
{
if (!errorDic.Keys.Contains(key))
{
errorDic.Add(key, value);
}
}
private void RemoveErrorDic(string key)
{
errorDic.Remove(key);
}
}
使用的时候继承该类,然后使用属性 IsBeginValidation 控制校验的时机,方法 ValidationKey (string [] keys) 添加需要校验的属性,方法 Validation () 可以触发校验,属性 Error 显示校验信息,为空则校验正确,不为空校验错误。
校验规则是使用特性的方式,引用名称空间
using System.ComponentModel.DataAnnotations;
/// <summary>
/// 获取或设置
/// </summary>
private string name;
/// <summary>
/// 获取或设置
/// </summary>
[Required(ErrorMessage = "用户名称不能为空")]
public string Name
{
get { return name; }
set { Set<string>(ref name, value, "Name"); }
}
不想写了,好像这里又用到了 MVC 的一些知识,后面再更新吧。