WPF MVVM Toolkit

93
UNIVERSITA' DEGLI STUDI DI TRIESTE FACOLTA' DI INGEGNERIA CORSO DI LAUREA IN INGEGNERIA INFORMATICA Separazione dei ruoli tra Designer e Developer nello sviluppo di applicazioni Desktop: uso di WPF e del pattern Model-View-ViewModel Laureando: Relatore: Alessandro Andreose' Chiar.mo Prof. Maurizio Fermeglia Anno Accademico 2008 2009

description

Tesi di laurea: Separazione dei ruoli tra Designer e Developer nello sviluppo di applicazioni Desktop: uso di WPF e del pattern Model-View-ViewModel

Transcript of WPF MVVM Toolkit

Page 1: WPF MVVM Toolkit

UNIVERSITA' DEGLI STUDI DI TRIESTE

FACOLTA' DI INGEGNERIA CORSO DI LAUREA IN INGEGNERIA INFORMATICA

Separazione dei ruoli tra Designer e Developer nello

sviluppo di applicazioni Desktop: uso di WPF e del

pattern Model-View-ViewModel

Laureando: Relatore: Alessandro Andreose' Chiar.mo Prof. Maurizio Fermeglia

Anno Accademico 2008 – 2009

Page 2: WPF MVVM Toolkit

2

Sommario

Introduzione ............................................................................................................ 6

Un po’ di storia .................................................................................................... 7

Capitolo uno ............................................................................................................ 8

Windows Presentation Foundation .................................................................... 8

Perché WPF ..................................................................................................... 9

XAML ............................................................................................................. 10

Layout e controlli .......................................................................................... 11

Logical Tree e Visual Tree ............................................................................. 11

Command ...................................................................................................... 13

Routed Event ................................................................................................. 14

Dependency Property ................................................................................... 14

Attached Property (o Attached Behaviour) .................................................. 15

Object Resources e Resource Dictionary ...................................................... 15

Data Binding .................................................................................................. 16

Data Template ............................................................................................... 18

Separazione dei ruoli .................................................................................... 19

Model – View – ViewModel .............................................................................. 20

Model ............................................................................................................ 21

ViewModel .................................................................................................... 21

View .............................................................................................................. 22

Separazione dei ruoli .................................................................................... 23

Capitolo due: Il toolkit .......................................................................................... 24

INotifyPropertyChanged ................................................................................... 24

Page 3: WPF MVVM Toolkit

3

Delegate Command .......................................................................................... 27

Scrivere il codice del comando nel ViewModel ............................................ 29

Sapere quando un comando può essere eseguito ....................................... 30

Aggiungere degli shortcut ............................................................................. 31

Associare il comando al controllo nel codice XAML e minimizzare la scrittura

di codice ripetitivo ........................................................................................ 32

Come si usa ................................................................................................... 36

IMessageBroker ................................................................................................ 36

UI Composition ................................................................................................. 48

IRegion .......................................................................................................... 49

IModule ......................................................................................................... 54

ViewModel .................................................................................................... 57

Associare una region al ViewModel ............................................................. 58

Application Controller ................................................................................... 60

Capitolo tre: L’applicazione .................................................................................. 63

Obiettivo dell'applicazione ............................................................................... 63

Architettura hardware e software dell'ambiente di produzione ..................... 63

Requisiti tecnici non funzionali ......................................................................... 64

Architettura dell'applicazione ........................................................................... 64

Application Server / Web Server .................................................................. 66

Client ............................................................................................................. 68

Attori ................................................................................................................. 69

Struttura dell'applicazione ................................................................................ 71

Alcune user story .............................................................................................. 72

Page 4: WPF MVVM Toolkit

4

Conclusioni ............................................................................................................ 80

Appendice uno: come scrivere un'applicazione WPF ........................................... 82

XAML Conventions ............................................................................................ 82

Utilizzare x:Name al posto di Name ............................................................ 82

Posizionare il primo attributo di un elemento nella riga sotto il nome

dell'elemento ................................................................................................ 82

Preferire StaticResource a DynamicResource .............................................. 83

Naming Convention per gli elementi ............................................................ 84

Unire le risorse a livello di Applicazione ....................................................... 84

Organizzazione delle risorse ............................................................................. 84

Strutturare le risorse ..................................................................................... 84

Naming Convention ...................................................................................... 86

Appendice due: come disegnare una Window in WPF ........................................ 87

Grid .................................................................................................................... 88

Bibliografia ............................................................................................................ 92

Web ................................................................................................................... 93

Page 5: WPF MVVM Toolkit

5

Page 6: WPF MVVM Toolkit

6

Introduzione

L’obiettivo primario di questa tesi è lo sviluppo di un toolkit per scrivere

applicazioni desktop in WPF applicando il pattern Model-View-ViewModel

(MVVM). Questo toolkit permette sia di non dover ogni volta riscrivere quelle

parti di codice che naturalmente si ripetono in ogni progetto, sia di avere del

codice testato, aggiornato e facile da manutenere.

Lo sviluppo di applicazioni senza l’utilizzo di pattern come Model View Controller

(MVC)1, Presentation Model, Model-View-ViewModel e derivati soffre di tre

gravi problemi:

Non c’è separazione dei ruoli tra Designer e Developer.

Non c’è una gestione dello stato dei controlli.

È molto difficile testare le funzionalità legate alla user interface.

Tutti questi pattern cercano di risolvere questi tre problemi, però hanno un

costo: si deve scrivere molto più codice che poi deve essere manutenuto.

Inoltre, l’utente di oggi si aspetta di utilizzare applicazioni che abbiano

un’interfaccia utente accattivante e user-friendly. Ora più che mai c’è bisogno

che figure diverse partecipino alla realizzazione dell’intero progetto software.

Per quanto riguarda lo sviluppo della interfaccia utente (user interface, UI) sono

due i ruoli fondamentali: il developer e il designer. Il primo è in grado di creare

tutta la logica, il secondo invece deve curare l'aspetto grafico.

Il problema principale di questo approccio è che developer e designer devono

lavorare assieme, ma non devono, per quanto possibile, intralciarsi a vicenda.

Negli anni sono state sviluppate varie tecniche, sia architetturali, che

tecnologiche. Nel mondo web ad esempio la tecnologia dei Cascading Style Sheet

1 http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

Page 7: WPF MVVM Toolkit

7

(CSS) aiuta a suddividere i compiti delle due figure professionali. Dal punto di

vista architetturale esistono modelli di riferimento per la scrittura del codice

(pattern) come il Model View Controller, che separa il codice di visualizzazione

(View) dalle entità di business (Model): per comunicare hanno bisogno di un

Controller. Ed è proprio in quest’ultimo che viene inserita la maggior parte della

logica. La view ha poco codice (sperabilmente nessuno) e la maggior parte del

lavoro lo deve fare il designer.

Quando si sviluppano applicazioni con WPF, non è obbligatorio implementare il

pattern MVVM e gli altri principi che saranno spiegati in questa tesi. Tutti questi

principi, però, definiscono una metodologia di sviluppo che aiuta a separare il

ruolo del developer da quello del designer. Il toolkit, insomma, è la

formalizzazione di una metodologia che il developer applicherà ripetutamente

nelle sue applicazioni desktop con WPF.

Un po’ di storia

La separazione tra l’interfaccia utente (view) e il modello di business (model) non

è una nuova idea dello sviluppo software: ha circa trenta anni2. Recentemente è

rinato l’interesse per le architetture view-model, soprattutto a causa della

crescita della complessità dei sistemi software moderni e della necessità di

visualizzare un prodotto software su diverse interfacce utente (desktop, web, …)

senza per questo motivo dover riscrivere l’intera applicazione.

Il pattern Model-View-ViewModel è una variazione del pattern Model View

Controller, nato verso la fine degli anni settanta come framework sviluppato in

Smalltalk, e presentato come pattern da Martin Fowler nel suo libro Patterns of

Enterprise Application Architecture3.

2 http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller#History

3 http://martinfowler.com/books.html#eaa

Page 8: WPF MVVM Toolkit

8

Il pattern MVVM è stato presentato per la prima volta al pubblico nell’ottobre

del 2005 in un post4 sul blog di John Gossman, ed è stato utilizzato per la prima

volta in Microsoft per sviluppare Expression Blend, primo software Microsoft a

essere sviluppato interamente in Windows Presentation Foundation (WPF) ed

orientato alla figura del designer.

Capitolo uno

Windows Presentation Foundation

Niente è più importante della user experience di un'applicazione. Mentre molti

professionisti sono più interessati a come lavora un'applicazione, gli utenti che

utilizzano il software si preoccupano dell'interfaccia utente. L'interfaccia di

un'applicazione compone la stragrande maggioranza della user experience di un

software. Per molti utenti l'interfaccia e l'esperienza sono l'applicazione. Se si

produce una buona esperienza utente attraverso l'interfaccia, si aumenta la

produttività del software stesso.

Windows Presentation Foundation (WPF) è una parte del Framework .NET 3.0

uscito nel novembre 2006, aggiornato in seguito con il Framework 3.5 nel

novembre 2007 e con il Framework 3.5 SP1 nell’agosto 2008. WPF è la nuova

tecnologia Microsoft .NET per sviluppare l’interfaccia utente di un’applicazione

desktop ed è l’unica che viene aggiornata con l’aggiunta di nuove feature. Si

possono creare interfacce utente (UI) sia scrivendo codice managed, sia

utilizzando un linguaggio di markup XAML (eXtensible Application Markup

Language), una sintassi Xml per un approccio "dichiarativo", più da designer. È

possibile anche utilizzare una tecnica mista che è quella più comunemente

utilizzata. Per ogni window o user control di solito si hanno 2 file: un file .xaml

4 http://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx

Page 9: WPF MVVM Toolkit

9

che contiene il codice XAML e un file .xaml.cs (o .xaml.vb) che contiene il codice

associato (nel linguaggio scelto).

La prima parte di questo capitolo spiega le caratteristiche principali di WPF e

l’ausilio che WPF può dare per separare il lavoro del designer da quello dello

sviluppatore. La seconda parte del capitolo, invece, introduce il pattern Model –

View – ViewModel e come si collega a WPF.

Perché WPF

Lo scopo di Windows Presentation Foundation è di facilitare lo sviluppo

dell'interfac-cia utente, per migliorare la user experience. Utilizzando WPF

sviluppatori e designer possono creare interfacce che incorporino documenti,

video, immagini, grafica 2D e 3D, animazioni e molto altro.

Piattaforma unificata

Prima di WPF la creazione di interfacce utente seguendo i requisiti appena citati,

richiedeva l'utilizzo di molte tecnologie differenti. Questo di per sé non è un

problema.

Page 10: WPF MVVM Toolkit

10

Il problema è che molto spesso uno sviluppatore non conosce tutte queste

tecnologie e quindi non le usa. Questo comportamento ha come conseguenza

diretta una minore user experience che va tutta a danno dell'utente finale.

WPF unifica tutte queste aree in un'unica tecnologia. Gli sviluppatori possono

creare applicazioni accattivanti conoscendo a fondo una singola tecnologia.

XAML

XAML è un linguaggio di markup utilizzato per istanziare oggetti .NET.

Nonostante XAML sia una tecnologia che può essere applicata a differenti domini

di problemi, la sua principale applicazione è di costruire interfacce utente in

WPF: i documenti XAML definiscono la creazione e il posizionamento di pannelli,

bottoni e controlli che vivono nelle window di un’applicazione WPF.

Lo standard XAML si fonda sull'applicazione di alcune semplici regole di base:

Ogni elemento in un documento XAML mappa un’istanza di una classe

.NET. Il nome del tag di ogni elemento è identico al nome della classe.

Come per tutti i documenti XML, è possibile inserire un elemento

all’interno di un altro.

È possibile settare le proprietà di ogni classe attraverso gli attributi.

Comunque, in certe situazioni un attributo non è sufficientemente

potente. In questi casi è possibile inserire dei tag all’interno

dell’elemento con una sintassi speciale.

L'utilizzo di codice XAML per creare interfacce utente permette a developer e

designer di lavorare sullo stesso file utilizzando strumenti diversi. Questo porta

ad una maggiore interazione tra le due figure professionali.

Page 11: WPF MVVM Toolkit

11

Layout e controlli

Per organizzare le varie parti di un'interfaccia, WPF utilizza dei contenitori per il

layout. Ogni panel può contenere dei figli, inclusi dei controlli come bottoni e

blocchi di testo o altri panel. Tutto ciò, crea una struttura ad albero che ha come

padre la window che contiene un panel che a sua volta è il padre di tutti gli altri

controlli.

Logical Tree e Visual Tree

La struttura ad albero principale in WPF è la struttura ad albero dell'elemento. In

Windows Presentation Foundation, sono disponibili due modi di elaborazione e

concettualizzazione della struttura ad albero dell'elemento: come albero logico e

come struttura ad albero visuale. Le distinzioni tra albero logico e struttura ad

albero visuale non sono sempre necessariamente importanti. Tuttavia possono

talvolta causare problemi ad alcuni sottosistemi WPF e influire sulle scelte fatte

nel markup o nel codice.

Logical Tree

In WPF è possibile aggiungere contenuti agli elementi utilizzando le proprietà. Ad

esempio, è possibile aggiungere elementi a un controllo ListBox utilizzando la

proprietà Items. In questo modo, gli elementi vengono collocati nell'oggetto

ItemCollection del controllo ListBox. Per aggiungere elementi a un

Page 12: WPF MVVM Toolkit

12

oggetto DockPanel è necessario utilizzare la relativa proprietà Children. In

questo caso, gli elementi vengono aggiunti all'oggetto UIElementCol-

lection dell'oggetto DockPanel.

Grazie all'albero logico, i modelli di contenuto possono scorrere prontamente i

possibili elementi figlio e quindi essere estendibili. Inoltre, l'albero logico fornisce

un framework per alcune notifiche, ad esempio quando tutti gli elementi

dell'albero logico sono caricati.

Ancora, i riferimenti di risorsa5 vengono risolti cercando verso l'alto nell'albero

logico gli insiemi di risorse relativi all'elemento di richiesta iniziale e succes-

sivamente gli elementi padre.

Visual Tree

Nella struttura ad albero visuale viene descritta la struttura degli elementi visivi

rappresentati dalla classe base Visual. Un'esposizione della struttura ad albero

visuale come parte della programmazione di applicazioni WPF convenzionale è

necessario, poiché su di esso è implementato il meccanismo degli eventi,

5 Per avere maggiori informazioni sulle risorse, vedere il paragrafo Object Resources e Resource

Dictionary, presente in questo capitolo.

Page 13: WPF MVVM Toolkit

13

chiamato "routed events"6. Gli "eventi instradati" percorrono la struttura

dell'albero visuale e non dell'albero logico.

Command

In un'applicazione reale, le funzionalità sono divise in attività (task) di alto livello.

Questi task possono essere attivati da varie azioni differenti e da molti elementi

dell’interfaccia utente, inclusi i menù, i bottoni, le scorciatoie da tastiera e le

toolbar.

WPF permette di definire questi task, conosciuti come command, ed associare

ad essi i controlli, così non c’è bisogno di scrivere codice ripetitivo (code bloat)

quando si sottoscrive un evento. Ancora più importante, i comandi controllano lo

stato dell’interfaccia utente e automaticamente disabilitano i controlli a essi

collegati quando il task non è permesso.

L'esecuzione di comandi è un meccanismo di input di WPF che fornisce la

gestione di input a un livello semantico maggiore rispetto all'input del

dispositivo. Le operazioni Copia, Taglia e Incolla presenti in molte applicazioni

sono esempi di comandi.

La differenza tra i comandi e un semplice gestore eventi associato a un pulsante

o a un timer consiste nel fatto che i comandi separano la semantica e la creazio-

6 Per avere maggiori dettagli sui ruoted event, vedere il paragrafo Routed Event, presente in

questo capitolo.

Page 14: WPF MVVM Toolkit

14

ne di un'azione dalla relativa logica. In questo modo, più codici sorgente diversi

possono richiamare la stessa logica di comando che pertanto può essere

personalizzata per obiettivi differenti.

Routed Event

Ci sono tre tipi di routed event: direct, bubbling e tunneling. I Direct Event sono

eventi che possono essere intercettati solo dall'elemento che ha creato l'evento.

I Bubbling Event sono eventi che viaggiano verso l'alto attraverso il visual tree e

possono essere intercettati da ogni parent dell'elemento sorgente. Infine, i

Tunneling Event sono eventi che viaggiano verso il basso attraverso il visual tree

e possono essere intercettati da ogni child dell'elemento sorgente.

Dependency Property

Le Dependency Properties evolvono il concetto standard di proprietà in .NET e

sono caratteristiche di WPF. Utilizzano lo storage in maniera più efficiente e

supportano alcune feature di alto livello come per esempio la notifica quando

cambia il valore e la capacità di propagare i valori di default verso il basso per

tutto l’albero di elementi. Le dependency property sono alla base di molte

caratteristiche di WPF, come il data binding e gli stili.

public string MyProperty

{

get { return (string)GetValue(MyPropProperty); }

set { SetValue(MyPropProperty, value); }

}

public static readonly DependencyProperty

MyPropProperty =

DependencyProperty.Register("MyProp",

typeof(string),

typeof(MyClass), new

PropertyMetadata(""));

Dal codice si può osservare che una property, anziché memorizzare il valore in un

Page 15: WPF MVVM Toolkit

15

campo privato, definisce una variabile di tipo DependencyProperty. Per conven-

zione, la DependencyProperty termina con la parola "Property".

Il metodo Register() accetta una classe di tipo PropertyMetadata che,

fra le altre cose, setta il valore di default della property.

Attached Property (o Attached Behaviour)

Le attached properties sono delle dependency properties particolari che possono

essere agganciate ad un oggetto. La cosa importante è che non devono essere

definite nella classe alla quale devono essere aggiunte.

Le attached properties sono particolarmente utili per agganciare i command

anche agli oggetti WPF e agli eventi che non supportano nativamente i

Command. Per esempio la TextBox non accetta nativamente un comando per

l’evento KeyPress: tramite un’attached property si può aggiungere. Può essere

utile, anche se si utilizza una TextBox come campo di ricerca: è più comodo per

l’utente scrivere il testo e premere invio senza dover prima spostare il focus su

un bottone “Cerca”.

Un altro esempio potrebbe essere il doppio clic su una ListView: è utile per

esempio per visualizzare il dettaglio di un oggetto visualizzato in una lista. WPF

non lo supporta nativamente, ma tramite le attached property si può aggiungere

questa caratteristica.

Object Resources e Resource Dictionary

WPF introduce un nuovo sistema di risorse che si integrano strettamente con

XAML. Questo sistema permette di definire risorse in molti posti all’interno del

markup (in un controllo specifico, in una window o per tutta l’applicazione) e

riutilizzarle facilmente.

Le Object Resource hanno un numero importante di benefici:

Page 16: WPF MVVM Toolkit

16

Efficienza. Le risorse permettono di definire un oggetto una volta sola e

utilizzarlo in molti posti nel markup.

Manutenibilità. Le risorse permettono di avere pochi dettagli di

formattazione legati direttamente al controllo (come per esempio la

dimensione del font) e di spostarli in un luogo centrale dove è più facile

cambiarli. È l’equivalente XAML di creare costanti nel codice.

Adattabilità. Se certe informazioni sono separate dal resto

dell’applicazione e sono salvate in una resource section, è possibile

modificarle dinamicamente.

Ogni elemento include una proprietà Resources di tipo Dictionary, in cui è

possibile mettere le risorse. Questa collection può contenere qualsiasi tipo di

oggetto .NET. Ogni oggetto è indicizzato da una stringa.

Nonostante ogni oggetto ha una proprietà Resources, è più comune inserire le

risorse a livello di Window. Questo perché ogni oggetto figlio condivide le risorse

esposte dal padre.

Se si vogliono condividere le risorse, è possibile creare un Resource Dictionary.

Un resource dictionary è un documento XAML che fa da contenitore per le

risorse che si vogliono utilizzare.

La cosa importante delle risorse è che possono essere create e recuperate

direttamente tramite markup. Le risorse possono essere create direttamente nel

file xaml di una view, oppure in un file esterno. Per fare un paragone si possono

immaginare i file di risorse come un file CSS, nel quale sono salvati vari stili.

Data Binding

Il Data Binding è la possibilità di collegare un dato direttamente all’interfaccia

utente, senza doversi preoccupare di aggiornare la UI quando cambia il dato e

viceversa. In WPF non c’è limite a cosa sia un “dato”: può essere una stringa, un

Page 17: WPF MVVM Toolkit

17

numero, un oggetto di business più o meno complesso, un altro controllo utente,

eccetera. L’unico accorgimento è che si può fare il collegamento solo delle

proprietà e non dei campi pubblici.

<TextBlock Text="{Binding Person, Path=FirstName}" />

Il data binding in WPF può essere di vari tipi:

"OneWay" indica che l'interfaccia utente è aggiornata quando l'oggetto

cambia.

"OneWayToSource" è aggiornato l'oggetto quando l'interfaccia utente

cambia.

"TwoWay" l'aggiornamento è bidirezionale.

"OneTime" è visualizzato il valore quando l’interfaccia utente viene

visualizzata per la prima volta e poi non viene più aggiornato.

Un oggetto (source) collegato tramite data binding a una proprietà di un control-

lo (target) che risiede nell’interfaccia utente per funzionare correttamente deve

avere una serie di caratteristiche:

La proprietà dell’oggetto target deve essere una Dependency Property;

Se si tratta di TwoWay o OneWayToSource allora la proprietà da mettere

in binding deve avere definito anche il setter;

Se si tratta di OneWay o TwoWay, source deve implementare

INotifyPropertyChanged o INotifyCollectionChanged,

Page 18: WPF MVVM Toolkit

18

rispettivamente se la proprietà restituisce un singolo valore o una lista di

oggetti;

Se si vuole supportare la gestione degli errori, una soluzione possibile è

far implementare a source IDataErrorInfo, oppure si crea una classe che

eredita da ValidationRule e si associa al binding che interessa.

È possibile che si debba fare una conversione tra il valore della proprietà del

source e la proprietà del target. Questo può accadere per esempio perché da

una parte si ha una stringa, ma dall’altra parte si vuole avere un bool, oppure

perché si vuole formattare in un determinato modo una stringa. Per fare questo

esistono i ValueConverter, cioè degli oggetti che implementano l’interfaccia

IValueConverter.

Data Template

I Data Template sono dei descrittori: descrivono come deve essere visualizzato

un determinato oggetto. Per esempio si può decidere che un oggetto Person sia

visualizzato come una Label contenente la stringa Cognome, Nome. A questo

punto se in una listbox si inseriscono degli oggetti Person, verranno visualizzate

una lista di Label contente la stringa precedentemente creata. Unendo i

concetti di Data Template e risorse, è possibile creare una serie di resource

dictionary in file separati con i vari Data Template. In questo modo si definisce

una volta sola il template per un determinato oggetto.

Page 19: WPF MVVM Toolkit

19

Separazione dei ruoli

Con il solo utilizzo di tutte le caratteristiche di WPF, designer e developer

possono lavorare assieme senza intralciarsi troppo. Il file con il code behind può

essere ridotto al minimo grazie all’utilizzo del Data Binding e dei command.

Sia il designer che il developer lavorano sul codice XAML: l’unica cosa che deve

fare il designer per agevolare il lavoro del developer è quello di associare (o non

modificare) gli handler d’evento ai controlli ed eventi corretti. Sarà poi il

developer a scrivere il codice all’interno degli handler nel code behind. Inoltre

dovrà associare i controlli tramite data binding ai command e agli oggetti

corretti. Grazie agli stili, i resource dictionary e ai template il designer può

modificare l‘interfaccia utente modificando poco il file xaml.

Il developer dal canto suo deve evitare di modificare gli stili associati ai vari

controlli nel file xaml.

Page 20: WPF MVVM Toolkit

20

Model – View – ViewModel

Il pattern Model-View-ViewModel (MVVM) ha come scopo principale quello di

separare l’interfaccia utente dall’implementazione. È stato creato pensando

principalmente al Data Binding, in modo che sia possibile minimizzare (fino ad

annullare) il codice nel code behind della View. Tutto il codice applicativo si trova

nel ViewModel, che fa da ponte con il Model. Il designer lavora solo sulla View, il

developer su ViewModel. Per collegare View e ViewModel si utilizzano Data

Binding e i Command.

Lo scopo del pattern MVVM non è quello di non scrivere codice nel code behind

della View e nemmeno quello di scrivere migliaia di righe di codice. Ci sono

alcune cose che è corretto scrivere nella View, come per esempio la modifica del

focus di un controllo. Quest'operazione è possibile farla sia nella View che nel

ViewModel: il posto corretto è nella View (ed è molto più semplice). Inoltre se si

scrive il codice per gestire il focus nel ViewModel si crea una dipendenza del

ViewModel alla View: questo, però è un esempio di cosa NON fare, poiché

questo non è un compito del ViewModel, ma della View.

Page 21: WPF MVVM Toolkit

21

La cosa importante quando ci si chiede dove inserire una funzionalità è di

prendere in considerazione anche il code behind delle View e non escluderlo a

priori perché si sta utilizzando il pattern Model-View-ViewModel.

Model

Il model rappresenta il dominio applicativo, gli oggetti di business dell’applica-

zione, i dati. È completamente indipendente dall’interfaccia utente. Per esempio

la classe Person di seguito riportata è un esempio di classe appartenente al

modello ed ha due proprietà, FirstName e LastName.

ViewModel

Il ViewModel (VM) è un “Model of a View”. Può essere immaginato come

un’astrazione della view, ma nello stesso tempo è anche una specializzazione del

model che la view utilizza per il data binding. Il VM è particolarmente utile

quando il Model è complesso, o è già esistente e non si può modificare, oppure

quando i tipi di dato del model non sono facilmente collegabili alla view.

Come regola, ogni User Story ha il suo ViewModel. Mentre non è detto che ci sia

una corrispondenza uno a uno tra View e ViewModel. Può capitare di avere

molte View con un solo ViewModel o viceversa.

Il ViewModel si pone in mezzo tra View e Model e fa da ponte tra i due mondi:

quello del dominio applicativo e l’interfaccia utente. Non ha bisogno di

conoscere la view: tramite i Data Template si collega il ViewModel alla view. Il

ViewModel deve avere delle proprietà che rappresentano gli elementi che

Page 22: WPF MVVM Toolkit

22

interessano alla View, oppure esporre direttamente parte del Model. Per

esempio deve avere le proprietà FirstName e LastName oppure una

proprietà che restituisce un oggetto Person. La prima soluzione è più elegante

perché si disaccoppia completamente la View dal Model, ma richiede la scrittura

di più codice. La seconda invece obbliga a scrivere gli oggetti di business

implementando determinate interfacce, se si vuole che il Data Binding, la

visualizzazione degli errori e altre cose funzionino correttamente. Se non si può

modificare il Model, la prima soluzione diventa una scelta obbligata.

Il ViewModel deve anche avere delle proprietà che restituiscono degli oggetti

che implementano l’interfaccia ICommand. Per esempio SaveCommand e

CloseCommand.

View

La view è formata prevalentemente da codice xaml. È formata da elementi

visuali, bottoni, finestre e controlli più complessi. Codifica le scorciatoie da

tastiera e controlla gli input dei vari device che è responsabilità del controller nel

MVC. Spesso è definita dichiarativamente, spesso tramite tool.

Ogni View deve conoscere il suo ViewModel associato perché deve agganciare,

tramite Data Binding, tutte le proprietà che servono: per esempio FirstName

e LastName se la View rappresenta l’anagrafica di un cliente. Inoltre deva

Page 23: WPF MVVM Toolkit

23

agganciare, sempre tramite Data Binding, i command ai vari eventi, come per

esempio il clic sul bottone “Ok” per salvare le modifiche.

Separazione dei ruoli

Unendo le potenzialità di WPF al pattern Model – View – ViewModel si ottiene

un code behind della Window, o View, che non contiene codice. Questo

permette di separare maggiormente il lavoro del designer da quello del

developer.

Il developer deve creare la logica a partire dal ViewModel. Il designer invece

deve fare l’interfaccia utente. L’unico punto di contatto tra i due ruoli si ha nel

file XAML: devono coesistere la parte di data binding e command da una parte e

la parte di stili e template dall’altra.

Se la View è in mano completamente al designer, l’unica cosa che deve sapere

per far funzionare correttamente l’interfaccia utente è il contratto esposto dal

ViewModel. A questo punto sta al designer agganciare i command che servono,

dove servono e recuperare i dati tramite binding.

Il developer dal canto suo, può dimenticarsi dell’esistenza della View, il suo

lavoro termina con la definizione del contratto e l’implementazione del

ViewModel.

Page 24: WPF MVVM Toolkit

24

Capitolo due: Il toolkit

In questo capitolo si parlerà in dettaglio del toolkit che è stato costruito per

agevolare lo sviluppo di applicazioni basate sul pattern Model-View-ViewModel

per non dover riscrivere quelle parti di codice che naturalmente si ripetono in

ogni progetto. Sarà spiegato quali sono i problemi che si possono riscontrare e

come sono stati risolti.

Creare un'applicazione WPF senza utilizzare il pattern MVVM ovviamente è

possibile, e molto probabilmente l'interfaccia utente è identica a un'applicazione

scritta utilizzando il pattern. La vera differenza si nota soprattutto quando si

deve manutenere l'applicazione. Se si applica il pattern, ci sarà una divisione

netta tra l'interfaccia utente (e le operazioni proprie della UI) e il modello a

oggetti. In caso contrario, una modifica fatta a una tabella del database,

potrebbe essere molto più difficile da gestire.

INotifyPropertyChanged

Come già detto in precedenza, se si vuole aggiornare l'interfaccia utente quando

cambia il valore di una proprietà messa in binding, si deve implementare

l'interfaccia INotifyPropertyChanged.

Questo è indipendente dall'uso del pattern MVVM. La differenza risiede in quale

classe deve implementare questa interfaccia.

Page 25: WPF MVVM Toolkit

25

Se non si utilizza il pattern, e si collegano alla finestra, tramite data binding,

direttamente gli oggetti di dominio, allora saranno gli oggetti di dominio stessi a

dover implementare l'interfaccia.

Se invece si utilizza il pattern MVVM, View e Model non si conoscono, quindi

deve essere il ViewModel a implementare INotifyPropertyChanged.

Una soluzione per fare implementare questa interfaccia a tutti i ViewModel è

fare in modo che tutti loro ereditino da una classe base ViewModelBase.

Questa classe avrà tutte le funzionalità comuni, fra cui l'implementazione delle

varie interfacce.

using System.Componentmodel;

using System.Diagnostics;

public class ViewModelBase : INotifyPropertyChanged

{

public ViewModelBase()

{

}

public event PropertyChangedEventHandler

PropertyChanged = delegate {

};

protected virtual void OnPropertyChanged(

string

propertyName)

{

this.VerifyPropertyName(propertyName);

var handler = this.PropertyChanged;

if (handler != null)

Page 26: WPF MVVM Toolkit

26

{

handler(

this,

new PropertyChangedEventArgs(propertyName));

}

}

[Conditional("DEBUG")]

[DebuggerStepThrough]

private void VerifyPropertyName (string propName)

{

if (TypeDescriptor

.GetProperties(this)[propName] == null)

{

var msg = "Invalid property name: " + propName;

Debug.Fail(msg);

}

}

}

Il metodo VerifyPropertyName serve per bloccare il debug con un errore se

il nome della proprietà passato non esiste. Questo può accadere quando si

modifica il nome di una proprietà, ma ci si dimentica di cambiare la stringa che si

passa a OnPropertyChanged.

Di seguito un breve esempio di come si usa questa classe.

public class MyViewModel : ViewModelBase

{

private string myProperty;

public string MyProperty

{

get

{

return this.myProperty;

}

set

{

if (value != this.myProperty)

{

this.OnPropertyChanged("MyProperty");

Page 27: WPF MVVM Toolkit

27

this.myProperty = value;

}

}

}

}

Delegate Command

Si supponga di dover eseguire quest'operazione: la finestra View1 ha un

bottone, al clic bisogna scrivere in una TextBox "Bottone premuto".

Se non si applica il pattern MVVM la soluzione più semplice è quella di

intercettare l'evento Click del bottone e scrivere il testo nella TextBox.

Questo è il flusso di esecuzione:

1. L'utente preme il bottone.

2. Si esegue l'handler associato all'evento Click del bottone.

3. Il codice setta la proprietà Text della TextBox.

Page 28: WPF MVVM Toolkit

28

Se invece si implementa il pattern, il primo problema che si incontra è quello di

associare il command all'evento Click del bottone. L'altro problema è come fare a

scrivere il codice nel ViewModel e non nella View.

Ecco il flusso che si vuole ottenere per aggiornare la TextBox al clic del

bottone:

1. L'utente preme il bottone.

2. Viene scatenato il comando associato al bottone.

Page 29: WPF MVVM Toolkit

29

3. Si esegue il metodo Execute del comando.

4. Il codice setta la proprietà Text di tipo string.

5. Tramite data binding viene aggiornata l'interfaccia utente.

6. La TextBox contiene il testo "Bottone premuto".

Ci sono poi altre due cose che uno sviluppatore vorrebbe avere quando associa

un command ad un evento tramite codice xaml: la possibilità di sapere quando si

può eseguire quel comando e la possibilità di aggiungere degli shortcut da

tastiera.

Ci sono, quindi, quattro problemi da risolvere:

1. Scrivere il codice del comando nel ViewModel.

2. Sapere quando un comando può essere eseguito.

3. Aggiungere degli shortcut.

4. Associare il comando al controllo nel codice XAML e minimizzare la

scrittura di codice ripetitivo.

Scrivere il codice del comando nel ViewModel

La soluzione a questo problema è di creare un proprio comando che implementa

l'interfaccia System.Windows.Input.ICommand.

public interface ICommand

{

void Execute(object parameter);

bool CanExecute(object paramenter);

event CanExecuteChanged;

}

L'unica accortezza è di accettare un delegato nel quale è scritto il codice che si

deve eseguire quando si richiama il metodo Execute di ICommand.

public class DelegateCommand : ICommand

{

private readonly Action<object> executeMethod;

Page 30: WPF MVVM Toolkit

30

public DelegateCommand(Action<object>

executeMethod)

{

this.executeMethod = executeMethod;

}

public void ExecuteMethod(object parameter)

{

if (this.executeMethod == null) return;

this.executeMethod(parameter);

}

// ...

}

In questo modo, per creare un comando basta passare al costruttore un

delegato.

Sarà poi compito dell'infrastruttura chiamare il metodo Execute dell'interfac-

cia ICommand.

Sapere quando un comando può essere eseguito

Quest'operazione è già prevista dall'interfaccia ICommand, che ha il metodo

CanExecute e l'evento CanExecuteChanged.

Bisogna quindi lavorare in maniera analoga al punto precedente, facendo in

modo che il costruttore accetti un secondo delegato che verrà poi eseguito dal

metodo CanExecute. Inoltre, bisogna far sapere all'infrastruttura di WPF

quando deve controllare nuovamente se può eseguire o no il comando. La

soluzione più semplice è quella di dire a WPF di controllare ogni volta che

succede qualche modifica.

Di seguito c'è il nuovo codice:

public class DelegateCommand : ICommand

{

// ...

Page 31: WPF MVVM Toolkit

31

private readonly Func<object, bool>

canExecuteMethod;

public void CanExecuteMethod(object parameter)

{

if (this.canExecuteMethod == null) return;

this.canExecuteMethod(parameter);

}

public event EventHandler CanExecuteChanged

{

add { CommandManager.requerySuggested += value;

}

remove { CommandManager.requerySuggested -= value;

}

}

}

Aggiungere degli shortcut

Per risolvere questo problema si deve creare una nuova interfaccia

IBindableCommand che eredita da ICommand.

public interface IBindableCommand : ICommand

{

InputBindingCollection InputBindings { get; }

string DisplayText { get; }

}

Fatto ciò, si fa implementare a DelegateCommand questa interfaccia e non

ICommand.

Seguono le aggiunte alla classe DelegateCommand.

using System;

using System.Windows.Input;

public class DelegateCommand : IBindableCommand

{

// ...

Page 32: WPF MVVM Toolkit

32

public void AddGesture(InputGesture gesture)

{

if (this.inputBindings == null)

{

this.inputBindings =

new InputBindingCollection();

}

this.inputBindings.Add(

new InputBinding(this, gesture);

}

public InputBindingCollection Inputbindings

{

get { return this.inputBindings; }

}

public string DisplayText { get; private set; }

}

Associare il comando al controllo nel codice XAML e minimizzare la

scrittura di codice ripetitivo

Qui le cose da fare sono due: creare una Markup Extension per il codice XAML e

associare agli shortcut ai controlli.

Una markup extension è un'estensione alla sintassi del codice XAML.

È possibile implementare una markup extension per fornire valori per le proprie-

tà nell'utilizzo di un attributo, per le proprietà nell'utilizzo di un elemento pro-

prietà o in entrambi i casi.

Quando è utilizzata per fornire un valore di attributo, la sintassi che distingue

un'estensione di markup in un processore XAML è la presenza delle parentesi

graffe di apertura e chiusura { e }. Il tipo di estensione di markup è quindi

identificato tramite il token di stringa immediatamente successivo alla parentesi

graffa di apertura.

Page 33: WPF MVVM Toolkit

33

Quando è utilizzata nella sintassi degli elementi proprietà, un'estensione di

markup corrisponde visivamente a qualsiasi altro elemento utilizzato per fornire

un valore di elemento proprietà, ovvero una dichiarazione di elementi XAML che

fa riferimento alla classe dell'estensione di markup come elemento, racchiuso tra

parentesi angolari (<>).

Spesso si utilizzano le markup extension per trasformare molte righe di codice

XAML in un'espressione molto più concisa.

Per fare in modo di poter associare direttamente nello XAML un

IBindableCommand, si crea una classe BindCommand che eredita da

System.Windows.Markup.MarkupExtension.

Nel metodo HandleLoaded si associa lo shortcut.

using System;

using System.ComponentModel;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

using System.Windows.Markup;

using ComponentModel;

[MarkupExtensionReturnType(typeof(IBindableCommand))]

public class BindCommand : MarkupExtension

{

public BindCommand()

{

}

public BindCommand(string commandName)

{

this.Name = commandName;

}

public string Name { get; set; }

public override object ProvideValue(

IServiceProvider serviceProvider)

{

var pvt =

serviceProvider.GetService(

typeof(IProvideValueTarget))

as IProvideValueTarget;

Page 34: WPF MVVM Toolkit

34

if (pvt != null)

{

var fe = pvt.TargetObject as FrameworkElement;

if (fe != null)

{

fe.Loaded += this.HandleLoaded;

}

}

return null;

}

private void HandleLoaded(object sender, RoutedEventArgs

e)

{

var fe = sender as FrameworkElement;

if (fe == null ||

DesignerProperties.GetIsInDesignMode(fe))

{

return;

}

if (fe.DataContext == null)

{

return;

}

// Use reflection to retrieve provided Command.

var property =

fe.DataContext.GetType().GetProperty(this.Name);

// Get root elements.

var rootElement = this.GetRootElement(fe);

var command = (IBindableCommand)

property.GetValue(fe.DataContext, null);

var source = sender as ICommandSource;

if (source != null)

{

var button = fe as Button;

var menuItem = fe as MenuItem;

// Is it a button?

if (button != null)

{

button.Command = command;

if (!String.IsNullOrEmpty(command.DisplayText))

{

button.Content =

new AccessText { Text = command.DisplayText

};

}

}

Page 35: WPF MVVM Toolkit

35

else if (menuItem != null)

{

// MenuItem.

menuItem.Command = command;

if (!String.IsNullOrEmpty(command.DisplayText))

{

menuItem.Header =

new AccessText { Text = command.DisplayText

};

}

if (command.InputBindings != null)

{

var keyGesture =

command.InputBindings[0].Gesture as

KeyGesture;

if (keyGesture != null)

{

menuItem.InputGestureText =

keyGesture.DisplayString;

}

}

}

// Add commandbindings to root element.

if (command.InputBindings != null)

{

foreach (InputBinding ib in

command.InputBindings)

{

rootElement.InputBindings.Add(ib);

}

}

}

// De-register loaded event.

fe.Loaded -= this.HandleLoaded;

}

private FrameworkElement GetRootElement(

FrameworkElement fe)

{

if (fe.Parent == null)

{

return fe;

}

return this.GetRootElement(

fe.Parent as FrameworkElement);

}

}

Page 36: WPF MVVM Toolkit

36

Come si usa

Per utilizzare queste classi, nel ViewModel si crea un BindableCommand.

public class MyViewModel

{

public ICommand MyCommand { get; private set; }

public MyViewModel()

{

this.MyCommand = new DelegateCommand(

o =>

{

// TODO: Add Code here...

});

}

}

Nel codice XAML si associa a un controllo il comando.

<UserControl

x:Class="MyView"

xmlns="..."

xmlns:x="..."

xmlns:cmd=

"clr-

namespace:Toolkit.Commands;assembly=Toolkit"

>

<Button

Command="{cmd:BindCommand Name=MyCommand}"

/>

</UserControl>

IMessageBroker

Si supponga di dover eseguire quest'operazione: una finestra, View1 ha un

bottone. Al clic del bottone si deve visualizzare un'altra finestra, View2.

Se non si è applica il pattern MVVM, la soluzione più semplice è creare e visualiz-

zare View2 direttamente dall'handler associato all'evento Click del bottone.

Page 37: WPF MVVM Toolkit

37

Se però si implementa il pattern MVVM, per eseguire un'operazione bisogna

passare dal ViewModel.

Una prima soluzione che può venire in mente è:

Page 38: WPF MVVM Toolkit

38

1. Associare un comando al clic del bottone e nel metodo Execute, che si

trova nel ViewModel e non nella View.

2. Creare ViewModel2 (VM associato a View1).

3. Nel metodo Show di ViewModel2, creare View2 e visualizzare la

finestra.

Questo scenario però ha un problema:

I due moduli non sono indipendenti: ViewModel1 conosce View-

Model2.

Per risolvere questo problema si utilizza il broker. Esso è utilizzato per far comu-

nicare due moduli che non si conoscono.

La soluzione è creare un terzo attore, il broker appunto, che si mette in mezzo ed

è conosciuto da entrambi. In questo modo il modulo A se deve inviare qualcosa

al modulo B, invia un messaggio al broker che lo consegna al modulo B.

Più in dettaglio un modulo sottoscrive un messaggio e quando qualcuno invia

quel particolare messaggio, il broker lo recapita a tutti quelli che lo hanno

sottoscritto.

Il sistema di comunicazione del broker, sottoscrizione tramite delegato e invio

del messaggio, è molto simile agli eventi: sottoscrizione tramite handler e lancio

dell'evento. L'utilizzo del broker, quindi, è molto simile ad utilizzare gli eventi,

però è stato ottenuto il disaccoppiamento tra i moduli.

Il compito del "passacarte" è svolto da una classe che implementa l'interfaccia

IMessageBroker.

Page 39: WPF MVVM Toolkit

39

L'interfaccia ha due metodi per sottoscrivere un messaggio: il sottoscrittore

chiede al message broker di essere notificato, invocando il delegato passato

come parametro, quando un messaggio di tipo T è inviato; ha cinque metodi per

cancellare la sottoscrizione e un metodo per inviare un messaggio.

Page 40: WPF MVVM Toolkit

40

Gli unici metodi che hanno bisogno di una spiegazione sono i cinque metodi

Unsubscribe.

void Unsubscribe(object subscriber);

Cancella tutte le sottoscrizioni di un sottoscrittore.

void Unsubscribe<T>(object subscriber)

where T : IMessage;

Cancella tutte le sottoscrizioni di un sottoscrittore per un determinato messaggio

T.

void Unsubscribe(object subscriber, object sender);

Cancella tutte le sottoscrizioni di un sottoscrittore per un determinato sender.

void Unsubscribe<T>(object subscriber, object sender)

where T : IMessage;

Cancella tutte le sottoscrizioni di un sottoscrittore per un determinato sender e

solo per un certo tipo di messaggio T.

void Unsubscribe<T>(

object subscriber,

System.Action<T> callback)

where T : IMessage;

Cancella tutte le sottoscrizioni di un sottoscrittore per una determinata callback.

L'interfaccia IMessage è così definita.

Page 41: WPF MVVM Toolkit

41

L'utilizzo del message broker per comunicare tra più viewmodel ha anche il

vantaggio che il broker stesso non deve sapere a priori, quali saranno i messaggi

da inviare. Questo permette di creare messaggi ad hoc per ogni esigenza,

conosciuti solo da sender e sottoscrittore.

Di seguito è mostrato un esempio di utilizzo del broker per sottoscrivere un

messaggio.

class LoadGuardianCostListViewModel : IModule

{

public void Initialize(

object sender,

IUnityContainer container)

{

var broker = container.Resolve<IMessageBroker>();

broker.Subscribe<GetAllItemsMessage<GuardianCost>>(

sender,

message =>

{

var vm = new GuardianCostListViewModel(

container,

message.Context);

vm.Show();

});

}

}

La classe LoadGuardianListViewModel ha un unico metodo che crea una

sottoscrizione a un messaggio che quando viene inviato causa la visualizzazione

della view GuardianCostListViewModel.

Per inviare invece un messaggio ecco un esempio.

this.Broker.Dispatch(

new GetAllItemsMessage<GuardianCost>(

this,

ServerContextRegistry.GetNew(container)))

Page 42: WPF MVVM Toolkit

42

ServerContextRegistry.GetNew restituisce una nuova unit of work da

utilizzare per lavorare con il database. Container è un container di inversion of

control (IoC), nello specifico Unity di Microsoft.

Dependency Inversion Principle (DIP) e Inversion of Control (IoC)

Secondo il Dependency Inversion Principle7 ogni modulo di alto livello non

dovrebbe dipendere da un modo di livello più basso. Entrambi dovrebbero

dipendere da un'astrazione. L'astrazione deve essere indipendente dai dettagli. I

dettagli dovrebbero dipendere dall'astrazione.

In seguito sono mostrati due esempi: il primo non rispetta questo principio, il

secondo sì. La grossa differenza sta nel fatto che nel secondo esempio gli oggetti

di basso livello sono iniettati nel costruttore e non creati dall'oggetto di alto

livello.

public class FinanceInfoService

{

public string GenerateAsHtml(string symbol)

{

SomeStockFinder finder = new SomeStockFinder();

StockInfo[] stocks =

finder.FindQuotesInfo(symbol);

HtmlTableRenderer renderer =

new HtmlTableRenderer ();

return renderer.RenderQuoteInfo(stocks);

}

//...

}

L'obiettivo è ottenere una struttura molto simile all'immagine seguente.

7 Il DIP è stato formalizzato da Robert Martin. È possibile leggere altri dettagli sul sito web

http://www.objectmentor.com/resources/articles/dip.pdf

Page 43: WPF MVVM Toolkit

43

In questo modo, GenerateAsHtml diventa indipendente da SomeStock-

Finder e HtmlTableRenderer.

public class FinanceInfoService

{

IFinder finder;

IRenderer renderer;

public FinanceInfoService(

IFinder finder,

IRenderer renderer)

{

this.finder = finder;

this.renderer = renderer;

}

public string GenerateAsHtml(string symbol)

{

StockInfo[] stocks =

finder.FindQuotesInfo(symbol);

return renderer.RenderQuoteInfo(stocks);

}

//...

}

L'Inversion of Control è l'applicazione del DIP. Spesso IoC è anche detta

Dependency Injection (DI). A volte in letteratura si trova che la DI è il principio,

Page 44: WPF MVVM Toolkit

44

mentre IoC è l'applicazione del principio. In ogni caso IoC è basato sul pattern

DIP.

Oggi IoC/DI è spesso associato con dei framework particolari che hanno una serie

di caratteristiche.

Esistono vari framework di IoC, ma tutti hanno delle caratteristiche comuni: sono

costruiti attorno ad un container che, in base ad alcune informazioni di

configurazione, risolve le dipendenze. Il chiamante istanzia il container e gli

passa l'interfaccia desiderata come parametro. Come risposta, il framework di

IoC/DI restituisce un oggetto concreto che implementa quell'interfaccia.

In questa tesi, come framework di IoC è stato utilizzato Microsoft Unity

Application Block.

Struttura del broker

Il broker ha un campo privato: un dictionary chiamato subscriptions.

private IDictionary<Type, IList<Action<T>>> subscriptions;

Questo dictionary usa come chiave il tipo del messaggio che ha una sottoscrizio-

ne e come valore una lista di delegati, che devono essere invocati quando è in-

viato un messaggio del tipo presente nella chiave.

In WPF non è possibile modificare un oggetto dell'interfaccia utente da un

thread diverso da quello che ha creato l'oggetto stesso, pena una

CrossThreadException. In WPF esiste quindi un oggetto Dispatcher,

che evita questo problema. Il suo metodo Invoke, accetta un delegato, che

viene eseguito sempre nel thread visuale dell'applicazione.

Page 45: WPF MVVM Toolkit

45

Per questo motivo, il broker ha anche un altro campo privato, un dispatcher,

che serve al metodo Dispatch per evitare le CrossThreadException.

private readonly Dispatcher dispatcher;

Sottoscrivere un messaggio

Per sottoscrivere un messaggio, si deve chiamare un overload del metodo

Subscribe.

Page 46: WPF MVVM Toolkit

46

Questo metodo ha almeno un parametro di tipo Action<T> che è l'azione da

eseguire quando viene inviato il messaggio e un parametro generico di tipo T

che indica a quale messaggio ci si deve sottoscrivere.

Quando qualcuno chiama Subscribe, il broker, scopre se il dictionary

subscriptions contiene già il messaggio T, e in caso contrario lo aggiunge.

Poi aggiunge il delegato alla lista di delegati di quel messaggio.

public void Subscribe<T>(Action<T> callback)

where T : IMessage

{

if (this.subscriptions.ContainsKey(typeof(T)))

{

var subscribers = this.subscriptions[typeof(T)];

subscribers.Add(callback);

}

else

{

this.subscriptions.Add(

typeof(T),

new List<Action<T>>() { callback });

}

}

Inviare un messaggio

Per inviare un messaggio si chiama il metodo Dispatch<T>().

Quando viene chiamato questo metodo, il broker cerca nel dictionary

subscriptions se qualcuno ha sottoscritto il messaggio T. In caso

affermativo chiama tutti i delegati associati.

Page 47: WPF MVVM Toolkit

47

public void Dispatch<T>() where T : IMessage

{

if (this.subscriptions.ContainsKey(typeof(T)))

{

var subscribers = this.subscriptions[typeof(T)];

subscribers

.ToList()

.ForEach(callback =>

{

try

{

this.dispatcher.Invoke(callback);

}

catch (Exception e)

{

if (e.InnerException != null)

throw e.InnerException;

else throw;

}

});

}

}

Page 48: WPF MVVM Toolkit

48

UI Composition

Un'applicazione desktop spesso è formata da molte finestre distinte. Una cosa

importante nello sviluppo di quel genere di applicazioni è cercare di tenere le

varie finestre fra loro indipendenti.

Di seguito ci sono alcune definizioni.

Modulo

Un modulo è una parte dell'applicazione, generalmente visuale. I moduli fra loro

devono essere indipendenti e nel caso di moduli visuali sono visualizzati nella

shell all'interno di una region.

Region

La region contiene i moduli visuali.

Shell

Finestra principale dell'applicazione. Esiste solo una shell e contiene, in varie

region, gli altri moduli. La shell non deve conoscere i moduli, conosce solo le

region che li conterranno.

Se i moduli sono indipendenti fra loro, ci sono una serie di vantaggi:

Possono essere sviluppati indipendentemente l'uno dall'altro.

È possibile mostrare certi moduli a certi utenti ed altri ad altri senza

difficoltà: basta non caricare il modulo che non si vuole visualizzare.

È possibile caricare i moduli che interessano solo a runtime, magari

utilizzando un file di configurazione.

Per implementare il pattern MVVM non c'è nessuna necessità di scrivere

applicazioni composite. Creando applicazioni di questo tipo, però, si riesce a

modificare o aggiungere una parte d'interfaccia utente senza interferire con

tutto il resto dell'applicazione. E questo è un grosso vantaggio.

Page 49: WPF MVVM Toolkit

49

Il pattern MVVM è un buon punto di partenza per creare applicazioni composite

per due motivi:

Il broker: poiché i moduli non si possono conoscere, sarà lui l'unico in

grado di farli comunicare.

L'indipendenza intrinseca dei ViewModel (Moduli se si parla in termini di

UI Composition) che è collegata alla regola "Una User Story, un

ViewModel": se si aggiungono, o si modificano alcune User Story (cioè

alcuni requisiti del software), basta modificare solo il VM direttamente

interessato (e la View naturalmente) lasciando inalterato tutto il resto.

Per avere un'applicazione composita mancano ancora alcuni dettagli, che

saranno spiegati nei prossimi paragrafi.

IRegion

Il primo problema che si riscontra nello sviluppo di applicazioni composite è

quello di fare in modo che la Shell non abbia alcuna conoscenza dei moduli che

andrà a visualizzare.

Page 50: WPF MVVM Toolkit

50

Per fare ciò, la Shell avrà una o più region. All'interno di ogni region è possibile

visualizzare uno o più moduli.

Naturalmente anche un modulo a sua volta può contenere una region, nella

quale ci sono altri moduli, e così via.

Si supponga di dover eseguire quest'operazione: nella finestra principale

(Shell), quando si preme un bottone, si deve inserire nella shell stessa una

view (View1).

Se non applichiamo la UI Composition, la soluzione più semplice è creare View1

e associarlo alla region nell'handler associato evento Click del bottone.

Questo è il flusso di esecuzione:

1. L'utente preme il bottone.

2. Si esegue l'handler associato all'evento Click.

Page 51: WPF MVVM Toolkit

51

3. Il codice, che si trova nella Shell, crea un'istanza di View1 e la inserisce

nella region.

Quest'approccio ha due problemi:

La Shell crea il modulo View1.

Il codice per inserire un modulo nella region si trova sempre nella Shell.

Quello che si vuole ottenere è qualcosa di simile all'immagine sottostante.

Page 52: WPF MVVM Toolkit

52

Quello che cambia rispetto alla situazione precedente è tutta racchiusa nel punto

due:

Non si crea più il modulo nella Shell, ma si recupera un'istanza dal

container di IoC.

Il codice per inserire un modulo non si trova più nella Shell, ma nel

metodo ShowViewModel della classe region.

Se si implementa anche il pattern MVVM, l'unica differenza è che il codice in 2 si

troverebbe nel ViewModel e non nella View.

Implementazione

Una region implementa l'interfaccia IRegion.

public interface IRegion

{

IEnumerable<IWorkspaceViewModel> ViewModels { get;

}

void ShowViewModel(IWorkspaceViewModel item);

bool? ShowDialogViewModel(IWorkspaceViewModel

item);

}

L'interfaccia ha una proprietà che restituisce la lista di moduli che contiene, e

due metodi: ShowViewModel e ShowDialogViewModel. Il secondo apre

una finestra modale.

Come si può notare entrambi i metodi accettano come unico parametro un

oggetto che implementa l'interfaccia IWorkspaceViewModel. Questa interfaccia

è la base di tutti i moduli visuali che devono essere visualizzati in una region.

public interface IWorkspaceViewModel : IViewModel

{

event EventHandler Closed;

ICommand CloseCommand { get; }

Page 53: WPF MVVM Toolkit

53

object Content { get; }

void Show();

bool? ShowDialog();

}

Utilizzando questa struttura, la shell ha almeno una region con all'interno

almeno un modulo da visualizzare. Manca ancora qualcosa però: la region ha una

lista di ViewModel, mentre bisogna visualizzare le rispettive View. Questo si

risolve utilizzando una caratteristica di WPF. In Windows Presentation

Foundation può essere visualizzato qualunque oggetto. Se è un oggetto visuale,

è visualizzato, altrimenti è chiamato ToString() dell'oggetto per visualizzare

una stringa. C'è una terza possibilità: per un oggetto si può definire quale sarà la

sua visualizzazione utilizzando i Data Template.

<DataTemplate DataType="{x:Type vm:PhoneViewModel}">

<ScrollViewer

HorizontalScrollBarVisibility="Auto"

VerticalScrollBarVisibility="Auto"

>

<vw:PhoneView />

</ScrollViewer>

</DataTemplate>

Questo template nello specifico, visualizza uno ScrollViewer e all'interno la

view associata a PhoneViewModel.

Il toolkit contiene tre classi diverse che implementano l'interfaccia IRegion:

ContentContainer: può contenere un solo viewmodel.

ListContainer: può contenere più viewmodel.

DialogContainer: apre una finestra modale e può contenere un solo

viewmodel.

Page 54: WPF MVVM Toolkit

54

IModule

Quando si scrivono applicazioni composite formate da tanti moduli, bisogna

trovare un modo semplice per inizializzare i moduli stessi. Portando all'estremo

deve essere possibile indicare in un file di configurazione quali moduli

inizializzare. Quest'ultima cosa non è presente nel toolkit. Quello che è presente,

invece, è la possibilità di scoprire a runtime quali sono i moduli e come

inizializzarli indicando a compile time solo gli assembly che contengono i moduli

stessi.

Inizializzazione di un modulo

Tornando all'esempio della figura precedente, manca qualcosa: anziché creare

un oggetto View, si utilizza il container di IoC per risolvere la dipendenza.

Bisogna però ancora capire come si fa a inizializzare View1.

In altre parole, manca chi deve eseguire RegisterInstance(new

View1());

public interface IModule

{

void Initialize(object sender, IUnityContainer

container);

}

L'interfaccia ha solo un metodo che inizializza il modulo. Una piccola nota. Il

metodo Initialize non restituisce nulla, quindi se si deve recuperare il

Page 55: WPF MVVM Toolkit

55

modulo in un secondo momento, nel metodo suddetto si deve inserire il modulo

nel container di IoC. In questo modo poi nell'applicazione sarà possibile

recuperare il modulo inizializzato.

Di seguito c'è un esempio di inizializzazione di un modulo visuale.

internal sealed class LoadPersonViewModel : IModule

{

public void Initialize(

object sender,

IUnityContainer container)

{

var broker = container.Resolve<IMessageBroker>();

broker.Subscribe<EditingMessage<Person>>(

sender,

message =>

{

var myContainer = container;

var pvm =

new PersonViewModel(

message.Context,

myContainer,

message.Item,

true);

pvm.Show();

});

}

}

Questa classe è un modulo non visuale che recupera l'istanza del message

broker, e sottoscrive un messaggio. Il delegato che deve essere eseguito quando

il messaggio è inviato altro non fa che creare un ViewModel e visualizzarlo.

Questo ViewModel è un modulo visuale che sarà visualizzato all'interno di una

region.

Discovery dei moduli

Naturalmente, dopo aver scritto il codice di inizializzazione dei moduli si deve

eseguire il codice stesso. Bisogna prima però scoprire quali moduli sono stati

creati per l'applicazione.

Page 56: WPF MVVM Toolkit

56

Per fare questo, bisogna dire al toolkit quali assembly contengono le classi che

implementano l'interfaccia IModule.

public static void RegisterModules(

IUnityContainer container,

Assembly assembly)

{

if (container == null)

throw new ArgumentNullException("container");

if (assembly == null)

throw new ArgumentNullException("assembly");

assembly

.GetTypes()

.Where(t => t.IsInterfaceImplemented<IModule>())

.ForEach(t =>

container.RegisterType(

typeof(IModule),

t,

t.FullName,

new

ContainerControlledLifetimeManager()));

}

Questo metodo controlla tutti i tipi di un assembly alla ricerca di classi che

implementano l'interfaccia IModule. Quando ne trova una, la inserisce nel

container di IoC come singleton.

ForEach e IsInterfaceImplemented sono due extension method.

Gli Extension Method consentono di "aggiungere" metodi ai tipi esistenti senza

creare un nuovo tipo derivato, ricompilare o modificare in altro modo il tipo

originale. Gli extension method sono uno speciale tipo di metodo statico, ma

sono chiamati come se fossero metodi d'istanza sul tipo esteso.

L'ultima cosa da fare è quella di recuperare i moduli dal container di IoC e

inizializzarli.

foreach (var item in this.Container.ResolveAll<IModule>())

Page 57: WPF MVVM Toolkit

57

{

item.Initialize(this, this.Container);

}

ViewModel

Tutti i ViewModel implementano l'interfaccia IViewModel. Questa interfaccia

ha quattro proprietà. IsInDesignMode serve per indicare se il ViewModel è

stato creato a runtime oppure se è stato creato all'interno di un designer come

Visual Studio o Expression Blend. Quest'operazione è utile soprattutto in

Expression Blend 3 per poter associare il ViewModel direttamente alla View: così

facendo il designer elenca tutte le property del ViewModel che possono essere

messe in Data Binding con la View. Inoltre nel codice è possibile inizializzare il

ViewModel con alcuni valori di test solo quando il ViewModel è utilizzato

all'interno di un designer.

public interface IViewModel

: IDisposable,

INotifyPropertyChanged

{

string DisplayName { get; }

IMessageBroker Broker { get; }

IUnityContainer Container { get; }

bool IsInDesignMode { get; }

}

Ci sono tre interfacce che ereditano da IViewModel:

ICommandViewModel: si utilizza quando un singolo comando deve

avere una view per essere inserito in una view container.

IWorkspaceViewModel: tutti i ViewModel che devono essere inseriti

in una region devono implementare questa interfaccia che altro non fa

che aggiungere alcune funzionalità di visualizzazione e chiusura della

view.

Page 58: WPF MVVM Toolkit

58

IShellViewModel: il ViewModel associato alla Shell deve

implementare questa interfaccia.

Associare una region al ViewModel

Quando si crea una classe che implementa IWorkspaceViewModel è

indispensabile anche indicare in quale region deve essere inserito. Questa

informazione però interessa solamente a runtime, quindi si può utilizzare un

attributo per salvare questa informazione e un po' di reflection per recuperarla a

runtime.

L'attributo ha due proprietà: una che indica il tipo di region, l'altra, opzionale,

che rappresenta il nome di quella region all'interno del container di IoC.

A runtime si devono recuperare le informazioni presenti nell'attributo per

inserire il ViewModel nella region corretta.

Page 59: WPF MVVM Toolkit

59

[Region(typeof(ListContainer), Name = "MyRegion")]

class MyViewModel : IWorkspaceViewModel

{

private IRegion region;

public MyViewModel()

{

RegionAttribute regionAttribute;

if (this.GetType()

.TryGetCustomAttribute(out

regionAttribute))

{

if

(string.IsNullOrEmpty(regionAttribute.Name))

{

this.region =

(IRegion)container.Resolve(

regionAttribute.Region);

}

else

{

this.region =

(IRegion)container.Resolve(

regionAttribute.Region,

regionAttribute.Name);

}

}

}

public void Show()

{

this.region.ShowViewModel(this);

}

// ...

}

Il codice recupera le informazioni sulla region a runtime e risolve la dipendenza

utilizzando il framework di IoC. E questa region sarà poi utilizzata dal metodo

Show. TryGetCustomAttribute è un extension method che altro non fa

che valorizzare regionAttribute se il tipo è decorato con l'attributo

RegionAttribute.

L'ultima cosa da fare è quella di creare un'istanza della region nella quale inserire

il ViewModel e registrare la stessa nel framework di IoC.

Page 60: WPF MVVM Toolkit

60

this.Container.RegisterType<ListContainer>(

"MyRegion",

new ContainerControlledLifetimeManager());

Questo codice va inserito nella fase di inizializzazione dell'applicazione, cioè

nell'Application Controller del quale parleremo a breve.

Application Controller

L'ultima cosa che rimane da fare è l'inizializzazione dell'applicazione.

Le operazioni da fare sono le seguenti:

Creare un container di IoC e registrare tutto quello che serve.

Creare un'unica istanza del MessageBroker.

Creare un'unica istanza della Shell.

Cercare e inizializzare tutti i moduli.

Per eseguire queste operazioni per prima cosa si crea una classe base astratta

ApplicationControllerBase: questa classe ha tutte le funzionalità

indipendenti dalla particolare applicazione.

public abstract class ApplicationControllerBase

{

private readonly IUnityContainer container;

private IShell shell;

private IMessageBroker broker;

protected ApplicationControllerBase(

IUnityContainer container)

{

this.container = container;

}

public IShell Shell

{

get { return this.shell; }

}

Page 61: WPF MVVM Toolkit

61

protected IUnityContainer Container

{

get { return this.container; }

}

public void Run()

{

// Registra il MessageBroker.

this.container

.RegisterType<IMessageBroker, MessageBroker>(

new ContainerControlledLifetimeManager(),

new InjectionConstructor(

Dispatcher.CurrentDispatcher));

// Trova tutti i moduli presenti negli assembly

// e li registra nel container di IoC.

foreach (var assembly in

this.GetViewModelAssemblies())

Unity.Discovery.RegisterModules(

this.Container,

assembly);

this.broker =

this.Container.Resolve<IMessageBroker>();

this.shell = this.CreateShell();

// Recupera tutti i moduli e li inizializza.

foreach (var item in

this.Container.ResolveAll<IModule>())

item.Initialize(this, this.Container);

}

protected abstract IShell CreateShell();

protected abstract

IEnumerable<Assembly> GetViewModelAssemblies();

}

Questa classe ha due metodi astratti che devono essere implementati dalla

classe che eredita da essa, ApplicationController, che personalizza il

comportamento di default creando la shell e indicando quali sono gli assembly

nei quali si devono cercare i moduli.

class ApplicationController :

ApplicationControllerBase

{

Page 62: WPF MVVM Toolkit

62

public ApplicationController(

IUnityContainer container)

: base(container)

{

this.Container.RegisterType<ListContainer>(

new ContainerControlledLifetimeManager());

}

protected override IShell CreateShell()

{

this.Container.RegisterType<IShell, Shell>(

new ContainerControlledLifetimeManager());

var shell = this.Container.Resolve<IShell>();

shell.DataContext =

new ShellViewModel(this.Container);

return shell;

}

protected override IEnumerable<Assembly>

GetViewModelAssemblies()

{

return new[] { Assembly.GetExecutingAssembly()

};

}

}

Il costruttore registra nel container di IoC l'unica region dell'applicazione. In

questa region saranno visualizzati tutti i moduli visuali.

Il metodo CreateShell crea un'istanza della shell e setta anche il ViewModel.

Il metodo GetViewmodelAssemblies in questo caso restituisce semplice-

mente l'assembly corrente.

Infine, all'avvio dell'applicazione, si crea un'istanza della classe Application-

Controller al quale si passa un'istanza del container di IoC e si chiama il

metodo Run().

var controller =

new ApplicationController(new UnityContainer());

controller.Run();

Page 63: WPF MVVM Toolkit

63

Capitolo tre: L’applicazione

Questo capitolo parlerà di un'applicazione sviluppata utilizzando il toolkit

esposto nel capitolo due per realizzare un'interfaccia utente modulare in

Windows Presentation Foundation, basata sul pattern Model-View-ViewModel.

Obiettivo dell'applicazione

Lo scopo primario dell'applicazione è di gestire le palestre di Trieste. Il

committente deve gestire le palestre, assegnare i turni alle varie società che

vogliono utilizzarle, inviare dei custodi ad aprire e chiudere gli edifici, pagare i

custodi stessi per il loro lavoro e farsi pagare dalle società per l'utilizzo delle

palestre.

Architettura hardware e software dell'ambiente di produzione

L'ambiente di produzione è un dominio Active Directory formato da tre

macchine server con Windows Server 2008: la prima è un database server con

installato SQL Server 2008, la seconda è un web server con IIS 7.0. Il terzo server

è il domain controller della rete.

Page 64: WPF MVVM Toolkit

64

Inoltre ci sono una serie di macchine client con Windows Vista. Tutti i computer,

client e server, hanno installato il .NET Framework 3.5 Service pack 1.

Requisiti tecnici non funzionali

Il committente vuole un'applicazione desktop progettata in ambiente Microsoft.

In questo momento i client sono pochi, ma in un prossimo futuro è molto

probabile che aumentino. Quest'applicazione deve essere visibile e utilizzabile

solo all'interno della rete interna.

L'interfaccia utente dell'applicazione deve essere personalizzabile in funzione

dell'utente che utilizzerà l'applicazione, per esempio user o admin. Inoltre, deve

essere estendibile con nuove interfacce e moduli in futuro.

Architettura dell'applicazione

Visti l'architettura hardware e i requisiti tecnici non funzionali, l'applicazione

deve essere sviluppata utilizzando una serie di tecnologie.

1. Deve avere un database SQL Server 2008.

2. Deve essere sviluppata in .NET.

3. Deve avere, se serve, un Web Server IIS 7.0

Il non sapere a priori quanti siano i client e il fatto che possano aumentare

molto, porta come conseguenza diretta che il server non deve avere alcuna

conoscenza dei client e soprattutto non deve fare nessuna assunzione su di essi.

Infine, l'interfaccia utente deve essere modulare: questo per migliorare

l'estendibilità e la personalizzazione.

Detto questo, l'applicazione è sviluppata secondo un'architettura three-tier,

ovvero con un database su una macchina, una parte di application server su un

secondo server e la parte client da installare su tutti i computer client della rete.

Page 65: WPF MVVM Toolkit

65

La comunicazione tra l'application server e i client avviene tramite servizi REST su

http.

Representational State Transfer – REST

REST è l’acronimo di Representational Transfer State, ed è un paradigma per la

realizzazione di applicazioni Web che permette la manipolazione delle risorse per

mezzo dei metodi GET, POST, PUT e DELETE del protocollo HTTP. Basando le

proprie fondamenta sul protocollo HTTP, il paradigma REST restringe il proprio

campo d’interesse alle applicazioni che utilizzano questo protocollo per la

comunicazione con altri sistemi.

Page 66: WPF MVVM Toolkit

66

Il termite REST è stato coniato nel 2000 da Roy Fielding, uno degli autori del

protocollo HTTP, per descrivere un sistema che permette di descrivere ed

identificare le risorse web8.

REST prevede che la scalabilità del Web e la crescita siano diretti risultati di pochi

principi chiave di progettazione:

Lo stato dell'applicazione e le funzionalità sono divisi in Risorse WEB.

Ogni risorsa è unica e indirizzabile usando sintassi universale per uso dei

link ipertestuali.

Tutte le risorse sono condivise come interfaccia uniforme per il

trasferimento di stato tra client e risorse, questo consiste in:

o un insieme vincolato di operazioni ben definite;

o un insieme vincolato di contenuti, opzionalmente supportato da

codice on demand.

Un protocollo che è:

o Client-Server;

o Stateless;

o Cachable;

o A livelli.

Application Server / Web Server

La parte di application server è formata dall'accesso ai dati utilizzando un O/RM,

nello specifico Entity Framework di Microsoft. La parte invece di creazione dei

servizi REST è creata utilizzando ADO.NET Data Services v1.0, sempre di

Microsoft.

8 http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

Page 67: WPF MVVM Toolkit

67

Object / Relational Mapping – O/RM

L'Object-Relational Mapping (ORM) è una tecnica di programmazione per

convertire dati fra RDBMS e linguaggi di programmazione orientati agli oggetti.

In buona sostanza, associa a ogni operazione ed elemento usato nella gestione

del database degli oggetti con adeguate proprietà e metodi, astraendo l'utilizzo

del database dal DBMS specifico.

I principali vantaggi nell'uso di questo sistema sono i seguenti.

Il superamento (più o meno completo) dell'incompatibilità di fondo tra il

progetto orientato agli oggetti ed il modello relazionale sul quale è basata

la maggior parte degli attuali DBMS utilizzati; con una metafora legata al

mondo dell'elettrotecnica, si parla in questo caso di disadattamento

dell'impedenza (Impedence Mismatch) tra paradigma relazionale e ad-

oggetti (object/relational impedance mismatch).

Un'elevata portabilità rispetto alla tecnologia DMBS utilizzata: cambiando

DBMS non devono essere riscritte le routine che implementano lo strato

Page 68: WPF MVVM Toolkit

68

di persistenza; generalmente basta cambiare poche righe nella

configurazione del prodotto per l'ORM utilizzato.

Facilità d'uso, poiché non si è tenuti a utilizzare direttamente un

linguaggio di query come l'SQL (che comunque rimane disponibile per

eseguire compiti complessi o che richiedono un'elevata efficienza).

I prodotti per l'ORM in questo momento più diffusi, offrono spesso nativamente

funzionalità che altrimenti andrebbero realizzate manualmente dal

programmatore:

caricamento automatico del grafo degli oggetti secondo i legami di asso-

ciazione definiti a livello di linguaggio;

gestione della concorrenza nell'accesso ai dati durante conversazioni;

meccanismi di caching dei dati (con conseguente aumento delle presta-

zioni dell'applicazione e riduzione del carico sul sistema RDBMS).

Client

Il client è sviluppato in Windows Presentation Foundation, utilizzando il toolkit

presentato nel capitolo due.

Questo tier è formato da tre livelli:

Accesso ai dati e model.

ViewModel.

View.

La parte di accesso ai dati e Model è creata utilizzando la parte client di ADO.NET

Data Services; i ViewModel e le View si basano sul toolkit. Cosa fondamentale,

poiché si parla di MVVM, la View non conosce il Model.

Page 69: WPF MVVM Toolkit

69

Attori

In questa parte sono riassunti tutti gli attori, siano essi persone fisiche, società o

luoghi o oggetti.

Committente

Il committente è la società che gestisce le palestre situate

sul territorio della provincia di Trieste, siano esse

comunali o provinciali.

Società

La società è chi utilizza la palestra. Una società può

utilizzare più palestre diverse in orari differenti. Ogni

società deve pagare il committente per l’utilizzo della

palestra. Il costo dipende dalla categoria della palestra e

Page 70: WPF MVVM Toolkit

70

se la società è affiliata al CONI e/o socia del committente. A ogni società sono

associati dei turni.

Custode

Un custode apre e chiude una palestra. Alla fine di ogni mese

deve consegnare al committente un documento, nel quale si

attesta quante ore ha fatto.

Federazione

Una federazione sportiva è paragonabile a una società.

L'unica differenza è che una federazione fa parte del

CONI.

Istituto Comprensivo

Rappresenta l’ente che ha in gestione le

varie scuole del Comune (e le relative

palestre).

Ogni istituto può avere uno o più palestre.

Solo le palestre comunali hanno un istituto

comprensivo.

Palestra / Scuola

Una palestra può essere collegata a un istituto

comprensivo e può avere uno o più custodi.

Una scuola (o palestra) è o del comune o della

provincia.

Ogni palestra ha degli orari di disponibilità.

Page 71: WPF MVVM Toolkit

71

Sala CONI

Una Sala CONI è paragonabile a una palestra. Ci

sono in totale tre sale, tutte di proprietà del

CONI.

È previsto un custode per la pulizia e la custodia.

Turno

Un turno in una palestra è un orario della settimana nel quale una determinata

società può svolgere attività in palestra. Un turno si deve riferire a un anno

sportivo.

Struttura dell'applicazione

Poiché questa tesi è incentrata sull'interfaccia utente, in quest'ultima parte del

capitolo si parlerà solamente del client.

Il client è un'applicazione modulare. La shell dell'applicazione è formata da due

region:

Una ribbon region, nella quale sono contenuti tutti i comandi.

Una tab region che contiene, all'interno di un TabControl, tutte le

view che devono essere visualizzate.

L'immagine sottostante, per esempio, visualizza cinque view. Ognuna di esse è

uno UserControl che viene iniettato nella shell e naturalmente ogni view non ha

nessuna conoscenza né della shell, né delle altre view.

Page 72: WPF MVVM Toolkit

72

Alcune user story

In quest'ultima parte del capitolo saranno elencate tre user story che producono

dei ViewModel molto diversi fra loro.

Una User Story9 è un requisito di un sistema software formato da una o due frasi

scritte il linguaggio corrente o nel linguaggio di business dell'utente. Le user story

sono utilizzate nelle metodologie di sviluppo agili10. Ogni user story è scritta

generalmente in un piccolo pezzo di carta, per assicurarsi che non cresca troppo.

Le user story dovrebbero essere scritte dai clienti di un progetto software, e

sono lo strumento principale per influenzare lo sviluppo del software.

9 http://www.agilemodeling.com/artifacts/userStory.htm

10 http://agilemanifesto.org/ e http://www.agilemodeling.com/

Page 73: WPF MVVM Toolkit

73

Le user story sotto elencate rappresentano la maggior parte dei ViewModel

possibili. La prima user story produrrà un ViewModel che è collegato a un solo

oggetto del modello per eseguire operazioni d'inserimento e modifica dei dati.

Anche il secondo è collegato a un solo oggetto del modello, ma esegue delle

operazioni di ricerca, e non visualizza tutti i dati presenti nell'oggetto di dominio.

Infine, la terza user story è collegata a molti oggetti del modello e visualizza

solamente i dati che interessano.

US1 – Inserimento e modifica di un custode

Un utente deve poter inserire un nuovo custode. Deve poter anche modificare i

dati inseriti.

Page 74: WPF MVVM Toolkit

74

L'immagine precedente mostra la View quando si deve inserire un nuovo

custode, quella sottostante, invece, visualizza la stessa View quando si deve

modificare un custode in precedenza inserito.

La prima cosa da notare è che la View è identica: indipendentemente

dall'operazione da eseguire, non cambia. L'unica differenza degna di nota è il

titolo del Tab che passa da "Nuovo" a "Carla Riboni". Capire in quale stato ci si

trova è compito del ViewModel, che fra le altre cose modifica anche il titolo della

View.

Questo è il primo tipo di ViewModel che capita spesso di trovare. Ecco le sue

caratteristiche:

Page 75: WPF MVVM Toolkit

75

Comanda più operazioni (inserimento e aggiornamento) su una sola

View.

È associato a un solo oggetto del modello.

Espone tutti i campi dell'oggetto.

L'utilizzo principale di questo ViewModel è di eseguire operazioni d'inserimento

e aggiornamento di un singolo oggetto del dominio.

Page 76: WPF MVVM Toolkit

76

Infatti, il toolkit contiene una classe base, ItemViewModelBase, che

incapsula tutte le caratteristiche fondamentali per eseguire queste operazioni.

US2 – Lista dei custodi

Un utente deve poter cercare un custode per cognome. Deve inoltre poter

modificare o eliminare il custode appena trovato.

Come si può vedere dall'immagine, la View è molto semplice: ha un'area di

ricerca e una zona nella quale sono visualizzati i risultati. Per eseguire la ricerca è

possibile cliccare sul bottone cerca o premere il tasto Invio dopo aver scritto

qualcosa nel box di ricerca. Facendo doppio clic su un custode, si apre una nuova

View per modificare i dati. Infine c'è un bottone per eliminare un custode.

Page 77: WPF MVVM Toolkit

77

Questo è il secondo tipo di ViewModel molto frequente. Ecco le sue

caratteristiche:

Permette la ricerca su un unico oggetto del dominio.

Visualizza i risultati in una lista.

Permette operazioni di aggiornamento e cancellazione.

Espone solo alcuni campi dell'oggetto.

Page 78: WPF MVVM Toolkit

78

Poiché anche questa serie di operazioni sono molto frequenti, esiste una classe

nel toolkit che raggruppa tutte queste funzioni di base: ListViewModelBase.

US3 – Creazione Turno

Si deve poter assegnare un turno a una società presso una palestra e il costo che

la società deve accollarsi per usufruire dello spazio. Contestualmente si deve

anche associare il custode e quanto deve essere dovuto allo stesso per il lavoro

svolto.

Questa View permette di scegliere ora, giorno e tipo di turno. A essi associa una

palestra, una società e un custode. È possibile fare una ricerca per trovare quello

che serve. In oltre, in automatico il sistema propone un costo per la palestra e

per il custode; costi che possono essere modificati se necessario.

Page 79: WPF MVVM Toolkit

79

Questo è l'ultimo tipo di ViewModel. Sono ViewModel generici, che hanno solo

una cosa in comune fra loro: recuperano i dati da molti oggetti diversi del

modello e poi visualizzano solo quello che è necessario.

Per questi ViewModel non esiste una classe base e spesso sono molto complessi

da realizzare perché fanno molte cose diverse.

In questo caso specifico, queste sono le caratteristiche del ViewModel:

Legge i dati da sei oggetti diversi.

Esegue tre tipi di ricerche manuali.

Esegue due ricerche automatiche.

Permette la modifica delle ricerche automatiche.

Inserisce il turno nel database.

Page 80: WPF MVVM Toolkit

80

Conclusioni

In questa tesi si è parlato in dettaglio del pattern Model-View-ViewModel, delle

sue caratteristiche, vantaggi e svantaggi ed è stata anche proposta un'imple-

mentazione. Il pattern è molto giovane, non esistono ancora delle linee guida:

per adesso ci sono solamente una serie di problemi e la comunità degli svilup-

patori sta proponendo soluzioni diverse.

Anche Windows Presentation Foundation è giovane come tecnologia, e,

nonostante Microsoft sia spingendo molto, non è ancora entrata a far parte del

bagaglio di tutti gli sviluppatori di applicazioni desktop in ambiente Windows.

Tutto ciò ha una serie di cause, prima fra tutte, la curva di apprendimento di

WPF molto ripida. Inoltre, mancano ancora una serie di controlli che sono

presenti in Windows Forms e rendono ancora questa tecnologia appetibile.

Infine solo a metà luglio 2009 è uscito Expression Blend 3, il primo designer che

finalmente riesce ad esprimere tutte le potenzialità di WPF.

In rete si trovano tutta una serie di articoli e toolkit che implementano il pattern

MVVM. Tra i più famosi ci sono il MVVM Light Toolkit11 di Laurent Bugnion,

Cinch12 di Sasha Barber e Goldlight13 di Peter O'Hanlon. Questi tre toolkit hanno

implementazioni anche molto diverse fra loro, ma tutte risolvono i problemi che

sono stati affrontati in questa tesi.

Se invece, oltre ad affrontare i problemi associati al pattern MVVM, si vuole

anche creare un'applicazione modulare sfruttando la UI composition, il toolkit di

riferimento e Prism14 sviluppato dal team di Pattern and Practices di Microsoft.

11

Per maggiori informazioni è possibile consultare il sito http://www.galasoft.ch/mvvm/getstarted/

12 Per maggiori informazioni è possibile consultare il sito http://cinch.codeplex.com/

13 Per maggiori informazioni è possibile consultare il sito http://goldlight.codeplex.com/

14 Per maggiori informazioni è possibile consultare il sito http://compositewpf.codeplex.com/

Page 81: WPF MVVM Toolkit

81

La giovane età di WPF e del pattern MVVM possono far pensare che non sia

ancora il caso di sviluppare un'applicazione enterprise utilizzando queste

tecniche e tecnologie. Io ritengo invece, che nonostante tutti i problemi che la

loro giovinezza può portare, ha assolutamente senso sviluppare una nuova

applicazione in WPF in sinergia con il pattern MVVM. Questo perché la versatilità

e l'espandibilità, sia nel senso delle caratteristiche, che del layout

dell'applicazione portano un vantaggio intrinseco che supera notevolmente

qualsiasi problema.

Page 82: WPF MVVM Toolkit

82

Appendice uno: come scrivere un'applicazione WPF

Quest'appendice contiene alcuni suggerimenti per scrivere applicazioni WPF.

XAML Conventions

XAML è XML e come tutti i formati XML ha una formattazione standard che è

bene utilizzare. Ci sono, però, un paio di cose da prendere in considerazione.

Utilizzare x:Name al posto di Name

Nonostante entrambi gli attributi facciano la stessa cosa, Name può essere

utilizzato solo per alcuni elementi, mentre x:Name funziona per tutti gli

elementi (eccetto gli elementi in un resource dictionary). Quando si guarda una

lunga lista di attributi di un elemento, gli attributi che cominciano con il prefisso

"x:" tendono ad essere più facili da vedere. Per rendere più facile trovare il

nome di un elemento è sempre meglio utilizzare x:Name.

Posizionare il primo attributo di un elemento nella riga sotto il nome

dell'elemento

Questa regola esiste perché la maggior parte degli editor XML indenta ogni riga

allo stesso livello del primo attributo. Per esempio:

<StackPanel DockPanel.Dock="Top"

Orientation="Horizontal"

Margin="3"

Background="{TemplateBinding Background}">

<Button Content="Hello"

Background="{StaticResource

Brush_BtnBackground}"

Width="300" />

<ImageButton Source="{StaticResource Image_Copy}"

Width="16"

Height="16" />

Page 83: WPF MVVM Toolkit

83

Il nome degli elementi XML denota il livello d'indentazione, ma per gli attributi

XML non è corretto. Se invece si mette il primo attributo in una nuova riga, si

ottiene un layout migliore, come questo:

<StackPanel

DockPanel.Dock="Top"

Orientation="Horizontal"

Margin="3"

Background="{TemplateBinding Background}"

>

<Button

Content="Hello"

Background="{StaticResource Brush_BtnBackground}"

Width="300"

/>

<ImageButton

Source="{StaticResource Image_Copy}"

Width="16"

Height="16"

/>

Utilizzando questo sistema, tutti gli attributi dello stesso livello hanno la

medesima indentazione.

Preferire StaticResource a DynamicResource

La differenza tra {StaticResource } e {DynamicResource } consiste

soprattutto nella possibilità di cambiare la risorsa a runtime o meno.

Una risorsa statica è collegata allo startup dell'applicazione e poi non può essere

più modificata. Una risorsa dinamica invece può essere modificata a runtime.

È importante sapere quando utilizzare un tipo di risorsa e quando l'altra.

Generalmente StaticResource è il default. Purtroppo, tool come Blend, hanno la

naturale tendenza a inserire DynamicResource. Combattere contro un tool non è

mai una buona idea, per cui questa più che una regola diventa una preferenza.

Page 84: WPF MVVM Toolkit

84

Naming Convention per gli elementi

Quando si utilizza x:Name su un elemento, cercare di essere consistenti. Per gli

elementi in un ControlTemplate, PART_Qualcosa è lo standard per gli

elementi che hanno un significato speciale per il controllo al quale si applica il

template. Per tutti gli altri elementi, iniziare il nome con il prefisso "_", oppure

nomi camel case, o pascal case vanno bene. In ogni caso, non chiamare mai gli

elementi in questo modo: "n", o "foo", o "border1".

Unire le risorse a livello di Applicazione

Quando si utilizzano dei merged resource dictionary, è possibile creare un

riferimento al file XAML della risorsa da ogni finestra ed anche nella root, cioè

nello XAML del file Application. Il problema nasce quando il resource dictionary è

collegato sia al file della window sia alla root. Questo accade spesso quando i

controlli sono dichiarati in progetti separati, perché i tool come Blend tendono

ad inserire un riferimento in ogni file che utilizza la risorsa. Quello che accade è

che ogni volta che il resource dictionary è istanziato, le risorse sono

completamente ricaricate, anche se esistono già più in alto nell'albero dei

controlli.

Organizzazione delle risorse

Non appena i progetti WPF crescono, bisogna spostare le risorse dal file

App.xaml ai file di risorse. Ma come organizzare tutti questi file?

Strutturare le risorse

I file XAML possono essere divisi in due categorie:

File XAML funzionali – per esempio Window, UserControl o Page.

File XAML di risorse – Resource Dictionary e il file App.xaml.

I file di risorse possono a loro volta essere suddivisi in categorie:

Page 85: WPF MVVM Toolkit

85

File XAML di risorse, generici – questi file contengono cose che possono

essere utilizzate trasversalmente da tutta l'applicazione. Brush,

ImageSource, alcuni stili e i converter sono ottimi candidati.

File XAML di risorse, specifici – questi file contengono solamente le

risorse di una specifica Window o UserControl o Page.

Naturalmente è possibile inserire le risorse specifiche di una window

direttamente nel file XAML stesso, per esempio nell'elemento

Window.Resources. Questo funziona bene all'inizio, poi, però, con il crescere

di data template, stili e quant'altro, le risorse diventano tante. A questo punto la

soluzione migliore è quella di migrare tutte le risorse in un file collegato che può

essere utilizzato solo da questa determinata window.

Una struttura d'esempio potrebbe essere questa:

MyWpfApplication/

o /Shared/ – una cartella che contiene tutte le risorse condivise

dall'applicazione.

/Converters/ – contiene tutti i converter.

/Images/ – contiene tutte le immagini.

/BrushResources.xaml – file che contiene solid brush,

grandient brush eccetera.

/ConverterResources.xaml – crea una risorsa statica per

tutti i converter. In questo modo per utilizzare un

converter basta collegare questo resource dictionary senza

dover prima creare la risorsa. Questo ha come vantaggio

che il converter è creato una volta sola.

/ImageResources.xaml – analogamente al precedente, solo

che crea una risorsa per tutte le immagini.

o /View/ – una cartella che contiene tutte le view di

un'applicazione.

Page 86: WPF MVVM Toolkit

86

MyView.xaml – file xaml funzionale che contiene la

struttura di MyView.

MyView.resources.xaml – file XAML che contiene tutte le

risorse utilizzate solo da MyView.

o /ViewModel/ – una cartella che contiene tutti i view model.

MyViewModel.cs – file che contiene il viewmodel

associato a MyView.

Questa struttura ha il vantaggio di mantenere i file XAML piccoli e permette allo

sviluppatore di trovare rapidamente la risorsa che sta cercando. Se un file XAML

ha più di 400 righe, probabilmente bisogna decidere se è il caso di suddividere il

file.

Naming Convention

Il problema maggiore di avere molti file di risorse è che non si sa in quale file si

trova una risorsa. In C#, è possibile spostare il mouse su una variabile, e Visual

Studio mostra una serie d'informazioni, non ultima in quale file quella variabile è

definita. Sfortunatamente non esiste nulla di analogo in XAML. Per limitare

questo problema si deve utilizzare una naming convention ben precisa. Le regole

sono semplici:

Ogni file di risorse deve terminare con Resources.xaml.

Ogni risorsa, o stile, definito nel file XYZResource.xaml deve iniziare con il

prefisso "XYZ_". In questo modo, semplicemente leggendo il nome di

una risorsa si capisce in quale file è stata definita.

Ogni risorsa definita in un file XAML funzionale deve iniziare con il

prefisso "Local_".

Page 87: WPF MVVM Toolkit

87

Appendice due: come disegnare una Window in WPF

Metà della battaglia di ogni interfaccia utente è organizzare il contenuto in modo

che sia attraente, pratico e flessibile. Ma la vera sfida è fare in modo che il layout

si adatti da solo alle differenti dimensioni della finestra.

In WPF, si controlla il layout utilizzando diversi contenitori. Ogni contenitore ha

la sua specifica logica di layout. Se si proviene da Windows Forms, si rimarrà

stupiti che utilizzare un layout basato su coordinate, è fortemente scoraggiato in

WPF. Al suo posto, sono consigliati layout più flessibili che si possano adattare al

cambiamento di contenuto, lingue differenti e alla varietà di dimensioni della

finestra. Per la maggior parte degli sviluppatori che si muovono verso WPF, il

nuovo sistema di layout è una grande sorpresa, e la prima vera sfida.

Una finestra WPF può contenere solo un singolo elemento. Per inserirne più di

uno e creare un'interfaccia utente più pratica, c'è bisogno di inserire un

contenitore nella finestra e aggiungere gli altri elementi al contenitore. Questa

limitazione deriva dal fatto che la classe Windows eredita da

ContentControl.

In WPF, il layout è determinato dal contenitore che si utilizza. Nonostante ci

siano vari controlli che si possono scegliere, una finestra WPF "ideale" segue

questi principi:

Gli elementi (come i controlli) non devono avere una dimensione fissa. In

questo modo possono crescere in funzione del loro contenuto. Per

esempio, un bottone si espande se si aggiunge più testo. Si possono

limitare i controlli settando una dimensione minima e massima.

Gli elementi non devono indicare la loro posizione tramite coordinate. In

questo modo sono posizionati dal contenitore in funzione della loro

dimensione, ordine e altre informazioni che specifica il contenitore di

Page 88: WPF MVVM Toolkit

88

layout. Se c'è bisogno di aggiungere spazio tra i controlli si può utilizzare

la proprietà Margin.

I contenitori di layout "condividono" lo spazio disponibile con i loro figli.

Essi cercano di dare a ogni elemento la dimensione richiesta se c'è spazio

libero. Inoltre possono distribuire lo spazio extra tra i figli.

È possibile inserire i controlli di layout uno dentro l'altro. Una tipica

interfaccia utente inizia con una Grid, e contiene altri contenitori di

layout per posizionare piccoli gruppi di elementi.

Tutti i controlli che ereditano da Panel sono controlli di layout. WPF ne mette a

disposizione diversi. Ecco i principali:

Canvas: contenitore base. Permette di posizionare i controlli indicando

esplicitamente le coordinate.

StackPanel: contenitore molto popolare a causa della sua semplicità

di utilizzo. Come il nome suggerisce, posiziona i controlli figli in pila.

WrapPanel: simile allo StackPanel. Oltre a mettere i figli in pila, se

lo spazio sulla riga (o colonna) termina, posiziona gli oggetti rimanenti

nella riga sottostante (o colonna a destra).

DockPanel: permette di attaccare un elemento a un bordo del

pannello.

Grid: contenitore più versatile. Permette di posizionare i figli in una

griglia. Lavorare con una Grid per certi versi è come utilizzare una Table

in HTML.

Grid

La Grid è il più potente contenitore di layout presente in WPF. Tutto quello che

si può fare con gli altri contenitori si può fare anche con la Grid. La Grid è

ottima anche per suddividere la finestra in piccole regioni che si possono

controllare tramite altri contenitori.

Page 89: WPF MVVM Toolkit

89

<Window x:Class="WpfApplication2.Window1"

xmlns="http://.../winfx/2006/xaml/presentation"

xmlns:x="http://.../winfx/2006/xaml"

Title="Window1" Height="300" Width="300">

<Grid>

</Grid>

</Window>

La Grid separa gli elementi in una griglia invisibile di righe e colonne. Anche se

più di un oggetto può stare nella stessa cella, generalmente ha più senso mettere

un solo elemento per cella. Naturalmente, questo elemento può essere un altro

contenitore che organizza il suo gruppo di elementi.

Creare un layout basato su griglia è un processo in due fasi.

1. Scegliere il numero di righe e colonne.

<Grid>

<Grid.RowDefinitions>

<RowDefinition></RowDefinition>

<RowDefinition></RowDefinition>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition></ColumnDefinition>

<ColumnDefinition></ColumnDefinition>

<ColumnDefinition></ColumnDefinition>

</Grid.ColumnDefinitions>

...

</Grid>

2. Assegnare a ogni controllo il suo numero di riga e colonna, per inserirlo

nella cella corretta.

<Grid>

...

<Button Grid.Row="0" Grid.Column="0">

Top Left

</Button>

Page 90: WPF MVVM Toolkit

90

<Button Grid.Row="0" Grid.Column="1">

Middle Left

</Button>

<Button Grid.Row="1" Grid.Column="2">

Bottom Right

</Button>

<Button Grid.Row="1" Grid.Column="1">

Bottom Middle

</Button>

</Grid>

Se la Grid fosse semplicemente una lista di righe e colonne della stessa

dimensione non ci sarebbero problemi. Fortunatamente non è così. Per

sbloccare tutto il potenziale della Grid si può cambiare il modo che ogni riga e

colonna usa per calcolare la dimensione.

La Grid supporta tre strategie di dimensionamento:

Dimensione assoluta. È possibile scegliere la dimensione esatta di una

riga (o colonna). È la strategia meno utile, perché è la meno flessibile.

<ColumnDefinition Width="100" />

Dimensione automatica. Ogni riga (o colonna) ha esattamente la

dimensione che serve e non di più. È la strategia più utilizzata.

<ColumnDefinition Width="Auto" />

Page 91: WPF MVVM Toolkit

91

Dimensione proporzionale. Lo spazio è diviso tra tutte le righe (o

colonne). Questo è il valore standard per tutte le righe e colonne.

<ColumnDefinition Width="*" />

Infine ecco un esempio di utilizzo.

<Grid ShowGridLines="True">

<Grid.RowDefinitions>

<RowDefinition Height="*"></RowDefinition>

<RowDefinition Height="Auto"></RowDefinition>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="Auto"/>

</Grid.ColumnDefinitions>

<TextBox Margin="10" Grid.Row="0" Grid.Column="0"

Grid.ColumnSpan="3">

This is a test.</TextBox>

<Button Margin="10,10,2,10" Padding="3"

Grid.Row="1"

Grid.Column="1">OK</Button>

<Button Margin="2,10,10,10" Padding="3"

Grid.Row="1"

Grid.Column="2">Cancel</Button>

</Grid>

Page 92: WPF MVVM Toolkit

92

Bibliografia

Pro WPF in C# 2008: Windows Presentation Foundation with .NET 3.5, 2nd

Edition di Matthew MacDonald.

Windows Presentation Foundation Unleashed di Adam Nathan.

Patterns of Enterprise Application Architecture di Martin Fowler.

Design Patterns: Elements of Reusable Object – Oriented Software, di Erich

Gamma, Richard Helm, Ralf Johnson, John M. Vlissides.

Test – Driven Development: By Example di Kent Beck.

Microsoft .NET: Architecting Applications for the Enterprise di Dino Esposito e

Andrea Saltarello.

Refactoring – Improving the Design of Existing Code di Martin Fowler.

Code Complete 2nd Edition di Steve McConnell.

Framework Design Guidelines – Conventions, Idioms, and Patterns for Reusable

.NET Libraries di Krzysztof Cwalina e Brad Adam.

CLR via C# 2nd Edition di Jeffrey Richter.

Page 93: WPF MVVM Toolkit

93

Web

WPF Disciples:

http://groups.google.com/group/wpf-disciplines

Blog di Mauro Servienti:

http://blogs.ugidotnet.org/topics/Default.aspx

Blog di Corrado Cavalli:

http://blogs.ugidotnet.org/corrado/Default.aspx

Blog di John Gossman:

http://blogs.msdn.com/johngossman/default.aspx

Blog di Lauren Bugnion:

http://geekswithblogs.net/lbugnion/Default.aspx

Blog di Josh Smith:

http://joshsmithonwpf.wordpress.com/

MSDN Magazine:

http://msdn.microsoft.com/en-us/magazine/default.aspx

Wikipedia:

http://www.wikipedia.org/

Prism:

http://www.codeplex.com/CompositeWPF

Microsoft Be. IT:

http://www.microsoft.com/italy/beit/