Информирование ViewModel об ошибках ввода ValidatesOnExceptions - PullRequest
5 голосов
/ 19 октября 2010

В моем приложении у меня есть числовые (двойные или целые) свойства ViewModel, которые связаны с TextBoxes. ViewModel реализует IDataErrorInfo, чтобы проверить, попадают ли введенные значения в приемлемые диапазоны для «бизнес-логики» (например, высота не может быть отрицательным значением). У меня есть несколько TextBox для каждой страницы, и у меня есть кнопка (думаю, «далее» в мастере), свойство которой включено, связано с логическим значением ViewModel, которое указывает, есть ли какие-либо ошибки на странице в целом. Состояние включения / выключения кнопки корректно обновляется с помощью допустимых / недействительных значений в соответствии с правилами IDataErrorInfo, которые я написал.

Однако нет способа сообщить моей модели представления, когда было сгенерировано исключение, потому что входное значение не преобразуется (т. Е. «12bd39» не является допустимым двойным числом), и, как результат, в случае исключений преобразования my ' Кнопка «Далее» останется включенной, несмотря на неправильный ввод. Однако графический интерфейс корректно отражает ошибку с рекламодателем из-за моей привязки:

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

Как я могу сообщить представлению, что произошла ошибка стиля 'ValidatesOnExceptions'. Взятие Джоша Смита здесь , по-видимому, основано на том, чтобы сделать каждое свойство ViewModel строкой и выполнить собственную проверку исключений, что кажется большой дополнительной работой. Кроме того, я начал изучать реализацию Карла Шиффлетта здесь , но я не могу перехватить перенаправленное событие, которое я ожидал бы при помещении этого кода в файл codebehind вида:

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 Ответ

3 голосов
/ 19 октября 2010

У меня есть базовый класс для 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, который используется только для того, чтобы пометить класс как часть распространения ошибок пользовательского интерфейса (без членов). (вы можете использовать атрибут для той же цели).

...