WPF 如何在控件上手动设置 Validation.HasError 属性?

WPF How to set Validation.HasError property on controls manually?

我有一个 wpf window,它会在用户与控件交互时触发验证(进入控件并更改导致更新 属性 的值)和 属性已更改,验证会触发并按应有的方式显示。

但是我想在用户单击保存按钮而不遍历控件时在屏幕上手动显示所有验证错误,否则如果用户加载屏幕并单击保存按钮它应该如何显示。

即使我创建一个像 IsValid() 这样的方法并在单击保存按钮时调用它,它也会验证整个表单并告诉我它是否有效,但文本框周围的红色边框不会显示(因为 Validation.HasError 属性 没有被更新),这是我需要的,因为以几种形式 controls 我需要通知用户导致问题的确切控件。

你可以从这个link得到有问题的示例工程 https://1drv.ms/u/s!AuCr-YEWkmWUiopdQ-eZ17IC7IAJnA

当我们验证 属性 而不遍历它时。它不会更新控件的 Validate.HasError 属性。对此的解决方案是简单的老式简单 NotifyPropertyChanged(属性Name).

当我的 属性 值发生变化(在集合中)时,我正在使用 NotifyPropertyChanged,但没有遍历它,它永远不会触发。

所以我们应该在 属性 的验证失败时调用 NotifyPropertyChanged 或者我们应该调用 NotifyPropertyChanged(null) 通知所有控件刷新它们的属性。

添加我的 INotifyDataErrorInfo 的完整实现

    public class NotifyDataErrorInfoBase<T> : INotifyDataErrorInfo
{
    public NotifyDataErrorInfoBase(T model)
    {
        Model = model;
    }

    public T Model { get; set; }

    protected void SetValue<TValue>(string propertyName, TValue value)
    {
        typeof(T).GetProperty(propertyName).SetValue(Model, value);
        ValidateProperty<TValue>(propertyName);
    }

    public bool ValidateAllProperties()
    {

        List<KeyValuePair<string, Type>> lstOfProperties = typeof(T).GetProperties().
             Select(u => new KeyValuePair<string, Type>(u.Name, u.PropertyType)).ToList();
        foreach (var property in lstOfProperties)
        {
           Type currentType = property.Value;
            if (property.Value == typeof(string))
            {
                ValidateProperty<string>(property.Key);
            }
            else if (property.Value == typeof(int))
            {
                ValidateProperty<int>(property.Key);
            }
        }
        return !HasErrors;
    }

    private void ValidateProperty<TValue>([CallerMemberName]string propertyName = null)
    {
        ClearErrors(propertyName);
        var validationContext = new ValidationContext(Model) { MemberName = propertyName };
        List<ValidationResult> results = new List<ValidationResult>();

        var userName = GetValue<TValue>(propertyName);
        Validator.TryValidateProperty(userName, validationContext, results);

        if (results.Any())
        {
            foreach (var item in results)
            {
                AddError(propertyName, item.ErrorMessage);
            }
        }
    }

    protected TValue GetValue<TValue>(string propertyName)
    {
        return (TValue)typeof(T).GetProperty(propertyName).GetValue(Model);
    }

    Dictionary<string, List<string>> _lstOfErrors = new Dictionary<string, List<string>>();

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public bool HasErrors => _lstOfErrors.Any();

    public IEnumerable GetErrors(string propertyName)
    {
        return _lstOfErrors.ContainsKey(propertyName) ? _lstOfErrors[propertyName] : null;
    }

    protected void AddError(string propertyName, string errorMessage)
    {
        if (!_lstOfErrors.ContainsKey(propertyName))
        {
            _lstOfErrors[propertyName] = new List<string>();
        }
        _lstOfErrors[propertyName].Add(errorMessage);
    }

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

    protected void ClearErrors(string propertyName)
    {
        if (_lstOfErrors.ContainsKey(propertyName))
            _lstOfErrors.Remove(propertyName);
    }
}