web-dev-qa-db-fra.com

Commande routée et relais MVVM

Quelle est la différence entre RoutedCommand et RelayCommand ? Quand utiliser RoutedCommand et quand utiliser RelayCommand dans le modèle MVVM?

75
user72799

RoutedCommand fait partie de WPF, tandis que RelayCommand a été créé par un disciple de WPF, Josh Smith;).

Sérieusement, cependant, RS Conley a décrit certaines des différences. La principale différence est que RoutedCommand est une implémentation ICommand qui utilise un RoutedEvent pour router à travers l'arborescence jusqu'à ce qu'un CommandBinding pour la commande soit trouvé, tandis que RelayCommand n'effectue aucun routage et exécute à la place directement un délégué. Dans un scénario M-V-VM, une RelayCommand (DelegateCommand in Prism) est probablement le meilleur choix tout autour.

68
wekempf

En ce qui concerne l'utilisation de RelayCommand et RoutedCommand dans MVVM, la principale différence pour moi est la suivante:

Emplacement du code

RelayCommand vous permet d'implémenter la commande dans n'importe quelle classe (en tant que propriété ICommand avec des délégués), qui est alors classiquement liée aux données du contrôle, qui appelle la commande. Cette classe est le ViewModel . Si vous utilisez une commande routée, vous devrez implémenter les méthodes liées à la commande dans le codebehind du contrôle, car les méthodes sont spécifiées par le les attributs de l'élément CommandBinding. En supposant que MVVM strict signifie avoir un code-fichier "vide", il n'y a en fait aucune possibilité d'utiliser des commandes routées standard avec MVVM.

Ce que RS Conley a dit, que RelayCommand vous permet de définir le RelayCommand en dehors du ViewModel est juste, mais d'abord il vous permet de le définir à l'intérieur du ViewModel , ce que RoutedCommand ne fait pas.

Routage

En revanche, RelayCommands ne prend pas en charge le routage à travers l'arborescence (comme indiqué précédemment), ce qui n'est pas un problème, tant que votre interface est basée sur un seul viewModel. Si ce n'est pas le cas, par exemple si vous avez une collection d'éléments avec leurs propres viewModels et que vous souhaitez appeler une commande du ViewModel enfant pour chaque élément de l'élément parent à la fois, vous devrez utiliser le routage (voir aussi CompositeCommands) .

Dans l'ensemble, je dirais que les commandes routées standard ne sont pas utilisables dans MVVM strict. RelayCommands sont parfaits pour MVVM mais ne prennent pas en charge le routage, dont vous pourriez avoir besoin.

34
Marc

La différence est que RelayCommand peut accepter des délégués. Vous pouvez définir le RelayCommand en dehors du ViewModel. Le ViewModel peut ensuite ajouter des délégués à la commande lorsqu'il crée et lie la commande à un objet d'interface utilisateur comme un contrôle. Les délégués peuvent à leur tour accéder à la variable privée du ViewModel telle qu'elle est définie dans la portée du modèle de vue lui-même.

Il est utilisé pour réduire la quantité de code contenu dans le ViewModel car la tendance est de définir une commande Routed en tant que classe imbriquée dans le ViewModel. La fonctionnalité des deux est par ailleurs similaire.

22
RS Conley

Je dirais que RoutedCommands sont parfaitement légaux dans MVVM strict. Bien que RelayCommands soient souvent préférables pour leur simplicité, RoutedCommands offre parfois des avantages organisationnels. Par exemple, vous pouvez souhaiter que plusieurs vues différentes se connectent à une instance ICommand partagée sans exposer directement cette commande aux ViewModels sous-jacents.

En remarque, n'oubliez pas que MVVM strict n'interdit pas l'utilisation de code-behind. Si cela était vrai, vous ne pourriez jamais définir de propriétés de dépendance personnalisées dans vos vues!

Afin d'utiliser un RoutedCommand dans un cadre MVVM strict, vous pouvez suivre ces étapes:

  1. Déclarez une instance de RoutedCommand statique pour votre commande personnalisée. Vous pouvez ignorer cette étape si vous prévoyez d'utiliser une commande prédéfinie de la classe ApplicationCommands. Par exemple:

    public static class MyCommands {
        public static RoutedCommand MyCustomCommand = new RoutedCommand();
    }
    
  2. Attachez les vues souhaitées à RoutedCommand à l'aide de XAML:

    <Button Command="{x:Static local:MyCommands.MyCustomCommand}" />
    
  3. L'une de vos vues qui est liée à un ViewModel approprié (c'est-à-dire quel que soit le ViewModel qui implémente la fonctionnalité de commande) doit exposer une DependencyProperty personnalisée qui sera liée à l'implémentation de votre ViewModel:

    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. La même vue doit se lier à RoutedCommand à partir de l'étape 1. Dans le XAML:

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

    Dans le code-behind de votre vue, les gestionnaires d'événements associés délégueront simplement à l'ICommand à partir de la propriété de dépendance déclarée à l'étape 3:

    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. Enfin, liez l'implémentation de la commande de votre ViewModel (qui devrait être un ICommand) à la propriété de dépendance personnalisée dans XAML:

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

L'avantage de cette approche est que votre ViewModel n'a besoin que de fournir une seule implémentation de l'interface ICommand (et il peut même s'agir d'une RelayCommand), tandis qu'un nombre illimité de vues peuvent s'y attacher via RoutedCommand sans avoir besoin d'être directement lié à celle-ci ViewModel.

Malheureusement, il y a un inconvénient dans la mesure où l'événement ICommand.CanExecuteChanged ne fonctionnera pas. Lorsque votre ViewModel souhaite que la vue actualise la propriété CanExecute, vous devez appeler CommandManager.InvalidateRequerySuggested ().

14
RogerN