alessandro melchiori
() => melkio.OnStage();

Alessandro Melchiori

Prism: il bootstrapper

11 Jan 2012

Il punto di ingresso di ogni applicazione che si basa su Prism è il bootstrapper.

Il bootstrapper deve contenere tutto quel codice di setup che permetterà alla nostra applicazione di funzionare. Questo è l’unico punto della nostra applicazione in cui mi concedo di mettere un po’ di “sporcizia”: non pongo molta attenzione alla pulizia del codice in questo contesto. Ma è proprio questo contesto che mi permette di gestire, successivamente, in modo “pulito” la parte infrastrutturale della mia applicazione: logging, container di IoC…

Prism utilizza nativamente Unity (o MEF) come container di IoC. Qualora si decida di non utilizzare altro tipo di container, metà dell’opera è già “archiviata”. E quindi partiamo così, scegliendo Unity :-)

Un ottimo punto di partenza è, quindi, quello di sfruttare la classe UnityBootstrapper e customizzare solo gli aspetti che “deviano” dal tradizionale. Creiamoci quindi il nostro bootstrapper, andando a ridefinire solo lo stretto indispensabile:

public class LittleJohnBootstrapper : UnityBootstrapper     
{
    protected override DependencyObject CreateShell()
    {
        return new Shell();
    }

    protected override void InitializeShell()
    {
        //base.InitializeShell();

        Application.Current.MainWindow = (Window) Shell;
        Application.Current.MainWindow.ShowDialog();
     }
}

…e inizializzando la nostra applicazione affinché utilizzi il bootstrapper per “setuppare” l’infrastruttura su cui si baserà il resto:

public partial class App     
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var bootstrapper = new LittleJohnBootstrapper();
        bootstrapper.Run();
     }
}

Il setup minimale prevede di istruire il nostro bootstrapper su come creare ed inizializzare la shell, ovvero l’involucro della nostra applicazione. Fatto questo, il resto del setup è già compreso nella classe UnityBootstrapper.

Andiamo,però, a sbirciare il dietro le quinte di questa classe, giusto per capire cosa “combina”, nel caso volessimo customizzarla un po’ o prevederne una basata, per esempio, su un container differente. Il punto che da l’avvio a tutta la procedura di setup del bootstrapper e, quindi, dell’applicazione stessa è il metodo Run. Nell’ordine vengono eseguite le seguenti operazioni:

  • Creazione del logger: di default prism utilizza un TextLogger per eseguire le operazioni di logging. Tutte le informazioni vengono “streammate” di default sulla console.

  • Creazione e configurazione del module catalog: contiene le informazioni relative ai moduli che sono utilizzabili dall’applicazione. Prism contiene diverse implementazioni che permettono diverse sorgenti per il caricamento dei moduli (le vedremo in seguito)

  • Creazione e configurazione del container: il container viene configurato con tutti i servizi trasversali all’applicazione (logger, module catalog, module initializer, module manager, region manager, event aggregator e altro)

  • Creazione e configurazione della shell (invocando i metodi che abbiamo ridefinito nel nostro bootstrapper, visti in precedenza)

  • Inizializzazione degli eventuali moduli dell’applicazione

Una cosa molto importante, che non dobbiamo dimenticarci di eseguire qualora decidessimo di crearci un nostro bootstrapper da zero (non utilizzando UnityBoostrapper, ma partendo direttamente da Bootstrapper), è quella di associare la nostra Shell al RegionManager di default, garantendoci tutte le inizializzazioni necessarie per sfruttare il meccanismo, che vedremo in seguito, dell’iniezione delle viste nelle regioni.

Il codice incriminato è molto semplice, ma indispensabile. Eccolo, copiato “as-is” dalla classe UnityBootstrapper:

this.Shell = this.CreateShell();      
if (this.Shell != null)
{
    // tolto il log per evitare inutile rumore
    RegionManager.SetRegionManager(
        this.Shell, 
        UnityContainerExtensions.Resolve<iregionmanager>(
            this.Container, 
            new ResolverOverride[0]));
    // tolto il log per evitare inutile rumore        
    RegionManager.UpdateRegions();

    // tolto il log per evitare inutile rumore        
    this.InitializeShell();
}

Be, per ora un po’ di carne al fuoco ne abbiamo messa, ma…siamo solo agli inizi.

Comments