Utilizzare il pattern MVVM e Caliburn Micro in Windows Phone

di Matteo Pagani, in Windows Phone,

Model-View-ViewModel (per gli amici, MVVM) è sicuramente il pattern architetturale più diffuso nello sviluppo di progetti XAML/C#, tra cui rientrano di diritto anche le applicazioni Windows Phone. Tale pattern consente infatti, rispetto al tradizionale approccio che prevede l'utilizzo del code behind, di organizzare meglio il proprio codice, separando la logica dall'interfaccia grafica. In questo modo si riescono a garantire una serie di caratteristiche (quali manutenibilità, testabilità, leggibilità), che aiutano a mantenere e ad aggiornare il progetto nel corso del tempo, soprattutto se viene sviluppato da un team di persone e non da un singolo sviluppatore.

Non entreremo nei dettagli di questo pattern nel corso di questo articolo, dato che esulerebbe dal contesto, ma prima di entrare nel cuore mi limiterò a sottolineare che l'utilizzo di MVVM prevede la suddivisione del progetto in tre ambiti differenti:

  • Model: è lo strato più a basso livello, che comprende tutte le entità e i servizi che recuperano ed elaborano i dati grezzi dell'applicazione. Cosa si intende per 'grezzi'? Che non devono avere dipendenze dall'interfaccia grafica e da come i dati devono essere presentati.
  • View: all'opposto, è lo strato a livello più alto, ovvero l'interfaccia grafica. In un'applicazione Windows Phone è rappresentato dallo XAML, il linguaggio di markup che permette di definire il layout dell'interfaccia. L'utilizzo del pattern MVVM porta, come conseguenza, ad avere un file di code behind praticamente vuoto: tipicamente, l'unico codice che andremo a scrivere sarà quello relativo al controllo dell'interfaccia grafica (come la gestione delle animazioni).
  • ViewModel: è lo strato che fa da collante tra il Model e la View. Si tratta di una semplice classe, la quale viene definita come DataContext della View: in questo modo, il ViewModel si fa carico di interagire con i servizi e le entità del Model, recuperare i dati e adattarli per la presentazione; dopodichè, grazie al meccanismo del binding, il ViewModel è in grado di riportare questi dati alla View, affinchè sia in grado di mostrarli.

Implementare MVVM in un'applicazione richiede un minimo di infrastruttura, che sia in grado di gestire gli scenari più comuni, quali la comunicazione tra i ViewModel o la propagazione delle modifiche dal ViewModel alla View: in questo modo, possiamo essere operativi sin da subito su un nuovo progetto, evitando di dover reinventare la ruota tutte le volte.

Nel momento in cui iniziate a sviluppare la vostra prima applicazione Windows Phone utilizzando MVVM vi sorgeranno molti dubbi, principalmente legati all'implementazione di scenari che, invece, sarevveri semplici da gestire con il tradizionale approccio basato su code behind, quali:

  • Come navigo da una View all'altra all'interno di un ViewModel?
  • Come gestisco il tombstoning?
  • Come gestisco i deep link (ad esempio, in caso di utilizzo di tile secondarie o speech API)?

Caliburn Micro è uno dei più celebri framework per l'implementazione del pattern MVVM che vi aiuta a risolvere questi e tanti altri scenari offrendo, oltre all'infrastruttura base, una serie di helper specifici. Qual è la differenza principale rispetto a MVVM Light, un'altra celebre libreria per l'implementazione del pattern? MVVM Light non è un framework ma un toolkit: è estremamente flessibile e in grado di adattarsi alle vostre esigenze, ma offre solo gli strumenti base necessari per l'infrastruttura di MVVM, senza risolvere problemi specifici della piattaforma.

Il primo progetto con Caliburn Micro

Vediamo, con un esempio concreto, quali sono i concetti base di Caliburn Micro. Il primo da affrontare è quello di bootstrapper: per funzionare correttamente, Caliburn Micro richiede l'inizializzazione di una serie di parametri e servizi, che devono essere caricati assieme all'applicazione. Per questo motivo, Caliburn Micro offre una classe chiamata boostrapper che fa tutto questo per noi: il modo più semplice per aggiungere questa classe è tramite NuGet, installando il package Caliburn.Micro.Start che, oltre ad aggiungere tutte le librerie necessarie, creerà una serie di classi base per il progetto.

Una di queste si chiama AppBoostrapper e rappresenta il bootstrapper dell'applicazione: al suo interno troverete una serie di operazioni di inizializzazione dei servizi, come il metodo Configure(). In questo metodo trovete una riga di codice molto importante:

container.PerRequest<MainPageViewModel>();

Caliburn Micro è basato sul concetto di dependency injection: non è questa la sede adatta per approfondire un argomento così complesso, per ora vi basti sapere che è un meccanismo che consente di risolvere le dipendenze di una classe a runtime anzichè in fase di compilazione. In questo modo, siamo in grado di registrare nel container tutti i servizi e le classi che ci servono (come i ViewModel) e far si che, in automatico, vengano istanziati al bisogno (anzichè istanzarli manualmente). Ciò significa che, ogni volta che andremo ad aggiungere una nuova pagina alla nostra applicazione (e, di conseguenza, un nuovo ViewModel), dovremo farci carico di registrarlo all'interno del metodo Configure(), così come nell'esempio precedente, usando il metodo PerRequest dell'oggetto container, dove T è il tipo di ViewModel che stiamo registrando.

La seconda, importante, considerazione da fare è che il boostrapper si fa carico non solo di inizializzare i servizi specifici di Caliburn Micro, ma anche l'intera applicazione, incluso il frame di navigazione e di gestione delle pagine. Di conseguenza, il codice incluso nel file App.xaml.cs diventa inutile: è necessario, perciò, eliminare tutto il codice, lasciando solo la chiamata al metodo InitializeComponent() all'interno del costruttore della classe App, come nell'esempio.

public partial class App : Application
{
    /// <summary>
    /// Constructor for the Application object.
    /// </summary>
    public App()
    {
        // Standard XAML initialization
        InitializeComponent();
    }
}

Come conseguenza, dovrete eliminare anche alcune dichiarazioni nel file App.xaml: nello specifico, occorre rimuovere il riferimento alla classe PhoneApplicationService contenuta nella sezione Application.ApplicationLifeTimeObjects, il quale serve per gestire gli eventi relativi al ciclo di vita dell'applicazione (avvio, sospensione, chiusura, ecc.). Anche in questo caso, il bootstrapper funge da sostituto, in quanto permette di sottoscrivere una serie di eventi che sono in grado di rimpiazzarli, come OnActivate(), OnLaunch() e OnClose().

L'ultimo passaggio per rendere l'applicazione operativa è quello di inizializzare il boostrapper: è sufficiente dichiarlo come risorsa globale dell'applicazione, in modo che all'avvio, in automatico, venga istanziato e inizializzato. Per raggiungere questo scopo, dovete aggiungere la classe che funge da boostrapper (se utilizzate il pacchetto NuGet standard che vi ho suggerito, si chiamerà AppBootstrapper) nella sezione Application.Resources del file App.xaml. Ecco compare un tipico file App.xaml di un'applicazione che fa uso di Caliburn Micro:

  <Application
    x:Class="Webinar.CaliburnMicro.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:caliburnMicro="clr-namespace:Webinar.CaliburnMicro">
    <!--Application Resources-->
    <Application.Resources>
        <local:LocalizedStrings xmlns:local="clr-namespace:Webinar.CaliburnMicro" x:Key="LocalizedStrings"/>
        <caliburnMicro:AppBootstrapper x:Key="AppBootstrapper" />
    </Application.Resources>
</Application>

Naming convention

Una caratteristica di Caliburn Micro è quello di offrire una serie di naming convention, che vanno a sostituire il binding tradizionale. Tipicamente, l'approccio nello sviluppo di un'applicazione MVVM richiede i seguenti passaggi:

  • Si definisce il ViewModel come DataContext della View (tramite binding diretto o tramite una classe ad hoc come il ViewModelLocator).
  • Il ViewModel definisce una o più proprietà, che contengono i dati da portare alla View.
  • Tali proprietà vengono collegate alla UI tramite il binding (ad esempio, una proprietà di tipo string viene collegata alla proprietà Text di un controllo TextBlock).

Ecco un esempio di una proprietà definita in un ViewModel:

private string name;
public string Name
{
    get { return name; }
    set
    {
        name = value;
        RaisePropertyChanged(() => Name);
    }
}

la quale viene poi collegata alla View tramite binding in questo modo:

<TextBlock Text="{Binding Path=Name}" />

Caliburn Micro mette a disposizione una serie di convenzioni tramite le quali il binding non deve essere esplicitato, ma viene applicato in automatico. Questo approccio non è obbligatorio: potete continuare ad usare il binding tradizionale senza problemi. Vediamo le principali convenzioni.

3 pagine in totale: 1 2 3

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

Top Ten Articoli

Articoli via e-mail

Iscriviti alla nostra newsletter nuoviarticoli per ricevere via e-mail le notifiche!

In primo piano

I più letti di oggi

In evidenza

Misc