Qual è la differenza tra RoutedCommand e RelayCommand? Quando utilizzare RoutedCommand e quando utilizzare RelayCommand nel pattern MVVM?MVVM Routed and Relay Command
risposta
La differenza è che RelayCommand può accettare delegati. È possibile definire RelayCommand al di fuori di ViewModel. ViewModel può quindi aggiungere delegati al comando quando crea e associa il comando a un oggetto UI come un controllo. I delegati a loro volta possono accedere alla variabile privata di ViewModel così come sono definiti nell'ambito del modello di vista stesso.
Viene utilizzato per ridurre la quantità di codice contenuta nel ViewModel poiché la tendenza è definire un comando indirizzato come classe nidificata all'interno di ViewModel. La funzionalità dei due è altrimenti simile.
RoutedCommand è parte di WPF, mentre RelayCommand è stato creato da un discepolo WPF, Josh Smith;).
Seriamente, RS Conley ha descritto alcune delle differenze. La differenza principale è che RoutedCommand è un'implementazione ICommand che utilizza un RoutedEvent per il routing attraverso l'albero finché non viene trovato un CommandBinding per il comando, mentre RelayCommand non esegue il routing e invece esegue direttamente alcuni delegati. In uno scenario M-V-VM un RelayCommand (DelegateCommand in Prism) è probabilmente la scelta migliore in assoluto.
Per quanto riguarda l'uso di RelayCommand e RoutedCommand in MVVM la differenza principale per me è la seguente:
Location di codice
RelayCommand consente di implementare il comando a qualsiasi classe (come ICommand-proprietà con delegati), che quindi è convenzionalmente in databound al controllo, che invoca il comando. Questa classe è il ViewModel. Se si utilizza un comando instradato, sarà necessario implementare i metodi relativi al comando nel codice dietro il del controllo, poiché i metodi sono specificati dagli attributi dell'elemento CommandBinding. Supposto che MVVM rigido significhi avere un file di codice "vuoto", non esiste in realtà alcuna possibilità di utilizzare i comandi di instradamento standard con MVVM.
Cosa RS Conley ha detto, che RelayCommand consente di definire il RelayCommand al di fuori del ViewModel è giusto, ma prima di tutto permette di definire lo all'interno ViewModel, che RoutedCommand non lo fa.
Routing
D'altra parte, RelayCommands non supportano il routing attraverso l'albero (come detto prima), che non è un problema, a patto che l'interfaccia è basata su un singolo ViewModel. In caso contrario, ad esempio se si dispone di una raccolta di elementi con i propri viewModels e si desidera richiamare un comando del child ViewModel per ciascun elemento all'esterno dell'elemento padre in una volta, sarà necessario utilizzare il routing (vedere anche CompositeCommands) .
Tutto sommato, direi che i RoutedCommands standard non sono utilizzabili in MVVM rigido. RelayCommands sono perfetti per MVVM ma non supportano il routing, che potrebbe essere necessario.
Direi che RoutedCommands è perfettamente legale in MVVM rigoroso. Sebbene RelayCommands siano spesso preferibili per la loro semplicità, i RoutedCommands offrono talvolta vantaggi organizzativi.Ad esempio, è possibile che si desideri che diverse viste si connettano a un'istanza di ICommand condivisa senza esporre direttamente tale comando ai ViewModels sottostanti.
Come nota a margine, ricordare che MVVM rigoroso non proibisce l'uso di code-behind. Se fosse vero allora non potresti mai definire proprietà di dipendenza personalizzate nelle tue viste!
Per poter utilizzare un RoutedCommand all'interno di un quadro rigoroso MVVM si potrebbe procedere come segue:
Dichiarare un'istanza RoutedCommand statico per il comando personalizzato. È possibile saltare questo passaggio se si prevede di utilizzare un comando predefinito dalla classe ApplicationCommands. Ad esempio:
public static class MyCommands { public static RoutedCommand MyCustomCommand = new RoutedCommand(); }
Fissare le viste desiderate al RoutedCommand utilizzando XAML:
<Button Command="{x:Static local:MyCommands.MyCustomCommand}" />
una delle viste che è legato ad una opportuna ViewModel (cioè qualunque ViewModel implementa la funzionalità di comando) esigenze per esporre un DependencyProperty personalizzato che sarà legato al vostro implementazione di 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); } }
Th e stessa vista si dovrebbe legarsi alla RoutedCommand dal punto 1. In XAML:
<UserControl.CommandBindings> <CommandBinding Command="{x:Static local:MyCommands.MyCustomCommand}" CanExecute="MyCustomCommand_CanExecute" Executed="MyCustomCommand_Executed" /> </UserControl.CommandBindings>
Nel code-behind per la visualizzazione dei gestori di eventi associati sarà solo delegare al ICommand dalla proprietà di dipendenza dichiarato nella fase 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); } }
Infine, vincolare il proprio comando di implementazione ViewModel (che dovrebbe essere un ICommand) per la proprietà di dipendenza personalizzata in XAML:
<local:MainView DataContext="{Binding MainViewModel}" MyCustomCommand="{Binding CustomCommand}" />
Il vantaggio di questo approccio è che il tuo ViewModel deve solo fornire una singola implementazione dell'interfaccia ICommand (e può anche essere un RelayCommand), mentre un numero qualsiasi di Views può collegarsi ad esso tramite RoutedCommand senza bisogno di essere direttamente associato a quel ViewModel.
Sfortunatamente, l'evento ICommand.CanExecuteChanged non funziona. Quando ViewModel richiede alla vista di aggiornare la proprietà CanExecute, è necessario chiamare CommandManager.InvalidateRequerySuggested().
Grazie per la profondità in più nella spiegazione e il riferimento a CompositeCommands, mi ha aiutato a vedere dove si inseriscono. –