Finalmente ci siamo, dopo anni luce dall’ultima volta, siamo all’ultimo step della nostra parte introduttiva su Model View ViewModel. Il bello inzia adesso :-)
L’ultimo requisito che abbiamo, per questa parte, è la possibilità di cancellare un indirizzo dalla lista degli indirizzi della persona corrente.
Commanding
Prima di addentrarci nei tecnicismi dell’implementazione introduciamo una delle “novità”, alla faccia della novità… 6 anni sono passati, di Wpf, l’accoppiata ICommand/ICommandSource.
ICommand
Un command è una classe che rappresenta un’azione, concetto semplicissimo per chi arriva da Delphi in cui esistono proprio le Action. Per Wpf un command è una qualsiasi classe che implementa l’interfaccia ICommand che è caratterizzata da:
  • Boolean CanExecute( Object ): l’infrastruttura di Wpf chiama CanExecute per sapere se in quel determinato momento il comando debba essere considerato “disponibile”;
  • void Execute( Object ): Execute viene chiamato per effettivamente invocare il comando e permettergli di eseguire l’azione per cui è pensato, Execute verrà invocato se e solo se CanExecute ritorna true;
  • EventHanlder CanExecuteChanged: Un comando può sollevare questo evento per notificare al motore di Wpf che il suo stato è cambiato ed è quindi il caso che Wpf venga a controllare nuovamente con una chiamata a CanExecute;
Un esempio tipico potrebbe essere un ipotetico SaveCommand che a seguito di un chiamata a CanExecute ritorna true se il grafo è effettivamente modificato, lo stesso command può notificare a Wpf che il suo stato è cambiato quando lo stato del grafo cambia ed infine il command si preoccuperà di innescare la logica per il salvataggio all’atto dell’execute.
n.d.r.: CanExecute ed Execute prendono in ingresso un parametro di tipo object che possiamo usare sulla falsa riga di un command argument che potrebbe essere utile per pilotare dall’esterno parte della logica interna de command.
ICommandSource
La controparte di un ICommand è ICommandSource. Una classe che implementa ICommandSource è qualcosa, tipicamente un controllo della UI, che è in grado di invocare un comando. ICommandSource è così definita:
  • ICommand Command{ get; set; }: è la dependency property, quindi con pieno supporto per data binding, che ci consente di impostare quale sia il comando che vogliamo venga eseguito;
  • Object CommandParameter{ get; set; }: è la dependency property che ci consente di impostare l’eventuale parametro da passare al comando;
  • IInputElement CommandTarget{ get; set; }: Ha senso, e viene utilizzata, solo ed esclusivamente se il comando è un RoutedCommand, tipologia di comando che non ci interessa trattare;
Anche in questo caso un esempio tipico è un bottone, quando impostiamo la proprietà command la prima cosa che succede è che il bottone fa una chiamata a CanExecute per capire lo stato del comando e nel caso in cui l’esito sia negativo automaticamente si disabilita. Allo stesso modo quando il comando scatena l’evento CanExecuteChanged il bottone fa nuovamente una chiamata a CanExecute per rivalutare il suo stato e in caso affermativo si riabilita.
Ogni controllo è libero di implementare a suo piacimento la logica e il comportamento da seguire a seguito della variazione di stato di un comando, come abbiamo detto, ad esempio, il bottone si disabilita.
DelegateCommand
Wpf mette a disposizione un largo set di command già pronti all’uso, questi sono tutti di tipo RoutedCommand che, in un mondo basato su Model View ViewModel, ha una serie di difetti: in primis quello di non avere nessuna possibilità di controllo sul processo di notifica della variazione di stato del comando. I limiti di RoutedCommand e di RoutedUICommand hanno portato alla nascita di svariate implementazioni dell’interfaccia ICommand, implementazioni che tipicamente cadono sotto il nome di DelegateCommand o RelayCommand.
Il tutto si “limita” ad un’implementazione più o meno stilisticamente bella che consente di generare un comando agganciando al volo dei delegati, tipicamente facendo largo uso di una sintassi basata sulle lambda, che verranno eseguiti sia per determnare lo stato del comando (CanExecute) sia per eseguire il comando (Execute); inoltre tutti espongono un meccamismo per poter triggherare la notifica della varizione di stato del comando stesso.
Anche Radical non è da meno e offre un’interessante implementazione.
AddressDeleteCommand
Adesso che sappiamo un po’ meglio la teoria non ci resta che metterla in pratica. La prima cosa che ci interessa fare è esporre la funzionalità dal nostro MainViewModel, semplicemente esponendo una proprietà di tipo ICommand:
public ICommand DeleteAddressCommand
{
	get;
	private set;
}
contestualmente agganciando, via DataBinding, la nuova proprietà ad un nuovo elemento della UI:
image
<Button Content="Delete" 
        Command="{Binding Path=DeleteAddressCommand}" 
        Margin="0,0,317,0" />
l’implementazione del nostro comando è decisamente semplice anche se dietro le quinte succedono parecchie cose:
this.DeleteAddressCommand = DelegateCommand.Create()
	.OnCanExecute( o => this.SelectedAddress != null )
	.OnExecute( o =>
	{
		var add = this.SelectedAddress;
		this.SelectedAddress = null;
		this.Person.Addresses.Remove( add );
	} )
	.AddMonitor
	( 
		PropertyObserver.For( this )
			.Observe( vm => vm.SelectedAddress ) 
	);
Radical offre un entry point statico, e anche un costruttore volendo, per la creazione di un nuovo comando. La cosa comoda è il supporto per le fluent interface che vi permette di definire le caratteristiche del comando in maniera decisamente intuitiva:
  • OnCanExecute si aspetta un Func il cui scopo è quello di determinare l’eseguibilità del comando, nel nostro esempio il DeleteCommand è disponibile se c’è un indirizzo selezionato;
  • OnExecute accetta invece come parametro una Action che rappresenta il delegato che deve essere eseguito all’atto dell’esecuzione del comando;

La parte veramente interessante, di cui ho già parlato (sintassi a parte che è leggermente cambiata), è AddMonitor che vi permette di agganciare dei “trigger” al command, trigger che altro non fanno che automatizzare il processo di notifica, alla UI, della variazione di stato del comando stesso.

Fatto tutto ciò se mandiamo in esecuzione la nostra bellissima applicazione (bellissima è auto-consolatorio Smile) quello che otteniamo è questo:

imageimageimage

  1. All’avvio il bottone “Delete” è disabilitato perchè non c’è nessun indirizzo selezionato;
  2. Alla selezione di un indirizzo il bottone si abilita:
    1. quando selezioniamo un indirizzo il motore di data binding fa il set della proprietà SelectedAddress;
    2. il MainViewModel notifica, via INotifyPropertyChanged, che la proprietà è cambiata;
    3. il PropertyObserver (IMonitor) agganciato al comando se ne accorge e notifica al comando che qualcosa è cambiato (poco importa cosa);
    4. Il comando notifica (via CanExecuteChanged) che il suo stato potrebbe essere cambiato;
    5. il motore di commanding di Wpf se ne accorge e chiede (CanExecute) al comando il suo nuovo stato;
    6. in base alla risposta il pulsante, nel nostro esempio, si attiva o disattiva;
  3. A seguito della pressione del bottone viene eseguito il comando che:
    1. rimuove l’indirizzo dalla lista in binding con la ListView;
    2. resetta lo stato di SelectedAddress causando la rivalutazione del comando…;

Come tradizione allego i sorgenti: Mvvm.Application 1.3

Il prossimo requisito? essere in grado di creare una nuova “Person” con la sua simpatica lista di “Address”.

.m