【WPF 数据验证机制】三、INotifyDataErrorInfo接口+DataAnnotation数据特性实现model属性验证机制

环境

vs2022+.net6.0+wpf+MVVM+EFcore6.0

MVVM验证示意图

INotifyDataErrorInfo接口功能

public interface INotifyDataErrorInfo
{
    bool HasErrors { get; }//提供给Validation 使用,对应Validation.HasError
    event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    IEnumerable GetErrors(string propertyName);//获取一组验证信息, 多个 ValidationAttribute验证后返回来的信息,提供给Validation 使用,对应Validation.ErrorsBing会在绑定的控件上生成 附加属性Validation.Errors,保存验证信息。
}

1、异步验证

2、跨属性验证

3、返回一组(IEnumerable)错误信息,可以分行显示或者连接成一组字符窜显示。以下案例分别展示这两种方式。

 

实现方式

1、反射 :实现Model实体映射和数据验证分离。

2、Validator验证器:Model实体映射和数据验证混合在一起

3、以上两者都可以实现异步验证。

Bing在UI绑定中作用

1、属性验证,并且记录结果。

2、Bing会在绑定的控件上生成 附加属性Validation.Errors,保存验证信息。

反射实现属性验证

目的:实现实体映射和数据验证分离。

具体步骤:

1、新建(或自动生成)Student类,这个类不做修改,保存原样。该类主要实现实体映射或者知识单纯的PoCo。

2、新建Student_Matedata的元数类,将Student_Matedata类附加Student类的元数据。Student_Matedata的元数类只有ValidationAttribute 特性。

3、新建StudentViewModel,该类实现INotifyDataErrorInfo接口,实现验证功能。

4、UI层通过Bing绑定控件,Bing会在绑定的控件上生成Validation.Errors附加属性,保存验证信息。

Model

Student类 、Student_Matedata类。

1、新建(或自动生成)Student类,这个类不做修改,保存原样。该类主要实现实体映射或者知识单纯的PoCo。
2、新建Student_Matedata的元数类,将Student_Matedata类附加Student类的元数据。Student_Matedata的元数类只有ValidationAttribute 特性。

Student类

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.ComponentModel;

namespace CTMvvmDemo.MVVM.Models
{
    [MetadataType(typeof(Student_MateData))]
    [Table("Students")]
   public sealed   partial class Student:ObservableValidator
    {
        [Key]//主键
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]//非自增长,自增长为Identity
        public int StudentID { get; set; }

      
        [Column("name")]
        [DataType(DataType.Text)]
       
        public string Name { get; set; }

        [Column("age")]
        public int Age { get; set; }

        [Column("tel")]
       [DataType(DataType.PhoneNumber)]
        public string Tel { get; set; }

        [Column("email")]
       [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
    }
}

 

Student_Matedata类

 

using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace CTMvvmDemo.MVVM.Models
{
    public sealed class Student_MateData : ObservableValidator
    {
        [Required(ErrorMessage = "名字不能为空")]
        [StringLength(20, ErrorMessage = "名字超过20个字符了")]
        [RegularExpression("^[\u4e00-\u9fa5|a-z]+$", ErrorMessage = "请输入汉族或者小写拼英")]
        public string Name { get; set; }

        
        [Range(0, 200, ErrorMessage = "年龄输入不正确")]
        public int Age { get; set; }

        
        [RegularExpression(@"^1[34578]\d{9}$", ErrorMessage = "电话号码不正确")]
        [Required(ErrorMessage = "不能为空")]
        [DataType(DataType.PhoneNumber)]
        public string Tel { get; set; }

        
        [RegularExpression("^[\\w-]+@[\\w-]+\\.(com|net|org|edu|mil|tv|biz|info)$", ErrorMessage = "邮箱不正确")]
        [Required(ErrorMessage = "不能为空")]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }


    }
}

 

ViewModel

新建StudentViewModel,该类实现INotifyDataErrorInfo接口,实现验证功能。

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CTMvvmDemo.MVVM.Models;
using CTMvvmDemo.Respositoies;

namespace CTMvvmDemo.MVVM.ViewsModels
{
    public  sealed partial class StudentViewsModel : ViewModelBase, INotifyDataErrorInfo
    {


        private Student student;
       
        public StudentViewsModel(Student _student) => this.student = _student;
        public string Name
        {
            get => student.Name;
            set
            {

                //先设置后验证,暂时保存本地,除非sava实体后才会更新数据库
                SetProperty(student.Name, value, student, (student, name) => student.Name = name);
                ValidateProperty2(student.Name,  nameof(Name));
                //ValidateProperty3(student,  nameof(Name));
            }
        }

        public int Age
        {
            get => student.Age;
            set
            {

               //先设置后验证,暂时保存本地,除非sava实体后才会更新数据库
                SetProperty(student.Age, value, student, (u, n) => u.Age = n);
               ValidateProperty2(student.Age, nameof(Age));
             //   ValidateProperty3(student,  nameof(Age));
            }
        }

        public int StudentID
        {
            get => student.StudentID;
            set
            {
                SetProperty(student.StudentID, value, student, (u, n) => u.StudentID = n);
                 ValidateProperty2(student.StudentID, nameof(StudentID));
               // ValidateProperty3(student,  nameof(StudentID));
            }
        }

        public string Tel
        {
            get => student.Tel;
            set
            {
                SetProperty(student.Tel, value, student, (u, n) => u.Tel = n);
          ValidateProperty2(student.Tel, nameof(Tel));
              //  ValidateProperty3(student, nameof(Tel));
            }
        }

        public string Email
        {
            get => student.Email;
            set
            {
                SetProperty(student.Email, value, student, (u, n) => u.Email = n);
                 ValidateProperty2(student.Email, nameof(Email));
              //  ValidateProperty3(student, nameof(Email));
            }
        }
        private readonly Dictionary<string, ICollection<string>> _validationErrors = new Dictionary<string, ICollection<string>>();

        public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;

        /// <summary>
        /// 判断是否验证
        /// </summary>
        public bool HasErrors => _validationErrors is not null && _validationErrors.Count > 0;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        private void RaiseErrorsChanged(string propertyName)
        {
            if (ErrorsChanged != null)
                ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }

        /// <summary>
        /// 向Bing提供错误信息。
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public IEnumerable GetErrors(string? propertyName)
        {
            return _validationErrors.ContainsKey(propertyName) ? _validationErrors[propertyName] : null;
        }


        /// <summary>
        /// 方法2
        /// 用Validator 实现验证具体步骤
        /// 1、获取model 实体中属性属性的数据特性。
        /// 2、创建包含model实例的和属性 ValidationContext
        /// 3、用Validator.TryValidateProperty方法验证,并且记录验证结果
        /// 4、将验证结果保存在字典中,该字典提供给GetErrors()使用,GetErrors()是向bing提供验证结果
        /// 验证属性  验证结果保存在_validationErrors中,GetErrors会提取相应错误信息
        /// 该方法要求 数据特性添加在model属性上,不能添加在model的数据中。
        /// </summary>
        /// <param name="val">属性值 例如:student.Name</param>
        /// <param name="propertyName">属性名</param>
        private bool ValidateProperty2(object val, [CallerMemberName] string propertyName = null)
        {

            if (_validationErrors.ContainsKey(propertyName)) _validationErrors.Remove(propertyName);

            ValidationContext context = new ValidationContext(student) { MemberName = propertyName };

            List<ValidationResult> results = new();
            bool isvild = Validator.TryValidateProperty(val, context, results);
            if (!isvild)
            {
                _validationErrors[propertyName] = results.Select(x => x.ErrorMessage).ToList();
            }

            ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
            return !isvild;
        }
 protected override void Dispose(bool IsDisposed)
        {
            student = null;
            _validationErrors.Clear();

        }


    }
}

 

 

View

UI层通过Bing绑定控件,Bing会在绑定的控件上生成Validation.Errors附加属性,保存验证信息。

<UserControl x:Class="CTMvvmDemo.MVVM.Views.StudentView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:CTMvvmDemo.MVVM.Views"
             
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ControlTemplate x:Key="displayErrorMsgFomatte">
            <WrapPanel>
                <!-- Placeholder for the TextBox itself -->
                <AdornedElementPlaceholder x:Name="textBox"/>
                <ItemsControl ItemsSource="{Binding}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding ErrorContent}" Foreground="Red"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </WrapPanel>
        </ControlTemplate>
    </UserControl.Resources>
        
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="*"/>
          
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">学号:</TextBlock>
        <TextBox Grid.Row="0" 
                 Height="40" 
                 Padding="10,0"
                 Margin="0,10"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  StudentID ,UpdateSourceTrigger=PropertyChanged }"
              />

        <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">学生名字:</TextBlock>
        <TextBox Grid.Row="1" 
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Name ,UpdateSourceTrigger=PropertyChanged }"
              />

        <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">年龄:</TextBlock>
        <TextBox Grid.Row="2" 
                 Margin="0,10,0,0"
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Age ,UpdateSourceTrigger=PropertyChanged }"
              />

        <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">Tel:</TextBlock>
        <TextBox Grid.Row="3" 
                 Margin="0,10,0,0"
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Tel ,UpdateSourceTrigger=PropertyChanged }"
              />


        <TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">Email:</TextBlock>
        <TextBox Grid.Row="4" 
                 Margin="0,10,0,0"
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Email ,UpdateSourceTrigger=PropertyChanged }"
              />

 

    </Grid>
</UserControl>

 

 效果

 

 完整代码下载


Validator验证器实现属性验证

Model

新建Student实体,该实体有数据验证和实体映射混合, 混乱 容易搞错。

 

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.ComponentModel;

namespace CTMvvmDemo.MVVM.Models
{
    [MetadataType(typeof(Student_MateData))]
    [Table("Students")]
   public sealed   partial class Student:ObservableValidator
    {
        [Key]//主键
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]//非自增长,自增长为Identity
        public int StudentID { get; set; }

      
        [Column("name")]
        [DataType(DataType.Text)]

        [Required(ErrorMessage = "名字不能为空")]
        [StringLength(20, ErrorMessage = "名字超过20个字符了")]
        [RegularExpression("^[\u4e00-\u9fa5|a-z]+$", ErrorMessage = "请输入汉族或者小写拼英")]
        public string Name { get; set; }

        [Column("age")]
        [Range(0, 200, ErrorMessage = "年龄输入不正确")]
        public int Age { get; set; }

        [Column("tel")]
        [RegularExpression(@"^1[34578]\d{9}$", ErrorMessage = "电话号码不正确")]
        [Required(ErrorMessage = "不能为空")]
        [DataType(DataType.PhoneNumber)]
        public string Tel { get; set; }

        [Column("email")]
        [RegularExpression("^[\\w-]+@[\\w-]+\\.(com|net|org|edu|mil|tv|biz|info)$", ErrorMessage = "邮箱不正确")]
        [Required(ErrorMessage ="不能为空")]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
    }
}

 

ViewModel

        /// 用Validator 实现验证具体步骤
        /// 1、获取model 实体中属性属性的数据特性。
        /// 2、创建包含model实例的和属性 ValidationContext
        /// 3、用Validator.TryValidateProperty方法验证,并且记录验证结果
        /// 4、将验证结果保存在字典中,该字典提供给GetErrors()使用,GetErrors()是向bing提供验证结果
        /// 验证属性  验证结果保存在_validationErrors中,GetErrors会提取相应错误信息
        /// 该方法要求 数据特性添加在model属性上,不能添加在model的数据中。

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CTMvvmDemo.MVVM.Models;
using CTMvvmDemo.Respositoies;

namespace CTMvvmDemo.MVVM.ViewsModels
{
    public  sealed partial class StudentViewsModel : ViewModelBase, INotifyDataErrorInfo
    {


        private Student student;
       
        public StudentViewsModel(Student _student) => this.student = _student;
        public string Name
        {
            get => student.Name;
            set
            {

                //先设置后验证,暂时保存本地,除非sava实体后才会更新数据库
                SetProperty(student.Name, value, student, (student, name) => student.Name = name);
                // ValidateProperty2(student.Name,  nameof(Name));
                ValidateProperty3(student,  nameof(Name));
            }
        }

        public int Age
        {
            get => student.Age;
            set
            {

               //先设置后验证,暂时保存本地,除非sava实体后才会更新数据库
                SetProperty(student.Age, value, student, (u, n) => u.Age = n);
                //  ValidateProperty2(student.Age, nameof(Age));
                ValidateProperty3(student,  nameof(Age));
            }
        }

        public int StudentID
        {
            get => student.StudentID;
            set
            {
                SetProperty(student.StudentID, value, student, (u, n) => u.StudentID = n);
                // ValidateProperty2(student.StudentID, nameof(StudentID));
                ValidateProperty3(student,  nameof(StudentID));
            }
        }

        public string Tel
        {
            get => student.Tel;
            set
            {
                SetProperty(student.Tel, value, student, (u, n) => u.Tel = n);
             //   ValidateProperty2(student.Tel, nameof(Tel));
                ValidateProperty3(student, nameof(Tel));
            }
        }

        public string Email
        {
            get => student.Email;
            set
            {
                SetProperty(student.Email, value, student, (u, n) => u.Email = n);
                // ValidateProperty2(student.Email, nameof(Email));
                ValidateProperty3(student, nameof(Email));
            }
        }
        private readonly Dictionary<string, ICollection<string>> _validationErrors = new Dictionary<string, ICollection<string>>();

        public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;

        /// <summary>
        /// 判断是否验证
        /// </summary>
        public bool HasErrors => _validationErrors is not null && _validationErrors.Count > 0;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        private void RaiseErrorsChanged(string propertyName)
        {
            if (ErrorsChanged != null)
                ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }

        /// <summary>
        /// 向Bing提供错误信息。
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public IEnumerable GetErrors(string? propertyName)
        {
            return _validationErrors.ContainsKey(propertyName) ? _validationErrors[propertyName] : null;
        }


     /// <summary>
        /// 方法3
        /// 用反射实现验证 具体步骤
        /// 1、获取model类上的元数据,该元数据是独立的类通过特性附加在model上。该元数据保存着model属性的数据特性
        /// 2、运行ValidationAttribute的IsValid()方法验证特性,并且返回验证信息。
        /// 3、将信息保存在字典中, 该字典提供给GetErrors()使用,GetErrors()是向bing提供验证结果
        /// 获取model类的上的 MetadataType 特性 中的数据特性 用于验证,这样可以实现model和model数据验证分离。
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        public void ValidateProperty3(object obj, string propertyName)
        {


            // 获取model类的上的 MetadataType 特性 , GetCustomAttributes(true)表示 是否搜索该成员的继承链以查找这些特性
            Type metadatatype = obj.GetType().GetCustomAttributes(true).OfType<MetadataTypeAttribute>().First().MetadataClassType;

            // 在元数据上获取指定属性propertyName的元数据,如果不存该属性就return,如果存在就获取值。
            PropertyInfo property = metadatatype.GetProperty(propertyName);
            if (property is null) return;

            // 在实例上获取指定属性propertyName的属性值 
            object value = obj.GetType().GetProperty(propertyName).GetValue(obj, null);

            // 运行属性上附加特性的IsValid(IsValid) 进行验证,并且返回验证信息 v.ErrorMessage  
            List<string> errors = (from v in property.GetCustomAttributes(true).OfType<ValidationAttribute>() where !v.IsValid(value) select v.ErrorMessage).ToList();

            List<string> vs = new();
            vs.Add(String.Join(",", errors));

            //将指定属性  错误信息保存到字典中,该字典提供给GetErrors()使用,GetErrors()是向bing提供验证结果。
            _validationErrors[propertyName] = (errors.Count > 0) ? vs : null;

        }



        protected override void Dispose(bool IsDisposed)
        {
            student = null;
            _validationErrors.Clear();

        }


    }
}

 

 

View

将保存在bing控件的附加属性中的验证信息显示出来。

 

<UserControl x:Class="CTMvvmDemo.MVVM.Views.StudentView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:CTMvvmDemo.MVVM.Views"
             
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ControlTemplate x:Key="displayErrorMsgFomatte">
            <WrapPanel>
                <!-- Placeholder for the TextBox itself -->
                <AdornedElementPlaceholder x:Name="textBox"/>
                <ItemsControl ItemsSource="{Binding}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding ErrorContent}" Foreground="Red"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </WrapPanel>
        </ControlTemplate>
    </UserControl.Resources>
        
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="*"/>
          
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">学号:</TextBlock>
        <TextBox Grid.Row="0" 
                 Height="40" 
                 Padding="10,0"
                 Margin="0,10"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  StudentID ,UpdateSourceTrigger=PropertyChanged }"
              />

        <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">学生名字:</TextBlock>
        <TextBox Grid.Row="1" 
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Name ,UpdateSourceTrigger=PropertyChanged }"
              />

        <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">年龄:</TextBlock>
        <TextBox Grid.Row="2" 
                 Margin="0,10,0,0"
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Age ,UpdateSourceTrigger=PropertyChanged }"
              />

        <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">Tel:</TextBlock>
        <TextBox Grid.Row="3" 
                 Margin="0,10,0,0"
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Tel ,UpdateSourceTrigger=PropertyChanged }"
              />


        <TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right">Email:</TextBlock>
        <TextBox Grid.Row="4" 
                 Margin="0,10,0,0"
                 Height="40" 
                 Padding="10,0"
                 Grid.Column="1"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                 Validation.ErrorTemplate="{StaticResource  displayErrorMsgFomatte}"
                  Text="{Binding  Email ,UpdateSourceTrigger=PropertyChanged }"
              />

 

    </Grid>
</UserControl>

 

效果

  完整案例下载

 

 异步验证

反射和验证器方法都是可以实现异步验证,这边拿ValidateProperty2()方法做demo,其他代码和反射验证一样。
       private async void ValidateProperty2(object val, [CallerMemberName] string propertyName = null)
        {
            bool allowvalid = await Task.Run<bool>(() => { Thread.Sleep(2000);return true; });

            if (_validationErrors.ContainsKey(propertyName)) _validationErrors.Remove(propertyName);
          
            ValidationContext context = new ValidationContext(student) { MemberName = propertyName };

            List<ValidationResult> results = new();
            bool isvild = Validator.TryValidateProperty(val, context, results);
            if (!isvild)
            {
                _validationErrors[propertyName] = results.Select(x => x.ErrorMessage).ToList();
            }

            ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
        }

 

posted @ 2022-10-31 15:44  小林野夫  阅读(1953)  评论(1编辑  收藏  举报
原文链接:https://www.cnblogs.com/cdaniu/