Как принудительно обновить ошибки проверки на View из ViewModel, используя IDataErrorInfo? - PullRequest
17 голосов
/ 03 апреля 2012

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

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

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

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

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

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

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

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

Ответы [ 3 ]

13 голосов
/ 15 мая 2012

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

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

myControl.GetBindingExpression(ControlType.ControlProperty).UpdateSource();
2 голосов
/ 15 мая 2012

Лучшее решение, которое я нашел на сегодняшний день, - изменить DataContext на null и вернуться к экземпляру ViewModel.

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

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

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

1 голос
/ 17 марта 2015

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

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

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

        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;
    }
...