webentwicklung-frage-antwort-db.com.de

MVVM-Routing- und Relay-Befehl

Was ist der Unterschied zwischen RoutedCommand und RelayCommand ? Wann wird RoutedCommand und wann RelayCommand in MVVM-Mustern verwendet?

75
user72799

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 die Struktur zu routen, bis ein CommandBinding für den Befehl gefunden wird, während RelayCommand kein Routing ausführt und stattdessen einen Delegaten direkt ausführt. In einem M-V-VM-Szenario ist ein RelayCommand (DelegateCommand in Prism) wahrscheinlich die bessere Wahl.

68
wekempf

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

Ort 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 weitergeleiteten Befehl verwenden, müssen Sie die auf den Befehl bezogenen Methoden im Codebehind des Steuerelements implementieren, da die Methoden durch das angegeben werden Attribute des CommandBinding-Elements. Vorausgesetzt, dass strenges 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 mit RelayCommand das RelayCommand außerhalb des ViewModel definiert werden kann, ist richtig, aber zuallererst können Sie es innerhalb des ViewModel definieren , was RoutedCommand nicht tut.

Routing

Andererseits 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 ViewModels 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 strengen MVVM nicht verwendbar sind. RelayCommands eignen sich perfekt für MVVM, unterstützen jedoch kein Routing, das Sie möglicherweise benötigen.

34
Marc

Der Unterschied besteht darin, dass RelayCommand Delegierte akzeptieren kann. Sie können den RelayCommand außerhalb des ViewModel definieren. Das ViewModel kann dann Delegaten zum Befehl 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 Gültigkeitsbereich 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.

22
RS Conley

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 zuzuführen.

Beachten Sie, dass strenges 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. Beispielsweise:

    public static class MyCommands {
        public static RoutedCommand MyCustomCommand = new RoutedCommand();
    }
    
  2. Hängen 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 (d. H. Welches ViewModel die Befehlsfunktionalität implementiert), muss eine benutzerdefinierte DependencyProperty verfügbar machen, die an die Implementierung Ihres ViewModel gebunden ist:

    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 zugeordneten Ereignishandler nur von 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 schließlich die Befehlsimplementierung von 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 sich sogar um einen RelayCommand handeln), während über den RoutedCommand eine beliebige Anzahl von Views mit ihr verbunden werden können, ohne dass eine direkte Bindung an diese erforderlich ist ViewModel.

Leider besteht ein Nachteil darin, dass das ICommand.CanExecuteChanged-Ereignis nicht funktioniert. Wenn Ihr ViewModel möchte, dass View die CanExecute-Eigenschaft aktualisiert, müssen Sie CommandManager.InvalidateRequerySuggested () aufrufen.

14
RogerN