2012-04-03 8 views
15

Ich habe ein MVVM-basiertes Fenster mit vielen Steuerelementen und mein Modell implementiert IDataErrorInfo.Wie erzwinge Validierungsfehler Update auf Ansicht von ViewModel mit IDataErrorInfo?

Es gibt auch eine SaveCommand Schaltfläche, die die Validierung durch Analyse der Model.Error Eigenschaft durchführt.

Die Ansicht zeigt den standardmäßigen roten Rahmen um Steuerelemente mit Fehlern nur, wenn ich den Wert eines bestimmten Steuerelements ändern, oder wenn ich über die Änderung dieser Eigenschaft mit PropertyChanged benachrichtigen.

Wie kann ich View erzwingen, um alle Validierungsfehler anzuzeigen, auch wenn ich die Steuerelemente nicht berührt habe?

Alle meine Validierungsbindungen sind ValidatesOnDataErrors=True, NotifyOnValidationError=True.

Ich weiß eine Lösung ist, eine Sammelkiste mit allen Fehlern zu haben, aber ich würde es vorziehen, Fehler pro Kontrolle zu zeigen.

Ich möchte Model.NotifyPropertyChanged für jede gebundene Eigenschaft von ViewModel nicht auslösen.

Ich benutze WPF 4.0, nicht Silverlight, so INotifyDataErrorInfo wird nicht funktionieren.

Antwort

12

Sie erwähnen, dass Sie die Eigenschaften, die für die Eigenschaften, an die Sie binden, geändert haben, nicht erhöhen möchten, aber das ist wirklich der einfachste Weg, dies zu erreichen. Das Aufrufen von PropertyChanged ohne Parameter wird für alle Eigenschaften in Ihrem Ansichtsmodell ausgelöst.

Alternativ können Sie die Bindungen (und Kraft Revalidierung) aktualisiert werden auf jede Kontrolle wie folgt aus:

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

Danke für den Trick mit PropertyChanged. Ich wusste nicht, dass es möglich ist. Ich habe eine andere Diskussion zu diesem Thema gefunden: http://stackoverflow.com/questions/1135012/wpf-mvvm-can-a-single-propertychanged-update-all-the-data-bindings-of-a-datate wenn Jeder ist interessiert. Dies ist eine gute Antwort, wenn jemand ein einzelnes, einfaches viewModel hat.Allerdings habe ich eine komplexe Ansicht mit verschachtelten ViewModels, also müsste ich Code schreiben, um PropertyChanged einmal für jedes geschachtelte gebundene Model/ViewModel aufzurufen, das INotifyPropertyChanged implementiert. – surfen

+0

Es ist gut diesen Trick zu kennen, wenn man nur einen Teil der View aktualisieren will zu einem bestimmten ViewModel – surfen

+0

myControl.GetBindingExpression (ControlType.ControlProperty) .UpdateTarget(); Aktualisiert Ihre Validierung tatsächlich, ohne Ihre Quelleigenschaft zu aktualisieren. – r41n

2

Die beste Lösung, die ich bisher gefunden habe, besteht darin, DataContext in null und zurück in die Instanz von ViewModel zu ändern.

Dies löst das Update für Kontrollen auf der Ansicht, dass DataContext gebunden InnerViewModel hat:

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

Es wird empfohlen, zu überprüfen, ob keine Daten nach diesem Trick verloren. Ich hatte einen Fall, dass dieser Code Source-Update für ComboBox.SelectedItem mit Null ausgelöst, aber ich es geschafft, es zu lösen. Es wurde verursacht, indem eine ressourcenbasierte BindingProxy und die Reihenfolge der DataContext=null Ausbreitung über die Steuerungshierarchie verwendet.

1

Dieses ‚Hack‘ vorübergehend für mich gearbeitet, um das Ereignis zu InotifyChanged zu zwingen, weisen nur, dass die Kontrolle seiner eigenen Rücken Inhalt. Tun Sie dies, bevor Sie die HasError-Funktion von Bindungen auswerten. Zum Beispiel wäre eine Textbox sein:

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

Und dann ein komplettes Beispiel (bevor ich höre, das ist nicht wahr MVVM, ich habe direkt einen Griff auf dem Gitter für eine einfache Ansicht des Codestückes)

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