2012-04-03 14 views
15

Mam okno oparte na MVVM z wieloma kontrolkami, a mój Model implementuje IDataErrorInfo.Jak wymusić aktualizację błędów sprawdzania poprawności w widoku z ViewModel przy użyciu obiektu IDataErrorInfo?

Istnieje również przycisk SaveCommand, który przeprowadza walidację, analizując właściwość Model.Error.

Widok wyświetla domyślny czerwone obramowanie wokół kontrolnych z błędami tylko przy zmianie wartości konkretnej kontroli, lub kiedy powiadomić o zmianie tej właściwości przy użyciu PropertyChanged.

Jak zmusić narzędzie View do wyświetlenia wszystkich błędów sprawdzania poprawności, nawet jeśli nie dotknąłem elementów sterujących?

Wszystkie moje powiązania sprawdzania poprawności obejmują ValidatesOnDataErrors=True, NotifyOnValidationError=True.

Wiem, że jednym z rozwiązań jest posiadanie pola zbiorczego z wszystkimi błędami, ale wolałbym wyświetlać błędy na podstawie kontroli.

Nie chcę wyzwalać Model.NotifyPropertyChanged dla każdej powiązanej właściwości z ViewModel.

Używam WPF 4.0, nie Silverlight, więc INotifyDataErrorInfo nie będzie działać.

Odpowiedz

12

Podkreślasz, że nie chcesz podnosić właściwości zmienionej dla właściwości, z którymi się wiążesz, ale to naprawdę najprostszy sposób, aby to osiągnąć. Wywołanie właściwości PropertyChanged bez parametru spowoduje podniesienie dla wszystkich właściwości w twoim viewmodelu.

Alternatywnie można zaktualizować powiązań (i wymusić ponowne zatwierdzenie) na jakiejkolwiek kontroli jak ten:

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

Dzięki za podpowiedź z PropertyChanged. Nie wiedziałem, że to możliwe. Znalazłem kolejną dyskusję na ten temat: http://stackoverflow.com/questions/1135012/wpf-mvvm-can-a-single-propertychanged-update-all-the-data-indings-of-a-datate if ktokolwiek jest zainteresowany. Jest to dobra odpowiedź, jeśli ktoś ma pojedynczy, prosty model viewModel.Mam jednak złożony widok z zagnieżdżonymi ViewModels, więc musiałbym napisać kod wywołujący PropertyChanged raz dla każdego zagnieżdżonego modelu/ViewModel, który implementuje INotifyPropertyChanged – surfen

+0

Dobrze jest znać tę sztuczkę, jeśli chce się aktualizować tylko część widoku do określonego ViewModel – surfen

+0

myControl.GetBindingExpression (ControlType.ControlProperty) .UpdateTarget(); faktycznie uzyskuje aktualność sprawdzania poprawności bez aktualizacji właściwości źródła. – r41n

2

Najlepszym rozwiązaniem, jakie dotychczas znalazłem, jest zmiana DataContext na wartość null i powrót do instancji ViewModel.

To wyzwala aktualizację do kontroli na widoku, który ma DataContext związany InnerViewModel:

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

zaleca się sprawdzenie, czy dane nie są tracone po tej sztuczki. Miałem przypadek, że ten kod wyzwalał aktualizację źródła dla ComboBox.SelectedItem z wartością null, ale udało mi się go rozwiązać. Było to spowodowane używaniem BindingProxy opartego na zasobach i porządkiem propagacji DataContext=null w hierarchii kontroli.

1

Ten „Hack” pracował dla mnie chwilowo, aby wymusić zdarzenie InotifyChanged, po prostu przypisać tę kontrolę z powrotem swój własny zadowolony. Zrób to przed oceną funkcji powiązań HasError. Na przykład pole tekstowe będzie:

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

A potem kompletnym przykładem (przed słyszę to nieprawda MVVM, ja bezpośrednio dostał uchwyt na siatce dla ułatwienia pokazując ten kod 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; 
    } 
Powiązane problemy