WPF 绑定数据的验证

绑定数据验证的方式:

验证类型说明
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 的一些知识,后面再更新吧。 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值