通知ViewModel的ValidatesOnExceptions输入错误

时间:2010-10-18 20:26:14

标签: c# wpf mvvm idataerrorinfo

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

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

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

如何让视图知道发生了'ValidatesOnExceptions'样式错误。 Josh Smith的take here似乎依赖于使每个ViewModel属性成为一个字符串并滚动自己的异常检查,这看起来像是很多额外的工作。我还开始研究Karl Shifflett的实现here,但我似乎无法捕获将此代码放入视图的代码隐藏文件时所期望的路由事件:

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)
    {    
    }
}

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

1 个答案:

答案 0 :(得分:3)

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

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错误传播(没有成员)。 (您可以将属性用于相同目的)。