MVVM-Routing- und Relay-Befehl

Antworten:

69

RoutedCommand ist Teil von WPF, während RelayCommand von einem WPF-Schüler, Josh Smith, erstellt wurde;).

Im Ernst, RS Conley beschrieb einige der Unterschiede. Der Hauptunterschied 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 kein Routing ausführt und stattdessen einen Delegaten direkt ausführt. In einem MV-VM-Szenario ist ein RelayCommand (DelegateCommand in Prism) wahrscheinlich die bessere Wahl.

wekempf
quelle
34

In Bezug auf die Verwendung von RelayCommand und RoutedCommand in MVVM ist der Hauptunterschied für mich der folgende:

Speicherort des Codes

Mit RelayCommand können Sie den Befehl in einer beliebigen Klasse (als ICommand-Eigenschaft mit Delegaten) implementieren, die dann üblicherweise 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. Angenommen, striktes MVVM bedeutet, eine "leere" Codebehind-Datei zu haben, gibt es tatsächlich keine Möglichkeit, Standard-Routing-Befehle mit MVVM zu verwenden.

Was RS Conley gesagt hat, dass Sie mit RelayCommand den RelayCommand außerhalb des ViewModel definieren können, ist richtig, aber zunächst können Sie ihn innerhalb des ViewModel definieren, was RoutedCommand nicht tut.

Routing

Auf der anderen Seite unterstützen RelayCommands kein Routing durch den Baum (wie bereits erwähnt), was kein Problem darstellt, solange Ihre Schnittstelle auf einem einzelnen viewModel basiert. Wenn dies nicht der Fall ist, z. B. wenn Sie eine Sammlung von Elementen mit eigenen viewModels haben und für jedes Element gleichzeitig einen Befehl des untergeordneten ViewModel aus dem übergeordneten Element aufrufen möchten, müssen Sie das Routing verwenden (siehe auch CompositeCommands). .

Alles in allem würde ich sagen, dass Standard-RoutedCommands in striktem MVVM nicht verwendet werden können. RelayCommands sind perfekt für MVVM, unterstützen jedoch kein Routing, das Sie möglicherweise benötigen.

Marc
quelle
Vielen Dank für die zusätzliche Tiefe in Ihrer Erklärung und den Verweis auf CompositeCommands - das hat mir geholfen zu sehen, wo sie hineinpassen.
Lars Kemmann
22

Der Unterschied besteht darin, dass RelayCommand Delegaten akzeptieren kann. Sie können den RelayCommand außerhalb des ViewModel definieren. Das ViewModel kann dann dem Befehl Delegaten hinzufügen, wenn es den Befehl erstellt und wie ein Steuerelement an ein UI-Objekt bindet. Die Delegaten können wiederum auf die private Variable des ViewModel zugreifen, wie sie im Bereich des View-Modells selbst definiert sind.

Es wird verwendet, um die im ViewModel enthaltene Codemenge zu reduzieren, da der Trend darin besteht, einen Routed-Befehl als verschachtelte Klasse im ViewModel zu definieren. Die Funktionalität der beiden ist ansonsten ähnlich.

RS Conley
quelle
15

Ich würde argumentieren, dass RoutedCommands in strengen MVVM vollkommen legal sind. Obwohl RelayCommands aufgrund ihrer Einfachheit häufig vorzuziehen sind, bieten RoutedCommands manchmal organisatorische Vorteile. Beispielsweise möchten Sie möglicherweise, dass mehrere verschiedene Ansichten eine Verbindung zu einer gemeinsam genutzten ICommand-Instanz herstellen, ohne diesen Befehl direkt den zugrunde liegenden ViewModels zur Verfügung zu stellen.

Denken Sie als Randnotiz daran, dass strikte MVVM die Verwendung von Code-Behind nicht verbietet. Wenn das wahr wäre, könnten Sie niemals benutzerdefinierte Abhängigkeitseigenschaften in Ihren Ansichten definieren!

Um einen RoutedCommand in einem strengen MVVM-Framework zu verwenden, können Sie die folgenden Schritte ausführen:

  1. Deklarieren Sie eine statische RoutedCommand-Instanz für Ihren benutzerdefinierten Befehl. Sie können diesen Schritt überspringen, wenn Sie einen vordefinierten Befehl aus der ApplicationCommands-Klasse verwenden möchten. Zum Beispiel:

    public static class MyCommands {
        public static RoutedCommand MyCustomCommand = new RoutedCommand();
    }
    
  2. Fügen Sie die gewünschten Ansichten mit XAML an den RoutedCommand an:

    <Button Command="{x:Static local:MyCommands.MyCustomCommand}" />
    
  3. Eine Ihrer Ansichten, die an ein geeignetes ViewModel gebunden ist (dh welches ViewModel die Befehlsfunktionalität implementiert), muss eine benutzerdefinierte DependencyProperty verfügbar machen, die an die Implementierung Ihres ViewModel gebunden 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); }
        }
    
  4. Dieselbe Ansicht sollte sich ab Schritt 1 an den RoutedCommand binden. In der XAML:

    <UserControl.CommandBindings>
        <CommandBinding Command="{x:Static local:MyCommands.MyCustomCommand}"
                        CanExecute="MyCustomCommand_CanExecute"
                        Executed="MyCustomCommand_Executed"
                        />
    </UserControl.CommandBindings>
    

    Im Code-Behind für Ihre Ansicht delegieren die zugehörigen Ereignishandler nur aus der in Schritt 3 deklarierten Abhängigkeitseigenschaft an den ICommand:

    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);
        }
    }
    
  5. Binden Sie abschließend die Befehlsimplementierung Ihres ViewModel (die ein ICommand sein sollte) an die benutzerdefinierte Abhängigkeitseigenschaft in XAML:

    <local:MainView DataContext="{Binding MainViewModel}"
                    MyCustomCommand="{Binding CustomCommand}" />
    

Der Vorteil dieses Ansatzes besteht darin, dass Ihr ViewModel nur eine einzige Implementierung der ICommand-Schnittstelle bereitstellen muss (und es kann sogar ein RelayCommand sein), während eine beliebige Anzahl von Views über den RoutedCommand daran angehängt werden kann, ohne direkt daran gebunden zu sein ViewModel.

Leider hat das ICommand.CanExecuteChanged-Ereignis einen Nachteil. Wenn Ihr ViewModel möchte, dass die Ansicht die CanExecute-Eigenschaft aktualisiert, müssen Sie CommandManager.InvalidateRequerySuggested () aufrufen.

RogerN
quelle