2012-04-03 1 views
15

У меня есть окно на основе MVVM со многими элементами управления, а моя модель реализует IDataErrorInfo.Как закрепить обновление ошибок проверки при просмотре ViewModel с помощью IDataErrorInfo?

Существует также кнопка SaveCommand, которая производит валидацию, анализируя Model.Error.

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

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

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

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

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

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

ответ

12

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

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

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

Спасибо за трюк с PropertyChanged. Я не знал, что это возможно. Я нашел еще одно обсуждение этой темы: http://stackoverflow.com/questions/1135012/wpf-mvvm-can-a-single-propertychanged-update-all-the-data-bindings-of-a-datate if кто-то заинтересован. Это хороший ответ, если у кого-то есть простой, простой viewModel.Однако у меня сложный вид с вложенными ViewModels, поэтому мне пришлось бы писать код для вызова PropertyChanged один раз для каждой вложенной связанной модели/ViewModel, которая реализует INotifyPropertyChanged. – surfen

+0

. Полезно знать этот трюк, если вы хотите обновить только часть связанного с представлением к определенному ViewModel – surfen

+0

myControl.GetBindingExpression (ControlType.ControlProperty) .UpdateTarget(); фактически обновляет вашу версию без обновления исходного свойства. – r41n

2

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

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

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

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

1

Это «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; 
    }