如何在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
相关产品推荐
相关产品推荐

