Confessions of a Dangerous Mind

Brain.FlushBuffer()
posts - 176, comments - 234, trackbacks - 93

WPF Prism Technical Concepts: Commands

I comandi sono fondamentali nella realizzazione di applicazioni interattive. In ultima analisi, essi rappresentano le azioni che possono essere intraprese dall’utente: Salva, Cancella, Salva tutto, Successivo, Precedente etc. In Prism, e più in generale nelle applicazioni implementate con pattern MVVM, è importante poter effettuare il binding dei comandi in modo “lasco”, ovvero senza dover utilizzare handler nel code behind della view e facendo in modo di non propagare il comando all’interno del command tree del controllo. In questo modo è possibile indirizzare l’esecuzione di un comando direttamente ad un metodo contenuto nel ViewModel, cosa che impieganto i classici RoutedEvents non è possibile.

Prism offre due classi per implementare questa funzionalità: DelegateCommand e CompositeCommand, entrambe implementanti l’interfaccia ICommand. Vediamo in che situazione possono essere usati questi comandi e come devono essere collegati all’interfaccia che li scatena.

DelegateCommand<T>

La classe DelegateCommand<T> consente di creare un comando che può essere collegato direttamente all’interfaccia e delega l’esecuzione dello stesso ad un metodo presente nel ViewModel (in caso di MVVM) o nel Presenter (nel caso di pattern MVP). Per creare un delegate command bisogna fornire due delegates: uno andrà a gestire l’esecuzione vera e propria del comando, mentre l’altro verrà impiegato per ritornare all’interfaccia il valore CanExecute, ovvero quando il comando può essere eseguito. Vediamo nel ViewModel come un comando di questo tipo debba essere dichiarato ed utilizzato:

   1: //ViewModel
   2:  
   3: private DelegateCommand<string> _sendMessageCommand;
   4:  
   5: public DelegateCommand<string> SendMessageCommand
   6: {
   7:     get 
   8:     { 
   9:         if (_sendMessageCommand == null)
  10:             //Create Delegate Command with a string parameter
  11:             _sendMessageCommand = new DelegateCommand<string>(OnSendMessageCommandExecute, OnSendMessageCommandCanExecute);
  12:         return _sendMessageCommand;
  13:     }
  14: }
  15:        
  16: void OnSendMessageCommandExecute(string parameter)
  17: {
  18:     //Execute command logic
  19: }
  20:  
  21: bool OnSendMessageCommandCanExecute(string parameter)
  22: {
  23:     //Evaluate conditions and return if related command can or cannot be executed
  24:     return true;
  25: }

In pratica abbiamo definito un comando di tipo DelegateCommand che accetta un parametro di tipo stringa e contestualmente abbiamo definito i due metodi che vanno a gestire l’esecuzione del comando stesso e la possibilità o meno di eseguirlo. Nella View, ovvero nello XAML, il binding al comando appena definito avviene come segue:

   1: <UserControl x:Class="SimpleModule1.Views.OrdersView"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   4:     <StackPanel>
   5:         <Button Command="{Binding SendMessageCommand}" CommandParameter="Hello!">Click me!</Button>
   6:     </StackPanel>
   7: </UserControl>

Ovviamente sarà necessario impostare il DataContext della View ad un istanza del ViewModel definito sopra per far funzionare il tutto. Il pregio di tale implementazione è presto detto: esiste un unico punto dove è presente l’implementazione di una certa azione ed è quindi facilmente testabile l’azione stessa, senza dover per forza intervenire tramite l’interfaccia. Inoltre, è possibile collegare lo stesso comando a diversi controlli, per cui si riducono i rischi di generazione di codice duplicato.

CompositeCommand

Un composite command rappresenta una raccolta di DelegateCommands. Solitamente, un comando di questo tipo viene impiegato in contesti di azioni di tipo “Salva tutto”, “Chiudi tutto” o comunque azioni che necessitino di invocare più comandi in sequenza. L’utilizzo del CompositeCommand è identico a quello del singolo Delegate Command, con l’unica differenza che i singoli DelegateCommands devono essere registrati nel comando composito tramite il metodo RegisterCommand, come si può vedere nel seguente snippet:

   1: private CompositeCommand _saveAllCommand = new CompositeCommand();
   2:  
   3: _saveAllCommand.RegisterCommand(_saveOrdersCommand);
   4: _saveAllCommand.RegisterCommand(_saveCustomersCommand);

Una limitazione del composite command è data dall’impossibilità di controllare l’ordine di esecuzione dei comandi. Va comunque detto, però, che non è a questo livello che si deve implementare una “pseudo-pipeline” di comandi, in quanto i comandi stessi dovrebbero essere atomici e non dovrebbero dipendere dall’ordine di esecuzione. Se comunque fosse necessario, è possibile derivare una classe OrderedCompositeCommand dalla classe CompositeCommand che esegua i comandi secondo un ordine stabilito.

L’argomento dei comandi non finisce di certo qui, ma ci sono molti altri aspetti che andrebbero approfonditi. Sono comunque argomenti piuttosto specifici, che vanno trattati con un esempio per essere compresi a fondo.Concludendo, possiamo affermare che Prism mette a disposizione un “nuovo” tipo di comando, che travalica il comune concetto di “Event Handler” e consente di spingere le applicazioni sviluppate verso una maggiore testabilità ed un più rigoroso stile di realizzazione. Per approfondimenti, come sempre, vi invito a consultare la documentazione ed il codice di Prism che trovate a questo indirizzo.

Print | posted on venerdì 18 settembre 2009 19:24 |

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET