Was ist der Unterschied zwischen dem RoutedCommand und dem RelayCommand ? Wann sollte man RoutedCommand und wann RelayCommand im MVVM-Muster verwenden?
Antworten
Zu viele Anzeigen?RoutedCommand ist Teil von WPF, während RelayCommand von einem WPF-Jünger, Josh Smith, erstellt wurde ;).
Im Ernst, RS Conley hat einige der Unterschiede beschrieben. Der entscheidende Unterschied besteht darin, dass RoutedCommand eine ICommand-Implementierung ist, die ein RoutedEvent verwendet, um durch den Baum zu routen, bis eine CommandBinding für den Befehl gefunden wird, während RelayCommand keine Routen verwendet und stattdessen direkt einen Delegaten ausführt. In einem M-V-VM-Szenario ist ein RelayCommand (DelegateCommand in Prism) wahrscheinlich die bessere Wahl in allen Bereichen.
In Bezug auf die Verwendung von RelayCommand und RoutedCommand in MVVM ist der Hauptunterschied für mich folgender:
Ort des Codes
RelayCommand ermöglicht es Ihnen, den Befehl in jeder Klasse zu implementieren (als ICommand-Eigenschaft mit Delegaten), die dann konventionell an das Steuerelement gebunden ist, das den Befehl aufruft. Diese Klasse ist das ViewModel. Wenn Sie einen gerouteten Befehl verwenden, müssen Sie die mit dem Befehl verbundenen Methoden im codebehind des Steuerelements implementieren, da die Methoden durch die Attribute des CommandBinding-Elements angegeben werden. Unter der Annahme, dass ein strenges MVVM bedeutet, eine "leere" codebehind-Datei zu haben, gibt es tatsächlich keine Möglichkeit, Standard geroutete Befehle mit MVVM zu verwenden.
Was RS Conley gesagt hat, dass RelayCommand es Ihnen ermöglicht, den RelayCommand außerhalb des ViewModels zu definieren, stimmt, aber vor allem ermöglicht es Ihnen, ihn innerhalb des ViewModels zu definieren, was RoutedCommand nicht kann.
Routing
Andererseits unterstützen RelayCommands keine Routen durch den Baum (wie bereits erwähnt), was kein Problem ist, solange Ihre Oberfläche auf einem einzigen viewModel basiert. Wenn nicht, zum Beispiel, wenn Sie eine Sammlung von Elementen mit ihren eigenen viewModels haben und den Befehl des Kind-ViewModels für jedes Element gleichzeitig aus dem übergeordneten Element aufrufen möchten, müssen Sie Routing verwenden (siehe auch CompositeCommands).
Alles in allem würde ich sagen, dass Standard-RoutedCommands in striktem MVVM nicht verwendbar sind. RelayCommands sind perfekt für MVVM, unterstützen jedoch kein Routing, was Sie möglicherweise benötigen.
Der Unterschied besteht darin, dass RelayCommand Delegaten akzeptieren kann. Sie können das RelayCommand außerhalb des ViewModel definieren. Das ViewModel kann dann Delegaten zu dem Befehl hinzufügen, wenn es den Befehl erstellt und an ein UI-Objekt wie eine Steuerung bindet. Die Delegaten können wiederum auf die privaten Variablen des ViewModel zugreifen, da sie im Bereich des ViewModel selbst definiert sind.
Es wird verwendet, um den im ViewModel enthaltenen Code zu reduzieren, da der Trend darin besteht, einen gerouteten Befehl als geschachtelte Klasse innerhalb des ViewModel zu definieren. Ansonsten ist die Funktionalität der beiden ähnlich.
Ich würde argumentieren, dass RoutedCommands im strengen MVVM vollkommen legal sind. Obwohl RelayCommands aufgrund ihrer Einfachheit oft bevorzugt werden, bieten RoutedCommands manchmal organisatorische Vorteile. Zum Beispiel möchten Sie möglicherweise, dass mehrere verschiedene Ansichten mit einer gemeinsamen ICommand-Instanz verbunden sind, ohne diesen Befehl direkt an die zugrunde liegenden ViewModels weiterzugeben.
Als kleine Anmerkung: Strenges MVVM verbietet nicht die Verwendung von Code-Behind. Wenn das der Fall wäre, könnten Sie niemals benutzerdefinierte Abhängigkeitseigenschaften in Ihren Ansichten definieren!
Um ein RoutedCommand innerhalb eines strengen MVVM-Frameworks zu verwenden, können Sie die folgenden Schritte befolgen:
-
Deklarieren Sie eine statische RoutedCommand-Instanz für Ihren benutzerdefinierten Befehl. Sie können diesen Schritt überspringen, wenn Sie planen, einen vordefinierten Befehl aus der Klasse ApplicationCommands zu verwenden. Zum Beispiel:
public static class MyCommands { public static RoutedCommand MyCustomCommand = new RoutedCommand(); }
-
Binden Sie die gewünschten Ansichten an den RoutedCommand mithilfe von XAML:
-
Eine Ihrer Ansichten, die an ein geeignetes ViewModel gebunden ist (d. h. das ViewModel, das die Befehlsfunktionalität implementiert), muss eine benutzerdefinierte DependencyProperty freigeben, die mit der Implementierung Ihres ViewModels verbunden wird:
public partial class MainView : UserControl { public static readonly DependencyProperty MyCustomCommandProperty = DependencyProperty.Register("MyCustomCommand", typeof(ICommand), typeof(MainView), new UIPropertyMetadata(null)); public ICommand MyCustomCommand { get { return (ICommand)GetValue(MyCustomCommandProperty); } set { SetValue(MyCustomCommandProperty, value); } }
-
Dieselbe Ansicht sollte sich an den RoutedCommand aus Schritt 1 binden. Im XAML:
Im Code-Behind für Ihre Ansicht delegieren die zugehörigen Ereignishandler einfach an das ICommand aus der im Schritt 3 deklarierten Abhängigkeitseigenschaft:
private void MyCustomCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e) { var command = this.MyCustomCommand; if (command != null) { e.Handled = true; e.CanExecute = command.CanExecute(e.Parameter); } } private void MyCustomCommand_Executed(object sender, ExecutedRoutedEventArgs e) { var command = this.MyCustomCommand; if (command != null) { e.Handled = true; command.Execute(e.Parameter); } }
-
Zu guter Letzt binden Sie die Implementierung des Befehls Ihres ViewModels (die ein ICommand sein sollte) an die benutzerdefinierte Abhängigkeitseigenschaft in XAML:
Der Vorteil dieses Ansatzes besteht darin, dass Ihr ViewModel nur eine einzige Implementierung des ICommand-Interfaces bereitstellen muss (und es kann sogar ein RelayCommand sein), während eine beliebige Anzahl von Ansichten sich über den RoutedCommand daran anschließen kann, ohne direkt an dieses ViewModel gebunden zu werden.
Leider gibt es einen Nachteil, nämlich dass das ICommand.CanExecuteChanged-Ereignis nicht funktioniert. Wenn Ihr ViewModel möchte, dass die Ansicht die CanExecute-Eigenschaft aktualisiert, müssen Sie CommandManager.InvalidateRequerySuggested() aufrufen.