Questo post è stato scritto da Matteo Pagani, Support Engineer in Microsoft per il programma AppConsult
Model-View-ViewModel (da ora in poi, MVVM) è sicuramente uno degli argomenti più "delicati" quando si parla di sviluppo di Universal Windows app. Chi non lo ha mai utilizzato e ne sente parlare per la prima volta ne rimane spiazzato, perché è un approccio completamente diverso da quello a cui sono abituati. Chi lo utilizza da tempo, invece, non riuscirebbe più farne a meno. Da questa considerazione nasce questa serie di post: che cos'è MVVM? Perché è così diffuso nello sviluppo di Universal Windows app? Quali sono i concetti base da sapere? Lo scopo di questi post è quello di cercare di dare una risposta a tutte queste domande.
Il pattern MVVM
La prima cosa importante da chiarire è che MVVM non è un framework o una libreria, ma un pattern, ovvero un approccio allo sviluppo. Spesso si sente parlare di librerie come MVVM Light o Caliburn Micro e le si confondono con MVVM, ma in realtà sono solo strumenti che aiutano lo sviluppatore nell'adozione di questo pattern.
Lo scopo di un pattern architetturale, come dice il nome stesso, è quello di definire un'architettura ben precisa dell'applicazione, ovvero un modo in cui organizzare il nostro progetto. Perché sorge questa necessità e non possiamo semplicemente continuare a sviluppare l'applicazione come siamo abituati, ovvero a scrivere tutto il codice nel code-behind?
L'approccio tradizionale basato sul code-behind è sicuramente molto veloce e pratico ma, alla lunga e su progetti di maggiore complessità, mostra diversi limiti. Questo perché la classe di code-behind ha una fortissima dipendenza con la pagina XAML a cui è collegata. Di conseguenza, molte parti di codice sono difficilmente isolabili e ciò si traduce, spesso e volentieri, nell'includere all'interno del code-behind sia logica applicativa che la gestione dell'interfaccia grafica (animazioni, interazioni con l'utente, ecc.).
Ciò rende:
- Più complicato mantenere ed evolvere il codice: ogni volta che dobbiamo aggiungere una nuova funzionalità o risolvere un bug, è complicato capire qual è il punto più adatto, perché non c'è una chiara distinzione tra le varie componenti dell'applicazione.
- Difficile l'esecuzione di test automatici. Nel mondo dello sviluppo software, soprattutto in progetti di media / grande complessità, è sempre più diffusa la pratica dello unit testing: si tratta di test eseguiti in maniera automatica su piccole e isolate porzioni di codice, che ne verificano la validità e la bontà. Grazie allo unit testing, diventa più semplice per lo sviluppatore gestire l'evoluzione del progetto: i test, infatti, ci aiuteranno a validare l'aggiunta di una nuova feature o la risoluzione di un bug, assicurandoci che il nuovo codice non vada a "rompere" quello già esistente.
- Complesso fare il design dell'interfaccia grafica. Dato che c'è un collegamento così stretto tra logica e UI, non è possibile astrarre l'interfaccia e concentrarsi solamente sulla parte di presentazione dei dati, ma dobbiamo conoscere bene anche i dettagli implementativi. Ad esempio, i dati arrivano da un database? Da un servizio web? Dal cloud? Sono tutte domande che chi si sta occupando del design dell'interfaccia grafica non dovrebbe porsi.
L'obiettivo principale di MVVM (ma non solo, è lo scopo principale di molti altri pattern architetturali come MVC o MVP) è proprio quello di "rompere" questa forte dipendenza tra il code-behind e l'interfaccia grafica, rendendo chiara e netta la separazione tra le componenti dell'applicazione che si occupano di gestire la logica e quelle che invece definiscono la parte di presentazione.
Il nome del pattern deriva dal fatto che, applicandolo, l'applicazione viene idealmente suddivisa in tre componenti, che ora vediamo in dettaglio.
Model
La componente definita modelè quella che sta nel gradino più basso della scala ed è rappresentata da tutte le classi che definiscono e manipolano le entità base dell'applicazione. La caratteristica di questo strato è che non deve avere alcuna dipendenza da come i dati saranno poi effettivamente rappresentati. Idealmente, dovrebbe essere possibile prendere le classi che fanno parte di questa componente e riutilizzarle in un'altra applicazione dallo scopo completamente diverso senza problemi.
Ad esempio, in un'applicazione per gestire gli ordini di un'azienda, il model potrebbe essere costituito dalle classi che definiscono le entità base, come il cliente, l'ordine, il prodotto, ecc.
View
La componente definita view, invece, è quella che sta nel gradino più alto della scala ed è rappresentata dall'interfaccia grafica. Nel mondo delle Universal Windows app, le view non sono nient'altro che le pagine XAML della nostra applicazione, che contengono tutti gli elementi che servono a definire il layout visivo.
Sempre ritornando all'esempio della gestione degli ordini, l'applicazione potrebbe avere diverse view per mostrare l'elenco dei clienti, per mostrare i prodotti in magazzino, per scoprire quali ordini ha fatto un cliente, ecc.
ViewModel
La componente definita ViewModelè il punto di contatto tra il model e la view: si fa carico di prendere i dati grezzi dal model, manipolarmi in modo da adattarli per la presentazione e di passarli alla view. Per certi versi, è un po' il ruolo che, nello sviluppo tradizionale, è delegato alla classe di code-behind. C'è, però, un'importantissima differenza: il ViewModel non ha alcuna dipendenza dalla View, è una normalissima classe che contiene del codice.
Tipicamente, in un'applicazione esiste una classe di ViewModel per ogni View, che si fa carico di gestire le interazioni con l'utente e di preparare i dati da presentare.
Perché il pattern MVVM?
Dopo questa breve analisi, dovrebbe essere più semplice capire l'importanza di MVVM e come l'adozione di questo pattern sia in grado di risolvere i problemi evidenziati in precedenza:
- La separazione in tre differenti strati rende molto più semplice, anche all'interno di un team, mantenere ed evolvere l'applicazione. Se si deve aggiungere una feature o risolvere un bug, è facile capire in quale "strato" intervenire, a seconda dello scenario. Inoltre, il lavoro può essere anche parallelizzato, dato che i tre strati sono completamente indipendenti tra di loro.
- Lo unit testing, per definizione, richiede che il codice da testare sia il più isolato e semplice possibile. Nel code-behind, ciò non è possibile: spesso la logica è inclusa in un event handler (ad esempio, perché associato alla pressione di un pulsante) e si dovrebbe in qualche modo simulare lo scatenarsi di tale evento per poter testare l'esecuzione di quel codice. Con l'adozione del pattern MVVM, invece, non c'è più questa stretta dipendenza e la logica definita in un ViewModel, ad esempio, può essere facilmente isolata e testata.
- Dato che non c'è più questo collegamento diretto tra logica e interfaccia utente, chi si occupa del design non deve più necessariamente conoscere i dettagli implementativi. Ad esempio, se il designer deve farsi carico di realizzare lo XAML per mostrare una lista di ordini, possiamo tranquillamente sostituire il ViewModel reale (che magari si appoggia a dei servizi che recuperano i dati da un database o dal web) con uno fittizio, che sia in grado di restituire dei dati esemplificativi che permettano al designer di capire che tipo di informazioni vogliamo mostrare.
Come mai nel mondo delle Universal Windows app si è diffuso particolarmente questo pattern e non altri? Principalmente perché l'implementazione si basa su una serie di caratteristiche native del mondo XAML, come binding, dependency property, ecc. Nel corso di questo e dei prossimi post approfondiremo questi aspetti, così che possano essere chiari anche a chi magari si è appena affacciato a questo mondo e non ha molta dimestichezza.
Potete notare come, poco fa, ho parlato di "mondo XAML": questo perché quanto detto fino ad ora non si applica solo alle Universal Windows app, ma a qualsiasi tecnologia basata su XAML. Il pattern MVVM, infatti, è uno degli approcci più diffusi dai tempi di WPF ed è utilizzabile anche con Silverlight, Windows Phone 7.x, Windows Phone 8.x, Windows 8.x, Xamarin Forms, ecc.
Vediamo ora, in dettaglio, quali sono le caratteristiche fondamentali dello XAML su cui questo pattern si appoggia.
Il binding
Il binding è probabilmente uno dei principali punti di forza dello XAML e costituisce la possibilità di creare un canale di comunicazione (anche bidirezionale) tra due elementi differenti. Possono essere due controlli presenti in una pagina oppure un controllo con un oggetto dichiarato nel codice.
Quello che fa la differenza nel pattern MVVM è proprio il secondo scenario: il collegamento tra la View e il ViewModel avviene proprio grazie al binding. Il ViewModel si farà carico di esporre i dati che deve mostrare la view sotto forma di proprietà, che poi saranno collegate ai controlli nello XAML proprio tramite il binding.
Ipotizziamo, ad esempio, che l'applicazione degli ordini debba mostrare una pagina con una lista di prodotti. Il ViewModel si farà carico di recuperare questa informazione (ad esempio, da un database locale) e di valorizzarla all'interno di una proprietà ad hoc (ad esempio, List<Order>, definita come nell'esempio seguente):
publicList<Order> Orders { get; set; }
La View, tramite il meccanismo del binding, collegherà poi la proprietà ItemsSource di un controllo ListView con tale proprietà del ViewModel:
<ListView ItemsSource="{Binding Path=Orders}" />
Il binding, come anticipato, può essere anche bidirezionale: è il caso in cui non è soltanto il ViewModel che debba passare un dato alla View, ma anche il contrario. Ipotizziamo, ad esempio, che la nostra applicazione abbia una pagina dove l'utente possa inserire un ordine e, di conseguenza, offra una casella di testo dove inserire il nome di un prodotto. Tale informazione deve essere recepita dal ViewModel, dato che sarà lui a gestire l'inserimento del nuovo ordine nel database. In questo caso, si applica al binding l'attributo Mode impostato come TwoWay. In questo modo, ogni volta che l'utente inserirà un testo nel controllo TextBox, la proprietà collegata nel ViewModel si aggiornerà per contenere tale valore.
<TextBox Text="{Binding Path=ProductName, Mode=TwoWay}" />
A questo punto la proprietà ProductName dichiarata nel ViewModel conterrà il testo inserito nella TextBox, dandoci la possibilità di manipolarlo a piacimento.
Il DataContext
Nel paragrafo precedente abbiamo visto come, grazie al binding, possiamo collegare le proprietà definite nel ViewModel con i controlli presenti nello XAML. Ma come fa la View a sapere da quale ViewModel deve recuperare i dati? Introduciamo, perciò, il concetto di DataContext, che è una proprietà esposta da tutti i controlli XAML. Il DataContext definisce il contesto del binding: quando associamo una classe, ad esempio, come DataContext di un controllo, saremo in grado di accedere a tutte le proprietà che essa espone. È importante sottolineare come il DataContext sia gerarchico: non solo il controllo stesso, ma anche tutti i controlli figli saranno in grado di accedere alle medesime proprietà.
Il cuore dell'implementazione del pattern MVVM sta proprio in questa gerarchia: la classe che funge da ViewModel viene impostata come DataContext dell'intera pagina. Di conseguenza, qualsiasi controllo presente all'interno della pagina sarà in grado di accedere a tutte le proprietà esposte dal ViewModel e, di conseguenza, di mostrare o elaborare i dati richiesti.
In un'applicazione sviluppata con il pattern MVVM potrete trovare molto frequentemente una dichiarazione delle pagine XAML simile alla seguente:
<Page
x:Class="Sample.MainPage"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding Source={StaticResource MainViewModel}}"
mc:Ignorable="d">
</Page>
La proprietà DataContext della pagina (rappresentata dalla classe Page) è stata collegata ad una risorsa rappresentata da un'istanza della classe MainViewModel. Questo è il "cuore" del pattern MVVM, che consente di collegare View e ViewModel tra di loro.
L'interfaccia INotifyPropertyChanged
Se provassimo a scrivere una semplice applicazione MVVM con quanto imparato finora, ci accorgeremmo di un problema. Per capirlo, riprendiamo l'esempio precedente della pagina dedicata all'inserimento degli ordini e ipotizziamo di avere, nel ViewModel, una proprietà che ci serve per mostrare il nome di un prodotto.
public string ProductName { get; set; }
Questa proprietà è collegata ad un controllo TextBlock presente nella pagina:
<TextBlock Text="{Binding Path=ProductName}" />
Ora ipotizziamo che, per qualsiasi motivo, all'interno del ViewModel il valore della proprietà ProductName cambi: ad esempio, perché l'utente ha premuto un pulsante, oppure perché l'operazione di caricamento dei dati è terminata. Ci accorgeremmo di come, nonostante questo cambiamento, il controllo TextBlock nella pagina continui a mostrare il vecchio valore.
Questo perché l'utilizzo del binding non è sufficiente: il binding ha creato un canale di comunicazione tra la proprietà ProductName e il controllo TextBlock, ma nessuno ha notificato le due estremità del canale che il valore della proprietà è cambiato. Per questo motivo, lo XAML mette a disposizione le dependency property, ovvero delle proprietà che possono definire una logica anche complessa e, in fase di binding, inviare una notifica ogni volta che il suo valore cambia.
La quasi totalità delle proprietà dei controlli XAML (come, ad esempio, la proprietà Text del controllo TextBlock) sono dependency property. La dichiarazione di tali proprietà speciali, però, non è particolarmente intuitiva e, nella maggior parte dei casi, le funzionalità offerte sono fin troppo avanzate per i nostri scenari. Pensiamo all'esempio precedente: non abbiamo bisogno di implementare alcuna logica particolare, vogliamo semplicemente che ogni cambiamento nel valore della proprietà ProductName sia in grado di notificare l'altra estremità del binding che è successo qualcosa e che la UI si deve aggiornare.
Per questi scenari lo XAML mette a disposizione una specifica interfaccia chiamata INotifyPropertyChanged, che possiamo implementare nei nostri ViewModel. In questo modo, per raggiungere lo scopo di notificare la UI dei cambiamenti, non dovremo creare delle complesse dependency property, ma semplicemente implementare questa interfaccia e invocare il relativo metodo ogni volta che il valore della proprietà cambia.
Ecco come appare un ViewModel che implementa tale interfaccia:
publicclassMainViewModel: INotifyPropertyChanged
{
publiceventPropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protectedvirtualvoid OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, newPropertyChangedEventArgs(propertyName));
}
}
Come potete notare, l'implementazione dell'interfaccia ci mette a disposizione un metodo chiamato OnPropertyChanged(), da richiamare ogni volta che il valore di una proprietà è cambiato. Per raggiungere questo scopo, dobbiamo cambiare l'approccio con cui definiamo le proprietà all'interno del ViewModel. Prendiamo come esempio la dichiarazione standard vista in precedenza:
public string ProductName { get; set; }
Questa sintassi utilizza un getter e un setter predefiniti e, di conseguenza, non ci permette di personalizzare la logica con cui la proprietà viene assegnata. Abbiamo bisogno, perciò, di tornare ad utilizzare il vecchio approccio, basato su una variabile privata di appoggio: in questo modo, invece di usare il metodo set standard, potremo personalizzarlo a piacimento.
Ecco come appare la stessa proprietà modificata con il nuovo approccio:
privatestring _productName;
publicstring ProductName
{
get { return _productName; }
set
{
_productName = value;
OnPropertyChanged();
}
}
Come potete notare, in fase di set, oltre a salvare il valore che è stato assegnato, chiamiamo anche il metodo OnPropertyChanged. In questo modo, nel caso in cui tale proprietà sia collegata tramite binding ad un controllo della UI, verrà inviata una notifica sul canale: il controllo la riceverà e aggiornerà il proprio stato visivo per mostrare il nuovo valore.
I command: gestire gli eventi
Un altro scenario fondamentale nello sviluppo di applicazioni è la gestione delle interazioni con l'utente: la pressione di un pulsante, la scelta di un elemento da una lista, ecc. Nello XAML, questi scenari vengono gestiti tramite gli eventi che vengono messi a disposizione dai vari controlli. Ad esempio, se vogliamo gestire la pressione di un pulsante, dobbiamo sottoscriverci all'evento Click, come nell'esempio seguente:
<Button Content="Click me" Click="OnButtonClicked" />
L'evento viene gestito da un event handler, ovvero un metodo che include, tra i parametri, alcune informazioni che permettono di capire il contesto (quali il controllo che ha scatenato l'evento o quale elemento della lista è stato scelto), come nell'esempio:
privatevoid OnButtonClicked(object sender, RoutedEventArgs e)
{
//do something
}
Il problema di questo approccio è che gli event handler hanno una forte dipendenza con la View: possono, infatti, essere dichiarati solo nella classe di code behind. Quando si sviluppa un'applicazione utilizzando il pattern MVVM, invece, i dati e la logica sono definiti nel ViewModel: è altamente probabile che le informazioni che ci servono per gestire l'interazione dell'utente si trovino proprio lì.
Per questo motivo lo XAML ha introdotto il concetto di command, ovvero la possibilità di esprimere un'interazione dell'utente con una proprietà invece che con un event handler. Trattandosi di una semplice proprietà, non c'è più la necessità di avere uno stretto legame con la view: può essere tranquillamente dichiarata anche in una classe completamente indipendente come un ViewModel.
Il framework mette a disposizione un'interfaccia di nome ICommand per implementare i command, i quali sono rappresentati da una classe specifica. Ecco l'esempio dello scheletro di un comando:
publicclassClickCommand : ICommand
{
publicbool CanExecute(object parameter)
{
thrownewNotImplementedException();
}
publicvoid Execute(object parameter)
{
thrownewNotImplementedException();
}
publiceventEventHandler CanExecuteChanged;
}
Il cuore del command è il metodo Execute(), che contiene il codice che deve essere eseguito quando il comando viene invocato (ad esempio, la pressione del pulsante). Fondamentalmente, si tratta del codice che nell'approccio tradizionale avremmo scritto all'interno dell'event handler.
Il metodo CanExecute(), invece, è uno degli aspetti più interessanti dei command, in quanto permette di gestire il ciclo di vita di un comando che, in alcuni casi durante l'utilizzo dell'applicazione, potrebbe essere disabilitato. Ipotizziamo, ad esempio, di avere una pagina che contenga un form da compilare, con un pulsante da premere al termine della procedura. Tale pulsante, fino a che l'utente non ha compilato tutti i campi, deve essere disabilitato: in questo caso, è sufficiente che il metodo CanExecute() ritorni il valore false. In automatico, il controllo Button collegato a questo comando cambierà il proprio stato visivo, per far capire all'utente che non può essere premuto.
Infine, il comando espone anche un evento chiamato CanExecuteChanged, da invocare all'interno del ViewModel ogni qualvolta le condizioni che verificano l'abilitazione o meno del comando siano cambiate. Ad esempio, riprendendo l'esempio precedente del form, l'evento CanExecuteChanged dovrebbe essere chiamato ogni qualvolta uno dei campi è stato correttamente compilato.
Una volta definito un comando, lo possiamo collegare ai controlli nello XAML grazie alla proprietà Command, che viene esposta da tutti i controlli che prevedono l'interazione con l'utente (come Button).
<Button Content="Click me" Command="{Binding Path=ClickCommand}" />
Come vedremo nel prossimo post, in cui vedremo più in dettaglio come implementare il pattern in un'applicazione, in realtà l'approccio visto in precedenza (ovvero creare una classe per ogni comando) non viene quasi mai adottato. Tutti i principali toolkit per l'implementazione del pattern MVVM offrono infatti delle classi che sfruttano, dietro le quinte, l'interfaccia ICommand, e consentono di definire un comando direttamente nel ViewModel.
Ad esempio, il popolare toolkit MVVM Light offre una classe chiamata RelayCommand, che permette di esprimere un comando nel seguente modo:
privateRelayCommand _sayHello;
publicRelayCommand SayHello
{
get
{
if (_sayHello == null)
{
_sayHello = newRelayCommand(() =>
{
Message = string.Format("Hello {0}", Name);
}, () => !string.IsNullOrEmpty(Name));
}
return _sayHello;
}
}
Come vedete, il comando viene espresso come un semplice proprietà di tipo RelayCommand che, in fase di inizializzazione, accetta due parametri:
- Il metodo da eseguire quando il comando viene invocato (nell'esempio, espresso tramite un anonymous method).
- Il metodo da valutare per determinare se il comando sia abilitato o meno.
Approfondiremo il discorso nel prossimo post, in cui inizieremo a scrivere un po' di codice.
Come implementare il pattern MVVM: i toolkit e framework disponibili
Come anticipato in precedenza, MVVM è un pattern architetturale e non è rappresentato da una specifica libreria o da un framework. Come abbiamo potuto vedere fin qui, però, la creazione di un'applicazione realizzata con questo pattern richiede la messa in atto di una serie di processi standard: l'implementazione dell'interfaccia INotifyPropertyChanged, la gestione dei command, ecc.
Per questo scopo, diversi sviluppatori hanno dato vita a delle librerie che semplificano il lavoro per lo sviluppatore, dandogli la possibilità di concentrarsi non tanto sugli aspetti tecnici dell'implementazione del pattern, quanto sullo sviluppo dell'applicazione vera e propria.
Vediamo una breve panoramica delle librerie più diffuse.
MVVM Light
MVVM Light (http://www.mvvmlight.net/) è una libreria realizzata da Laurent Bugnion, Microsoft MVP da moltissimi anni e uno degli sviluppatori più influenti nel mondo Microsoft.
La grande diffusione di questa libreria è dovuta alla sua estrema semplicità e flessibilità. MVVM Light, infatti, si limita ad offrire gli strumenti base per l'implementazione del pattern, come:
- Una classe base da cui far ereditare i ViewModel, per avere accesso ad una serie di funzionalità base come la propagazione delle notifiche.
- Una classe base per la gestione dei command.
- Un sistema di messaggistica, per gestire la comunicazione tra classi diverse (come due ViewModel).
- Un sistema base per la gestione della dependency injection, ovvero un meccanismo alternativo per l'inizializzazione dei ViewModel. Ne parleremo in maniera più approfondita nei prossimi post.
La grande semplicità di MVVM Light fa sì che tale libreria non sia compatibile solamente con lo sviluppo di Universal Windows app, ma anche con applicazioni WPF, Silverlight e persino Android e iOS tramite Xamarin. Inoltre, ciò la rende anche estremamente flessibile e facile da adattare alle proprie esigenze o come punto di partenza per le proprie personalizzazioni. Tale semplicità, però, è anche il punto di debolezza di MVVM Light. Come vedremo nei prossimi post, lo sviluppo di Universal Windows app con il pattern MVVM introduce una serie di sfide, legate al fatto che molte delle funzionalità base sono sfruttabili solo da code behind (come la navigazione tra le pagine, la gestione dei contratti, ecc.). In quest'ottica, MVVM Light non aiuta lo sviluppatore in alcun modo: offrendo solo gli strumenti base per l'implementazione del pattern, tutto il resto è a carico dello sviluppatore.
Proprio per questo motivo sono nate molte librerie (come Cimbalino Toolkit) che consentono di estendere MVVM Light e di aggiungere una serie di servizi e di funzionalità specifiche per le Universal Windows app.
Caliburn Micro
Caliburn Micro (http://caliburnmicro.com/) è un framework realizzato originalmente da Rob Eisenberg e ora mantenuto principalmente da Nigel Sampson e Thomas Ibel. Se per MVVM Light si parla di toolkit, qui parliamo di un vero e proprio framework, che ha un approccio completamente opposto: se con MVVM Light abbiamo a disposizione solo gli strumenti base, Caliburn Micro invece offre un ricco set di servizi e funzionalità specifici per l'implementazione di scenari specifici delle Universal Windows app, come la navigazione, lo storage, lo sharing, ecc.
La caratteristica principale di Caliburn Micro è quella di sfruttare un approccio basato su convenzioni: molte delle funzionalità base del pattern (come il binding o la definizione del DataContext) vengono nascoste da una serie di regole che definiscono i nomi che devono avere le varie componenti dell'applicazione.
Ad esempio, se volessimo collegare una proprietà di un ViewModel ad un controllo nello XAML non è necessario usare il binding: possiamo semplicemente assegnare al controllo e alla proprietà lo stesso nome e, dietro le quinte, Caliburn Micro si farà carico di implementare il binding per noi. Ciò è reso possibile dal bootstrapper, ovvero una classe speciale di Caliburn Micro che, in fase di avvio dell'applicazione, si fa carico di inizializzare tutta l'infrastruttura necessaria.
Il punto di forza principale di Caliburn Micro è sicuramente la potenza: sin da subito avrete a disposizione tutti gli strumenti necessari per realizzare una Universal Windows app con il pattern MVVM e risolvere le principali sfide. Non è, però, lo strumento più adatto per i neofiti: se il fatto di nascondere i meccanismi base del pattern può non essere un grosso problema per gli sviluppatori più esperti, potrebbe invece creare qualche grattacapo a chi si sta affacciando al pattern per la prima volta, che potrebbe non comprendere bene cosa stia succedendo dietro le quinte.
Prism
Prism (https://github.com/PrismLibrary/Prism) è un altro framework piuttosto diffuso, originalmente creato e mantenuto dalla divisione Pattern & Practises di Microsoft e ora mantenuto dalla community, nello specifico da un gruppo di sviluppatori indipendenti e MVP Microsoft.
L'approccio di Prism è il medesimo di Caliburn Micro: è basato su una serie di convenzioni, per gestire l'associazione tra le varie componenti dell'applicazione, e offre una serie di servizi già pronti per supportare gli scenari più comuni nello sviluppo di Universal Windows app, come la gestione della navigazione, del ciclo di vita dell'applicazione, ecc.
Come soluzione, si pone a metà strada tra MVVM Light e Caliburn Micro: non è così basilare come il toolkit di Laurent Bugnion, ma allo stesso tempo non fa un uso così "aggressivo" delle convenzioni come Caliburn Micro, rendendo più evidenti e comprensibili i meccanismi base del pattern.
Nei prossimi post
Nei prossimi post inizieremo a mettere in pratica la teoria appresa nel corso del questo post e sfrutteremo MVVM Light per i nostri esempi: questo non perché gli altri framework non siano validi (ad esempio, Caliburn Micro è alla base di molte applicazioni che ho sviluppato), ma perché MVVM Light, essendo il più semplice, è anche quello che ci permetterà di capire meglio il pattern MVVM, proprio perché non nasconde nessuno dei concetti base.