You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WPF/AvaloniaUI中为属性动态添加ValidationAttributes

动态添加ValidationAttribute并触发Avalonia/WPF验证的解决方法

问题根源

你通过TypeDescriptor.GetProperties能看到已添加的属性,但验证未触发,核心原因是:

  • Avalonia/WPF的默认验证引擎只会在属性初始化或首次绑定阶段读取ValidationAttribute,动态添加后不会自动重新扫描属性元数据;
  • 旧的IDataErrorInfo验证机制不支持动态规则的自动更新,必须通过INotifyDataErrorInfo主动管理验证状态并通知变化。

具体解决方案

1. 确保绑定启用数据验证

在XAML绑定中必须开启数据验证开关,否则控件不会响应验证状态:

<TextBox Text="{Binding MyProperty, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />

2. 实现INotifyDataErrorInfo接口(核心步骤)

让你的ViewModel实现这个接口,手动管理验证错误,并在属性变化或规则更新时触发验证刷新。示例代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.ComponentModel.DataAnnotations;

public class MyViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
    // 存储各属性的错误信息
    private readonly Dictionary<string, List<string>> _propertyErrors = new();
    // 存储动态添加的验证规则
    private readonly Dictionary<string, List<ValidationAttribute>> _dynamicValidations = new();

    private string _myProperty;
    public string MyProperty
    {
        get => _myProperty;
        set
        {
            _myProperty = value;
            OnPropertyChanged();
            // 属性值变化时自动验证
            ValidateProperty(nameof(MyProperty));
        }
    }

    /// <summary>
    /// 动态添加验证规则的方法
    /// </summary>
    public void AddDynamicValidation(string propertyName, ValidationAttribute attribute)
    {
        if (!_dynamicValidations.ContainsKey(propertyName))
            _dynamicValidations[propertyName] = new List<ValidationAttribute>();
        
        _dynamicValidations[propertyName].Add(attribute);
        // 添加规则后立即触发验证
        ValidateProperty(propertyName);
    }

    /// <summary>
    /// 执行属性验证:合并静态+动态规则
    /// </summary>
    private void ValidateProperty(string propertyName)
    {
        var property = GetType().GetProperty(propertyName);
        if (property == null) return;

        var propertyValue = property.GetValue(this);
        var allValidations = new List<ValidationAttribute>();

        // 读取静态标记的ValidationAttribute
        allValidations.AddRange(
            property.GetCustomAttributes<ValidationAttribute>(inherit: true));
        
        // 读取动态添加的ValidationAttribute
        if (_dynamicValidations.TryGetValue(propertyName, out var dynamicAttrs))
            allValidations.AddRange(dynamicAttrs);

        // 执行验证并收集错误
        var errors = allValidations
            .Where(attr => !attr.IsValid(propertyValue))
            .Select(attr => attr.ErrorMessage ?? $"字段{propertyName}验证失败")
            .ToList();

        // 更新错误字典并通知UI
        if (errors.Any())
        {
            _propertyErrors[propertyName] = errors;
            OnErrorsChanged(propertyName);
        }
        else if (_propertyErrors.ContainsKey(propertyName))
        {
            _propertyErrors.Remove(propertyName);
            OnErrorsChanged(propertyName);
        }
    }

    #region INotifyDataErrorInfo实现
    public bool HasErrors => _propertyErrors.Any();
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public IEnumerable GetErrors(string propertyName)
    {
        return _propertyErrors.TryGetValue(propertyName, out var errors) 
            ? errors 
            : Enumerable.Empty<string>();
    }

    private void OnErrorsChanged(string propertyName)
    {
        ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
    }
    #endregion

    #region INotifyPropertyChanged实现
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

3. 动态添加验证规则并触发刷新

调用ViewModel的AddDynamicValidation方法添加规则,无需依赖TypeDescriptor(如果坚持用TypeDescriptor,添加完成后必须手动调用ValidateProperty):

// 示例:给MyProperty添加必填和长度验证
var viewModel = new MyViewModel();
viewModel.AddDynamicValidation(nameof(viewModel.MyProperty), 
    new RequiredAttribute { ErrorMessage = "此字段不能为空" });
viewModel.AddDynamicValidation(nameof(viewModel.MyProperty), 
    new StringLengthAttribute(10) { ErrorMessage = "长度不能超过10个字符" });

补充说明

如果你一定要沿用TypeDescriptor的方式添加属性,只需在添加完成后手动调用viewModel.ValidateProperty("MyProperty")触发验证即可,核心还是通过INotifyDataErrorInfo通知UI更新状态。

内容的提问来源于stack exchange,提问作者Htennek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 19:23:19