2016-04-21 6 views
0

Vielleicht der längste Fragetitel aller Zeiten! Weil das eine zweiteilige Frage ist.Wie kann NotifyOnValidationError CanExecute aufrufen (und warum funktioniert es nicht mit MVVM Light RelayCommand)

(1) Ich verstehe nicht, wie Einstellung NotifyOnValidationError = "True" kann Updates für meine CanExecute auslösen. Hier ist ein bisschen Magie involviert, die ich verstehen muss. Jemand (Sache) abonniert das Ereignis CanExecuteChanged von meinem ICommand, aber der Aufruf-Stack zeigt auf External-Code, so dass ich nicht herausfinden kann, was vor sich geht.

(2) Vielleicht die wichtigsten Follow-up-Fragen ist: Warum funktioniert es nicht in MVVM Light RelayCommand! Der CanExecute wird nur einmal bei der Initialisierung und dann nie wieder aufgerufen. Wenn ich den Quellcode für RelayCommand in MVVM Light ansehe, ergeben sich im Vergleich zu meiner eigenen Implementierung keine Chocking-Unterschiede. Ich sollte erwähnen, dass Prism's DelegateCommand auch nicht zu funktionieren scheint.

(Bonus) Maybee Ich nähere mich diesem Problem auf die falsche Art? Ich möchte im Grunde Schaltflächen basierend auf Validierungsfehlern aktivieren/deaktivieren.

XAML (Snippet):

<TextBox Grid.Column="1" Grid.Row="0"> 
     <Binding Path="X" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True"> 
      <Binding.ValidationRules> 
       <ExceptionValidationRule></ExceptionValidationRule> 
      </Binding.ValidationRules> 
     </Binding> 
    </TextBox> 

    <Button Grid.Column="1" Grid.Row="3" Command="{Binding CalculateCommand}"> 
     Calculate 
    </Button> 

RelayCommand:

public class MyRelayCommand : ICommand 
{ 
    readonly Action<object> Execute_; 
    readonly Predicate<object> CanExecute_; 

    public MyRelayCommand(Action<object> Execute, Predicate<object> CanExecute) 
    { 
     if (Execute == null) 
      throw new ArgumentNullException("No action to execute for this command."); 

     Execute_ = Execute; 
     CanExecute_ = CanExecute; 
    } 

    public bool CanExecute(object parameter) 
    { 
     return (CanExecute_ == null) ? true : CanExecute_(parameter); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public void Execute(object parameter) 
    { 
     Execute_(parameter); 
    } 
} 

Ansichtsmodell:

private DelegateCommand _calculateCommmand; 
    public DelegateCommand CalculateCommand 
    { 
     get 
     { 
      return _calculateCommmand ?? (_calculateCommmand = new DelegateCommand(
      () => 
      { 
       Sum = X + X; 
      }, 
      () => 
      { 
       try 
       { 
        Convert.ChangeType(X, TypeCode.Byte); 
        return true; 
       } 
       catch 
       { 
        return false; 
       } 
      })); 
     } 
    } 

PS: Wenn Sie kaufen wollen mein X + X-Programm, wenn es geschieht, E-Mail ich bei [email protected]

Antwort

0

Ich glaube nicht, dass es auf die ICommand Implementierung ankommt. In Ihrem, sehe ich eine public event EventHandler CanExecuteChanged, wo Sie die CommandManager sagen, um den Aufruf der CanExecute() Methode Ihres Befehls zu behandeln. Ohne den CommandManager müssten Sie selbst damit umgehen, z. indem Sie Ihrer ICommand-Implementierung eine public void RaiseCanExecuteChanged()-Methode zur Verfügung stellen, die Ihr ViewModel für jeden Befehl aufrufen muss, dessen Neuberechnung er für notwendig erachtet, z. innerhalb des ViewModels OnPropertyChanged. Beispiel: https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right

So macht der CommandManager die Magie für Sie. Sobald Sie das PropertyChanged -Ereignis Ihres ViewModels aufrufen, behandelt der "externe Code" die betroffenen Befehle und fragt sie nach einem neuen CanExecute()-Wert.

+0

ich, dass meine Implementierung mag darauf hinweisen, ist eine abgespeckte Variante der Quelle gefunden für RelayCommand (MVVM Light), hier gefunden: https://mvvmlight.codeplex.com/SourceControl/latest#GalaSoft.MvvmLight/GalaSoft.MvvmLight (PCL)/Command/RelayCommand. cs Wie Sie sehen können, verwendet es auch den CommandManager. – sunefred

+0

Das wollte ich dir sagen. Die Magie, nach der Sie gefragt haben, wird vom CommandManager erledigt. Ich nehme an, Ihr ViewModel implementiert 'INotifyPropertyChanged', daher muss ein 'event PropertyChangedEventHandler PropertyChanged' vorhanden sein, das Sie aufrufen können:' PropertyChanged? .Invoke() '. Dies teilt dem Command Manager mit, dass er die Befehle '' CanExecute() 'erneut aufrufen muss. Versuch es! –

1

(2) Ich habe das selbst herausgefunden. Sie können wählen, RelayCommand aus zwei verschiedenen Namensräume enthalten, stellen Sie sicher, dass Sie verwenden

using GalaSoft.MvvmLight.CommandWpf; 

ich immer noch suche eine gute Antwort auf (1), wie die Sanitär-Arbeiten, die CanExecutetChanged auf Validierungsfehler basierend aufwirft.

0

(1) Ich denke, es geht so.

  1. Wenn wir eine RelayCommand binden: ICommand an Button.Command, wird der Bindungsprozess auch einen Eventhandler an den ICommand.CanExecuteChanged befestigen. Dies ist das Standardverhalten.
  2. Der an CanExecutedChanged übergebene Eventhandler wird an das statische Ereignis CommandManager.RequerySuggested übergeben und angefügt.
  3. Wenn ein Validierungsfehler auftritt und der NotifyOnValidationError eine externe Kraft oder ein Jedi setzt, wird das RequerySuggested-Ereignis ausgelöst, das an ALLE aktiven Befehle gesendet wird.
  4. Die Schaltfläche empfängt das Ereignis und ruft CanExecute daraufhin auf, ob die Schaltfläche deaktiviert/aktiviert werden soll.

Ich mag oben mehr über den dritten Aufzählungspunkt wissen, also werde ich die Frage offen für ein wenig länger halte den Experten die Möglichkeit zu geben in läuten.