Как принудительно обновить ошибки проверки в представлении ViewModel с помощью IDataErrorInfo?

У меня есть окно на основе MVVM со многими элементами управления, а моя модель реализует IDataErrorInfo.

Существует также кнопка SaveCommand, которая выполняет проверку путем анализа свойства Model.Error.

В представлении отображается красная рамка по умолчанию вокруг элементов управления с ошибками только при изменении значения конкретного элемента управления или когда я уведомляю об изменении этого свойства с помощью PropertyChanged.

Как я могу заставить View отображать все ошибки проверки, даже когда я не касался элементов управления?

Все мои привязки проверки включают ValidatesOnDataErrors=True, NotifyOnValidationError=True.

Я знаю, что одно решение состоит в том, чтобы иметь агрегатный ящик со всеми ошибками, но я бы предпочел отображать ошибки на основе управления.

Я не хочу запускать Model.NotifyPropertyChanged для каждого свойства привязки из ViewModel.

Я использую WPF 4.0, а не Silverlight, поэтому INotifyDataErrorInfo не будет работать.

Ответ 1

Вы упомянули, что не хотите поднять свойство, измененное для свойств, к которым вы привязываетесь, но это действительно самый простой способ выполнить это. Вызов свойстваChanged без параметра будет повышаться для всех свойств в вашей модели просмотра.

В качестве альтернативы вы можете обновить привязки (и принудительно повторить проверку) на любом элементе управления следующим образом:

myControl.GetBindingExpression(ControlType.ControlProperty).UpdateSource();

Ответ 2

Лучшее решение, которое я нашел до сих пор, заключается в том, чтобы изменить DataContext на null и вернуться к экземпляру ViewModel.

Это приводит к обновлению элементов управления в представлении с DataContext, привязанным к InnerViewModel:

public void ForceUpdateErrors() {
    var tmpInnerVM = _mainViewModel.InnerViewModel;
    _mainViewModel.InnerViewModel = null;
    _mainViewModel.InnerViewModel = tmpInnerVM;
}

Рекомендуется проверить, не потеряны ли данные после этого трюка. У меня был случай, когда этот код вызвал обновление исходного кода для ComboBox.SelectedItem с нулевым значением, но мне удалось его решить. Это было вызвано использованием BindingProxy на основе ресурсов и порядком распространения DataContext=null по иерархии управления.

Ответ 3

Этот "Hack" временно работал у меня, чтобы принудительно активировать событие InotifyChanged, просто назначьте этот элемент управления своим собственным контентом. Сделайте это перед оценкой функции HasError привязок. Например, текстовое поле будет:

 ((TextBox)child).Text = ((TextBox)child).Text;

И затем полный пример (прежде чем я узнаю, что это не правда, MVVM, я просто получил ручку на сетке, чтобы упростить отображение этого snipet кода)

        public bool Validate()
    {           
        bool hasErr = false;

        for (int i = 0; i != VisualTreeHelper.GetChildrenCount(grd); ++i)
        {
            DependencyObject child = VisualTreeHelper.GetChild(grd, i);
            if (child is TextBox)
            {
                bool pp = BindingOperations.IsDataBound(child, TextBox.TextProperty);
                if (pp)
                {

                     ((TextBox)child).Text = ((TextBox)child).Text;

                    hasErr = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).HasError;
                    System.Collections.ObjectModel.ReadOnlyCollection<ValidationError> errors = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).ValidationErrors;
                    if (hasErr)
                    {
                        main.BottomText.Foreground = Brushes.Red;
                        main.BottomText.Text = BindingOperations.GetBinding(child, TextBox.TextProperty).Path.Path.Replace('.', ' ') + ": " + errors[0].ErrorContent.ToString();
                        return false;
                    }
                }
            }
            if (child is DatePicker)
            {
                ...                    
            }
        }

        return true;
    }