MVVM Routed and Relay Command

Qual è la differenza tra RoutedCommand e RelayCommand ? Quando utilizzare RoutedCommand e quando utilizzare RelayCommand nel pattern MVVM?

RoutedCommand fa parte di WPF, mentre RelayCommand è stato creato da un discepolo WPF, Josh Smith;).

Seriamente, però, 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 MV-VM un RelayCommand (DelegateCommand in Prism) è probabilmente la scelta migliore in assoluto.

Per quanto riguarda l’uso di RelayCommand e RoutedCommand in MVVM, la principale differenza per me è la seguente:

Posizione del codice

RelayCommand consente di implementare il comando in qualsiasi class (come proprietà ICommand con delegati), che quindi è convenzionalmente in databound al controllo, che richiama il comando. Questa class è il ViewModel . Se si utilizza un comando instradato, sarà necessario implementare i metodi relativi al comando nel codebehind 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.

Che cosa ha detto RS Conley, che RelayCommand ti permette di definire RelayCommand al di fuori di ViewModel è giusto, ma prima di tutto ti permette di definirlo all’interno del ViewModel, che RoutedCommand non ha.

Routing

D’altra parte, RelayCommands non supporta il routing attraverso l’albero (come detto prima), che non è un problema, purché l’interfaccia sia 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 sumto, direi che i RoutedCommands standard non sono utilizzabili in MVVM. RelayCommands sono perfetti per MVVM ma non supportano il routing, che potrebbe essere necessario.

La differenza è che RelayCommand può accettare delegati. È ansible definire RelayCommand al di fuori di ViewModel. ViewModel può quindi aggiungere delegati al comando quando crea e associa il comando a un object 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 consiste nel definire un comando Routed come class nidificata all’interno di ViewModel. La funzionalità dei due è altrimenti simile.

Direi che i RoutedCommands sono perfettamente legali nel rigoroso MVVM. Sebbene RelayCommands siano spesso preferibili per la loro semplicità, i RoutedCommands offrono talvolta vantaggi organizzativi. Ad esempio, è ansible 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, ricorda 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 utilizzare un RoutedCommand all’interno di un framework MVVM rigido, è ansible seguire questi passaggi:

  1. Dichiarare un’istanza RoutedCommand statica per il comando personalizzato. È ansible saltare questo passaggio se si prevede di utilizzare un comando predefinito dalla class ApplicationCommands. Per esempio:

    public static class MyCommands { public static RoutedCommand MyCustomCommand = new RoutedCommand(); } 
  2. Albind le viste desiderate a RoutedCommand usando XAML:

      
  3. Uno dei tuoi punti di vista che è associato a un ViewModel adatto (cioè qualsiasi ViewModel implementa la funzionalità di comando) deve esporre una DependencyProperty personalizzata che sarà associata all’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); } } 
  4. La stessa vista dovrebbe collegarsi a RoutedCommand dal passo 1. In XAML:

        

    Nel code-behind per la tua vista i gestori di eventi associati delegheranno semplicemente a ICommand dalla proprietà di dipendenza dichiarata nel passaggio 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. Infine, associa l’implementazione del comando ViewModel (che dovrebbe essere un ICommand) alla proprietà di dipendenza personalizzata in XAML:

      

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 qualsiasi numero di Views può collegarsi ad esso tramite RoutedCommand senza bisogno di essere direttamente associato a quel ViewModel.

Sfortunatamente c’è un lato negativo nel fatto che l’evento ICommand.CanExecuteChanged non funzionerà. Quando ViewModel richiede alla vista di aggiornare la proprietà CanExecute, è necessario chiamare CommandManager.InvalidateRequerySuggested ().