Avalonia数据有效性验证与手动设置错误

发布时间 2023-04-14 21:52:57作者: bodong

示例代码:https://github.com/bodong1987/AvaloniaSamples/tree/main/AvaloniaDataValidation

官方文档:https://docs.avaloniaui.net/docs/data-binding/data-validation

      Avalonia通过插件来实现数据校验,有三种方法。

1. 直接在属性的set中抛出异常。(个人不推荐这种方案)

2. 使用DataAnnotations特性(Attributes)(个人推荐这种方法)

3. 实现INotifyDataErrorInfo接口

     下面是一个简单的数据校验的示例,主要使用了异常方案和DataAnnotations方案:

using ReactiveUI;
using System;
using System.ComponentModel.DataAnnotations;

namespace AvaloniaDataValidation.ViewModels
{
    public class MainWindowViewModel : ViewModelBase
    {
        string EmailCore = "";

        [EmailAddress]
        public string Email
        {
            get => EmailCore;
            set => this.RaiseAndSetIfChanged(ref EmailCore, value);
        }

        public string[] CombItems => new string[]
        {
            "C",
            "C++",
            "C#",
        };

        string ProgramLanguageCore = "C";

        [LaungageValidation]
        public string ProgramLanguage
        {
            get => ProgramLanguageCore;
            set 
            {
                // can't use this
//                 if(value == "C#")
//                 {
//                     throw new ArgumentException($"{nameof(ProgramLanguage)} can not use C#");
//                 }

                this.RaiseAndSetIfChanged(ref ProgramLanguageCore, value);
            }
        }
    }

    internal class LaungageValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
        {
            if(value is string lan)
            {
                if (lan == "C#")
                {
                    return new ValidationResult($"[{validationContext.DisplayName}] can not be C#");
                }
            }

            return ValidationResult.Success;
        }
    }
}

      除此之外,也可以手动设置错误,如:

        private void OnShowError(object sender, RoutedEventArgs e)
        {
            if(!DataValidationErrors.GetHasErrors(textBox_Errors))
            {
                DataValidationErrors.SetError(textBox_Errors, new ArgumentException("A custom exception error"));
            }
            else
            {
                DataValidationErrors.SetError(textBox_Errors, null);
            }            
        }

      这样就可以强制设置错误了。本质上底层也是通过调用这个接口来实现这个能力的。内部其实是设置的是DataValidationErrors.ErrorsProperty:

        public static IEnumerable<object> GetErrors(Control control)
        {
            return control.GetValue(ErrorsProperty);
        }
        public static void SetErrors(Control control, IEnumerable<object> errors)
        {
            control.SetValue(ErrorsProperty, errors);
        }
        public static void SetError(Control control, Exception error)
        {
            SetErrors(control, UnpackException(error));
        }
        public static void ClearErrors(Control control)
        {
            SetErrors(control, null);
        }
        public static bool GetHasErrors(Control control)
        {
            return control.GetValue(HasErrorsProperty);
        }