通知ViewModel的ValidatesOnExceptions输入错误

ast*_*ish 5 c# wpf mvvm idataerrorinfo

在我的应用程序中,我有绑定到TextBoxes的数字(double或int)ViewModel属性.ViewModel实现IDataErrorInfo以检查输入的值是否落在"业务逻辑"的可接受范围内(例如,高度不能为负值).我每页都有一些TextBox,并且有一个按钮(在向导中称为"next"),启用属性绑定到ViewModel布尔值,该布尔值指定整个页面上是否有任何错误.根据我编写的IDataErrorInfo规则,使用有效/无效值正确更新按钮的启用/禁用状态.

但是,由于输入值没有转换(即"12bd39"不是有效的双精度),因此无法让我的viewmodel知道何时抛出异常,因此在转换异常的情况下我的'next'按钮尽管输入错误,仍会保持启用 然而,由于我的绑定,GUI正确地反映了装饰者的错误:

<TextBox Text="{Binding Temperature, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
Run Code Online (Sandbox Code Playgroud)

如何让视图知道发生了'ValidatesOnExceptions'样式错误.Josh Smith在这里的看法似乎依赖于使每个ViewModel属性成为一个字符串并滚动自己的异常检查,这似乎是很多额外的工作.我还开始寻找卡尔Shifflett的实现在这里,但我似乎无法捕捉到的路由事件把这个代码到视图的代码隐藏文件时,我会想到:

public ViewClass()
{
 this.InitializeComponent();
        this.AddHandler(System.Windows.Controls.Validation.ErrorEvent, new RoutedEventHandler(ValidationErrorHandler));
}

private void ValidationErrorHandler(object sender, RoutedEventArgs e)
{
    var blah = e as System.Windows.Controls.ValidationErrorEventArgs;
    if (blah.Action == ValidationErrorEventAction.Added)
    {
    }
    else if (blah.Action == ValidationErrorEventAction.Removed)
    {    
    }
}
Run Code Online (Sandbox Code Playgroud)

Silverlight似乎也有一个你可以订阅的事件,但我找不到WPF(3.5)中的确切等价物.任何帮助表示赞赏!

Cla*_*ila 3

我有一个订阅 Validation.ErrorEvent 路由事件的 View 的基类

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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

以及 ViewModel = DataErrorInfoViewModelBase 的基类,由 AddUIValidationError 和 RemoveUIValidationError 通知

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