C# 通知ViewModel ValidatesOnExceptions输入错误

C# 通知ViewModel ValidatesOnExceptions输入错误,c#,wpf,mvvm,idataerrorinfo,C#,Wpf,Mvvm,Idataerrorinfo,在我的应用程序中,我有绑定到文本框的数值(double或int)ViewModel属性。ViewModel实现IDataErrorInfo以检查输入的值是否在“业务逻辑”的可接受范围内(例如,高度不能为负值)。我每页有许多文本框,还有一个按钮(在向导中考虑“下一步”),它的enabled属性绑定到一个ViewModel布尔值,该布尔值指定整个页面上是否有任何错误。根据我编写的IDataErrorInfo规则,使用有效/无效值正确更新按钮的启用/禁用状态 但是,无法让我的viewmodel知道何时

在我的应用程序中,我有绑定到文本框的数值(double或int)ViewModel属性。ViewModel实现IDataErrorInfo以检查输入的值是否在“业务逻辑”的可接受范围内(例如,高度不能为负值)。我每页有许多文本框,还有一个按钮(在向导中考虑“下一步”),它的enabled属性绑定到一个ViewModel布尔值,该布尔值指定整个页面上是否有任何错误。根据我编写的IDataErrorInfo规则,使用有效/无效值正确更新按钮的启用/禁用状态

但是,无法让我的viewmodel知道何时引发了异常,因为输入值未转换(即“12bd39”不是有效的双精度),因此在转换异常的情况下,尽管输入错误,我的“下一步”按钮仍将保持启用状态。但是,由于我的绑定,GUI正确地反映了装饰器的错误:

<TextBox Text="{Binding Temperature, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>

Silverlight似乎有一个您也可以订阅的事件,但我在WPF(3.5)中找不到完全相同的事件。感谢您的帮助

我有一个订阅Validation.ErrorEvent routed event的视图基类

public class MVVMViewBase : UserControl
    {
        private RoutedEventHandler _errorEventRoutedEventHandler;
        public MVVMViewBase()
        {
            Loaded += (s, e) =>
                {
                    _errorEventRoutedEventHandler = new RoutedEventHandler(ExceptionValidationErrorHandler);
                    AddHandler(Validation.ErrorEvent, _errorEventRoutedEventHandler);
                };

            Unloaded += (s, e) =>
                {
                    if (_errorEventRoutedEventHandler != null)
                    {
                        RemoveHandler(Validation.ErrorEvent, _errorEventRoutedEventHandler);
                        _errorEventRoutedEventHandler = null;
                    }
                };
        }

        private void ExceptionValidationErrorHandler(object sender, RoutedEventArgs e)
        {
            ValidationErrorEventArgs args = (ValidationErrorEventArgs) e;
            if (!(args.Error.RuleInError is IUiValidation)) return;

            DataErrorInfoViewModelBase viewModelBase = DataContext as DataErrorInfoViewModelBase;
            if(viewModelBase == null) return;

            BindingExpression bindingExpression = (BindingExpression) args.Error.BindingInError;
            string dataItemName = bindingExpression.DataItem.ToString();
            string propertyName = bindingExpression.ParentBinding.Path.Path;

            e.Handled = true;
            if(args.Action == ValidationErrorEventAction.Removed)
            {
                viewModelBase.RemoveUIValidationError(new UiValidationError(dataItemName, propertyName, null));
                return;
            }

            string validationErrorText = string.Empty;
            foreach(ValidationError validationError in Validation.GetErrors((DependencyObject) args.OriginalSource))
            {
                if (validationError.RuleInError is IUiValidation)
                {
                    validationErrorText = validationError.ErrorContent.ToString();
                }
            }
            viewModelBase.AddUIValidationError(new UiValidationError(dataItemName, propertyName, validationErrorText));
        }
    }
以及ViewModel=DataErrorInfoViewModelBase的基类,该基类由 AddUIValidationError和RemoveUIValidationError


此外,我的所有ValidationRule类都实现了IUiValidation,它仅用于将类标记为参与UI错误传播(无成员)。(您可以将属性用于相同的目的)。

也许这种替代方法对您有效:将路由事件连接到视图模型中并避免代码隐藏从来都不是一个好主意,我建议Josh Smith的解决方案。如果你想保持事情“干净”,IDataErrorInfo界面是一个不错的选择。我同意。当从头开始一个新的项目时,我一定会去Josh Smith的解决方案。不幸的是,在这种情况下这样做需要我在非常紧迫的时间框架下重构大约30个视图模型。
public class MVVMViewBase : UserControl
    {
        private RoutedEventHandler _errorEventRoutedEventHandler;
        public MVVMViewBase()
        {
            Loaded += (s, e) =>
                {
                    _errorEventRoutedEventHandler = new RoutedEventHandler(ExceptionValidationErrorHandler);
                    AddHandler(Validation.ErrorEvent, _errorEventRoutedEventHandler);
                };

            Unloaded += (s, e) =>
                {
                    if (_errorEventRoutedEventHandler != null)
                    {
                        RemoveHandler(Validation.ErrorEvent, _errorEventRoutedEventHandler);
                        _errorEventRoutedEventHandler = null;
                    }
                };
        }

        private void ExceptionValidationErrorHandler(object sender, RoutedEventArgs e)
        {
            ValidationErrorEventArgs args = (ValidationErrorEventArgs) e;
            if (!(args.Error.RuleInError is IUiValidation)) return;

            DataErrorInfoViewModelBase viewModelBase = DataContext as DataErrorInfoViewModelBase;
            if(viewModelBase == null) return;

            BindingExpression bindingExpression = (BindingExpression) args.Error.BindingInError;
            string dataItemName = bindingExpression.DataItem.ToString();
            string propertyName = bindingExpression.ParentBinding.Path.Path;

            e.Handled = true;
            if(args.Action == ValidationErrorEventAction.Removed)
            {
                viewModelBase.RemoveUIValidationError(new UiValidationError(dataItemName, propertyName, null));
                return;
            }

            string validationErrorText = string.Empty;
            foreach(ValidationError validationError in Validation.GetErrors((DependencyObject) args.OriginalSource))
            {
                if (validationError.RuleInError is IUiValidation)
                {
                    validationErrorText = validationError.ErrorContent.ToString();
                }
            }
            viewModelBase.AddUIValidationError(new UiValidationError(dataItemName, propertyName, validationErrorText));
        }
    }