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

如何在ViewModel中获取ValidationResult方法的布尔结果?

原始代码片段

XAML绑定代码

<TextBox.Text>
    <Binding Path="MyProperty"
             UpdateSourceTrigger="PropertyChanged"
             TargetNullValue="">
        <Binding.ValidationRules>
            <validation:IntRangeRule Min="-999"
                                     Max="999" />
        </Binding.ValidationRules>
    </Binding>
</TextBox.Text>

IntRangeRule验证类实现

public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
    if (value != null)
    {
        var stringToConvert = value as string;

        if (!string.IsNullOrEmpty(stringToConvert))
        {
            Int32 number;

            bool result = Int32.TryParse(stringToConvert, NumberStyles.Integer, cultureInfo, out number);
            if (!result)
            {
                var localizer = ServiceLocator.Current.GetInstance<IResourceLocalizer>();
                return new ValidationResult(false, string.Format(localizer["IntValidationError"], stringToConvert, Min, Max));
            }

            if ((number < Min) || (number > Max))
            {
                var localizer = ServiceLocator.Current.GetInstance<IResourceLocalizer>();
                return new ValidationResult(false, string.Format(localizer["IntRangeValidationError"], Min, Max));
            }
        }
    }

    return new ValidationResult(true, 0);
}

问题分析

WPF默认行为下,ValidationRule验证失败时,绑定的源属性(MyProperty)不会更新,且验证状态仅在视图层维护。要在ViewModel中获取验证结果,最符合MVVM规范的方案是通过**INotifyDataErrorInfo接口**接管验证逻辑,同时支持自定义源更新规则。


解决方案:使用INotifyDataErrorInfo实现VM端验证

1. 编写ViewModel基类(复用验证逻辑)

public class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
{
    private readonly Dictionary<string, List<string>> _errors = new();

    public event PropertyChangedEventHandler PropertyChanged;
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public bool HasErrors => _errors.Any(kv => kv.Value?.Any() == true);

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName) || !_errors.ContainsKey(propertyName))
            return null;
        return _errors[propertyName];
    }

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

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

        if (!_errors[propertyName].Contains(errorMessage))
        {
            _errors[propertyName].Add(errorMessage);
            RaiseErrorsChanged(propertyName);
        }
    }

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

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

2. 改造业务ViewModel,集成验证逻辑

将原IntRangeRule的验证逻辑移到ViewModel中,并添加验证状态属性:

public class YourViewModel : ViewModelBase
{
    private string _myProperty;
    private bool _isMyPropertyValid;
    private readonly int _minValue = -999;
    private readonly int _maxValue = 999;
    private readonly IResourceLocalizer _localizer;

    public YourViewModel()
    {
        _localizer = ServiceLocator.Current.GetInstance<IResourceLocalizer>();
    }

    public string MyProperty
    {
        get => _myProperty;
        set
        {
            _myProperty = value;
            ValidateMyProperty();
            OnPropertyChanged();
        }
    }

    public bool IsMyPropertyValid
    {
        get => _isMyPropertyValid;
        private set
        {
            _isMyPropertyValid = value;
            OnPropertyChanged();
        }
    }

    private void ValidateMyProperty()
    {
        ClearErrors(nameof(MyProperty));
        IsMyPropertyValid = true;

        if (string.IsNullOrEmpty(MyProperty))
            return;

        if (!int.TryParse(MyProperty, NumberStyles.Integer, CultureInfo.CurrentCulture, out var number))
        {
            var errorMsg = string.Format(_localizer["IntValidationError"], MyProperty, _minValue, _maxValue);
            AddError(nameof(MyProperty), errorMsg);
            IsMyPropertyValid = false;
            return;
        }

        if (number < _minValue || number > _maxValue)
        {
            var errorMsg = string.Format(_localizer["IntRangeValidationError"], _minValue, _maxValue);
            AddError(nameof(MyProperty), errorMsg);
            IsMyPropertyValid = false;
        }
    }
}

3. 修改XAML绑定,启用DataError验证

移除原ValidationRules,改用VM端的INotifyDataErrorInfo验证:

<TextBox.Text>
    <Binding Path="MyProperty"
             UpdateSourceTrigger="PropertyChanged"
             TargetNullValue=""
             ValidatesOnDataErrors="True"
             NotifyOnValidationError="True" />
</TextBox.Text>

使用验证结果

现在可以直接在ViewModel中通过IsMyPropertyValid属性判断验证状态:

if (IsMyPropertyValid)
{
    // 验证通过,执行业务逻辑
}
else
{
    // 验证失败,执行对应处理
}

替代方案:保留ValidationRule,通过附加属性传递状态

如果不想替换原ValidationRule,可以通过附加属性将视图层的验证状态同步到ViewModel:

1. 创建附加属性类

public static class ValidationHelper
{
    public static readonly DependencyProperty IsValidProperty =
        DependencyProperty.RegisterAttached(
            "IsValid",
            typeof(bool),
            typeof(ValidationHelper),
            new PropertyMetadata(true));

    public static bool GetIsValid(DependencyObject obj) => (bool)obj.GetValue(IsValidProperty);
    public static void SetIsValid(DependencyObject obj, bool value) => obj.SetValue(IsValidProperty, value);

    public static readonly DependencyProperty AttachValidationProperty =
        DependencyProperty.RegisterAttached(
            "AttachValidation",
            typeof(bool),
            typeof(ValidationHelper),
            new PropertyMetadata(false, AttachValidationChanged));

    public static bool GetAttachValidation(DependencyObject obj) => (bool)obj.GetValue(AttachValidationProperty);
    public static void SetAttachValidation(DependencyObject obj, bool value) => obj.SetValue(AttachValidationProperty, value);

    private static void AttachValidationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBox textBox)
        {
            if ((bool)e.NewValue)
                textBox.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(OnValidationError));
            else
                textBox.RemoveHandler(Validation.ErrorEvent, new RoutedEventHandler(OnValidationError));
        }
    }

    private static void OnValidationError(object sender, RoutedEventArgs e)
    {
        if (sender is TextBox textBox)
            SetIsValid(textBox, !Validation.GetHasError(textBox));
    }
}

2. 修改XAML绑定

<TextBox local:ValidationHelper.AttachValidation="True"
         local:ValidationHelper.IsValid="{Binding IsMyPropertyValid, Mode=OneWayToSource}">
    <TextBox.Text>
        <Binding Path="MyProperty"
                 UpdateSourceTrigger="PropertyChanged"
                 TargetNullValue="">
            <Binding.ValidationRules>
                <validation:IntRangeRule Min="-999" Max="999" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

3. ViewModel添加对应属性

private bool _isMyPropertyValid;
public bool IsMyPropertyValid
{
    get => _isMyPropertyValid;
    set
    {
        _isMyPropertyValid = value;
        OnPropertyChanged();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 01:24:29