首页 > 其他分享 >【WPF 数据验证机制】三、INotifyDataErrorInfo接口+DataAnnotation数据特性实现model属性验证机制

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

时间:2022-10-31 15:59:14浏览次数:63  
标签:DataAnnotation string 验证 System Student using 机制 public

环境

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实体映射和数据验证混合在一起

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 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 class Student_MateData : ObservableValidator
    {
        [Required]
        public int StudentID { get; set; }

        [Required]
        [StringLength(10, ErrorMessage = "名字太长了")]
        public string Name { get; set; }

        [Required]
        [Range(0,200,ErrorMessage ="年龄输入不正确")]
        public string Age { 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.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CTMvvmDemo.MVVM.Models;
using CTMvvmDemo.Respositoies;

namespace CTMvvmDemo.MVVM.ViewsModels
{
    public  partial class  StudentViewsModel: ViewModelBase,INotifyDataErrorInfo
    {
      
        private Student student ;

        public StudentViewsModel(Student _student)
        {
            student = _student;
            this.studentID = _student.StudentID;
            this.name = _student.Name;
            this.age = _student.Age;
            this.tel = _student.Tel;
            this.email = _student.Email;
        }
        private string name;
        public string Name
        {
            get => name;
            set
            {
                name = value;
                student.Name = value;
                ValidateProperty3(student,"Name");
                OnPropertyChanged("Name");
            }
        }

        [ObservableProperty]
        private int studentID;

     
       /* [ObservableProperty]
        private string name;*/

        [ObservableProperty]
        private int age;


        [ObservableProperty]
        private string tel;
        
        [ObservableProperty]
        private string 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

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">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
        </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"
                 Grid.Column="1"
                 Name="nametextbox"
                 MinWidth="200"
                 VerticalAlignment="Stretch" 
                 VerticalContentAlignment="Center"
                 HorizontalAlignment="Left" 
                  Text="{Binding  Name ,UpdateSourceTrigger=PropertyChanged }"
               >  </TextBox>

        <ContentPresenter Grid.Row="1"
                         Grid.Column="2" 
                          Content="{Binding ElementName=nametextbox,Path=(Validation.Errors)[0].ErrorContent}"
                           />
    </Grid>
</UserControl>

 

 

 

 

 

 

Validator验证器实现属性验证

 ViewModel中用反射实现model属性验证

标签:DataAnnotation,string,验证,System,Student,using,机制,public
From: https://www.cnblogs.com/cdaniu/p/16844583.html

相关文章