2010-10-18 1 views
5

In meiner Anwendung habe ich numerische (Double oder Int) ViewModel-Eigenschaften, die an TextBoxen gebunden sind. Das ViewModel implementiert IDataErrorInfo, um zu überprüfen, ob die eingegebenen Werte innerhalb akzeptabler Bereiche für die 'Geschäftslogik' liegen (z. B. Höhe kann kein negativer Wert sein). Ich habe eine Anzahl von TextBoxen pro Seite und habe eine Schaltfläche (denke "weiter" in einem Assistenten), die aktivierte Eigenschaft ist an einen ViewModel-Booleschen Wert gebunden, der angibt, ob Fehler auf der Seite als Ganzes vorhanden sind. Der Aktivierungs-/Deaktivierungsstatus der Schaltfläche wird ordnungsgemäß mit gültigen/ungültigen Werten gemäß den IDataErrorInfo-Regeln aktualisiert, die ich geschrieben habe.ViewModel über ValidatesOnExceptions-Eingabefehler informieren

Es gibt jedoch keine Möglichkeit, mein Viewmodel zu informieren, wenn eine Ausnahme ausgelöst wurde, weil ein Eingabewert nicht konvertiert wird (dh "12bd39" ist kein gültiges Double) und als Ergebnis bei Konvertierungsausnahmen mein Die Schaltfläche 'Weiter' bleibt trotz schlechter Eingabe aktiviert. Die GUI jedoch spiegelt richtig den Fehler mit einem adorner wegen meiner Bindung:

<TextBox Text="{Binding Temperature, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/> 

Wie kann ich die Ansicht, dass gerade ‚ValidatesOnExceptions‘ -Stil Fehler aufgetreten. Josh Smiths Take here scheint darauf zu beruhen, jede ViewModel-Eigenschaft zu einer Zeichenkette zu machen und eine eigene Exception-Prüfung zu erstellen, die wie viel zusätzliche Arbeit aussieht. Ich begann zusätzlich einen Blick in Karl Shifflett Implementierung here, aber ich kann nicht scheinen, um das Routingereignis zu erfassen ich erwarten würde, wenn Sie diesen Code in die Code-Behind-Datei der Ansicht setzen:

public ViewClass() 
{ 
this.InitializeComponent(); 
     this.AddHandler(System.Windows.Controls.Validation.ErrorEvent, new RoutedEventHandler(ValidationErrorHandler)); 
} 

private void ValidationErrorHandler(object sender, RoutedEventArgs e) 
{ 
    var blah = e as System.Windows.Controls.ValidationErrorEventArgs; 
    if (blah.Action == ValidationErrorEventAction.Added) 
    { 
    } 
    else if (blah.Action == ValidationErrorEventAction.Removed) 
    {  
    } 
} 

Silverlight erscheint ein Ereignis, das Sie abonnieren , aber ich kann das genaue Äquivalent in WPF (3.5) nicht finden. Jede Hilfe wird geschätzt!

+0

Vielleicht funktioniert dieser alternative Ansatz für Sie: http://stackoverflow.com/questions/921601/how-can-i-handle-a-validation-error-in-my-viewmodel-instead-of-my- views-code-beh – Ragepotato

+0

Es ist nie eine gute Idee, geroutete Ereignisse in Ihre View-Modelle einzubinden und Code dahinter zu vermeiden, schlage ich Josh Smiths Lösung vor. Die IDataErrorInfo-Schnittstelle ist der Weg zu gehen, wenn Sie Dinge "sauber" halten wollen. –

+0

Ich stimme zu. Wenn ich ein neues Projekt von Grund auf neu starte, werde ich definitiv zu Josh Smiths Lösung gehen. Leider würde ich in diesem Fall 30 Modelle in einem sehr engen Zeitrahmen umgestalten müssen. – astonish

Antwort

3

Ich habe eine Basisklasse für die Ansicht, die Validation.ErrorEvent Ereignis

geroutet abonniert
public class MVVMViewBase : UserControl 
    { 
     private RoutedEventHandler _errorEventRoutedEventHandler; 
     public MVVMViewBase() 
     { 
      Loaded += (s, e) => 
       { 
        _errorEventRoutedEventHandler = new RoutedEventHandler(ExceptionValidationErrorHandler); 
        AddHandler(Validation.ErrorEvent, _errorEventRoutedEventHandler); 
       }; 

      Unloaded += (s, e) => 
       { 
        if (_errorEventRoutedEventHandler != null) 
        { 
         RemoveHandler(Validation.ErrorEvent, _errorEventRoutedEventHandler); 
         _errorEventRoutedEventHandler = null; 
        } 
       }; 
     } 

     private void ExceptionValidationErrorHandler(object sender, RoutedEventArgs e) 
     { 
      ValidationErrorEventArgs args = (ValidationErrorEventArgs) e; 
      if (!(args.Error.RuleInError is IUiValidation)) return; 

      DataErrorInfoViewModelBase viewModelBase = DataContext as DataErrorInfoViewModelBase; 
      if(viewModelBase == null) return; 

      BindingExpression bindingExpression = (BindingExpression) args.Error.BindingInError; 
      string dataItemName = bindingExpression.DataItem.ToString(); 
      string propertyName = bindingExpression.ParentBinding.Path.Path; 

      e.Handled = true; 
      if(args.Action == ValidationErrorEventAction.Removed) 
      { 
       viewModelBase.RemoveUIValidationError(new UiValidationError(dataItemName, propertyName, null)); 
       return; 
      } 

      string validationErrorText = string.Empty; 
      foreach(ValidationError validationError in Validation.GetErrors((DependencyObject) args.OriginalSource)) 
      { 
       if (validationError.RuleInError is IUiValidation) 
       { 
        validationErrorText = validationError.ErrorContent.ToString(); 
       } 
      } 
      viewModelBase.AddUIValidationError(new UiValidationError(dataItemName, propertyName, validationErrorText)); 
     } 
    } 

und eine Basisklasse für die Ansichtsmodell = DataErrorInfoViewModelBase die

von AddUIValidationError und RemoveUIValidationError informiert

auch alle Meine ValidationRule-Klassen implementieren IUiValidation, das nur dazu dient, die Klasse als Teil der UI-Fehlerausbreitung (keine Mitglieder) zu markieren. (Sie können ein Attribut für den gleichen Zweck verwenden).