ComputerLand

Tu sei qui: Articoli MSDN

Notizie dal web

Guest post: #TecHeroes Recap n.2

E-mail Stampa PDF

Questo post è stato scritto da Clarissa Molfino, Technical Evangelist Intern.

Ciao a tutti, eccoci di nuovo a raccontarvi le ultime news del nostro show su Channel9 #TecHeroes!

Ad aprire la settimana è stato Sebastiano Galazzo, MVP in Windows Platform Development, che, assieme ad Erica Barone, ci ha portati alla scoperta degli Wearable Devices, mostrandoci alcune loro affascinanti e innovative applicazioni e le loro grandissime potenzialità.

 

 

Non allontanandoci troppo da innovazione e grandi potenzialità abbiamo avuto il piacere di avere ospiti Francesco Ceglie e Francesco Forte, rispettivamente CEO e CTO di quella che è stata eletta Startup del mese di Ottobre di Startup4you: Bewons, il social network dove gli artisti possono esprimere il proprio talento e incontrare opportunità di lavoro.
Assieme a Jessica ci hanno raccontato Bewons, dall'idea da cui è nato ai grandi progetti futuri di quella che oggi è una realtà consolidata e di successo, passando attraverso al contributo ricevuto dal supporto di BizSpark.

 

La settimana si è chiusa con un'importante puntata che dà il via ad un filone di episodi in cui ospiti esperti ci guidano alla scoperta di servizi di Azure che non tutti conoscono.
In particolare, in questa puntata, Alessandro Melchiori, software developer esperto di DocumentDB, ci ha mostrato, assieme a Jessica, questo database NoSQL innovativo offerto da Azure, anche attraverso semplici Demo.

 


 

Buona visione!!!


read full article

Guest post: #TecHeroes Recap n.1

E-mail Stampa PDF

Questo post è stato scritto da Clarissa Molfino, Technical Evangelist Intern.

Ciao a tutti, con questo inauguriamo una serie di articoli che hanno lo scopo di raccontarvi gli ultimi aggiornamenti su #TecHeroes. Non conoscete ancora #TecHeroes? E' il nostro show su Channel9 in cui raccontiamo la storia di coloro che attraverso la tecnologia sono diventati dei veri eroi.

La settimana si apre con Mattia De Rosa, Data Insight Product Marketing Manager, che assieme a Jessica ci ha introdotti ad una delle funzionalità più innovative che il cloud può offrirci, il Machine Learning. Più in particolare, abbiamo visto le sue principali funzionalità e una demo di quello che il Machine Learning ci permette di fare con estrema semplicità.

 

 

Dopo aver approfondito una delle tante meraviglie che il cloud ci offre, vi abbiamo portato dietro le quinte dello studio di #TecHeroes con due guide d'eccezione, Carlo Rinaldi e Roberto Andreoli, per vedere come la Computer Grafica riesce a dar vita al nostro bellissimo studio virtuale. Abbiamo mostrato quel che non si vede, svelando la magia che permette di creare il fantastico scenario virtuale in cui si animano le nostre puntate e tutta la tecnologia all'avanguardia che rende il nostro ambiente di registrazione un vero e proprio studio televisivo.

 

 Buona visione!!!

 


read full article

Guest post: Template10, un nuovo approccio nello sviluppo di Universal Windows app – Il pattern MVVM

E-mail Stampa PDF

Questo post è stato scritto da Matteo Pagani, Support Engineer in Microsoft per il programma AppConsult

Il pattern Model-View-ViewModel (da ora in poi, MVVM) è sicuramente il più diffuso in ambito di sviluppo di applicazioni XAML. La spiegazione dettagliata del pattern esula dallo scopo di questo post e, a tal proposito, vi rimando alle numerose risorse che si trovano in rete. Quello che è importante sapere è che lo scopo del pattern MVVM è di migliorare la testabilità, la manutenibilità e la progettazione di una Universal Windows app, in quanto aiuta a raggiungere uno dei principi chiave che guida lo sviluppo software (di qualsiasi tipo): la separazione dei ruoli, ovvero rendere ben distinte le parti che si occupano di definire l’interfaccia grafica e quelle, che invece, si fanno carico di gestire la logica applicativa (caricamento dei dati, interazione con il cloud o un database, ecc.).

Il pattern MVVM è particolarmente adatto per le Universal Windows app e, più in generale, per tutte le applicazioni basate su XAML, in quanto ne sfrutta largamente i meccanismi base come il binding, le dependency property e l’interfaccia INotifyPropertyChanged. Il pattern prevede la suddivisione del progetto in tre componenti:

1. Il model, ovvero le entità e i servizi che manipolano i dati dell’applicazione in maniera “grezza”, senza dipendenze da come devono essere presentati.

2. La view, ovvero l’interfaccia grafica con cui l’utente interagisce. Nel mondo delle Universal Windows app, le view sono rappresentate dalle pagine XAML.

3. I ViewModel, ovvero il collante tra il model e la view. I ViewModel sono delle classi che si fanno carico di recuperare i dati dal model, di prepararli per la presentazione ed i passarli alla view tramite il binding.

L’utilizzo del pattern MVVM, pur apportando tantissimi vantaggi, comporta comunque un tempo di configurazione e preparazione del progetto maggiore: è necessario, infatti, ogni volta mettere in piedi l’architettura necessaria in termini di servizi e classi di utilità per il collegamento dei vari “strati”. Nello sviluppo di una Universal Windows app, infatti, esistono tantissimi scenari (la gestione della navigazione, il ciclo di vita, i contratti, ecc.) che sono semplici da implementare quando si utilizza il code-behind, ma devono essere ripensati in ottica MVVM, in quanto andremo a scrivere gran parte del codice nel ViewModel (che è una classe indipendente). Uno scenario esemplificativo è la gestione del ciclo di vita di una pagina: tipicamente, viene affidata ai metodi OnNavigatedTo() e OnNavigatedFrom() che possono essere gestiti all’interno del code-behind. Questa gestione è resa possibile dal fatto che la classe di code-behind, essendo legata alla view, eredita direttamente dalla classe Page, che rappresenta la singola pagina dell’applicazione. Tale opportunità, invece, non è disponibile all’interno di un ViewModel, in quanto si tratta di una classe che non ha un legame diretto con la view come, invece, avviene con la classe che funge da code-behind.

Per tale scopo, diversi sviluppatori hanno realizzato delle librerie e dei framework che semplificano e velocizzano il loro lavoro, fornendo tutti gli strumenti base necessarie. I più popolari sono sicuramente MVVM Light di Laurent Bugnion, Caliburn Micro di Rob Eisenberg e Prism, originalmente creato dalla divisione Pattern & Practice di Microsoft e ora gestito da un gruppo di MVP.

Ognuno di questi framework ha le sue peculiarità e le sue caratteristiche: MVVM Light è il più semplice e flessibile di tutti, ma lascia molto lavoro allo sviluppatore in quanto non offre alcuno strumento nativo per gestire gli scenari tipici di una Universal Windows app. Caliburn Micro e Prism, invece, sono più complessi e più “rigidi”, ma offrono molti servizi già pronti per gestire gli scenari più comuni, come la gestione del ciclo di vita, la navigazione, ecc.

Template10 non si vuole porre come l’ennesimo framework di MVVM (anche se, come vedremo, al momento offre già molti strumenti base presenti anche nelle altre librerie), ma vuole essere un aiuto per tutti gli sviluppatori che utilizzano questo pattern e che vogliono essere produttivi sin da subito e non vogliono “reinventare la ruota” ogni volta che vogliono iniziare un nuovo progetto.

Vediamo in dettaglio quali sono le funzionalità più utili, fermo restando che quanto è stato descritto nei post precedenti (bootstrapper, extended splash screen, i nuovi controlli, ecc.) si può tranquillamente applicare anche ai progetti basati su MVVM.

 

Gli strumenti base per implementare il pattern

Anche se Template10 non si pone come alternativa ai framework già esistenti, offre comunque gli strumenti base indispensabili per implementarlo nella maniera più corretta. Non è obbligatorio usarli: potete decidere tranquillamente, ad esempio, di aggiungere MVVM Light al vostro progetto e di usare le classi offerte dal toolkit al posto di quelle che ora andrò a descrivere.

La prima importante classe offerta da Template10 si chiama ViewModelBase ed è la classe base da cui far ereditare i ViewModel della nostra applicazione. Oltre ad offrire l’accesso ad una serie di utili funzioni che vedremo in seguito, vi fornisce gli strumenti base per l’implementazione del pattern. Uno dei più importanti è sicuramente il metodo Set(), che vi permette di gestire la propagazione delle modifiche effettuate alle proprietà del ViewModel alla UI, tramite l’implementazione dell’interfaccia INotifyPropertyChanged. Ecco come appare la definizione di una proprietà all’interno di un ViewModel:

 

publicclassMainViewModel : Template10.Mvvm.ViewModelBase

{

 

    public MainViewModel()

    {

       

    }

 

    privatestring _name;

 

    publicstring Name

    {

        get { return _name; }

        set { Set(ref _name, value); }

    }

}

Il metodo Set(), chiamando all’interno del setter della proprietà, fa sì che, oltre a cambiarne il valore, venga inviata una notifica alla View: in questo modo, qualsiasi controllo sia in binding con tale proprietà, si aggiornerà per mostrare il nuovo valore. Ecco, ad esempio, come tale proprietà venga collegata ad un controllo TextBlock così che, ad ogni variazione della stessa, il nuovo valore venga immediatamente mostrato nell’interfaccia utente.

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

Se avete già esperienza con MVVM Light questo approccio vi sarà famigliare: anche il toolkit di Laurent Bugnion offre un omonimo metodo Set() con lo stesso scopo.

Un’altra funzionalità molto importante messa a disposizione da Template10 in ottica MVVM è la classe DelegateCommand, che consente di implementare in maniera semplice i command. Di cosa si tratta? Per gestire l’interazione dell’utente con l’interfaccia grafica, in un’applicazione tradizionale, si utilizzano gli event handler, ovvero metodi che vengono legati ad un evento sollevato da un controllo, come il tap su un pulsante o la selezione di un elemento da una lista. Tali event handler, però, sono legati a doppio filo con la view: di conseguenza, possono essere dichiarati solamente all’interno del code-behind. In un’applicazione MVVM, invece, abbiamo la necessità di gestire le interazioni dell’utente direttamente nei ViewModel, dato che sono loro a contenere tutta la logica. Per questo scopo sono nati i command, ovvero la possibilità di esprimere un metodo tramite una proprietà, che ci dà la possibilità di sfruttare il binding per collegarla ad un controllo. Un command è una proprietà che definisce:

1. Le azioni da eseguire quando il comando viene invocato.

2. Le condizioni che devono essere soddisfatte affinché il comando sia abilitato. Tale condizione fa sì che lo stato visuale del controllo cambi in automatico in base allo stato del comando. Ad esempio, se il command è disabilitato, anche il pulsante ad essa collegato apparirà non abilitato all’utente.

Il Windows Runtime include un’interfaccia base per l’implementazione dei command (chiamata ICommand): la realizzazione vera e propria, però, è a carico dello sviluppatore. Per velocizzare questa fase Template10 offre la classe DelegateCommand, che implementa tale interfaccia e che si inizializza semplicemente passando nel costruttore le due informazioni sopra indicate. Ecco un esempio:

 

privateDelegateCommand _setNameCommand;

 

publicDelegateCommand SetNameCommand

{

    get

    {

        if (_setNameCommand == null)

        {

            _setNameCommand = newDelegateCommand(() =>

            {

                Result = $"Hello {Name}";

            }, () => !string.IsNullOrEmpty(Name));

        }

 

        return _setNameCommand;

    }

}

Il primo parametro rappresenta una Action (in questo caso, definita con una funzione anonima), che definisce le operazioni da eseguire quando il comando viene eseguito. Il secondo parametro (opzionale), invece, è una funzione che deve restituire un valore booleano e che determina se il comando sia abilitato o meno. Nell’esempio, il comando è abilitato solo nel caso in cui il contenuto della proprietà Name non sia vuoto.

Una volta che avete definito il vostro command, potete collegarlo ad i controlli XAML tramite l’omonima proprietà. Tutti i principali controlli che sono in grado di gestire l’interazione con l’utente (ad esempio, Button) espongono infatti una proprietà di nome Command, che può essere collegata tramite binding alla proprietà di tipo DelegateCommand appena definita, come nell’esempio seguente:

 

<Button Content="Click me" Command="{Binding Path=SetNameCommand}" />

Anche in questo caso, se avete già esperienza con MVVM Light l’approccio vi sarà famigliare: l’unica differenza è che la classe messa a disposizione dal toolkit si chiama RelayCommand.

 

Navigazione e ciclo di vita della pagina

Come anticipato nell’introduzione, uno degli scenari più frequenti da gestire in una Universal Windows app è il ciclo di vita di una pagina: gli eventi di navigazione, infatti, vengono spesso usati per gestire operazioni fondamentali come il caricamento dei dati. Di conseguenza, avere accesso a tali eventi anche all’interno di un ViewModel è fondamentale, visto che tale classe costituisce proprio il punto di contatto tra View e Model.

A tale scopo, la casse ViewModelBase offre una serie di funzionalità aggiuntivo proprio per questo scenario. La prima è l’implementazione di un’interfaccia chiamata INavigable, che permette di accedere agli eventi di navigazione della pagina direttamente nel ViewModel, come nell’esempio seguente.

 

publicclassDetailViewModel : ViewModelBase

{

    publicoverridevoid OnNavigatedTo(object parameter, NavigationMode mode, IDictionary<string, object> state)

    {

if (parameter != null)

       {

              int id = (int) parameter;

              //load data

       }

    }

 

    publicoverrideTask OnNavigatedFromAsync(IDictionary<string, object> state, bool suspending)

    {

        returnbase.OnNavigatedFromAsync(state, suspending);

    }

}

Il metodo OnNavigatedTo() ci permette di scoprire la modalità di navigazione (tramite il parametro mode), così da poter differenziare le operazioni da fare. Ad esempio, è plausibile che il caricamento iniziale dei dati venga fatto solo nel caso in cui riceviamo il valore New dell’enumeratore NavigationMode, che rappresenta la prima inizializzazione della pagina. Senza questa discriminante, i dati sarebbero ricaricati ad ogni accesso alla pagina stessa.

Un’altra informazione utile è contenuta nell’oggetto parameter, che ci permette di gestire il passaggio di parametri da una pagina all’altra. Nel caso in cui, navigando verso questa pagina, avessimo scelto di passare un parametro (ad esempio, l’identificativo dell’elemento scelto in una lista), lo ritroveremo all’interno di questa proprietà.

Esistono poi altre utili proprietà (come state) che vedremo in dettaglio nel paragrafo successivo, dedicato alla gestione dello stato della pagina.

Rimanendo in ambito della navigazione, un’altra funzionalità messa a disposizione della classe ViewModelBase è l’accesso al NavigationService, ovvero un helper che vi permette di gestire la navigazione da una pagina all’altra direttamente dal ViewModel. Si tratta, infatti, di un altro degli scenari che può essere complicato da gestire in ottica MVVM: normalmente, la navigazione viene affidata alla classe Frame, che però è accessibile solamente dal code-behind.

La classe NavigationService è molto semplice da utilizzare, in quanto funge semplicemente da wrapper della classe Frame. Ad esempio, se vogliamo portare l’utente ad un’altra pagina dobbiamo usare il metodo Navigate(), che accetta come parametri il tipo di pagina di destinazione e un eventuale parametro.

 

NavigationService.Navigate(typeof(DetailPage), person?.Id);

 

E’ importante sottolineare come, nonostante il metodo Navigate() accetti come secondo parametro un generico object, sia indispensabile passare dati di tipo semplice (una stringa, un numero, ecc.). Questo perché, affinché la gestione dello stato che vedremo nel paragrafo successivo funzioni correttamente, è necessario che i parametri di navigazione siano serializzabili, cosa che spesso non accade invece per i tipi complessi.

Possiamo usare il NavigationService, in alternativa, per manipolare lo stack delle pagine: ad esempio, possiamo usare il metodo GoBack() per portare l’utente alla pagina precedente, oppure il metodo ClearHistory() per azzerare lo stack.

 

Gestire lo stato della pagina

Uno degli scenari più importanti da gestire in una Universal Windows app è il ciclo di vita dell’applicazione. Come abbiamo visto in uno dei post precedenti, le applicazioni, quando non sono più in foreground, vengono sospese: il processo viene congelato in memoria (così che lo stato venga mantenuto), ma tutti i thread che possono consumare CPU, batteria, rete o altre risorse vengono terminati. Nel momento in cui l’applicazione viene ripristinata, non dobbiamo fare nulla: dato che lo stato era stato mantenuto in memoria, l’utente la ritroverà esattamente come l’aveva lasciata.

Il sistema operativo, però, può terminare un’applicazione sospesa in caso si stiano esaurendo le risorse. In tal caso, dato che il processo è stato completamente terminato, l’applicazione non ritornerà allo stato in cui l’utente l’aveva lasciata, ma verrà riavviata da capo. Come sviluppatori, dobbiamo gestire invece questa situazione: dato che per l’utente la procedura di terminazione è completamente trasparente, lui si aspetterà di ritrovare l’applicazione esattamente come l’aveva lasciata. È compito nostro, perciò, salvare e ripristinare lo stato delle pagine. Ipotizziamo, ad esempio, di avere una una pagina che contenga una serie di campi che l’utente deve compilare: nel momento in cui l’applicazione viene sospesa e poi riattivata, l’utente si aspetterà di ritrovare i campi esattamente come li aveva lasciati.

Si tratta di un’operazione fondamentale, ma complessa da gestire sfruttando il template base delle Universal Windows app, principalmente per due motivi:

1. Dato che non possiamo sapere a priori se l’applicazione sospesa sarà terminata o meno, dobbiamo farci carico di salvare lo stato (ad esempio, il contenuto dei vari campi del form) ad ogni navigazione. In ottica MVVM (senza l’uso di Template10), questo complica le cose perché non abbiamo accesso, dal ViewModel, agli eventi di navigazione della pagina.

2. Nel momento in cui l’applicazione viene sospesa, dobbiamo farci carico di salvare nello storage locale lo stato dell’applicazione: il contenuto dei vari campi, l’ultima pagina visitata, ecc. Dopodiché, in fase di avvio, dobbiamo determinare se l’applicazione è stata riaperta in seguito ad una terminazione e, di conseguenza, ripristinare lo stato e portare l’utente all’ultima pagina visitata. Il template base, purtroppo, non offre alcuno strumento per gestire questa operazione, lasciando allo sviluppatore la necessità di farsi carico di tutto.

Template10 semplifica notevolmente questo scenario: ciò che dovremo fare, come sviluppatori, sarà solamente salvare e caricare lo stato sfruttando le funzionalità messe a disposizione dal template.

Lo strumento che Template10 ci mette a disposizione è una collezione di coppie chiave – valore, a cui si ha accesso tramite un parametro degli eventi OnNavigatedTo() e OnNavigatedFrom() implementati dalla classe ViewModelBase. In fase di sospensione dell’applicazione (quindi nell’evento OnNavigatedFrom()) andremo ad aggiungere a questa collezione tutte le informazioni che ci servono per preservare lo stato (ad esempio, il valore di ogni campo della form). In fase di caricamento della pagina, invece (quindi nell’evento OnNavigatedTo()) andremo a recuperare queste informazioni, se presenti, e le utilizzeremo per ripristinare il valore originale delle varie proprietà del ViewModel.

Vediamo un esempio concreto. Ipotizziamo che la pagina della nostra applicazione contenga un controllo TextBox, all’interno del quale l’utente può inserire del testo, che viene salvato in una proprietà del ViewModel. Nel ViewModel avremo perciò una proprietà di tipo string così definita:

 

privatestring _name;

 

publicstring Name

{

    get { return _name; }

    set { Set(ref _name, value); }

}

Tale proprietà sarà collegata al controllo TextBox nella view (quindi nella pagina XAML) tramite binding:

 

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

Possiamo notare come sia stata impostata la modalità TwoWay: in questo modo, ogni volta l’utente inserirà del testo all’interno della casella di testo, in automatico la proprietà Name nel ViewModel sarà aggiornata con il nuovo valore.

Se ora provassimo a sospendere l’applicazione e poi a riaprirla, potremmo notare come il testo inserito nella casella di testo sarà in automatico mantenuto: questo perché il processo, durante la sospensione, è stato solo congelato e non terminato. Di conseguenza, lo stato è mantenuto in memoria. Per simulare questo scenario possiamo sfruttare il menu a tendina Lifecycle events di Visual Studio, che permette di testare le varie fasi del ciclo di vita dell’applicazione.

clip_image002[12]

Se scrivessimo del testo nel controllo TextBox, sospendessimo l’app premendo il pulsante Suspend e poi la riaprissimo, troveremmo tutto come l’avevamo lasciato.

Se invece premessimo il pulsante Suspend and shutdown, che simula la terminazione da parte del sistema operativo, al riavvio il testo all’interno del controllo sarebbe andato perso: questo perché il processo è stato effettivamente terminato e, dato che noi non abbiamo salvato lo stato in un’area di memoria persistente come lo storage locale, non è stato possibile riportare l’applicazione allo stato originale.

In questo scenario subentra la collezione di coppie chiave-valore citata in precedenza: all’interno dell’evento OnNavigatedFrom() possiamo determinare se siamo in fase di sospensione oppure no e, in caso affermativo, salvare le informazioni che ci servono all’interno della collezione, come nell’esempio seguente.

 

publicclassDetailViewModel : ViewModelBase

{   

    privatestring _name;

 

    publicstring Name

    {

        get { return _name; }

        set { Set(ref _name, value); }

    }

 

 

    publicoverrideTask OnNavigatedFromAsync(IDictionary<string, object> state, bool suspending)

    {

        if (suspending)

        {

            state.Add("Name", Name);

        }

 

        returnbase.OnNavigatedFromAsync(state, suspending);

    }

}

Come potete vedere, sfruttiamo entrambi i parametri che ci vengono forniti dal metodo. Il primo è un booleano, che ci permette di capire se l’evento è scattato perché siamo in fase di sospensione o semplicemente perché ci stiamo spostando verso un’altra pagina. In caso di sospensione, andiamo a salvare nel secondo parametro (la collezione di nome state) il valore della proprietà Name, associandola ad una chiave dal nome omonimo. In questo modo, in fase di sospensione, l’infrastruttura di Template10 farà sì che, in automatico, il contenuto della collezione venga automaticamente serializzato in un file testuale e salvato nello storage locale. Da qui si evince uno dei requisiti fondamentali di questo approccio: i dati che memorizziamo all’interno della collezione devono essere serializzabili, ovvero devono poter essere tradotti in XML o JSON.

Il passaggio successivo è quello di gestire, invece, il caricamento della pagina: nel caso in cui ci siano dei dati all’interno della collezione state (che è resa disponibile anche all’interno del metodo OnNavigatedTo()), allora vuol dire che proveniamo da uno scenario di attivazione e dobbiamo, perciò, recuperarli. Anche in questo caso, il grosso del lavoro lo farà per noi Template10: all’avvio dell’applicazione, se si proviene da una terminazione del sistema operativo, il bootstrapper andrà a recuperare i dati serializzati nello storage locale e li ricaricherà all’interno della collezione. Ecco un esempio di codice che completa quello precedente:

 

publicclassDetailViewModel : ViewModelBase

{

    privatestring _name;

 

    publicstring Name

    {

        get { return _name; }

        set { Set(ref _name, value); }

    }

 

    publicoverridevoid OnNavigatedTo(object parameter, NavigationMode mode, IDictionary<string, object> state)

    {

        if (state.Any())

        {

            Name = state["Name"].ToString();

            state.Clear();

        }

    }

 

    publicoverrideTask OnNavigatedFromAsync(IDictionary<string, object> state, bool suspending)

    {

        if (suspending)

        {

            state.Add("Name", Name);

        }

        returnbase.OnNavigatedFromAsync(state, suspending);

    }

}

 

All’interno dell’evento OnNavigatedTo() verifichiamo la presenza di dati all’interno della collezione e, in caso affermativo, andiamo a recuperare il dato identificato dalla chiave Name e lo assegniamo all’omonima proprietà del ViewModel. Dopodiché procediamo a svuotare la collezione con il metodo Clear(), così da evitare che i dati vengano resi nuovamente disponibili anche in caso di semplici navigazioni e non di scenari di sospensione / attivazione.

Se ora riprovassimo a simulare la terminazione con il pulsante Suspend and shutdown e a rilanciare l’applicazione, sarà come se nulla fosse successo: l’applicazione si avvierà direttamente nell’ultima pagina aperta e la casella di testo conterrà il dato precedentemente inserito dall’utente.

Importante! Questo approccio deve essere utilizzato per salvare lo stato della pagina, non i dati veri e propri dell’applicazione. Un buon approccio allo sviluppo consiste nel salvare i dati generati dall’utente il prima possibile, così da minimizzare la possibile perdita di dati. Se cercassimo di salvare tutti i dati in fase di sospensione, il tempo a disposizione potrebbe non essere sufficiente. È, invece, corretto salvare solamente quei dati che consentono di ricreare l’illusione all’utente che l’applicazione non sia mai stata chiusa. Ad esempio, ipotizziamo che l’applicazione vista in precedenza consenta, con un pulsante, di salvare in maniera permanete il contenuto della casella di testo. In tal caso, il salvataggio (ad esempio, in un database) deve essere effettuato immediatamente. All’interno della collezione messa a disposizione dagli eventi OnNavigatedFrom() e OnNavigatedTo(), invece, andremo a salvare e a caricare solo il valore della proprietà Name associata al controllo TextBox: vogliamo evitare, infatti, che l’utente perda il dato nel caso in cui sospenda l’applicazione prima di premere il pulsante di salvataggio.

 

Accedere al thread della UI

In alcune situazioni può capitare, all’interno di un ViewModel, di effettuare alcune operazioni su thread differenti da quelli della UI. In tal caso, nel momento in cui dobbiamo interagire con i controlli presenti nella View (banalmente, perché dobbiamo cambiare il valore di una proprietà del ViewModel che è in binding con un controllo nella View), potremmo avere dei problemi: si scatenerà, infatti, un’eccezione, dovuta al fatto che stiamo tentando di accedere al thread della UI da un thread secondario.

In questi casi subentra una classe del Windows Runtime chiamata dispatcher che, come si evince dal nome stesso, funge da “postino” verso il thread della UI, indipendentemente dal thread in cui ci si trova. Anche in questo caso abbiamo il problema che l’accesso diretto al dispatcher viene fornito solamente all’interno della classe di code behind. A tale scopo, Template10 introduce un helper che consente di sfruttare il dispatcher anche da una classe qualsiasi, come un ViewModel. L’utilizzo è molto semplice, come potete vedere nell’esempio seguente:

 

await Dispatcher.DispatchAsync(() =>

{

    //do something on the UI thread

});

 

Si chiama il metodo DispatchAsync() della classe Dispatcher e, all’interno, si definiscono le operazioni che si vogliono eseguire sul thread della UI invece che su quello corrente.

 

In conclusione

Come potete vedere, in ottica di sviluppo con il pattern MVVM Template10 assume ancora maggiore importanza: ci permette, infatti, di gestire in maniera semplice molti scenari fondamentali, che però possono creare qualche grattacapo quando vengono approcciati con il pattern MVVM. Lo sviluppo di Template10, però, è solamente agli inizi e, nei prossimi mesi, crescerà notevolmente il numero di servizi che saranno messi a disposizione per integrare più facilmente le funzionalità di Windows all’interno dei ViewModel: tile, sharing, storage, ecc.

Per ora il nostro viaggio esplorativo di Template10 è terminato. Sono sicuro, però, che avremo occasione di riparlarne in futuro! Concludo ricordandovi il link ufficiale del progetto, dove trovate il codice sorgente, inclusi diversi progetti di esempio: https://github.com/Windows-XAML/Template10


read full article

Guest post: Template10, un nuovo approccio nello sviluppo di Universal Windows app – I controlli

E-mail Stampa PDF

Questo post è stato scritto da Matteo Pagani, Support Engineer in Microsoft per il programma AppConsult

Nel post precedente abbiamo iniziato a prendere famigliarità con Template10, il nuovo template open source realizzato da un team in Microsoft, che ha l'obiettivo di diventare il punto di partenza ideale per lo sviluppo di Universal Windows app.

Nel corso di questo post andremo a vedere alcune delle funzionalità che la libreria mette a disposizione sotto forma di nuovi controlli.

Il controllo PageHeader

Una delle novità principali, in ottica di interfaccia utente, nelle Universal Windows app di Windows 10 è la possibilità di posizionare la application bar non solo in basso, ma anche in cima. In Windows 8.1 si aveva la possibilità di includere una application bar superiore, ma solo per gestire la navigazione tra le varie sezioni dell'applicazione. I comandi dovevano essere necessariamente inclusi nella application bar inferiore, sfruttando un controllo chiamato CommandBar.

In Windows 10, invece, il controllo CommandBar può essere posizionato anche nella parte superiore dello schermo e includere dei pulsanti con cui l'utente può interagire. Il controllo PageHeader, incluso in Template10, nasce come estensione della CommandBar e permette di trasformarla in un vero e proprio header da usare come intestazione per le nostre pagine. Grazie a questo controllo, sarà più semplice ricreare il look & feel adottato da alcune applicazioni native come News o Money, che includono in tutte le pagine un header con il titolo della sezione e dei pulsanti per gestire la navigazione o l'interazione con i contenuti.

Il primo passo per utilizzare il controllo PageHeader è dichiarare nella pagina XAML il namespace a cui appartiene, ovvero Template10.Controls, come nell'esempio seguente:

<Page

x:Class="Controls.Views.MainPage"

xmlns:controls="using:Template10.Controls"

mc:Ignorable="d">

</Page>

 

Dopodiché, potete includere il controllo nella vostra pagina con il seguente codice:

<controls:PageHeader Frame="{x:Bind Frame}" />

Una delle proprietà chiave, che potete notare nell'esempio, è Frame. Come vedremo a breve, una delle principali caratteristiche del controllo è la sua capacità di gestire in automatico la navigazione tra le pagine dell'applicazione. Affinché questa feature funzioni correttamente, però, è indispensabile che il frame che contiene le pagine sia collegato al controllo: lo facciamo sfruttando la nuova markup extension x:Bind, che ci permette di accedere agli oggetti esposti nel code behind.

 

Personalizzare l'aspetto

La proprietà chiave per personalizzare l'aspetto del controllo PageHeader è Text, che permette di definire il testo da mostrare all'interno dell'header (tipicamente, il titolo della pagina). Eventualmente, è possibile inoltre cambiare il colore predefinito che vengono assegnati al testo e allo sfondo, tramite le proprietà HeaderForeground e HeaderBackground. Ecco un esempio di personalizzazione, in cui viene impostato il testo di colore rosso su uno sfondo arancione:

<controls:PageHeader Text="Main Page" Frame="{x:Bind Frame}" HeaderBackground="Orange" HeaderForeground="Red" />

Gestire la navigazione

Il controllo PageHeader include un pulsante per gestire la navigazione verso la pagina precedente, che è controllato da una proprietà di nome BackButtonVisibility. Quando questa proprietà viene impostata a Visible, il pulsante viene mostrato alla sinistra del testo che funge da header.

<controls:PageHeader Text="Detail" Frame="{x:Bind Frame}" BackButtonVisibility="Visible" />

 

 

È importante, però, sottolineare come la visibilità di tale pulsante non venga controllata solamente da tale proprietà, ma anche da altri fattori legati al sistema operativo e alla piattaforma su cui sta girando l'applicazione.

Nello specifico:

  1. Il controllo, tramite la proprietà Frame, è legato al frame di navigazione: di conseguenza, è in grado di determinare in automatico se ci sono pagine nello stack. Ciò significa che, ad esempio, il pulsante non sarà mai mostrato nella prima pagina dell'applicazione, perché lo stack sarà sempre vuoto.
  2. Le varie tipologie di device su cui gira Windows 10 hanno regole differenti per quanto riguarda la gestione del pulsante Back. Se, su desktop, abbiamo diverse possibilità (sfruttare il pulsante virtuale presente nella finestra, includerlo nella UI dell'applicazione, ecc.), su mobile invece le guideline prevedono l'uso del tasto Back fisico presente in tutti gli smartphone. Di conseguenza, ad esempio, se impostate la proprietà BackButtonVisibility a Visible ma la vostra applicazione è in esecuzione su un device mobile, il pulsante non sarà comunque mai visibile.
  3. Su desktop, come abbiamo visto nel post precedente, abbiamo la possibilità di sfruttare un pulsante Back virtuale, presente nella finestra dell'applicazione, per gestire la navigazione alle pagine precedenti. In tal caso, aggiungere troppi elementi nell'interfaccia utente per raggiungere lo stesso scopo potrebbe essere confusionario. Come comportamento predefinito, perciò, se la proprietà ShowShellBackButton è impostata a true, il pulsante sarà automaticamente disabilitato.

I comandi

Come anticipato all'inizio del post, PageHeader estende il controllo CommandBar, che viene utilizzato per aggiungere una application bar all'interno dell'applicazione, all'interno della quale poter aggiungere uno o più pulsanti che consentono all'utente di interagire con il contenuto corrente.

Di conseguenza, le stesse funzionalità offerte dal controllo CommandBar si possono riutilizzare anche all'interno di PageHeader, con lo stesso principio:

  • All'interno della collezione PrimaryCommands è possibile aggiungere uno o più pulsanti, i quali saranno sempre visibili con la relativa icona e testo descrittivo.
  • All'interno della collezione SecondaryCommands è possibile aggiungere uno o più pulsanti che, invece, saranno nascosti: l'utente potrà vederli premendo sui tre puntini mostrati al termine della barra. In tal caso, i pulsanti saranno solo testuali.

All'interno delle collezioni non si può includere qualsiasi controllo XAML, ma solo un sottoinsieme di elementi progettati ad hoc per l'application bar. Il controllo più utilizzato è AppBarButton, che rappresenta un pulsante che l'utente può premere; è caratterizzato da un'immagine (la proprietà Icon) e da una descrizione (la proprietà Label) e ci si può sottoscrivere all'evento Click per gestire l'interazione con l'utente.

Ecco un esempio di codice che definisce un comando primario e due secondari:

<controls:PageHeader Text="Main Page" Frame="{x:Bind Frame}">

<controls:PageHeader.PrimaryCommands>

<AppBarButton Icon="Forward" Label="Next" Click="OnNextClicked" />

</controls:PageHeader.PrimaryCommands>

<controls:PageHeader.SecondaryCommands>

<AppBarButton Label="Option 1" />

<AppBarButton Label="Option 2" />

</controls:PageHeader.SecondaryCommands>

</controls:PageHeader>

 

 

E' importante valutare bene l'ingombro dei pulsanti quando testate la vostra applicazione su differenti categorie di device. Ad esempio, l'utilizzo di numerosi comandi primari non costituisce un grosso problema su desktop: le maggiori dimensioni dello schermo e l'orientamento in landscape fanno sì che ci sia molto spazio a disposizione. Lo stesso, invece, non si può dire su uno smartphone: di conseguenza, in questo caso, è meglio ridurre al minimo i comandi primari e sfruttare maggiormente quelli secondari.

L'immagine seguente mostra come la stessa soluzione, che include tre comandi primari, si adatti perfettamente al desktop, ma crei problemi di layout in ambito mobile (a causa dello spazio ridotto, i pulsanti vanno a sovrapporsi al testo).

 

Il controllo HamburgerMenu

Uno delle novità dal punto di vista visuale di Windows 10 è il controllo SplitView, che permette di implementare il concetto di hamburger menu nelle vostre applicazioni. Tale approccio consiste in un menu laterale (tipicamente, viene posizionato a sinistra della pagina) che è possibile espandere premendo un pulsante in cima al pannello (solitamente, nell'angolo superiore sinistro). Il pannello, al suo interno, contiene diverse voci, che consentono di accedere alle varie sezioni dell'applicazione.

Diverse applicazioni native sfruttano questo approccio: News, che include nel pannello le varie sezioni di cui è composta l'applicazione; Mail, che sfrutta il menu per mostrare i vari account di posta con le relative cartelle, ecc.

Il controllo SplitView non nasce per "forzare" lo sviluppo esclusivamente di applicazioni basate sull'hamburger menu per gestire la navigazione, ma si affianca a quelli già disponibili e ancora utilizzabili, come Hub o Pivot. Di conseguenza, lo SplitView è estremamente flessibile e lascia la massima libertà allo sviluppatore di utilizzarlo come meglio preferisce. Si limita, infatti, a suddividere la pagina in due blocchi: un pannello a scomparsa e una sezione fissa, in cui inserire il contenuto. All'interno del pannello abbiamo la possibilità di inserire qualsiasi controllo XAML; di conseguenza, non deve necessariamente usato per la navigazione tra le varie sezioni.

Il rovescio della medaglia di questa flessibilità è che se vogliamo fornire all'utente un'esperienza basata sull'hamburger menu tradizionale (sulla falsa riga delle applicazioni native), abbiamo parecchio da lavoro da fare.

Template10 semplifica il lavoro dello sviluppatore in diversi modi:

  1. Offrendo un controllo di nome HamburgerMenu, che semplifica la definizione del pannello che andrà a contenere le sezioni dell'applicazione.
  2. Offrendo la possibilità di definire una shell, ovvero una pagina che funge da contenitore dell'applicazione. Sarà la shell a contenere il menu mentre, all'interno della sezione principale, saranno caricate le varie pagine dell'applicazione.
  3. Offrendo una serie di stili che permettono di ricreare un pannello con il look & feel delle applicazioni native (highlight della pagina correntemente caricata, supporto alle icone, ecc.)

In questo modo, saremo in grado di ottenere un risultato simile al seguente:

 

 

Vediamo i passi necessari per raggiungerlo.

Il controllo HamburgerMenu

Così come il controllo PageHeader, il controllo HamburgerMenu è contenuto all'interno del namespace Template10.Controls, che è quindi necessario aggiungere all'intestazione della pagina XAML.

A questo punto possiamo inserirlo all'interno della nostra pagina:

<controls:HamburgerMenu x:Name="Menu" />

La personalizzazione del controllo, dal punto di vista visuale, passa tramite diverse proprietà:

  • HamburgerBackground e HamburgerForeground, che definiscono il colore di sfondo e il colore del testo utilizzati per il pulsante che nasconde / mostra il pannello..
  • NavButtonBackground e NavButtonForeground, che definiscono il colore di sfondo e il colore del testo utilizzati per i pulsanti di navigazione.
  • NavAreaBackground, che definisce il colore di sfondo del pannello.

Una peculiarità del controllo HamburgerMenu, rispetto allo SplitView standard, è il supporto a due tipologie di comandi:

  • PrimaryButtons rappresentano i comandi principali utilizzati per la navigazione tra le varie sezioni e vengono posizionati in cima al pannello, subito sotto il pulsante per mostrarlo / nasconderlo.
  • SecondaryButtons rappresentano i comandi che, per la nostra applicazione, sono secondari o che sono collegati a sezioni che l'utente probabilmente visiterà meno frequentemente (come le Impostazioni). Tali comandi saranno mostrati in fondo al pannello, con un separatore all'inizio.

All'interno di ogni collezione possiamo andare a specificare uno o più comandi sfruttando un altro controllo offerto da Template10, chiamato NavigationButtonInfo. Si tratta, fondamentalmente, di una variante del controllo RadioButton: è il più adatto per il nostro scenario, in quanto implementa in automatico le caratteristiche necessarie (highlight della sezione corrente, scelta esclusiva tra un gruppo di pulsanti, ecc.)

Ecco un esempio completo di definizione di un controllo HamburgerMenu:

<controls:HamburgerMenu x:Name="Menu"

HamburgerBackground="#FFD13438"

HamburgerForeground="White"

NavAreaBackground="#FF2B2B2B"

NavButtonBackground="#FFD13438"

NavButtonForeground="White">

 

<controls:HamburgerMenu.PrimaryButtons>

<controls:NavigationButtonInfo PageType="views:MainPage" ClearHistory="True">

<StackPanel Orientation="Horizontal">

<SymbolIcon Symbol="Home" Width="48" Height="48" />

<TextBlock Text="Home" Margin="12, 0, 0, 0" />

</StackPanel>

</controls:NavigationButtonInfo>

 

<controls:NavigationButtonInfo PageType="views:DetailPage">

<StackPanel Orientation="Horizontal">

<SymbolIcon Symbol="Calendar" Width="48" Height="48" />

<TextBlock Text="Calendar" Margin="12, 0, 0, 0" />

</StackPanel>

</controls:NavigationButtonInfo>

</controls:HamburgerMenu.PrimaryButtons>

 

<controls:HamburgerMenu.SecondaryButtons>

<controls:NavigationButtonInfo PageType="views:SettingsPage">

<StackPanel Orientation="Horizontal">

<SymbolIcon Symbol="Setting" Width="48" Height="48" />

<TextBlock Text="Settings" Margin="12, 0, 0, 0" />

</StackPanel>

</controls:NavigationButtonInfo>

</controls:HamburgerMenu.SecondaryButtons>

 

</controls:HamburgerMenu>

 

 

Tale esempio include la definizione di due comandi primari e uno secondario. Ogni comando è rappresentato da un controllo NavigationButtonInfo, che ha una proprietà fondamentale chiamata PageType e serve a specificare la pagina a cui vogliamo portare l'utente quando ci farà tap sopra. Grazie a questa proprietà il controllo sarà in grado di:

  1. Lanciare in automatico la navigazione verso la pagina desiderata, senza necessità di sottoscrivere l'evento Click del pulsante e gestirla da codice.
  2. Applicare lo stile di highlight nel momento in cui la pagina correntemente visualizzata corrisponda a quella indicata dal pulsante.

Il controllo offre inoltre altre due proprietà per personalizzare la navigazione:

  1. ClearHistory che, quando viene impostata a true, forza la pulizia dello stack di navigazione quando si visita la pagina collegata. Tipicamente viene utilizzata in combinazione con la pagina principale, per evitare che si creino delle navigazioni di tipo circolare.
  2. PageParameter, per specificare eventuali parametri che devono essere passati alla pagina collegata (e, di conseguenza, recuperati tramite l'evento OnNavigatedTo()).

L'aspetto visuale del comando è a discrezione dello sviluppatore: è sufficiente definire, all'interno del controllo NavigationButtonInfo, lo XAML che vogliamo usare per rappresentarne il layout. Il codice che vedete nell'esempio permette di ricreare il look & feel delle applicazioni native: un'icona, definita con il controllo SymbolIcon (che viene visualizzata anche quando il menu viene mostrato in modalità minimale) e un testo descrittivo, definita con un semplice TextBlock.

Ecco come viene tradotto il codice precedente e declinato nei due stati possibili: pannello aperto e chiuso.

 

Affinché funzioni correttamente la gestione della navigazione, il controllo HamburgerMenu ha bisogno un riferimento al NavigationService, ovvero il servizio messo a disposizione da Template10 per gestire lo stack delle pagine. Tale riferimento viene passato, nel code-behind, alla proprietà NavigationService esposta dal controllo, come nell'esempio seguente:

public sealed partial class Shell : Page

{

public Shell(NavigationService navigationService)

{

this.InitializeComponent();

Menu.NavigationService = navigationService;

}

}

Come poter gestire il passaggio del NavigationService alla pagina che contiene il controllo HamburgerMenu? Lo vedremo nel prossimo paragrafo, dedicato alla shell.

Creare la shell

Abbiamo visto come, grazie a Template10, sia semplice definire una navigazione basata sull'hamburger menu in un'applicazione. Il problema, però, è che l'HamburgerMenu è un semplice controllo e, di conseguenza, va inserito in una pagina. Come gestire il fatto che, in realtà, tutte le pagine della nostra applicazione dovranno avere lo stesso menu?

Introduciamo perciò il concetto di shell, ovvero una pagina dell'applicazione che fungerà da contenitore per le varie pagine che compongono l'applicazione. La peculiarità è che il controllo HamburgerMenu sarà definito proprio all'interno della shell; di conseguenza, non dovremo ridefinirlo in tutte le pagine, ma potremo limitarci a definire nei vari file XAML il contenuto che vogliamo mostrare.

Il primo passo, perciò, è di creare una pagina vuota all'interno del nostro progetto e di riprendere i passaggi che abbiamo visto in precedenza, ovvero includere il controllo HamburgerMenu e collegare la proprietà NavigationService, come nell'esempio seguente:

<Page

x:Class="HamburgerSample.Views.Shell"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:views="using:HamburgerSample.Views"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:controls="using:Template10.Controls"

x:Name="ThisPage"

mc:Ignorable="d">

 

<controls:HamburgerMenu x:Name="Menu">

 

<controls:HamburgerMenu.PrimaryButtons>

<controls:NavigationButtonInfo PageType="views:MainPage" ClearHistory="True">

<StackPanel Orientation="Horizontal">

<SymbolIcon Symbol="Home" Width="48" Height="48" />

<TextBlock Text="Home" Margin="12, 0, 0, 0" />

</StackPanel>

</controls:NavigationButtonInfo>

 

<controls:NavigationButtonInfo PageType="views:DetailPage" >

<StackPanel Orientation="Horizontal">

<SymbolIcon Symbol="Calendar" Width="48" Height="48" />

<TextBlock Text="Calendar" Margin="12, 0, 0, 0" />

</StackPanel>

</controls:NavigationButtonInfo>

</controls:HamburgerMenu.PrimaryButtons>

 

<controls:HamburgerMenu.SecondaryButtons>

<controls:NavigationButtonInfo PageType="views:SettingsPage">

<StackPanel Orientation="Horizontal">

<SymbolIcon Symbol="Setting" Width="48" Height="48" />

<TextBlock Text="Settings" Margin="12, 0, 0, 0" />

</StackPanel>

</controls:NavigationButtonInfo>

</controls:HamburgerMenu.SecondaryButtons>

 

</controls:HamburgerMenu>

</Page>

 

public sealed partial class Shell : Page

{

public Shell(NavigationService navigationService)

{

this.InitializeComponent();

Menu.NavigationService = navigationService;

}

}

Ora abbiamo bisogno di:

  1. Definire la pagina appena creata come contenitore delle pagine della nostra applicazione, al posto del Frame tradizionale.
  2. Passare alla pagina un riferimento al NavigationService, così che possa essere passato al controllo HamburgerMenu

Entrambe le operazioni vengono effettuate nel bootstrapper; nello specifico, andremo a sfruttare il metodo InitializeAsync(), che viene chiamato prima della navigazione vera e propria verso la pagina principale.

sealed partial class App : BootStrapper

{

public App()

{

this.InitializeComponent();

}

 

public override Task OnInitializeAsync(IActivatedEventArgs args)

{

var nav = NavigationServiceFactory(BackButton.Attach, ExistingContent.Include);

Window.Current.Content = new Views.Shell(nav);

return Task.FromResult<object>(null);

}

 

public override Task OnStartAsync(BootStrapper.StartKind startKind, IActivatedEventArgs args)

{

NavigationService.Navigate(typeof(Views.MainPage));

return Task.FromResult<object>(null);

}

}

Innanzitutto utilizziamo il metodo offerto dal bootrapper NavigationServiceFactory per recuperare un riferimento al NavigationService. In questo modo, possiamo passarlo come parametro nel momento in cui creiamo una nuova istanza della pagina Shell. Dopodiché, associamo questa istanza alla proprietà Window.Current.Content, che specifica qual è il frame base dell'applicazione che fungerà da contenitore delle pagine. In un'applicazione tradizionale, tale proprietà viene valorizzata semplicemente con una nuova istanza della classe Frame.

Questa riga di codice fa sì che il contenitore delle nostre pagine non sia più un frame vuoto, ma la pagina che contiene il nostro HamburgerMenu. Il risultato sarà che tutte le pagine della nostra applicazione condivideranno il menu che abbiamo definito nella pagina denominata Shell.

HamburgerMenu e PageHeader: un'accoppiata vincente

I due controlli HamburgerMenu e PageHeader sono stati progettati proprio per essere utilizzati insieme. Il miglior risultato visivo, infatti, si ottiene quando le varie pagine della nostra applicazione che sfrutta l'hamburger menu utilizzano proprio il controllo PageHeader per definirne l'intestazione.

I due controlli, inoltre, hanno un comportamento pensato proprio per l'utilizzo combinato in ottica di adaptive layout. Come probabilmente saprete, una delle novità principali di Windows 10 è il fatto che le Universal Windows app sono costituite da un singolo binario che gira su tutte le piattaforme (al contrario di 8.1, dove si andavano a produrre due pacchetti differenti, da pubblicare su due store differenti). Qui entra in gioco il concetto di adaptive layout, ovvero l'interfaccia utente deve essere in grado di reagire ai cambiamenti nelle dimensioni della finestra per poter offrire sempre la migliore user experience possibile, indipendentemente che l'applicazione venga usata sullo schermo di un telefono o su una televisione.

A tale scopo, una delle principali novità introdotte dalla Universal Windows Platform sono i Visual State Trigger, ovvero dei trigger che consentono ad un controllo XAML o ad una pagina il passaggio in automatico da un visual state all'altro quando si verificano determinate condizioni. Nello specifico, la UWP mette a disposizione gli AdaptiveTrigger, che consentono il cambio di visual state in base alle dimensioni della finestra.

Sia il controllo HamburgerMenu che PageHeader sfruttano proprio questo meccanismo per ottimizzare l'esperienza d'uso in base alla dimensione dello schermo:

  1. In condizioni normali, il testo che funge da intestazione nel controllo PageHeader non ha alcun margine rispetto al bordo. Questo perché, quando la finestra è sufficientemente larga, il pannello del controllo HamburgerMenu sarà sempre visibile in modalità minimale (ovvero con le icone, ma senza la descrizione testuale). Di conseguenza, ci pensa già il pannello a creare il margine necessario per evitare che il pulsante di comparsa / scomparsa si sovrappongo all'intestazione.
  2. Quando le dimensioni della finestra sono inferiori (ad esempio, sullo schermo di uno smartphone) non c'è sufficiente spazio per mantenere visibile il pannello in modalità minimale. In tal caso, il pannello viene completamente nascosto e rimane visibile solamente il pulsante per mostrarlo / nasconderlo. In questo scenario il controllo PageHeader aggiunge un margine rispetto al bordo: dato che la colonna con le icone non è più presente, senza questo accorgimento il testo dell'intestazione andrebbe a sovrapporsi al pulsante.

Questo comportamento viene applicato in maniera predefinita tramite alcuni visual state trigger definiti nei due controlli. Avete, però, la possibilità di decidere a quale dimensione della finestra far scattare un comportamento piuttosto che l'altro, tramite le proprietà VisualStateNarrowMinWidth e VisualStateNormalMinWidth. Ecco un esempio:

<controls:PageHeader Text="Main page" Frame="{x:Bind Frame}"

VisualStateNarrowMinWidth="0"

VisualStateNormalMinWidth="700" />

Tale codice significa che:

  1. Quando la dimensione della finestra è compresa tra 0 e 700, viene applicato il visual state chiamato VisualStateNarrow, ovvero quello minimale in cui il pannello è nascosto (adatto, ad esempio, per gli smartphone) e, di conseguenza, il testo deve essere spostato.
  2. Quando la dimensione della finestra è superiore a 700, invece, viene applicato il visual state chiamato VisualStateNormal, che è quello tradizionale in cui il pannello è sempre visibile e non richiede uno spostamento del test.

Tali proprietà sono disponibili anche per il controllo HamburgerMenu: ovviamente, per ottenere il risultato migliore, è fondamentale che i valori di questa proprietà coincidano con quelli che sono stati utilizzati per i vari controlli PageHeader presenti nelle pagine.

Le due immagini seguenti vi aiuteranno meglio a capire la differenza: la prima mostra l'applicazione quando è attivo il visual state normal, il secondo invece quando è attivo quello narrow.

 

 

 

Se volete disabilitare questo comportamento del controllo PageHeader (ad esempio, perché la vostra applicazione non fa uso di un hambuger menu e, di conseguenza, lo spostamento del testo causerebbe un problema di layout) è sufficiente impostare la proprietà VisualStateNarrowMinWidth a -1.

In conclusione

Nel corso di questo post abbiamo esaminato i dettagli come alcuni dei controlli personalizzati offerti da Template10 possano semplificare notevolmente il nostro lavoro nella definizione del design della nostra applicazione. Nel prossimo post, invece, ci sposteremo sul versante architetturale e vedremo come Template10 ci aiuterà nell'implementazione del pattern Model-View-ViewModel.

Vi ricordo che il repository su GitHub, disponibile all'indirizzo http://github.com/Windows-XAML/Template10/, contiene anche degli esempi di codice con cui potete provare in prima persona quanto descritto nel corso di questo post.

 

 


read full article

Guest post: Template10, un nuovo approccio nello sviluppo di Universal Windows app – Le basi

E-mail Stampa PDF

Questo post è stato scritto da Matteo Pagani, Support Engineer in Microsoft per il programma AppConsult

Una delle novità che avrete sicuramente notato iniziando a sviluppare un progetto di tipo Universal Windows per Windows 10 è il ridotto numero di template disponibili in Visual Studio 2015. Se, per quanto riguarda Windows / Windows Phone 8.1, Visual Studio 2013 metteva a disposizione svariati template con i più diffusi layout grafici già impostati (Hub, Pivot, ecc.), in Visual Studio 2015 troviamo solo un template di nome "Blank app" o "Applicaziona vuota", che include semplicemente lo scheletro di una Universal Windows app.

La motivazione è che, spesso, i template predefiniti venivano usati dagli sviluppatori con meno esperienza nel campo della progettazione della User Experience per semplificare il loro lavoro e non dover creare da zero l'interfaccia grafica. Purtroppo però, alla lunga, questo approccio ha portato alla pubblicazione di molte applicazioni dall'interfaccia utente molto simile tra di loro. Di conseguenza, con Windows 10 si è deciso di lasciare molta più libertà creativa agli sviluppatori e di non costringerli o guidarli verso l'adozione di uno specifico design pattern. Attenzione, questo non significa che l'interfaccia utente si possa realizzare senza seguire alcun criterio: la qualità della user experience rimane sempre uno degli aspetti fondamentali di un'applicazione! A tal proposito, vi consiglio una lettura approfondita delle linee guida ufficiali per il design di Universal Windows app all'indirizzo https://dev.windows.com/en-us/design

Il nuovo template è molto semplice ed è il punto di partenza perfetto per realizzare qualsiasi applicazione. A volte, però, soprattutto quando si devono realizzare applicazioni complesse, può risultare fin troppo semplice: nel momento in cui abbiamo la necessità di gestisce scenari avanzati (l'uso del pattern MVVM, il salvataggio e il caricamento dello stato di una pagina, ecc.), dobbiamo farci carico di realizzare tutta l'infrastruttura necessaria.

A tale scopo, un team di persone in Microsoft, guidato da Jerry Nixon (Technical Evangelist noto per la sua serie di corsi su MVA, tenuti insieme ad Andy Wigley, dedicati allo sviluppo di applicazioni per Windows 10) ha iniziato lo sviluppo di un template più avanzato, chiamato Template10. Si tratta di un progetto open source, pubblicato su GitHub all'indirizzo https://github.com/Windows-XAML/Template10 e, di conseguenza, aperto ai contributi di qualsiasi sviluppatore voglia collaborare nella crescita di questo template.

Il progetto è in continua evoluzione e gli obiettivi sono molto ambiziosi: Template10 vuole diventare il punto di riferimento per gli sviluppatori di Universal Windows app, indipendentemente dall'approccio allo sviluppo che preferiscono (code behind o MVVM) e dalle librerie che già utilizzano. Se volete saperne di più, potete consultare la FAQ ufficiale all'indirizzo https://github.com/Windows-XAML/Template10/wiki/Questions. Nel prossimo futuro, Template10 sarà distribuito sia come pacchetto NuGet sia come estensione di Visual Studio, così da avere la possibilità di creare una nuova applicazione in grado di sfruttarlo direttamente dal menu New project di Visual Studio 2015. Per il momento, però, anche se il template è considerato stabile ed adatto all'utilizzo in progetti reali, è disponibile solamente su GitHub. Vediamo, perciò, i passi necessari, al momento, per iniziare lo sviluppo di una nuova applicazione basata su Template10.

Aggiornamento: Template10 è disponibile anche su NuGet http://www.nuget.org/packages/Template10/1.0.2.2-preview. Per poterlo trovare e installare da Visual Studio, però, è indispensabile abilitare l’opzione Include prerelease, dato che il pacchetto è stato pubblicato in modalità beta.

Nel corso di questo primo post vedremo la struttura di Template10 e il suo utilizzo in uno scenario base, ovvero lo sviluppo tradizionale in code behind. Nel prossimo post, invece, vedremo come Template10 dia il suo meglio quando viene usato in combinazione con il pattern MVVM.

Creare il primo progetto

Innanzitutto, dobbiamo clonare il progetto da GitHub. Possiamo farlo sfruttando direttamente gli strumenti messi a disposizione da Visual Studio 2015.

  1. Apriamo la finestra di Visual Studio 2015 denominata Team Explorer. Come impostazione predefinita, è accessibile dall'apposita voce in basso a destra, affiancata al Solution Explorer.
  2. Troveremo una sezione denominata Local Git Repositories, in cui vengono elencati tutti i repository Git che avete in locale. Premete il pulsante Clone.
  3. Nel primo campo (evidenziato dallo sfondo giallo) dovete inserire l'URL del repository GitHub, ovvero https://github.com/Windows-XAML/Template10
  4. Nel secondo campo, invece, dovete specificare il percorso locale sul vostro computer dove vorrete clonare il progetto.
  5. Ora premete il pulsante Clone: Visual Studio si farà carico di scaricare il progetto e di copiarlo sul vostro computer.

 

 

Da questo momento in poi, sempre tramite Team Explorer, avrete la possibilità di accedere al repository e di scegliere l'opzione Sync: tale funzionalità vi permetterà di scaricare i sorgenti più aggiornati e di avere sempre accesso, perciò, alla versione più recente della libreria.

Un buon punto di partenza per creare una nuova applicazione con Template10 è sfruttare il progetto già pronto denominato Blank e contenuto all'interno della cartella Templates (Project), il quale predispone l'infrastruttura di base. Noi, però, partiremo da un progetto vuoto e, man mano, configureremo manualmente le parti che ci servono, così da capire meglio come funziona il template e quali sono i vantaggi che apporta.

Per il nostro scopo, perciò, creiamo un nuovo progetto di tipo Universal Windows utilizzando il template Blank App. Dopodiché, facciamo clic con il tasto destro sulla soluzione e scegliamo Add existing project: a questo punto andiamo a cercare, sul nostro computer, la cartella in cui abbiamo clonato la libreria da GitHub. Nello specifico, dobbiamo andare ad aggiungere il progetto di nome Template10Library: lo troviamo all'interno dell'omonima cartella della libreria, rappresentato dal file Template10Library.csproj. A questo punto è sufficiente, tramite il menu Add reference di Visual Studio, aggiungere un riferimento a tale progetto all'interno della nostra applicazione. Ora siamo pronti per iniziare ad usare Template10.

Il bootstrapper

La classe App, come ben saprete, è il punto di partenza di tutte le Universal Windows app: si fa carico di inizializzare la finestra dell'applicazione con, all'interno, il frame principale, che si fa carico di gestire il rendering e la navigazione tra le varie pagine.

La struttura base della classe App (dichiarata nel file App.xaml.cs) può essere però, a volte, un po' confusionaria. Trovate, infatti, dichiarati numerosi parti di codice che fanno parte dell'inizializzazione standard e, di conseguenza, non devono essere modificati dallo sviluppatore. Anche la gestione del ciclo di vita dell'applicazione (descritta in un post precedente su questo stesso blog) può risultare difficile da comprendere: a seconda della modalità di avvio (lancio dalla tile, attivazione tramite tile secondaria, restore dalla terminazione, ecc.), ci sono diversi punti di accesso, che corrispondono ad altrettanti metodi.

Template10 si fa carico di semplificare il funzionamento di tale classe introducendo il concetto di bootstrapper. Tutto il codice "standard" per l'inizializzazione dell'applicazione è stato spostato all'interno di una classe specifica della libreria, dalla quale dovremo far ereditare la nostra classe App.

Ecco come appare, perciò, la definizione del file App.xaml in un progetto che fa uso di Template10:

<common:BootStrapper

x:Class="BasicSample.App"

xmlns:common="using:Template10.Common"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

</common:BootStrapper>

Come vedete, il nodo base App è stato sostituito dalla classe BootStrapper, contenuta all'interno del namespace Template10.Common. Di conseguenza, anche nel code-behind (quindi nel file App.xaml.cs) la classe App dovrà ereditare dalla classe BootStrapper, come nell'esempio seguente:

sealed partial class App : Template10.Common.BootStrapper

{

public App()

{

InitializeComponent();

}

}

Ecco come appare, invece, l'inizializzazione completa della classe App in un'applicazione basata su Template10:

sealed partial class App : Template10.Common.BootStrapper

{

public App()

{

InitializeComponent();

}

public override Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)

{

NavigationService.Navigate(typeof(MainPage));

return Task.FromResult<object>(null);

}

}

Come potete notare, rispetto all'inizializzazione standard, il codice è estremamente semplificato. Il cuore è il metodo OnStartAsync(), che rappresenta il punto di ingresso dell'applicazione, indipendentemente dallo scenario: sia che l'applicazione sia stata aperta tramite la tile principale, da una tile secondaria o da uno dei contratti disponibili nella Universal Windows Platform (come quello di sharing), passerete attraverso questo metodo. Nel caso più semplice, il vostro codice sarà come quello mostrato nell'esempio: una semplice navigazione verso la pagina principale. La seconda riga di codice serve solo come "workaround" per gestire il fatto che il metodo OnStartAsync() è dichiarato come asincrono (e quindi, come da best practice, restituisce Task), ma non è detto che al suo interno faccia uso di metodi basati sul pattern async / await (come nell'esempio precedente). Di conseguenza, in tal caso, si restituisce un Task vuoto.

Questo approccio semplifica di molto la gestione delle varie modalità di attivazione di un'applicazione perchè il bootstrapper si fa carico, dietro le quinte, di gestire la creazione della finestra e del frame in maniera corretta. Nell'approccio tradizionale, invece, il frame viene creato unicamente all'interno del metodo OnLaunched(), lasciandovi "scoperti" nel caso in cui invece l'applicazione sia stata aperta con una modalità differente dal semplice tap sulla tile principale.

A titolo di esempio, ecco come appare la classe App in caso l'applicazione supporti la creazione (e di conseguenza, l'attivazione) di tile secondarie:

sealed partial class App : Template10.Common.BootStrapper

{

public App()

{

InitializeComponent();

SplashFactory = e => new SplashScreenView(e);

}

public override Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)

{

AdditionalKinds cause = DetermineStartCause(args);

if (cause == AdditionalKinds.SecondaryTile)

{

LaunchActivatedEventArgs eventArgs = args as LaunchActivatedEventArgs;

NavigationService.Navigate(typeof (DetailPage), eventArgs.Arguments);

}

else

{

NavigationService.Navigate(typeof (MainPage));

}

 

return Task.FromResult<object>(null);

}

}

Come vedete, rispetto all'approccio tradizionale, il codice è estremamente semplificato. Tramite il metodo interno DetermineStartCause() siamo in grado di determinare la causa di attivazione dell'applicazione: in base ad essa, ci limitiamo a navigare verso una pagina piuttosto che l'altra dell'applicazione. Nell'esempio precedente, abbiamo ipotizzato che la tile secondaria dia la possibilità di aprire l'applicazione direttamente sul dettaglio di un elemento: ci limitiamo, perciò, a recuperare gli argomenti della tile (contenuti nella proprietà Arguments dei parametri di attivazione) e a portare l'utente verso la pagina predisposta della nostra applicazione, chiamata DetailPage. In caso, invece, di avvio dalla tile principale il flusso di navigazione rimane invariato e l'utente viene portato alla pagina principale.

Con lo stesso approccio, possiamo gestire tutti gli altri scenari di attivazione: da notifica toast, da Uri, da contratto di sharing, ecc. In questo caso, il metodo DetermineStartCause() vi restituirà il valore Other dell'enumeratore AdditionalKinds: dovremo perciò sfruttare la proprietà Kind del parametro di attivazione di tipo IActivatedEventArgs per determinare lo scenario di utilizzo.

All'interno del bootstrapper avete accesso a tre altri metodi di cui fare l'override, che possono risultare utili in alcuni scenari:

  1. OnInitializeAsync() viene chiamato durante l'inizializzazione dell'applicazione e prima del metodo OnStartAsync(). E' utile se dovete inizializzare qualche funzionalità in fase di startup (ad esempio, i servizi di analytics come Application Insights).
  2. OnResuming() equivale all'omonimo metodo disponibile nella classe App tradizionale e viene invocato quando l'applicazione viene riattivata dopo una sospensione. Normalmente, non è necessario gestire tale scenario: questo metodo, infatti, viene scatenato solamente quando l'applicazione è stata riattivata dallo stato di sospensione; il processo, perciò, non essendo stato terminato ma mantenuto in memoria, non ha bisogno di recuperare l'eventuale stato salvato in precedenza. Questo metodo può tornare utile quando, ad esempio, vogliamo forzare l'aggiornamento dei dati dell'applicazione, in caso l'utente l'abbia riattivata dopo lungo tempo dall'ultimo utilizzo.
  3. OnSuspending() viene invocato quando l'applicazione viene sospesa. Tipicamente, questo metodo viene utilizzato per salvare lo stato dell'applicazione, così da poterlo ripristinare in caso di terminazione. In realtà, con Template10 questa operazione non è necessaria: ci pensa, infatti, in automatico il bootstrapper a gestire questo scenario. Nei prossimi post vedremo come sfruttare tale funzionalità.

Gestione avanzata della splash screen

La splash screen è un'immagine statica che viene mostrata all'utente durante la fase di inizializzazione dell'applicazione. Nel momento in cui questa è terminata, allora la splash screen viene nascosta e viene lanciata la navigazione verso la pagina principale dell'applicazione.

Come regola generale, la fase di inizializzazione dell'applicazione deve essere il più breve possibile: se, entro 10 secondi dalla comparsa della splash screen, l'applicazione non è ancora pronta per l'utilizzo questa sarà terminata dal sistema operativo. Può capitare, però, che possa essere necessario più tempo: pensiamo, ad esempio, ad un'applicazione che deve scaricare dei dati da Internet. Per evitare la terminazione da parte del sistema, è buona prassi non effettuare il caricamento dei dati in fase di inizializzazione all'interno della classe App, ma direttamente nella prima pagina dell'applicazione. In questo modo, l'inizializzazione sarà molto veloce e il controllo sarà subito passato alla pagina principale; dato che, però, ormai l'applicazione è stata avviata, il sistema operativo non tenterà più di terminarla, anche se poi all'interno della pagina ci volessero più di 10 secondi per caricare tutti i dati.

Questo approccio ha però lo svantaggio, a volte, di non offrire una user experience eccezionale: l'utente viene portato alla pagina principale che, però, per forza di cose, sarà completamente vuota e conterrà, al massimo, un messaggio di caricamento fino a che tutti i dati non sono pronti. Di conseguenza, alcune applicazioni adottano il concetto di extended splash screen: terminata l'inizializzazione base dell'applicazione, l'utente viene portato in un'altra pagina che assomiglia, in tutto e per tutto, alla splash screen. La differenza, in questo caso, è che non trattandosi di una semplice immagine statica possiamo includere degli elementi visivi per segnalare all'utente che l'operazione di caricamento è in corso, come una ProgressBar o un testo. Se vi interessa approfondire l'argomento, su MSDN trovate una descrizione su come implementare questa procedura, all'indirizzo https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868191.aspx

Come vedete, c'è un po' di lavoro da fare, oltre a creare la extended splash screen. Template10 semplifica notevolmente questo scenario, mettendo a disposizione una classe che si fa carico di gestire la transizione splash screen -> extended splash screen -> prima pagina dell'applicazione per voi. Vediamo come fare.

Il primo passo è creare la splash screen estesa, tipicamente definita tramite uno User Control. Fate click con il tasto destro sul vostro progetto, scegliete Add new item e selezionate il template UserControl. All'interno di questo controllo potete mettere gli elementi grafici che volete, senza limitazioni di sorta. Dato, però, che l'obiettivo è quello di dare all'utente la parvenza di stare ancora visualizzando la splash screen, tipicamente viene incluso un controllo Image che punta all'immagine scelta come splash screen statica. Il resto, poi, è a vostro piacimento. Nell'esempio seguente, l'immagine viene inserita all'interno di un Canvas, con lo stesso colore di sfondo dell'immagine. In più, viene inserito un controllo ProgressRing, per notificare l'utente che c'è un caricamento in corso.

<UserControl

x:Class="BasicSample.Views.SplashScreenView"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

mc:Ignorable="d">

<Grid>

<Canvas x:Name="MyCanvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#0971C3">

<Image x:Name="MyImage" Stretch="None" Source="/Assets/SplashScreen.png" />

</Canvas>

<ProgressRing VerticalAlignment="Bottom" HorizontalAlignment="Center" Width="50" Height="50" IsActive="True" Foreground="White" Margin="0, 0, 0, 100"/>

</Grid>

</UserControl>

 

Nel code-behind sfruttiamo l'accesso alla classe SplashScreen (che da alcune informazioni sull'immagine, come la dimensione e la posizione) per impostare la nostra immagine allo stesso modo: in questo modo, l'utente non noterà la transizione dalla splash screen statica a quella estesa.

public sealed partial class SplashScreenView : UserControl

{

public SplashScreenView(SplashScreen splashScreen)

{

this.InitializeComponent();

Action resize = () =>

{

MyImage.Height = splashScreen.ImageLocation.Height;

MyImage.Width = splashScreen.ImageLocation.Width;

MyImage.SetValue(Canvas.TopProperty, splashScreen.ImageLocation.Top);

MyImage.SetValue(Canvas.LeftProperty, splashScreen.ImageLocation.Left);

};

Window.Current.SizeChanged += (s, e) => resize();

resize();

}

}

Il ridimensionamento e il posizionamento dell'immagine viene incluso all'interno di una Action che, oltre ad essere eseguita in fase di inizializzazione del controllo, viene legata all'evento di ridimensionamento della finestra (SizeChanged). Questo perché, in ambito desktop, l'utente potrebbe ridimensionare la finestra durante l'avvio dell'applicazione e, di conseguenza, la splash screen deve riadattarsi di conseguenza.

Ora che abbiamo definito la nostra splash screen personalizzata, per utilizzarla è sufficiente aggiungere la seguente riga di codice nel boostrapper:

public App()

{

InitializeComponent();

SplashFactory = e => new SplashScreenView(e);

}

SplashScreenView rappresenta il nome dello UserControl appena creato, mentre e è l'istanza della classe SplashScreen che viene passata al costruttore del controllo (quella che abbiamo usato per posizionare e ridimensionare l'immagine).

Il gioco è fatto: a questo punto, nel momento in cui l'applicazione dovesse impiegare più di qualche secondo per avviarsi, l'utente sarà portato alla nostra splash screen personalizzata e, solo al termine del caricamento dei dati, alla pagina principale dell'applicazione. Per testare questo scenario, è sufficiente includere all'interno del metodo OnStartAsync() un delay di qualche secondo prima di effettuare la navigazione verso la pagina principale, come nell'esempio seguente:

sealed partial class App : Template10.Common.BootStrapper

{

public App()

{

InitializeComponent();

SplashFactory = e => new SplashScreenView(e);

}

public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)

{

await Task.Delay(TimeSpan.FromSeconds(5));

NavigationService.Navigate(typeof(MainPage));

}

}

Una volta terminata l'inizializzazione, l'applicazione avvierà il metodo OnStartAsync() e, fintanto che non sarà scatenata la navigazione con il metodo Navigate del NavigationService, sarà caricata la splash screen personalizzata. Dato che, in questo caso, abbiamo inserito un delay di 5 secondi, tale splash screen rimarrà a video per 5 secondi, dopodiché l'utente verrà portato alla pagina principale.

La gestione della navigazione

Un'altra funzionalità offerta da Template10 è la gestione automatica dello stack delle pagine. Se avete sviluppato, in passato, applicazioni per Windows e Windows Phone 8.1, saprete sicuramente che la gestione della navigazione è una delle differenze principali tra le due piattaforme. Se, su Windows Phone, non è necessario includere alcun elemento visuale per portare l'utente alla pagina precedente, dato che gli smartphone sono già dotati di un pulsante Back hardware, lo stesso non si può dire di Windows.

Le cose con Windows 10 sono cambiate: anche la versione desktop / tablet, infatti, offre una gestione integrata del pulsante Back, sollevando gli sviluppatori (se lo ritengono opportuno) dal doverlo gestire manualmente. Nello specifico, quando questa funzionalità viene abilitata, gli utenti avranno la possibilità:

  • In caso di utilizzo dell'app in modalità desktop, il pulsante Back virtuale sarà incluso nella cornice della finestra in alto a destra.
  • In caso di utilizzo dell'app in modalità tablet, il pulsante Back virtuale sarà incluso nella barra di sistema, tra il pulsante Start e a Cortana.

 

 

 

 

 

 

 

Normalmente, in un'applicazione tradizionale, l'abilitazione e la gestione di questa funzionalità è a carico dello sviluppatore (allo stesso modo in cui, in Windows Phone 8.1, dovevamo prevedere un handler dell'evento BackPressed della classe HardwareButtons). Con Template10, invece, come comportamento predefinito, questa gestione è abilitata in automatico. Di conseguenza:

  • Quando l'applicazione viene eseguita su desktop e, nello stack delle pagine, sono presenti pagine precedenti a quella corrente, in alto a destra sarà abilitato il pulsante virtuale. Se, invece, lo stack è vuoto (ad esempio, perché ci si trova nella prima pagina), il pulsante sarà automaticamente nascosto.
  • Quando l'applicazione viene eseguita in ambito mobile, la pressione del pulsante Back hardware condurrà l'utente alla pagina precedente o, in alternativa, se lo stack è vuoto, alla Start screen del telefono.

Se vogliamo disabilitare questo comportamento perché vogliamo gestire la navigazione in maniera autonoma (ad esempio, includendo un pulsante direttamente nell'interfaccia grafica) possiamo impostare la proprietà ShowShellButton a false nel costruttore del bootstrapper, come nell'esempio seguente:

public App()

{

InitializeComponent();

ShowShellBackButton = false;

}

Con questa opzione vi accorgerete di come, quando l'applicazione viene utilizzata su desktop, il pulsante Back nella finestra non comparirà più, anche se vi trovate in una pagina "interna".

Nella prossima puntata J

Nel corso di questo post abbiamo visto alcuni concetti base di Template10 e quali sono le funzionalità che possiamo iniziare a sfruttare per rendere più semplice lo sviluppo di una Universal Windows app. Abbiamo, però solamente "grattato" la superficie e, nel corso dei prossimi post, scopriremo le vere potenzialità di Template10, soprattutto in ottica di utilizzo del pattern MVVM. Se, nel frattempo, volete iniziare a "giocare" con Template10, potete sfruttare gli esempi già pronti contenuti all'interno del repository su GitHub. Happy coding!


read full article

BizSpark: tutti gli aggiornamenti sul programma dedicato alle startup

E-mail Stampa PDF

Questo post è dedicato a tutti coloro che hanno una startup, un progetto in essere, o un'idea di progetto, che potrebbe realizzarsi in una nuova startup.

Come qualcuno di voi già saprà, Microsoft mette a disposizione BizSpark, un programma dedicato alle startup, pensato per aiutarle nelle prime fasi dello sviluppo, fornendo non solo i mezzi tecnici necessari, ma anche consulenza e supporto a livello di business. Per avere più dettagli a riguardo, se ancora non conoscete il programma, potete leggere il post di Mario Fontana e guardare il video di Daniele Pagani, Audience Marketing Manager del BizSpark Italy team.

Oggi, vorrei darvi qualche aggiornamento in merito alle novità che sono state apportate al programma negli ultimi mesi.

Iniziamo dalle novità world wide :)
Uno dei vantaggi offerti dal programma è quello di avere a disposizione fino a 8 sottoscrizioni Microsoft Azure con crediti gratuiti per poter attivare e utilizzare i servizi cloud.
Per le startup di BizSpark esiste la possibilità di potenziare questo vantaggio richiedendo BizSpark Plus. Questo "potenziamento" del programma offre la possibilità di incrementare il credito di una delle 8 sottoscrizioni Azure a disposizione.

Da luglio BizSpark Plus è diventato ulteriormente vantaggioso aumentando i crediti gratuiti da utilizzare su Azure da 60 mila dollari a 120 mila dollari per un anno per una a scelta delle 8 sottoscrizioni che si hanno a disposizione con BizSpark.
Richiedendolo avrete la possibilità di aumentare la complessità nelle vostre applicazioni e le risorse cloud che ad esse sono allocate, raggiungendo un bacino di utenti sempre più elevato, così da ottenere sempre più visibilità e successo.
Per ulteriori informazioni potete leggere il post sul blog di Steve Guggenheimer in cui viene annunciato l'aggiornamento.
Se siete già membri del programma BizSpark, richiedere questo ulteriore beneficio è molto semplice: vi basterà inviare una mail a Questo indirizzo e-mail è protetto dallo spam bot. Abilita Javascript per vederlo. e sarete contattati al più presto da un membro del team per procedere con l'application.
Se invece la vostra startup non è ancora iscritta al programma, potete scriverci esprimendo la volontà di accedere a tutti i vantaggi offerti, vi risponderemo con i dettagli per procedere all'iscrizione.

A livello nazionale, abbiamo intrapreso una nuova iniziativa per offrire alle startup che fanno parte del programma sempre più visibilità sul mercato italiano (e non solo), StartUp4You.
Si tratta di una piattaforma che permette di aggregare il meglio dell'ecosistema innovativo Italiano: da una parte le migliori startup digitali con prodotti e servizi innovativi, dall'altra aziende di industrie diverse alla ricerca di tecnologi e i partner.


La startup, iscritta a BizSpark, ha la possibilità di profilarsi sulla piattaforma e mettersi "in vetrina" per le aziende che accedono alla piattaforma, avendo così la possibilità di farsi notare e ricevere importanti offerte.
Le aziende possono, non solo, cercare team interessanti a cui proporre lo sviluppo di determinati progetti, ma anche lanciare delle vere e proprie Call for Action, a cui le startup profilate possono rispondere.
StartUp4You rappresenta per le startup una opportunità unica aggregando la domanda e l’offerta tecnologica.

Infine, ma non meno importante, abbiamo intrapreso, nell'ambito del nuovo show su Channel9 #TecHeroes , l'iniziativa della "Startup del mese". La startup scelta dal team BizSpark Italy avrà a disposizione una pagina all'interno del portale di StartUp4You in cui
potersi raccontare, apparirà sulla nuova pagina MSDN dedicata alle iniziative BizSpark e verrà invitata in sede Microsoft nello studio di #TecHeroes per registrare un breve video in cui potrà parlare delle applicazioni che sta sviluppando e di come il programma BizSpark sia stato d'aiuto durante le prime fasi della sua attività.

Potete trovare ai seguenti link le startup che sono già state intervistate:

Startup, che state aspettando? Scriveteci per entrare nel programma BizSpark se non l'avete ancora fatto, o per avere maggiori informazioni su tutte queste novità.

A presto :)


read full article

Guest post: Gestire le notifiche push di una Universal Windows app con i Notification Hub di Azure

E-mail Stampa PDF

Questo post è stato scritto da Matteo Pagani, Support Engineer in Microsoft per il programma AppConsult

Le notifiche push sono uno dei sistemi più diffusi per gestire la comunicazione asincrona tra un client e un server. Con questo meccanismo il controllo sulla presenza di nuovi dati o informazioni viene spostato dal client (che dovrebbe mantenere una comunicazione continua verso il server, con conseguente impatto sulla batteria e sul consumo di dati) al server (che, invece, si limita a notificare il client quando c'è qualche novità).

L'importanza delle notifiche push, inoltre, assume maggiore rilevanza in ambito mobile, in quanto permettono di comunicare con un'applicazione anche quando questa non è in esecuzione, andando a sopperire al fatto che quasi tutte le principali piattaforme non consentono alle applicazioni di girare indiscriminatamente in background, con lo scopo di preservare la durata della batteria e le performance del sistema operativo. Certo, esistono dei meccanismi per gestire queste situazioni anche senza le notifiche push (pensiamo, ad esempio, ai background task della Universal Windows Platform, citati nel precedente post), ma non sono adatti a soddisfare qualsiasi scenario.

Prendiamo, come esempio, un'applicazione dedicata al calcio. Se volessimo comunicare all'utente, in maniera tempestiva, ogni qualvolta la sua squadra del cuore ha segnato, le notifiche push sarebbero l'unico meccanismo che ci permetterebbe di implementare questo scenario, in quanto:

  1. L'evento (in questo caso, il gol) viene scatenato dall'esterno e non gestito localmente all'interno dell'applicazione.
  2. Si tratta di un evento asincrono e "imprevedibile": nessuno dei trigger supportati dai background task ci permetterebbe di gestire questo scenario, dato che la maggior parte sono legati ad eventi di sistema (presenza di connettività, blocco del telefono, ecc.) o a vincoli temporali (ad esempio, controlli periodici).

Le notifiche push permettono di soddisfare al meglio scenari di questo tipo, proprio perché vengono generate lato server (e quindi possono gestire eventi indipendenti dall'applicazione) e non hanno vincoli temporali (il canale di comunicazione rimane sempre attivo, quindi fintanto che non ci sono problemi di connettività le notifiche vengono spedite e ricevute senza problemi).

L'architettura delle notifiche push

Nel corso di questo post parleremo, in dettaglio, del funzionamento delle notifiche push nel mondo Windows, ma l'architettura generale è molto simile per tutte le principali piattaforme mobile. Tale architettura prevede il coinvolgimento di tre attori:

  1. Il client, ovvero l'applicazione mobile: si tratta dell'app che riceverà le notifiche push.
  2. Il server, ovvero l'applicazione che si farà carico di generare la notifica push.
  3. Un servizio cloud, che si fa carico di fare da tramite tra il client e il server e che viene fornito e gestito, solitamente, dal produttore della piattaforma mobile. Nel caso di Windows, questo servizio si chiama Windows Push Notification Service (WNS). Ogni qualvolta un'applicazione Windows o Windows Phone vuole sfruttare le notifiche push, questa si registra presso il WNS, il quale genererà un canale di comunicazione, rappresentato da un url. Dall'altro lato del canale si trova il server: ogni qualvolta vuole inviare una notifica push verso il device, dovrà inviare un pacchetto con una struttura ben precisa a tale url, sfruttando il comando POST del protocollo HTTP. Il WNS si farà carico di ricevere il pacchetto e inoltrarlo al device corretto e di restituire al server i dettagli dell'esito dell'operazione (notifica ricevuta, notifica non ricevuta per problemi di connettività, ecc.)

Tale architettura fa sì che l'invio di una notifica push da parte del server sia un'operazione molto semplice da implementare e, soprattutto, agnostica rispetto a qualsiasi piattaforma o tecnologia: trattandosi di un normale pacchetto HTTP inviato tramite una POST, è possibile aggiungere il supporto alle notifiche push in qualsiasi applicazione server, indipendentemente dalla tipologia (web, desktop, servizio, ecc.) e dalla tecnologia sottostante (.NET, Java, PHP, Ruby, etc.)

I Notification Hub di Azure

Nonostante l'architettura base di invio delle notifiche push sia molto semplice, richiede comunque un certo lavoro da parte dello sviluppatore per realizzare l'infrastruttura server necessaria. Nello specifico, è importante gestire una serie di requisiti fondamentali:

  1. Tutti i principali servizi cloud (incluso il WNS) implementano un meccanismo di sicurezza, per evitare che chiunque possa inviare notifiche push ai device semplicemente conoscendo l'URL del canale. Di conseguenza, lato server occorre gestire l'autenticazione a tali servizi. Il WNS, ad esempio, sfrutta il protocollo oAuth: nel momento in cui l'applicazione viene registrata sullo Store, vengono fornite una serie di credenziali per l'autenticazione. Se le credenziali vengono validate dal WNS si riceve in ritorno un token , da includere in tutte le chiamate successive al WNS: senza tale token, le chiamate non andranno a buon fine.
  2. Il server deve gestire l'elenco di tutti i dispositivi che si sono registrati per ricevere le notifiche push, con il relativo canale, memorizzandoli ad esempio in un database. Tale elenco, infatti, è indispensabile nel momento in cui il server vuole inviare una notifica a tutti i dispositivi che hanno installato e avviato l'applicazione. Opzionalmente, inoltre, potreste avere la necessità di memorizzare, oltre al canale, informazioni addizionali che vi permettano di identificare un singolo device o un gruppo di device. Ritornando all'esempio dell'applicazione calcistica di prima, probabilmente la notifica push relativa al gol segnato da una squadra dovrà essere inviata solamente agli utenti che hanno aggiunto tale squadra tra le preferite.
  3. L'applicazione server deve farsi di carico di gestire la logica di registrazione dei dispositivi ed evitare, ad esempio, che vengano registrati due dispositivi con lo stesso canale o che rimangano memorizzati dei dispositivi "orfani" (ovvero per i quali il canale non è più valido).

I Notification Hub sono uno dei tanti servizi di tipo PaaS (Platform as a Service) disponibili su Azure: con questo termine, si indicano tutti quei servizi cloud già "pronti" per essere utilizzati dagli sviluppatori, senza che si debbano preoccupare di gestire l'infrastruttura sottostante (manutenzione dell'hardware, aggiornamento del sistema operativo e del software, ecc.).

I Notification Hub sono un prezioso alleato per risolvere in un colpo solo tutte le complessità elencate poco fa:

  1. L'autenticazione viene gestita direttamente dal servizio: è sufficiente, nella configurazione, specificare le credenziali di autenticazione fornite dal servizio di notifiche push.
  2. Il servizio si fa carico di gestire l'elenco di tutti i dispositivi che si sono registrati per ricevere notifiche push. Inoltre, i device possono essere associati ad uno o più tag, ovvero delle parole chiave che possono essere utilizzate per raggrupparli in categorie. Ad esempio, l'applicazione sportiva potrebbe gestire un tag per ogni squadra di calcio e associarlo ad ogni device nel quale l'utente ha scelto tale squadra come preferita.
  3. Il servizio gestisce, in automatico, la logica necessaria per una corretta gestione dei dispositivi: ad esempio, in caso di canale già esistente, si farà carico di aggiornare la registrazione già esistente invece che crearne una nuova.

I Notification Hub, inoltre, hanno una caratteristica che li rende particolarmente adatti per scenari complessi: sono cross-platform, ovvero consentono di gestire dispositivi (e il relativo invio di notifiche) Windows, iOS, Android, ecc. Riprendendo l'esempio precedente, la vostra applicazione calcistica potrebbe essere disponibile per tutte le principali piattaforme: nel momento in cui una squadra ha segnato, sarete in grado, lato server, di inviare un'unica notifica a tutti gli utenti che hanno indicato tale squadra come preferite, indipendentemente dal fatto che stiano usando uno smartphone Windows Phone o un iPhone.

Per questo motivo, Microsoft mette a disposizione un SDK per interagire con i Notification Hub compatibile con le principali piattaforme e tecnologie, così da poterli facilmente integrare in qualsiasi scenario.

Nel corso di questo post, vedremo come farlo all'interno di un'applicazione Windows 10.

Creare un Notification Hub

I Notification Hub fanno parte dei servizi offerti dalla tecnologia Service Bus di Azure, che include, tra le altre, strumenti come Queue, Event Hub e Topic: si tratta, semplificando, di canali di comunicazione altamente scalabili che consentono di inviare e ricevere messaggi, anche ad una frequenza molto elevata.

Il punto di partenza per creare un Notification Hub, perciò, è accedere alla sezione del portale di Azure dedicata a Service Bus: ovviamente, vi servirà un account Azure valido, anche in modalità trial. Per il momento, Service Bus è gestibile solamente dal vecchio portale: non è ancora presente, infatti, nel nuovo portale in preview. Di conseguenza, dovrete fare login con il vostro account all'indirizzo http://manage.windowsazure.com.

All'interno della sezione chiamata Service Bus, premete il pulsante Create: ciò vi permetterà di creare un namespace, ovvero un "contenitore" all'interno del quale creare uno o più servizi. In realtà, i servizi di messaggistica e di Notification Hub sono mantenuti separati: una delle cose che vi sarà richiesta, infatti, è proprio la tipologia di servizio. Se scegliete Notification Hub, creerete un namespace all'interno del quale potrete aggiungere solamente servizi per le notifiche. La categoria Messaging, invece, vi consente di aggiungere uno qualsiasi dei servizi di messaggistica offerti da Service Bus.

Nel nostro caso, ovviamente, scegliamo Notification Hub: le altre informazioni richieste sono il nome del namespace (che costituirà la prima parte dell'indirizzo che verrà associato, ad esempio MyNotificationHub.servicebus.windows.net) e la regione di Azure in cui ospitarlo.

A questo punto abbiamo creato il namespace, che però sarà vuoto: dobbiamo creare il nostro primo Notification Hub, tramite il pulsante New posizionato in fondo alla pagina. Dovremo specificare il nome che vogliamo dare all'hub che, insieme all'indirizzo del namespace, lo identificherà in maniera univoca. Come impostazione predefinita, il Notification Hub viene creato sfruttando il piano gratuito, che offre 1 milione di notifiche push gratuite al mese e un massimo di 500 device registrati contemporaneamente. Esistono poi due piani a pagamento (Basic e Standard), nei quali questi limiti vengono progressivamente alzati (si arriva fino a 10 milioni di notifiche al mese e un numero illimitato di device registrati), e che offrono una serie di funzionalità più avanzate (come la possibilità di schedulare l'invio di notifiche o strumenti avanzati di telemetria). Troviamo un resoconto dettagliato delle caratteristiche dei vari piani all'indirizzo http://azure.microsoft.com/en-us/pricing/details/notification-hubs/

 

Il passo successivo è configurare il Notification Hub, così da poterlo usare con il servizio di notifiche push più adatto per la nostra applicazione. Nel nostro caso, andremo ad interagire con un'applicazione Windows 10, di conseguenza ci occuperemo di completare la configurazione relativa al supporto alle Universal Windows app.

Il primo passo è quello di recuperare le credenziali di accesso al WNS ed è indipendente dai Notification Hub: dovremmo seguire questa procedura anche se volessimo appoggiarci ad un'altra soluzione o farci carico noi di realizzarla. Per farlo, dobbiamo prima riservare un nome per la nostra applicazione Windows e associarlo: facciamo clic con il tasto destro sul nostro progetto in Visual Studio e, alla voce Store, scegliamo Associate app with Store. Dopo aver inserito le credenziali del nostro Microsoft Account legate al Dev Center, comparirà la lista di nomi che abbiamo già riservato: scegliamone uno o, in alternativa, riserviamone uno nuovo usando l'opzione in basso.

Dopo aver completato la procedura, dobbiamo portarci sul Dev Center, aprendo il browser all'indirizzo http://dev.windows.com. Entrando nella dashboard, troveremo tra le applicazioni recenti quella per cui abbiamo riservato il nome. Clicchiamoci sopra per accedere ai dettagli e, dal menu di sinistra, scegliamo la voce Push notifications dalla sezione Services.

Nella schermata che comparirà ci saranno descritte le varie opzioni disponibili per interagire con i servizi di notifica offerti da Microsoft. La sezione che interessa a noi è quella intitolata Windows Push Notification Services (WNS) and Microsoft Azure Mobile Services. La sezione sottostante, chiamata Microsoft Push Notification Service (MPNS), è infatti relativa al vecchio servizio di notifiche che veniva utilizzato nelle applicazioni Windows Phone 7.x e 8.0, il quale è ancora disponibile per retro compatibilità con le applicazioni già esistenti ma, a tutti gli effetti, da considerarsi deprecato in quanto sostituito dal WNS.

All'interno della pagina troveremo un link chiamato Live Services Site, evidenziato nella figura sottostante:

Clicchiamoci sopra per andare alla pagina dei servizi Live, in cui avremo accesso alle credenziali utilizzate per l'autenticazione. Nello specifico, quelle che servono a noi sono Package SID e Client secret.

Dopo che abbiamo preso nota di questi due valori, possiamo tornare sul portale di Azure e accedere alla sezione Configure del Notification Hub che abbiamo creato in precedenza. Tale sezione, tra le altre informazioni, offre la possibilità di indicare le credenziali di accesso per i principali servizi di notifiche push: oltre a quelli di Microsoft (WNS e MPSN), troviamo quelli di Google, Apple, Amazon e Baidu.

Nel nostro caso, ci interessa configurare la sezione chiamata Windows Notification Settings, all'interno della quale andare a specificare i Package SID e Client secret precedentemente recuperati, come nell'immagine seguente.

Gestire l'accesso al servizio da parte delle applicazioni

Abbiamo visto come un Notification Hub sia identificato dal namespace che lo contiene e da un nome univoco. Queste due informazioni, però, non sono sufficienti per consentire ad un'applicazione (sia essa client o server) di interagire con il servizio: per questi di sicurezza, infatti, le chiamate verso le API devono essere autenticate.

A questo scopo, i Notification Hub (ma, più generale, tutte le tipologie di servizi offerte da Service Bus) si basano sul concetto di Shared Access Signature, ovvero una chiave di accesso a cui possono essere legate differenti tipologie di permessi. Lo scopo di questo meccanismo è quello di consentire la distribuzione di tali chiavi a terze parti (ad esempio, se l'applicazione client e quella server sono sviluppate da due società differenti) senza, per questo, compromettere la sicurezza del servizio.

Una signature può avere tre tipologie di permessi (i quali possono anche essere combinati):

  • Manage: consente la gestione completa del servizio, garantendo l'accesso all'elenco dei dispositivi registrati, alla loro configurazione, ecc.
  • Send: consente il solo invio di notifiche push. E' utilizzata tipicamente dall'applicazione server.
  • Listen: consente la sola sottoscrizione al Notification Hub. E' utilizzata tipicamente dall'applicazione client.

Ogni signature è associata ad un nome univoco e le possiamo gestire all'interno della sezione Configure del servizio. Come impostazione predefinita, quando creiamo un Notification Hub vengono generate due chiavi:

  • Una con accesso completo (manage, send, listen) di nome DefaultFullSharedAccessSignature
  • Una per i client (con il solo permesso di listen) di nome DefaultListenSharedAccessSignature

Ogni signature è collegata a due chiavi di accesso (una primaria e una secondaria), che devono essere condivise con lo sviluppatore dell'applicazione. Il modo più semplice per condividere tali chiavi è tramite la relativa stringa di connessione. Analogamente a quando dobbiamo collegarci un database possiamo usare una stringa di connessione (ovvero una stringa che contiene tutte le informazioni necessarie per il collegamento, come l'indirizzo del server, la username, la password, ecc.), anche i Notification Hub mettono a disposizione una stringa di connessione per ogni signature, con tutte le informazioni che serviranno all'applicazione per effettuare il collegamento.

Possiamo trovarle nella dashboard del servizio, premendo il pulsante View connection strings, evidenziato nell'immagine seguente.

Ecco un esempio di stringa di connessione ad un Notification Hub:

Endpoint=sb://windows10samples.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=DlbQZoLHpq49BNJbP9YmkRVPoN4jqCfnJZwt+vAHU24=

Il client: l'applicazione Windows 10

Ora che abbiamo configurato il Notification Hub, possiamo iniziare a integrarlo nell'applicazione Windows, che sarà il destinatario delle nostre notifiche. Per interagire con i Notification Hub abbiamo a disposizione un'apposita libreria, disponibile su NuGet, dal nome WindowsAzure.Messaging.Managed. Si tratta di una versione dell'SDK per Azure Service Bus semplificata e specifica per i client: non offre, infatti, alcuna API per la gestione di scenari server (come l'invio di notifiche o la gestione dei device registrati). Esiste una libreria più completa, che vedremo più avanti nel corso del post, che però non possiamo usare in questo scenario perché richiede la versione full del framework .NET e, di conseguenza, non è compatibile con la Universal Windows Platform.

Il primo passo per integrare il Notification Hub è quello di registrare il canale per le notifiche push: in realtà, si tratta di un'azione indipendente dal servizio di Azure. Si tratta, infatti, della procedura standard da utilizzare quando vogliamo registrare un'applicazione Windows a ricevere notifiche push:

private async void OnRegisterForNotificationsClicked(object sender, RoutedEventArgs e)

{

PushNotificationChannel channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

if (channel != null)

{

    //gestisco il canale

}

}

La creazione del canale viene eseguita tramite il metodo CreatePushNotificationChannelForApplicationAsync() della classe PushNotificationChannelManager. Se la registrazione va a buon fine, ci viene restituito un oggetto di tipo PushNotificationChannel, che contiene tutte le informazioni sul canale appena creato.

Anche il secondo passaggio è parzialmente indipendente dai Notification Hub: una volta registrato il canale, infatti, dobbiamo comunicarne l'url alla nostra applicazione server, così che lo possa usare. In caso non ci appoggiassimo ai Notification Hub, la gestione di tale attività sarebbe a nostro carico: ad esempio, potremmo decidere di esporre un servizio REST che la nostra applicazione Windows 10 potrebbe contattare per comunicare le informazioni sul canale. Dato che, in questo caso, stiamo invece sfruttando i Notification Hub di Azure, possiamo utilizzare le API offerte dalla libreria appena installata.

Ecco come appare il flusso completo:

private async void OnRegisterForNotificationsClicked(object sender, RoutedEventArgs e)

{

PushNotificationChannel channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

if (channel != null)

{

NotificationHub hub = new NotificationHub("uwpsample", ConnectionString);

await hub.RegisterNativeAsync(channel.Uri);

}

}

Semplice no? Abbiamo creato un oggetto di tipo NotificationHub che, nel costruttore, richiede il nome del servizio e la stringa di connessione (di cui abbiamo parlato nel paragrafo precedente). Dopodichè, è sufficiente chiamare il metodo RegisterNativeAsync() passando, come parametro, l'url del canale, contenuto all'interno della proprietà Uri dell'oggetto di tipo PushNotificationChannel.

Esiste, inoltre, un overload del metodo RegisterNativeAsync() che accetta un secondo parametro, ovvero una collezione di tag, che ci permettono di categorizzare il device e, di conseguenza, inviare notifiche push ad un sottoinsieme dei dispositivi registrati.

private async void OnRegisterForNotificationsClicked(object sender, RoutedEventArgs e)

{

PushNotificationChannel channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

if (channel != null)

{

NotificationHub hub = new NotificationHub("uwpsample", ConnectionString);

await hub.RegisterNativeAsync(channel.Uri, new[] { "milan", "juventus" });

}

}

Riprendendo l'esempio dell'applicazione sportiva, questo esempio di codice potrebbe essere utilizzato per gestire un dispositivo nel quale l'utente, all'interno dell'applicazione, abbia indicato Milan e Juventus come squadre preferite.

Il punto di forza di queste API è che si fanno carico di gestire, in automatico, l'eventuale aggiornamento del canale. Tra le best practice nell'utilizzo delle notifiche push, infatti, troviamo il fatto che l'applicazione dovrebbe, ad ogni avvio, richiedere l'apertura del canale: questo perché gli URL non sono immutabili, ma possono scadere e cambiare nel tempo. Di conseguenza, dovremmo farci carico di memorizzare (ad esempio, nello storage locale) l'url del canale e andare a registrarlo nuovamente sul server solo se questo è cambiato. Sfruttando l'SDK dei Notification Hub ciò non è necessario: il metodo RegisterNativeAsync() si farà carico, in automatico, di registrare un nuovo dispositivo o di aggiornare quello esistente, in base allo scenario.

Volendo, ancora prima di realizzare l'architettura lato server, i Notification Hub ci danno la possibilità di testare che il lavoro svolto fin qui sia corretto. Nel portale su Azure, infatti, troverete una voce chiamata Debug, che vi permetterà di inviare una notifica di test a tutti i device registrati (o ad un sottoinsieme, specificando uno o più tag).

.

In base alla piattaforma scelta e al tipo di notifica, la sezione body si popolerà con il contenuto standard del pacchetto relativo. Nel caso di notifiche per le piattaforme Windows, il pacchetto è rappresentato da un file XML, che contiene al suo interno le varie informazioni. Nell'immagine, possiamo vedere il template per l'invio di una notifica toast. Se abbiamo seguito i passaggi precedenti nel modo corretto, premendo il pulsante Send la nostra applicazione per Windows 10 dovrebbe ricevere una notifica toast, con all'interno il messaggio che abbiamo definito nel nodo text.

Il server: gestire i device e inviare le notifiche

Vediamo ora come implementare l'applicazione server, ovvero quello che interagirà con il Notification Hub per gestire i dispositivi registrati e per inviare le notifiche. Al momento, il requisito che abbiamo per sfruttare l'SDK apposita è che l'applicazione deve essere basata sulla versione full del framework .NET: per questo esempio, sfrutterò un'applicazione WPF, dato che ho maggiore famigliarità con lo sviluppo client e con lo XAML. Nulla vieta, ad esempio, di realizzare un'applicazione web basata su ASP.NET.

Il primo passo è di installare nell'applicazione la libreria NuGet dedicata, che si chiama WindowsAzure.ServiceBus. Rispetto a quella client trattata in precedenza, offre metodi aggiuntivi per inviare notifiche, gestire i dispositivi registrati, ecc.

Partiamo dall'operazione principale, ovvero inviare le notifiche.

private async void OnSendNotificationsClicked(object sender, RoutedEventArgs e)

{

string xml = @"<toast>

<visual>

<binding template=""ToastGeneric"">

<text>Hello insiders!</text>

<text>This is a notification from Notification Hub</text>

</binding>

</visual>

</toast>";

 

NotificationHubClient client = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, "uwpsample");

await client.SendWindowsNativeNotificationAsync(xml);

}

Anche se con qualche differenza nel codice, il concetto base è lo stesso: le interazioni con il Notification Hub sono rese possibili dalla classe NotificationHubClient. Il modo più semplice per istanziarla è usare il metodo statico CreateClientFromConnectionString(), che accetta come parametri la stringa di connessione e il nome del Notification Hub.

Dopodichè troviamo diversi metodi per inviare notifiche, in base alla piattaforma di destinazione. Nel nostro caso, dato che stiamo interagendo con un'applicazione Windows 10, dobbiamo utilizzare il metodo SendWindowsNativeNotificationAsync() che accetta, come parametro, l'XML che descrive la notifica. In questo esempio, stiamo inviando una semplice notifica testa usando i nuovi adaptive template introdotti con la Universal Windows Platform.

Troviamo anche un metodo generico chiamato SendTemplateNotificationAsync(), che permette di inviare notifiche basate sul concetto di template. Anche se non sarà approfondito nel corso di questo post, vi consiglio di dare una lettura alla documentazione all'indirizzo https://msdn.microsoft.com/en-us/library/azure/dn530748.aspx: può risultare molto utile in caso di scenari avanzati, in quanto permette di gestire template per le notifiche che semplificano scenari di localizzazione, personalizzazione del contenuto o cross-platform.

In questo esempio, non abbiamo alcun specificato alcun criterio di selezione: di conseguenza, la notifica sarà inviata a tutti i dispositivi registrati nel nostro hub. Possiamo restringere l'invio sfruttando i tag, già nominati in precedenza. Esattamente come, lato client, avevamo a disposizione un overload del metodo di registrazione che ci permetteva di associare un device ad uno o più tag, qui possiamo sfruttare un overload del metodo di invio per specificare uno o più tag; la notifica sarà inviata solo ai dispositivi che sono associati ad esso.

Ecco un esempio di codice:

private async void OnSendNotificationsClicked(object sender, RoutedEventArgs e)

{

string xml = @"<toast>

<visual>

<binding template=""ToastGeneric"">

<text>Hello insiders!</text>

<text>This is a notification from Notification Hub</text>

</binding>

</visual>

</toast>";

 

NotificationHubClient client = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, "uwpsample");

await client.SendWindowsNativeNotificationAsync(xml, new[] { "Milan" });

}

Gestire i device registrati

L'SDK completa, come anticipato in precedenza, vi offre anche la possibilità di gestire i device registrati all'interno del Notification Hub. Ad esempio, ecco come possiamo recuperare l'elenco di tutti i dispositivi registrati:

private async Task RefreshDevices()

{

NotificationHubClient client = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, "uwpsample");

CollectionQueryResult<RegistrationDescription> results = await client.GetAllRegistrationsAsync(0);

}

Il metodo GetAllRegistrationsAsync() (che richiede come parametro l'indice del primo device che vogliamo ottenere, passando 0 siamo in grado di recuperare l'elenco completo) ci restituisce una collezione di oggetti di tipo RegistrationDescription, ognuno dei quali rappresenta un device registrato. Tale classe ci offre una serie di proprietà che ci permettono di recuperare informazioni dettagliate sulla registrazione, come l'id, la data di scadenza del canale o i tag associati. In alternativa, possiamo anche filtrare i risultati per tag (tramite il metodo GetRegistrationsByTagAsync()) o per canale (tramite il metodo GetRegistrationsByChannelAsync()).

Infine, abbiamo a disposizione una serie di metodi per modificare la registrazione, cancellandola dall'hub o aggiornandone i dati. Ecco un esempio di cancellazione, gestita all'interno di una lista di device mostrata nell'interfaccia grafica:

private async void OnDeleteDeviceClicked(object sender, RoutedEventArgs e)

{

Button button = sender as Button;

RegistrationDescription device = (button.DataContext) as RegistrationDescription;

 

NotificationHubClient client = NotificationHubClient.CreateClientFromConnectionString(ConnectionString, "uwpsample");

await client.DeleteRegistrationAsync(device);

}

La cancellazione viene effettuata tramite il metodo DeleteRegistrationAsync(), che accetta come parametro un oggetto di tipo RegistrationDescription.

Le notifiche push e Windows 10

Tramite l'uso dei Notification Hub, possiamo mettere in evidenza uno dei tanti vantaggi del nuovo modello di sviluppo introdotto dalle Universal Windows app di Windows 10. Dato che, rispetto al passato, non parliamo più di applicazioni separate (una per ogni piattaforma), ma di un unico binario che gira su tutte le piattaforme, anche il canale delle notifiche push sarà di conseguenza univoco.

Se lanciamo l'applicazione client sia sul nostro pc che sul telefono (usando l'emulatore o un telefono aggiornato alla preview di Windows 10 tramite il programma Insider), potremo notare che l'URL del canale sarà lo stesso. Di conseguenza, se proviamo ad inviare una notifica dalla sezione di debug del portale di Azure o dall'applicazione server, la vedremo arrivare sia sul pc che sul telefono contemporaneamente.

In conclusione

Nel corso di questo post abbiamo visto come integrare i Notification Hub di Azure all'interno di un'architettura basata sulle tecnologie Microsoft (sia lato server che client). E' importante sottolineare, però, come la libreria citata nel corso dell'articolo sia disponibile per tutte le principali piattaforme. Di conseguenza, non importa se state sviluppando un'applicazione Android o iOS o se avete un server basato su .NET, Java o Ruby: i Notification Hub sono la soluzione che fa per voi se avete bisogno di gestire invii massivi di notifiche push.

Vi lascio, in conclusione, due link:

Happy coding!


read full article

Guest post: le nuove funzionalità di esecuzione in background di Windows 10

E-mail Stampa PDF

Questo post è stato scritto da Matteo Pagani, Support Engineer in Microsoft per il programma AppConsult

Se avete già avuto occasione, in passato, di sviluppare Universal Windows app per Windows o Windows Phone 8.1, il concetto di "ciclo di vita dell'applicazione" dovrebbe esservi famigliare.

Rispetto al mondo desktop tradizionale, dove il ciclo di vita delle applicazioni è piuttosto semplice (sostanzialmente, un'applicazione rimane in esecuzione fintanto che è aperta), nel mondo mobile le cose sono più complicate. Un dispositivo mobile (come uno smartphone), infatti, deve far fronte ad una serie di scenari che non sono tipici del mondo desktop, quali il consumo di batteria, la quantità di memoria limitata, ecc.

Di conseguenza, le Universal Windows app (sia su 8.1 che su 10) adottano un ciclo di vita che può essere sintetizzato con l'immagine seguente:

Nel momento in cui l'applicazione viene aperta, questa entra in uno stato di esecuzione chiamato running, che le dà la possibilità di sfruttare memoria, CPU, rete, sensori e quant'altro sia disponibile sul device. Nel momento in cui viene sospesa (l'utente preme il pulsante Start, oppure riceve una notifica proveniente da un'altra applicazione e decide di aprirla) questa dopo 5 secondi viene "congelata" in memoria: ciò significa che il processo sarà mantenuto nella memoria RAM del dispositivo, ma non sarà in grado di eseguire alcuna operazione. Di conseguenza, tutte le risorse del dispositivo (CPU, fotocamera, sensori, ecc.) saranno liberate e pronte per essere utilizzate dalla nuova applicazione. Nel momento in cui l'utente sceglie di riaprire l'applicazione, ecco che si verifica l'evento di resuming: il processo viene "risvegliato" e l'applicazione può tornare a sfruttare le risorse messe a disposizione del dispositivo. Può capitare, però, che le risorse di sistema inizino ad esaurirsi: ad esempio, l'utente potrebbe aprire molte applicazioni, oppure averne sospese una o due molto onerose in termini di memoria occupata (come un gioco). In questo caso, all'apertura di una nuova applicazione, il dispositivo potrebbe non avere memoria sufficiente per gestirla: ecco, perciò, che il sistema operativo ha la possibilità di terminare le applicazioni più vecchie (in termini di frequenza di utilizzo) per liberare memoria. In questo scenario è compito dello sviluppatore, in fase di sospensione, salvare lo stato dell'applicazione in modo che, quando l'utente deciderà di riaprirla, la ritrovi esattamente come l'aveva lasciata: per l'utente finale, la terminazione del sistema operativo dovrebbe essere completamente trasparente.

Eseguire operazioni in background: i background task

Come potete intuire dalla spiegazione precedente, le applicazioni non hanno la possibilità di eseguire operazioni quando sono sospese. Il processo, infatti, è congelato e, di conseguenza, non può eseguire alcun tipo di attività che richieda l'uso di CPU, connettività, ecc.

Di conseguenza, per permettere agli sviluppatori di gestire la necessità di eseguire operazioni anche quando l'applicazione è sospesa, il Windows Runtime ha introdotto il concetto di background task: si tratta di progetti indipendenti, che fanno parte della stessa soluzione che contiene l'applicazione principale, i quali contengono blocchi di codice che vengono eseguiti dal sistema operativo anche quando l'applicazione non è in esecuzione.

La struttura di un background task è molto semplice:

using Windows.ApplicationModel.Background;

 

namespace Tasks

{

public sealed class ExampleBackgroundTask : IBackgroundTask

{

public void Run(IBackgroundTaskInstance taskInstance)

{

 

}

}

}

Semplificando, si tratta semplicemente di creare una classe che implementi l'interfaccia IBackgroundTask e che definisca il metodo Run(), che viene invocato nel momento in cui task viene attivato.

I background task sono dei progetti di tipo Windows Runtime Component e sono legati al concetto di trigger, ovvero degli eventi che possono far scatenare l'esecuzione del task: può essere un trigger periodico (ogni 30 minuti, ad esempio), legato ad un evento di sistema (l'utente ha bloccato / sbloccato il device) o a alla comunicazione con altri dispositivi (ad esempio, esistono una serie di trigger legati all'interazione con dispositivi Bluetooth Low Energy).

Non approfondirò l'argomento background task all'interno di questo post, dato che il loro funzionamento è rimasto sostanzialmente invariato nel passaggio da Windows 8.1 a Windows 10. Sono state aggiunte, infatti, numerose nuove tipologie di trigger, ma i concetti basi che ne guidano lo sviluppo sono i medesimi. Se siete interessati all'argomento, potete fare riferimento alla documentazione MSDN disponibile all'indirizzo https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh977056.aspx

Mantenere l'applicazione in background anche quando viene sospesa

La vera novità introdotta in Windows 10 e oggetto di questo post è la classe ExtendedExecutionSession, che vi consente di supportare due nuovi scenari:

  1. La vostra applicazione ha bisogno di salvare dei dati o terminare un'operazione in corso prima di essere sospesa e i 5 secondi assegnati dal sistema operativo non sono sufficienti.
  2. La vostra applicazione ha bisogno di continuare l'esecuzione anche se viene sospesa, ad esempio per tracciare in maniera continuativa la posizione dell'utente (navigatore satellitare, applicazioni di fitness, ecc.)

Vediamo in dettaglio come gestire entrambi gli scenari.

Terminare un'operazione in corso

La gestione di questo scenario avviene all'interno del metodo che gestisce la sospensione: la nostra applicazione sta per essere sospesa e vogliamo la possibilità di terminare l'operazione in corso (ad esempio, l'upload di un file sul server). Di conseguenza, il punto di partenza è la classe App, dichiarata nel file App.xaml.cs, presente in tutti i progetti di tipo Universal Windows app.

All'interno troverete un'implementazione base del metodo OnSuspending(), così definito:

private void OnSuspending(object sender, SuspendingEventArgs e)

{

var deferral = e.SuspendingOperation.GetDeferral();

 

deferral.Complete();

}

Tale blocco di codice, all'atto pratico, non esegue nulla: si limita a richiedere un deferral, che dà la possibilità di eseguire operazioni asincrone (quindi, sfruttando le keyword async e await) all'interno dell'evento di sospensione. Senza l'uso del deferral, l'avvio di un'operazione asincrona su un thread differente da quello principale potrebbe causare la sospensione immediata dell'applicazione.

All'interno di questo blocco dobbiamo andare a sfruttare la classe ExtendedExecutionSession per richiedere un'estensione di tempo, così da avere la possibilità di terminare l'operazione. Ecco un esempio di codice:

private async void OnSuspending(object sender, SuspendingEventArgs e)

{

var deferral = e.SuspendingOperation.GetDeferral();

 

var extendedSession = new ExtendedExecutionSession();

extendedSession.Reason = ExtendedExecutionReason.SavingData;

extendedSession.Description = "Complete synchronization";

 

ExtendedExecutionResult result = await extendedSession.RequestExtensionAsync();

if (result == ExtendedExecutionResult.Allowed)

{

UploadCompleteData();

}

else

{

UploadBasicData();

}

 

deferral.Complete();

}

Nel momento in cui creiamo un nuovo oggetto di tipo ExtendedExecutionSession, dobbiamo specificare:

  1. La motivazione per cui vogliamo che la nostra operazione continui anche dopo la sospensione. Nel nostro scenario, dobbiamo specificare il tipo SavingData dell'enumeratore ExtendedExecutionReason.
  2. Una descrizione che riassume brevemente il tipo di operazione che andremo ad eseguire. E' una semplice stringa testuale.

Dopodichè possiamo chiamare il metodo RequestExtensionsAsync(). Come si evince dal nome stesso, tale metodo non ci garantisce che la richiesta andrà a buon fine: il sistema operativo, infatti, potrebbe decidere di negarla, in base alle risorse disponibili in quel momento. È importante, perciò, verificare il risultato della chiamata, che è di tipo ExtendedExecutionResult. Solo nel caso in cui sia di tipo Allowed, allora siamo autorizzati ad eseguire un'operazione più complessa, dato che l'applicazione non sarà sospesa dopo 5 secondi. In caso contrario, invece, dobbiamo gestire il fatto che il sistema operativo ci abbia negato l'esecuzione in background: di conseguenza, dobbiamo rispettare il vincolo di 5 secondi prima che qualsiasi processo attivo venga terminato.

Tale approccio deve essere usato per gestire operazioni che abbiano, comunque, una durata non eccessiva: il sistema operativo, infatti, in caso le risorse si stiano esaurendo potrebbe terminare preventivamente l'attività in corso.

Mantenere l'applicazione attiva in background

La seconda funzionalità garantita dalla classe ExtendedExecutionSession offre più flessibilità rispetto allo scenario precedente, ma anche più vincoli. Con questo approccio, infatti, nel momento in cui l'applicazione viene sospesa, in realtà, questa viene mantenuta attiva in background: il processo non viene congelato, ma rimane attivo e in grado di eseguire operazioni, reagire agli eventi, ecc. È come se l'applicazione fosse in esecuzione in foreground, solamente non visibile. Si tratta di una funzionalità molto utile in particolar modo per le applicazioni che fanno un uso intensivo del tracciamento della posizione dell'utente: un'applicazione dedicata al fitness, ad esempio, potrebbe continuare a tracciare la corsa dell'utente anche se il telefono si trova bloccato in tasca.

È proprio per questo motivo che la classe ExtendedExecutionSession identifica questo approccio assegnando il valore LocationTracking alla proprietà Reason. Inoltre, per poterla sfruttare è necessario che la capability Location, all'interno del file di manifest, sia abilitata. In realtà, il sistema operativo non vi obbliga ad usare le API di geolocalizzazione per mantenere attiva questa modalità: è sconsigliato, però, utilizzarla per scopi differenti dal tracciamento della posizione dell'utente, in virtù del fatto che dareste un'informazione errata all'utente. Quanto questi consulterà, infatti, la pagina sullo Store della vostra applicazione, troverà tra l'elenco di funzionalità richieste il tracciamento della posizione, anche se non la utilizzate realmente.

Rispetto allo scenario precedente di salvataggio dei dati, esiste un'importante differenza nell'utilizzo della classe ExtendedExecutionSession: se, nell'esempio di codice visto poco fa, la utilizzavamo all'interno del metodo OnSuspending() della classe App, in questo caso invece dobbiamo richiedere il permesso al sistema operativo di continuare l'operazione in background il prima possibile. Un buon punto, ad esempio, dove inizializzarla è all'interno dell'evento OnNavigatedTo() della pagina principale dell'applicazione, come nell'esempio seguente:

protected override async void OnNavigatedTo(NavigationEventArgs e)

{

if (e.NavigationMode == NavigationMode.New)

{

var extendedSession = new ExtendedExecutionSession();

extendedSession.Reason = ExtendedExecutionReason.LocationTracking;

extendedSession.Description = "Location tracking";

 

ExtendedExecutionResult result = await extendedSession.RequestExtensionAsync();

if (result == ExtendedExecutionResult.Allowed)

{

Debug.WriteLine("Background execution approved");

}

else

{

Debug.WriteLine("Background execution denied");

}

}

}

La prima riga di codice ci assicura che l'inizializzazione sia fatta solo quando la modalità di navigazione è New, ovvero si tratta del primo caricamento della pagina: questo per evitare che, ad esempio, l'inizializzazione venga ripetuta ogni volta che l'utente, da una pagina secondaria, torni indietro a quella principale.

Dopodiché, il codice è praticamente identico a quello visto in precedenza. Le uniche differenze sono:

  • L'utilizzo del valore LocationTracking per la proprietà Reason della classe ExtendedExecutionSession
  • Anche in questo caso, è importante verificare se il sistema operativo ci abbia autorizzato a continuare l'esecuzione in background: in realtà, però, in caso affermativo non dobbiamo fare nulla di particolare, dato che l'applicazione continuerà semplicemente le sue attività in background, come se non fosse mai stata sospesa. Di conseguenza, possiamo sfruttare il risultato dell'operazione RequestExtensionAsync() per scopi di logging e per dare un avviso all'utente, ma non dobbiamo eseguire alcuna operazione particolare. Attenzione: se vi dimenticate di abilitare la capability Location nel file di manifest, la chiamata a questo metodo vi ritornerà sempre Denied.

E' importante sottolineare come, anche se l'applicazione sia effettivamente in esecuzione, non abbia comunque il controllo della UI: se avete bisogno di mostrare degli avvisi di tipo visivo all'utente, perciò, dovrete sfruttare i meccanismi predisposti per questo scenario, come le notifiche.

Ovviamente, dato che questa modalità lascia maggiore libertà, ha anche dei vincoli più stringenti rispetto allo scenario di semplice salvataggio dei dati. Nello specifico, è possibile avere solamente una applicazione in esecuzione sul dispositivo in questo stato; se l'utente avviasse un'altra applicazione che fa uso dell'esecuzione in background, la nostra verrebbe terminata per lasciare spazio a quella nuova.

Inoltre, anche in questo caso il sistema operativo ha la possibilità di valutare, in base allo stato delle risorse di sistema, se negare l'esecuzione in background o se terminarla prematuramente.

In conclusione

Le funzionalità di esecuzione in background descritte in queste post saranno molto apprezzate dagli sviluppatori, in quanto aprono una serie di nuovi scenari che non era possibile gestire in Windows 8.1. In conclusione, vi ricordo che la classe ExtendedExecutionSession fa parte della Universal Windows Platform di Windows 10 e, di conseguenza, anche se risulta sicuramente più utile in scenari mobile, è utilizzabile anche da applicazioni desktop, IoT, ecc.

Happy coding!


read full article

Pagina 1 di 49

 
 
 
 
Certificazioni