Nel post precedente abbiamo visto la teoria necessaria per capire il funzionamento di Caliburn Micro e perchè il suo approccio è differente da quello di altri toolkit (come MVVM Light) che sono stati sviluppati per l’implementazione del pattern MVVM in un’applicazione Windows Phone.
In questo post inizieremo a fare un po’ di pratica e a vedere come creare un’applicazione utilizzando il pattern MVVM e Caliburn Micro.
Creare il primo progetto
Innanzitutto dovete creare un nuovo progetto Windows Phone (7.5 o 8 è indifferente, Caliburn Micro supporta entrambi) e, tramite NuGet, installare il progetto chiamato Caliburn.Micro: NuGetsi farà carico di soddisfare tutte le dipendenze necessarie.
Il primo passo nel momento in cui lavorate con Caliburn è configurare il boostrapper, ovvero la classe che si fa carico di creare l’infrastruttura necessaria per far funzionare correttamente le varie convenzioni ed helper disponibili. Per farlo, dovete creare una nuova classe nel vostro progetto che erediti dalla classe PhoneBootstrapper, come nell’esempio seguente:
public class Bootstrapper : PhoneBootstrapper { PhoneContainer container; protected override void Configure() { container = new PhoneContainer(RootFrame); container.RegisterPhoneServices(); container.PerRequest<MainPageViewModel>(); AddCustomConventions(); } static void AddCustomConventions() { //ellided } protected override object GetInstance(Type service, string key) { return container.GetInstance(service, key); } protected override IEnumerable<object> GetAllInstances(Type service) { return container.GetAllInstances(service); } protected override void BuildUp(object instance) { container.BuildUp(instance); } }
Quella che vedete è l’implementazione standard di un bootstrapper per Caliburn Micro: a meno che non abbia esigenze particolari, come registrari delle convenzioni personalizzate, potete tranquillamente copiare ed incollare questo codice in ogni vostra applicazione. L’unica cosa che dovrete cambiare è la registrazione dei ViewModel e delle vostre classi: all’interno del metodo Configure() dovrete registrare, infatti, ogni ViewModel ed ogni servizio (ad eccezione di quelli forniti con il toolkit, come vedremo) utilizzato dalla vostra applicazione. In questo esempio registriano la classe MainPageViewModel utilizzando il metodo PerRequest<T>(): in questo modo, ogni qualvolta l’applicazione richiederà un’istanza della classe MainPageViewModel, ne sarà restituita una nuova.
Ora dobbiamo inizializzare il bootstrapper: in Windows Phone possiamo farlo semplicemente aggiungendolo come risorsa globale nell’applicazione all’interno del file App.xaml.
<Application x:Class="CaliburnMicro.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:caliburnMicro="clr-namespace:CaliburnMicro"><!--Application Resources--><Application.Resources><caliburnMicro:Bootstrapper x:Key="bootstrapper" /></Application.Resources></Application>
Ecco come appare il file App.xaml di un’applicazione che fa uso di Caliburn Micro. La particolarità è che il boostrapper non si occupa solamente di inizializzare l’infrastruttura di Caliburn Micro, ma anche l’applicazione vera e propria: di conseguenza, è possibile “ripulire” il file App.xaml.cs, altrimenti l’applicazione sarà inizializzata due volte. Ecco come appare il file App.xaml.xs dopo l’operazione:
public partial class App : Application { /// <summary> /// Provides easy access to the root frame of the Phone Application. /// </summary> /// <returns>The root frame of the Phone Application.</returns> public static PhoneApplicationFrame RootFrame { get; private set; } /// <summary> /// Constructor for the Application object. /// </summary> public App() { // Standard XAML initialization InitializeComponent(); // Show graphics profiling information while debugging. if (Debugger.IsAttached) { // Display the current frame rate counters. Application.Current.Host.Settings.EnableFrameRateCounter = true; // Show the areas of the app that are being redrawn in each frame. //Application.Current.Host.Settings.EnableRedrawRegions = true; // Enable non-production analysis visualization mode, // which shows areas of a page that are handed off to GPU with a colored overlay. //Application.Current.Host.Settings.EnableCacheVisualization = true; // Prevent the screen from turning off while under the debugger by disabling // the application's idle detection. // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run // and consume battery power when the user is not using the phone. PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled; } } }
Ora siamo pronti per iniziare a sviluppare l’applicazione vera e propria. In questo primo esempio ci limiteremo a mostrare qualche informazione all’utente e a leggere l’input dell’utente, giusto per familiarizzare con le convenzioni utilizzate dal toolkit.
Collegare la View e il ViewModel
Come ho già anticipato nel post precedente, non c’è bisogno di collegare manualmente la View con il relativo ViewModel: è sufficiente utilizzare una specifica convenzione e Caliburn Micro si farà carico dell’operazione. La convenzione è la seguente: ViewModel e View devono avere lo stesso nome, seguito dal suffisso del ViewModel nel primo caso. Inoltre, è necessario seguire una regola ben precisa anche per i namespace: i ViewModel devono essere contenuti all’interno di un namespace che termina per ViewModels, mentre le View all’interno di un namespace che termina per Views.
Solitamente, questo obiettivo si raggiunge semplicemente creando due cartelle separate all’interno del progetto, una chiamata ViewModels e una Views: Visual Studio genererà per noi il namespace corretto. Vediamo un esempio concreto: così come per qualsiasi progetto Windows Phone, dovreste trovare già inclusa la pagina MainPage.xaml, che viene caricata di default quando l’applicazione parte. Eliminatela, dato che dovremo crearne una nuova secondo la convenzione di Caliburn.
Ora create una nuova cartella nel progetto chiamata Views e aggiungete una nuova pagina di nome MainPage.xaml (scegliete uno qualsiasi dei template disponibili per creare una pagina Windows Phone). Ricordatevi, inoltre, di aprire il file di manifest (il file WMAppManifest.xml contenuto all’interno della cartella Properties) e, all’interno della sezione Application UI, cambiate il campo NavigationPage affinchè punti al percorso corretto della nuova MainPage (ad esempio, /Views/MainPage.xaml); in caso contrario, l’applicazione andrà in crash all’avvio dato che la pagina di default non esiste più.
Ora create una cartella chiamata ViewModels e, all’interno, aggiungete una nuova classe chiamata MainPageViewModel. Come potete vedere, il nome della classe è lo stesso della nostra View (MainPage) seguita dal suffisso ViewModel. Ora facciamo un esperimento: nella classe MainPageViewModel aggiungete un costruttore e, al suo interno, mostrate un semplice messaggio usando una MessageBox.
public class MainPageViewModel { public MainPageViewModel() { MessageBox.Show("I'm the MainView!"); } }
Ora premete F5 e avviate il debug dell’applicazione: se avete fatto tutto correttamente, vedrete il messaggio comparire nell’emulatore. E’ tutto merito della “magia” delle convenzioni: dato che il ViewModel ha lo stesso nome della View seguito dal suffisso ViewModel, il ViewModel è automaticamente impostato come DataContext della View. Di conseguenza, quando la View viene visualizzata, il ViewModel viene automaticamente inizializzato e, di conseguenza, il codice contenuto all’interno del costruttore viene eseguito.
Importante! La magia della convenzione fa si che View e ViewModel siano automaticamente collegati tra loro, ma richiede comunque che lo sviluppatore definisca quali sono i ViewModel utilizzati, registrandoli all’interno del metodo Configure() del bootstrapper, come abbiamo visto all’inizio del posto. Ricordiamoci di farlo nel caso in cui dovremo aggiungere altre View (e, di conseguenza, altri ViewModel) alla nostra applicazione.
Mostrare qualche proprietà
Un ViewModel senza proprietà è praticamente inutile: aggiungiamo qualche proprietà e visualizziamola nella pagina. La prima cosa da fare è utilizzare alcuni degli helper messi a disposizione dal toolkit: in questo specifico caso, faremo ereditare il nostro ViewModel dalla classe PropertyChangedBase, che ci fornisce il supporto base all’interfaccia INotifyPropertyChanged, facendo si che eventuali modifiche alla proprietà vengano automaticamente recepite dai controlli a cui sono collegate (e viceversa).
Ecco come definire un semplice ViewModel con una proprietà:
public class MainPageViewModel: PropertyChangedBase { public MainPageViewModel() { Name = "Matteo"; } private string name; public string Name { get { return name; } set { name = value; NotifyOfPropertyChange(() => Name); } } }
Abbiamo definito una proprietà chiamata Name che, nel metodo set, chiama il metodo NotifyOfPropertyChange(), fornito dal toolkit, così da notificare i controlli che sono in binding che il valore della stessa è cambiato. Nel costruttore, invece, ci limitiamo a dare un valore a questa proprietà (il nome Matteo). Ora occorre visualizzare il valore di questa proprietà nello XAML: in un’applicazione standard avremmo usato il binding tra la proprietà Name e la proprietà Text esposta dal controllo TextBlock, come nell’esempio.
<TextBlock Text="{Binding Name}" />
Questo approccio funziona anche in Caliburn Micro, ma non è necessario: grazie ad una convenzione è sufficiente che il nome del controllo (ovvero il valore della proprietà x:Name) sia uguale a quello della proprietà affinchè queste siano collegate. Nel nostro caso, lo XAML sarà semplicemente questo:
<TextBlock x:Name="Name" />
Se lanciamo nuovamente l’applicazione nell’emulatore vedremo il testo Matteo mostrato all’interno della TextBlock.
Se non vi piace utilizzare questo approccio basato su convenzioni, potete continuare ad usare il binding standard: dato che il ViewModel è assegnato come DataContext della View, nulla vi vieta di continuare ad utilizzare il primo approccio.
Nei prossimi post
Nei prossimo post vedremo tanti altri scenari legati all’utilizzo di Caliburn: azioni e comandi, messaggi, tombstoning e tanto altro ancora!
Related Content
- Caliburn Micro e Windows Phone – Le azioni (3/26/2013)
- Caliburn Micro e Windows Phone – La teoria (3/18/2013)
- MVVM e Windows Phone – Gestire gli eventi tramite Command (6° parte) (8/23/2012)
- MVVM e Windows Phone – I command (5° parte) (8/9/2012)
- MVVM e Windows Phone – Two way binding (4° parte) (8/6/2012)
- More related document (278)