Sei sulla pagina 1di 726

Sviluppare

applicazioni per

Windows
Phone 8
Matteo Pagani
Sviluppare applicazioni per Windows Phone 8
Autore: Matteo Pagani
Collana:

Publisher: Fabrizio Comolli


Progetto grafico: escom - Milano
Coordinamento editoriale, prestampa e stampa: escom - Milano
ISBN: 978-88-6604-352-2
Copyright © 2013 Edizioni FAG Milano
Via G. Garibaldi 5 – 20090 Assago (MI) - www.fag.it
Finito di stampare in Italia presso Rotolito Lombarda - Seggiano di Pioltello (Mi) nel mese di aprile 2013
Nessuna parte del presente libro può essere riprodotta, memorizzata in un sistema che ne permetta l’elaborazione, né
trasmessa in qualsivoglia forma e con qualsivoglia mezzo elettronico o meccanico, né può essere fotocopiata, riprodotta
o registrata altrimenti, senza previo consenso scritto dell’editore, tranne nel caso di brevi citazioni contenute in articoli
di critica o recensioni.
La presente pubblicazione contiene le opinioni dell’autore e ha lo scopo di fornire informazioni precise e accurate.
L’elaborazione dei testi, anche se curata con scrupolosa attenzione, non può comportare specifiche responsabilità in
capo all’autore e/o all’editore per eventuali errori o inesattezze.
Nomi e marchi citati nel testo sono generalmente depositati o registrati dalle rispettive aziende.
L’autore detiene i diritti per tutte le fotografie, i testi e le illustrazioni che compongono questo libro.
Prefazione

Dopo un’euforica spinta iniziale, il settore del software per smartphone sta ora entrando in
una fase di maturità denominata app economy, l’economia delle app. Rispetto al concetto
di ecosystem, termine con il quale si definisce in modo generico l’offerta di terze parti su
un dato sistema operativo o su una piattaforma, l’app economy è un sistema economico
più complesso, che include molteplici variabili e attori.
L’economia delle app è una vera e propria catena del valore, che trascende la singola
piattaforma e tiene conto dei complessi equilibri tra domanda e offerta, delle dinamiche
distributive e di marketing, dei temi locali e culturali, della segmentazione e dei
comportamenti degli utenti, solo per citare alcuni dei temi più frequenti. Secondo alcune
stime, l’app economy ha generato oltre 500.000 posti di lavoro solamente negli USA e nel
2013 si attesterà su un valore complessivo di circa 20 miliardi di euro, per raggiungere i
65 miliardi nel 2016. Il numero di applicazioni disponibili crescerà da circa 2 milioni nel
2013 a 16 milioni nel 2016.
Ma come cambia il ruolo dello sviluppatore nell’ambito della app economy?
Per prima cosa cambia la figura professionale: per avere successo in questa nuova
economia lo sviluppatore deve innanzitutto diventare imprenditore e non solo creatore di
software. L’imprenditore/sviluppatore, o app entrepreneur, deve avere competenze a 360
gradi, dalla tecnologia allo sviluppo del business, deve essere al contempo tecnologo,
uomo di marketing, creativo e studioso del comportamento umano con un unico mantra: la
qualità.
La qualità diventa il metro di valutazione non solo dell’app da parte del cliente finale, ma
anche dei singoli processi, deve essere usata come un vero e proprio strumento per
emergere dal mare magnum delle applicazioni, molte delle quali perfettamente identiche
tra loro.
Oggi esistono milioni di applicazioni disponibili nei vari store, ma solo alcune centinaia
sono state scoperte dagli utenti, solo alcune decine sono state scaricate e ancora meno
sono quelle usate con regolarità dall’utente medio, in quanto sono diventate un essenziale
complemento della sua quotidianità. La qualità diventa quindi l’elemento essenziale per
determinare il successo del developer nella app economy.
Il concetto di qualità è soggettivo, ma si basa su elementi concreti e valori universalmente
accettati e apprezzati dal cliente finale, come l’attenzione ai dettagli anche minimi
nell’interfaccia utente, il continuo affinamento della user experience per eliminare ostacoli
e barriere e completare le azioni nel minor numero possibile di passi, la fluidità dello
scorrimento e delle transizioni e, ovviamente, la freschezza, attualità e utilità del soggetto,
o semplicemente la capacità di intrattenere e divertire.
Il sistema operativo Windows Phone offre allo sviluppatore numerosi strumenti per
ottenere tutte le qualità sopra indicate senza dovere ogni volta ripartire da zero. In
particolare, questo OS mette a disposizione soluzioni innovative, quali:
• le Live Tiles, un modo immediato e veloce per permettere alle applicazioni di
comunicare con l’utente senza che lo stesso le debba lanciare;
• il concetto di hub, che raccoglie in sé applicazioni e funzionalità omologhe quali
messaggistica e social networking, unendo funzionalità del sistema ad applicazioni di
terze parti;
• la app to app communication, che permette a ciascuna app di esistere non solo nella
sua indipendenza, ma anche come fornitore di funzioni o di contenuto a qualsiasi
altra applicazione. Il tutto ha un valore maggiore della somma delle parti.
Parallelamente Nokia mette a disposizione asset e differenziatori quali la Location
Platform Here, la tecnologia PureView, la Near Field Communication e Nokia Music.
Integrando queste piattaforme, attraverso API dedicate, è possibile sviluppare applicazioni
uniche, che entrano a far parte, arricchendola, della vita quotidiana degli utenti. Nokia non
vuole essere solo un supporto tecnologico, ma vuole diventare un vero e proprio partner
strategico per gli sviluppatori. Per questo Nokia si impegna ogni giorno per offrire nuove
opportunità nell’ambito della app economy, attraverso attività atte a incrementare la
visibilità dei tanti talenti locali. Un esempio è la realizzazione di un application hub
specifico per il made in Italy, per sponsorizzare e accelerare il download di app innovative
generate da developer italiani, i quali, in questo modo, hanno avuto la possibilità di entrare
nella app economy generando nuovi e interessanti flussi di ricavi. Spero che la lettura del
libro di Matteo Pagani su come sviluppare applicazioni per Windows Phone sia uno
stimolo e uno strumento essenziale per ogni sviluppatore che voglia avvicinarsi alla app
economy con l’ambizione di creare qualità e valore, per se stesso e per gli utenti. In un
mondo tecnologico dove la qualità gioca un ruolo sempre più importante sono sicuro che
le doti che da sempre caratterizzano il prodotto made in Italy possano emergere con forza
e guidare gli app entrepreneur italiani verso un successo su scala globale, come in molti
settori industriali. Dal mio osservatorio vedo che questo sta già succedendo.
Marco Argenti
Senior Vice President
Developer Experience - Nokia
Introduzione

Sono passati ormai tre anni dall’uscita di Windows Phone 7, che ha costituito la base del
cambiamento di Microsoft verso un nuovo approccio e un nuovo modo di concepire i
dispositivi, sia tradizionali (come i computer) sia di nuova generazione (smartphone e
tablet).
Microsoft era impegnata a sviluppare Windows Mobile 7, quando si è accorta che il
mondo mobile stava cambiando, e molto in fretta, in seguito all’avvento dell’iPhone di
Apple e di Android di Google. Windows Phone è la risposta di Microsoft a questo
cambiamento radicale: non più una “versione ridotta di Windows”, con tutti i pro e i
contro del caso, ma un sistema operativo completamente nuovo, costruito e pensato
intorno alle persone e alle loro esigenze. Un sistema sicuro, veloce e affidabile, ma anche
e soprattutto originale, con un’interfaccia e un approccio molto differenti da quelli della
concorrenza: pensiamo, ad esempio, alle live tile o alla forte integrazione con i social
network.
Windows Phone 7 era un sistema molto promettente, ma ancora un po’ acerbo: i tempi
erano stretti e Microsoft non è riuscita a includere nella prima versione tutte le
caratteristiche desiderate. Proprio per questo motivo, la società di Redmond non si è
seduta sugli allori ma ha continuato a far evolvere la piattaforma in maniera sostanziale,
con Windows Phone 7.5 prima e con la versione attuale, Windows Phone 8, poi. Questo
libro è dedicato alla versione più recente, che ha fatto da spartiacque tra due generazioni:
Windows Phone 7, pur essendo un sistema totalmente nuovo dal punto di vista delle
funzioni e dell’interfaccia, è nato in un momento di transizione e, di conseguenza, si
appoggiava ancora alle tecnologie Microsoft della precedente generazione, come
Windows Mobile per quanto riguarda il kernel e Silverlight per quanto riguarda il
framework di sviluppo.
Windows Phone 8, invece, è la conseguenza delle novità emerse negli ultimi anni e che
saranno approfondite nel corso del Capitolo 1: Windows 8, con il suo nuovo kernel, e
Windows Runtime, un nuovo framework per lo sviluppo di applicazioni. Per questo
motivo, Windows Phone 8 ha introdotto tantissime novità sia lato utente sia per gli
sviluppatori, ma i cambiamenti sostanziali riguardano il cuore della piattaforma: in questo
modo, Microsoft è stata in grado di traghettare Windows Phone verso il futuro, offrendo
un ambiente di sviluppo più moderno, performance migliori e rendendo più semplice lo
sviluppo di applicazioni sia per il mondo tablet (supportato da Windows 8) sia per il
mondo smartphone.
In tutto questo si inserisce la preziosissima partnership con Nokia, che ha messo al
servizio di Windows Phone la vasta esperienza della casa finlandese nella realizzazione di
prodotti hardware di altissima qualità e dal design ricercato e originale.
Da questa collaborazione nasce Windows Phone, al momento una delle piattaforme più
interessanti per gli sviluppatori mobile, soprattutto in Italia: si tratta, infatti, di un mercato
in continua espansione, nel quale, in virtù della giovane età rispetto alle piattaforme
concorrenti, la possibilità di realizzare applicazioni originali e di qualità si fa ancora più
concreta.
Sta a voi trovare l’idea giusta: spero che questo libro possa essere un valido aiuto nel
trasformarla in realtà e nel saper sfruttare il più possibile le caratteristiche più esclusive
della piattaforma, come le live tile, le notifiche push, la fotocamera e così via.
Chi dovrebbe leggere questo libro?
Questo libro è stato scritto con l’obiettivo di essere d’aiuto sia a chi si sta avvicinando a
Windows Phone per la prima volta, sia a chi ne ha già fatto esperienza, magari con le
versioni precedenti della piattaforma. I primi troveranno una guida passo per passo, che
partirà dai concetti base (come lo XAML o il ciclo di vita delle applicazioni) per
proseguire poi introducendo gli aspetti specifici della piattaforma, come le tile,
l’integrazione con l’hardware e con il sistema operativo ecc.
I secondi, invece, avranno la possibilità di passare direttamente ai capitoli più avanzati e di
imparare a utilizzare le nuove caratteristiche della piattaforma, come le Speech API, lo
sviluppo di Lens App, l’integrazione con la lock screen.
Questo libro è rivolto a chi vuole sviluppare applicazioni esclusive per Windows Phone 8:
non saranno trattati, infatti, concetti e API relativi a Windows Phone 7, tranne nel caso in
cui non ci siano stati cambiamenti e quindi i due sistemi coincidano.
Cosa contiene questo libro?
Il libro presenta gli argomenti relativi allo sviluppo di applicazioni Windows Phone
secondo un percorso logico che vi accompagnerà dalle basi necessarie per creare il vostro
primo progetto fino a padroneggiare le tante caratteristiche di cui sono dotati gli
smartphone Windows Phone che vengono messe a disposizione degli sviluppatori.
Nel Capitolo 1 farete la conoscenza con i concetti base della piattaforma: le caratteristiche
hardware, l’interfaccia, l’evoluzione della piattaforma e i linguaggi disponibili per lo
sviluppo. Scoprirete anche quali sono i tool messi a disposizione da Microsoft per creare
le applicazioni, testarle e pubblicarle sullo Store.
Nel Capitolo 2 apprenderete le conoscenze base necessarie per iniziare a sviluppare,
analizzando la struttura di un progetto. In più, farete la conoscenza dei concetti chiave di
XAML e C#, i due linguaggi che è importante conoscere per scrivere il vostro codice.
Nel Capitolo 3 faremo una panoramica di tutti i controlli che Windows Phone ci mette a
disposizione per realizzare l’interfaccia grafica dell’applicazione, partendo da quelli base
per poi trattare anche quelli contenuti nei principali toolkit di terze parti disponibili.
Nel Capitolo 4 affronteremo una serie di concetti chiave per sviluppare un’applicazione,
come il ciclo di vita, la navigazione tra pagine e il supporto alle nuove risoluzioni
introdotte in Windows Phone 8.
Il Capitolo 5 è dedicato all’interazione con il mondo esterno, grazie alla connessione a
Internet: tratteremo il download e l’upload di file, la comunicazione con servizi web e il
collegamento con altri dispositivi tramite tecnologie come Bluetooth e NFC.
I telefoni Windows Phone sono dotati di numerosi sensori, illustrati nel Capitolo 6, dove
vedremo come sfruttare i servizi di geolocalizzazione e i sensori di movimento.
Il Capitolo 7 è rivolto agli sviluppatori interessati a integrare le proprie applicazioni con il
sistema operativo, per sfruttare le applicazioni native, oppure per accedere alla rubrica e al
calendario dell’utente. Tratteremo anche due importanti novità introdotte in Windows
Phone 8: le Speech API, per dialogare tramite servizi vocali, e Wallet, un portafoglio
digitale.
Il Capitolo 8 è dedicato, invece, alle applicazioni dalla vocazione multimediale.
Imparerete come sfruttare la fotocamera di Windows Phone per realizzare applicazioni
fotografiche e vedrete anche come integrarvi con gli hub multimediali di Windows Phone,
come Music e Photos.
Nel Capitolo 9 parleremo di dati locali: la presenza di connettività negli smartphone è
importante, ma spesso ci si trova a dover gestire dati che invece sono memorizzati sul
telefono. Nel corso del capitolo vedremo come salvare e caricare informazioni nello
storage locale, come sfruttare i database e come condividere dati con altre applicazioni.
Il Capitolo 10 è interamente dedicato al multitasking e alle possibilità che Windows
Phone offre agli sviluppatori per eseguire operazioni anche quando l’applicazione non è in
esecuzione.
Il Capitolo 11 è strettamente collegato a quello precedente: è dedicato, infatti, a uno degli
aspetti più interessanti di Windows Phone, ovvero il supporto alle Live Tile. Tramite i
servizi in multitasking o le notifiche push, vedremo come aggiornare le tile della nostra
applicazione e renderla “viva” e interattiva.
Il Capitolo 12 affronta l’argomento finale nel processo di sviluppo: la pubblicazione sullo
Store. Contestualmente, vedremo una serie di aspetti collegati alla pubblicazione, come la
localizzazione in diverse lingue, l’integrazione della pubblicità e il supporto all’in-app
purchase.
Nell’Appendice parliamo di argomenti legati al mondo dello sviluppo Windows Phone,
come il pattern MVVM, i servizi mobile di Azure o l’utilizzo di framework basati su
HTML e Javascript come PhoneGap.
Cosa non contiene questo libro
Sono tante le tecnologie coinvolte nello sviluppo di un’applicazione Windows Phone, in
virtù della molteplicità di linguaggi supportati: XAML per l’interfaccia grafica, C#,
VB.NET e C++, per il codice, XNA e DirectX per lo sviluppo di videogiochi.
Questo libro è focalizzato sullo sviluppo di applicazioni: di conseguenza, non saranno
approfonditi l’utilizzo di XNA e DirectX e di C++ come linguaggi per la scrittura di
codice (C++ può essere utilizzato anche per lo sviluppo di applicazioni tradizionali, ma
nella maggior parte dei casi viene utilizzato per lo sviluppo di videogiochi).
Il linguaggio per la scrittura di codice preso in considerazione è C#, in virtù della sua
maggiore diffusione rispetto a VB.NET: gli esempi presenti nel libro saranno, perciò,
presentati in C#. Questo testo rimane, però, una valida guida anche per gli sviluppatori
VB.NET: le API, le classi e i metodi presentati, infatti, sono i medesimi.
Per comprendere pienamente il contenuto del libro è consigliato avere una conoscenza
base di C#: non saranno, infatti, approfonditi argomenti specifici legati alla conoscenza del
linguaggio, come LINQ, i delegate, i tipi ecc.
Feedback e codice di esempio
I feedback sono molto importanti per un autore, perché aiutano a capire la validità del
lavoro svolto e a raccogliere utili suggerimenti per i lavori futuri.
Per questo motivo, vi invito a farmi sapere le vostre impressioni, sia positive che negative,
e i vostri suggerimenti su cosa si potrebbe migliorare.
Potete farlo mandandomi una e-mail all’indirizzo info@qmatteoq.com.
Il codice di esempio utilizzato nei vari capitoli del libro è disponibile sul mio blog
ufficiale all’indirizzo http://www.qmatteoq.com.
Avvertenze
Tutti i contenuti di questo libro sono relativi al periodo in cui è stato scritto: alcuni
argomenti (in particolar modo, l’utilizzo di librerie di terze parti e il processo di
pubblicazione descritto nel Capitolo 12) potrebbero subire delle modifiche nel corso del
tempo, dato che seguono un ciclo di vita indipendente da quello di Windows Phone.
Gli esempi sono forniti così come sono e nessuna garanzia è dovuta dall’autore o
dall’editore per eventuali danni che dovesse commettere l’utilizzatore, anche in base a
quanto contenuto in questo libro.
Risorse utili
Il mondo della tecnologia è in continua evoluzione: questo libro costituisce sicuramente
una risorsa importante per iniziare a sviluppare applicazioni per Windows Phone, ma è
altrettanto importante rimanere aggiornati e al passo con tutte le novità che vengono
rilasciate quotidianamente in rete.
Di conseguenza, ecco una serie di siti che vi possono aiutare nell’approfondire le
tematiche trattate nel corso del libro:
• i miei blog in italiano (http://www.qmatteoq.com) e in inglese
(http://wp.qmatteoq.com), dove pubblico regolarmente articoli e tutorial sullo
sviluppo;
• il portale ufficiale Windows Phone Dev Center (http://dev.windowsphone.com)
contiene, oltre alla documentazione ufficiale, tantissimi tutorial, esempi di codice e
risorse per il design;
• il portale di Nokia dedicato agli sviluppatori (http://www.developer.nokia.com/), che
contiene molti tutorial ed esempi di codice;
• il celebre sito italiano ASPitalia, dedicato agli sviluppatori Microsoft, offre un portale
chiamato WinPhoneItalia (http://www.winphoneitalia.com/) con tutorial, guide e
consigli specifici per Windows Phone;
• la pagina Facebook dedicata agli sviluppatori Nokia
(https://www.facebook.com/groups/SviluppatoriNokiaItalia/) è un luogo di ritrovo e
di discussione molto attivo per il supporto e il confronto su tematiche dedicate a
Windows Phone;
• i forum di supporto italiani Microsoft ospitano una sezione tutta dedicata a Windows
Phone, disponibile all’indirizzo http://social.msdn.microsoft.com/Forums/it-
IT/phoneit/;
• Twitter è una risorsa molto efficace per rimanere sempre aggiornati sulle novità
riguardanti lo sviluppo per Windows Phone: gli hashtag da seguire sono #wp7, #wp8
e, soprattutto, #wpdev;
• Windows Phone è anche e soprattutto una piattaforma per gli utenti finali; di
conseguenza, è importante essere aggiornati anche sulle novità in questo campo,
come le nuove applicazioni, il rilascio di nuovi update, i nuovi modelli di telefoni
ecc. A tale scopo il portale da seguire è Windows Phone Magazine Italia
(http://www.wpmitalia.com/), con il quale ho collaborato per la pubblicazione di
questo libro.
Un altro modo molto efficace per rimanere sempre aggiornati sulle novità nel mondo dello
sviluppo Windows Phone è la partecipazione al mondo community e ai tanti eventi che
Microsoft e Nokia organizzano durante l’anno. Per questo motivo, vi invito a seguire il
portale MSDN Italia (http://msdn.microsoft.com/it-IT/), nel quale vengono messi in
evidenza i principali eventi tecnici, compresi quelli dedicati a Windows Phone.
Ringraziamenti
La stesura di un libro offre molte soddisfazioni, ma è anche un processo particolarmente
impegnativo e richiede molto tempo. Il primo ringraziamento, perciò, va a mia moglie
Angela, che ha sopportato con pazienza le sere e i weekend sottratti alla nostra vita
famigliare per poter portare avanti questo progetto.
Un grazie anche ai miei genitori e a mio fratello Stefano, che mi hanno sempre
supportato e spronato a dare il massimo in tutto quello che faccio.
Ci sono poi tanti amici da ringraziare, senza i quali questo libro non sarebbe mai diventato
realtà. In primis Ugo Lattanzi, grazie al quale ho potuto scoprire il mondo delle
community Microsoft. Senza il suo supporto e i suoi stimoli, non solo questo libro, ma
anche tante, tantissime delle attività che ho svolto in questi ultimi anni sarebbero rimaste
solamente un sogno mai diventato realtà; Lorenzo Barbieri, Developer Evangelist di
Microsoft, che è stato il primo a trasmettermi la passione per Windows Phone e che mi ha
offerto numerose possibilità per mettere in gioco la mia passione negli ultimi anni;
Roberto Freato e tutte le persone della community DotNetLombardia, che mi hanno
dato la possibilità di muovere i miei primi passi in questo mondo, nonostante fino a
quattro anni fa fossi un perfetto sconosciuto; Alessandro Teglia, MVP Lead, e, in
generale, tutte le persone che fanno parte della famiglia degli MVP italiani: il bello di fare
parte di questo gruppo non è solo la possibilità di conoscere persone di eccezionale
competenza e con molta passione, ma anche e soprattutto il ritrovare tanti amici e persone
speciali. Estendo questo ringraziamento, poi, a tutte le persone di Microsoft e a chi si
occupa, sia in Italia che nel mondo, del programma MVP, che mi ha dato e continua a
darmi tante opportunità di crescere e di coltivare la mia passione per Windows Phone.
Un ringraziamento speciale va anche a tutte le persone che ho conosciuto nel mondo del
lavoro e delle community, che, con il tempo, si sono trasformate in amici e con le quali ho
condiviso alcuni tratti di questa avventura: Marco Leoncini, Alessio Leoncini (un
GROSSO grazie a entrambi!), Corrado Cavalli, Igor Antonacci, Joost van Schaik,
Giorgio Formica.
Un grazie molto importante va alle persone che hanno permesso a questo progetto di
diventare realtà: Maurizio Gasparro, il fondatore di Windows Phone Magazine Italia, che
mi ha offerto questa opportunità; Daniele Pagani, di Nokia, che ha supportato il progetto;
la casa editrice FAG.
Un grazie di cuore anche a Funambol, in particolar modo ad Andrea Gazzaniga e ai miei
colleghi di team Marco Magistrali, Andrea Toccalini e Luca Moscato, che mi danno
l’opportunità di portare avanti le mie attività community e di coniugarle con il lavoro
quotidiano.
Ultimo, ma non meno importante, il ringraziamento a voi che avete acquistato il libro e, in
generale, a tutte le persone che mi contattano tramite il blog o che conosco agli eventi e
che mi trasmettono continuamente la loro passione e il loro entusiasmo, che è il motore di
tutte le attività che faccio. Grazie!
Introduzione a Windows Phone

Il mercato degli smartphone è stato, senza ombra di dubbio, nel settore


tecnologico, uno tra i più movimentati degli ultimi anni: questa tipologia
di dispositivi ha conosciuto una vera e propria esplosione in termini di
diffusione e di vendite, superando i confini del mondo professionale per
invadere quello consumer.
Fino a qualche anno fa il cosiddetto “mercato dei palmari” strizzava l’occhio soprattutto al
mondo business: Microsoft in questo settore la faceva da padrone, grazie a Windows
Mobile, un sistema operativo che tentava di ricreare il più possibile l’esperienza del
fratello maggiore Windows, offrendo così ai professionisti tutti gli strumenti di cui
avevano bisogno: posta elettronica, agenda, Office ecc. Si trattava di prodotti di buona
qualità, ma che avevano uno scarso appeal nei confronti dell’utente medio: spesso e
volentieri erano device ingombranti, poco piacevoli da vedere, scomodi da usare a causa
dell’utilizzo di schermi touch screen che richiedevano un apposito pennino.
L’iPhone di Apple è stato sicuramente lo spartiacque tra la vecchia e la nuova generazione
nel mondo degli smartphone: con un uso innovativo del touch screen e un device
piacevole da vedere e da utilizzare, questi dispositivi hanno iniziato a fare breccia anche
nel mondo consumer. Il boom definitivo si è avuto con la diffusione degli store di
applicazioni: grazie a essi, gli sviluppatori hanno potuto mettersi al lavoro e manifestare
tutta la loro creatività, trasformando dei semplici telefoni dotati di connettività a Internet
in strumenti in grado di coprire qualsiasi esigenza, dal gioco all’uso di social network,
dalla produttività alla lettura, e così via.
In questo scenario si inserisce Microsoft, che, dopo aver dominato il mondo business
grazie a Windows Mobile, si è trovata in difficoltà a causa di un sistema operativo per
smartphone che non aveva la stessa attrattiva di iOS di Apple o di Android di Google.
Windows Mobile 7 era già entrato in fase di sviluppo quando Microsoft si accorse che
un’evoluzione della piattaforma non sarebbe stata sufficiente per competere con la
concorrenza: mai come in questo caso fu più azzeccato il termine “rivoluzione”.
Una nuova idea
Microsoft decise di fermare i lavori su Windows Mobile 7 e di mettere in discussione
quanto fatto fino a quel momento. Il risultato di questa scelta è sotto gli occhi di tutti:
cambio di nome, cambio di filosofia, cambio di grafica, cambio di esperienza utente;
Windows Phone sarebbe stata una piattaforma completamente nuova, in grado di
distinguersi dalla concorrenza e di offrire delle caratteristiche uniche.
Windows Phone venne svelato per la prima volta al MIX 2010 di Las Vegas, dove spiazzò
tutti proponendo un’interfaccia diversa dal solito e da quello a cui tutti erano abituati. Tale
interfaccia è basata sui principi stilistici dell’International Typographic Style, uno stile
sviluppatosi in Svizzera negli anni Cinquanta e fondato su tre principi chiave: chiarezza,
leggibilità e obiettività. Lo stile freddo e impersonale e la sua alta comprensibilità lo
hanno reso un perfetto candidato per i cartelli informativi: ecco perché tale stile ricorda
molto quelli segnaletici che si trovano in metropolitana o negli aeroporti.
Alessandro Scardova, MVP Microsoft, ha pubblicato un interessante articolo sull’origine e
le caratteristiche di questo stile grafico: lo potete leggere all’indirizzo
http://s.qmatteoq.com/MetroStyle.
Il Microsoft Design Style (originariamente conosciuto con il nome di Metro) è al giorno
d’oggi una realtà sempre più diffusa e apprezzata, soprattutto nel mondo Microsoft: dopo
aver fatto sentire i primi vagiti nel lontano 1995 in Encarta (la celebre enciclopedia
multimediale), è stato poi utilizzato intensivamente, per la prima volta, nella realizzazione
dell’interfaccia dei prodotti della serie Zune, per quanto riguarda sia l’hardware (gli
sfortunati lettori multimediali che non hanno mai visto la luce in Europa), sia il software
(il player multimediale per Windows).
Al giorno d’oggi, il Microsoft Design Style è lo stile che contraddistingue i prodotti
Microsoft e che è alla base dell’obiettivo della software house di Redmond di creare
un’esperienza d’uso trasversale e familiare per gli utenti dei loro prodotti: oltre che,
ovviamente, in Windows Phone, questo stile gioca un ruolo fondamentale in Xbox 360 e
in Windows 8.
Alcune caratteristiche del Microsoft Design Style
La caratteristica più evidente del Microsoft Design Style è la semplicità: le applicazioni
per Windows Phone presentano un’interfaccia più semplice, leggibile e con “meno
fronzoli” rispetto a quanto avviene su altre piattaforme. Da questa caratteristica emerge un
altro dei punti chiave dello stile: il contenuto.
Nelle applicazioni Windows Phone l’interfaccia è rappresentata dal contenuto stesso: le
decorazioni, le ombreggiature, gli elementi visivi sono ridotti al minimo, per lasciare più
spazio ai contenuti, ovvero testo, immagini e quant’altro.
Anche l’uso dei colori è ridotto, solitamente per evidenziare alcuni elementi rispetto ad
altri; ad esempio, nell’hub Messaging i messaggi non ancora letti sono evidenziati con un
colore differente, in genere rappresentato dall’accent color: con questo termine si indica il
colore predominante del sistema operativo, che l’utente ha la possibilità di personalizzare
nelle impostazioni, insieme al tema (che può essere chiaro o scuro), ovvero il colore di
fondo del sistema stesso. Possiamo ritrovare tale colore, ad esempio, nelle tile delle
applicazioni native.
Un’altra caratteristica del Microsoft Design Style è il font: quello utilizzato da Windows
Phone è il Segoe WP, disponibile in diverse varianti. In più, vengono adottati alcuni
accorgimenti, come l’utilizzo del minuscolo nei titoli delle pagine e del maiuscolo in
quello dell’applicazione.
Una nuova esperienza utente
Microsoft, con il Microsoft Design Style e Windows Phone, ha voluto offrire
un’esperienza d’uso molto differente da quella proposta dalle piattaforme concorrenti.
La frase chiave è “al centro l’utente”: quando, durante lo sviluppo della piattaforma, il
team di Windows Phone si è trovato di fronte a un bivio, ha sempre optato per la strada
che favoriva le scelte dell’utente, mettendolo in condizione di avere il pieno controllo
della situazione. È importante tenere a mente questo concetto quando si inizia a sviluppare
un’applicazione per Windows Phone: qualsiasi approccio che limiti la libertà di scelta o
che cerchi di imporre un comportamento non voluto dall’utente viene impedito.
Da questa idea possiamo comprendere l’ecosistema che ruota attorno a Windows Phone:
la presenza di uno store che impedisca che applicazioni potenzialmente dannose possano
arrivare sui telefoni degli utenti; alcuni limiti delle API, che impediscono che lo
sviluppatore possa intaccare il sistema operativo o danneggiare i dati dell’utente;
l’impossibilità, tranne che per gli sviluppatori, di installare applicazioni di terze parti
senza passare dallo store ecc.
”Al centro l’utente” non è solamente inteso come possibilità di interazione con la
piattaforma, ma anche come metafora della forte integrazione del telefono con i social
network: un contatto nella rubrica non è solamente una scheda con un numero di telefono
ed eventuali informazioni aggiuntive, ma è una persona a tutti gli effetti, con la quale
possiamo conversare, interagire sui social network, condividere delle foto ecc. Infine, un
aspetto fondamentale è quello delle tile: siamo abituati a concepire la home page di uno
smartphone principalmente come un punto di partenza per le applicazioni; per ognuna
delle esigenze che vogliamo soddisfare c’è un’applicazione: quella per telefonare, quella
per gestire la posta elettronica, quella per utilizzare Facebook ecc.
Windows Phone, invece, non utilizza un approccio di questo tipo: le applicazioni hanno
sicuramente la loro importanza, ma molte funzionalità sono già integrate all’interno del
sistema operativo. Ecco, perciò, che la home non diventa più solamente l’accesso alle
nostre applicazioni, ma, grazie alle tile, è anche un contenitore delle nostre informazioni:
non solo le applicazioni che utilizziamo più spesso, ma anche contatti della rubrica, brani
musicali, siti web e quant’altro.
In più, queste tile possono essere vive (da qui il nome live tile): le nostre applicazioni
possono interagire con esse, per mostrare informazioni all’utente senza che questi debba
per forza accedervi. Le condizioni meteo della nostra città, le notizie pubblicate sul nostro
quotidiano preferito non ancora lette, l’ultimo aggiornamento su Facebook di un nostro
amico: sono solo alcuni esempi delle tante possibilità che abbiamo a disposizione e che
impareremo a utilizzare nel corso di questo libro.
Alcuni nuovi concetti: panorama, pivot e application bar
Oltre alle tile, Windows Phone introduce alcuni nuovi concetti per migliorare l’esperienza
d’uso dell’utente e diversificarla ulteriormente rispetto a quella offerta dalle altre
piattaforme. I concetti che hanno un impatto maggiore sull’interfaccia utente sono quelli
di panorama, pivot e application bar. In questo paragrafo vedremo di cosa si tratta e come
essi possano migliorare l’esperienza d’uso delle nostre applicazioni; nei capitoli successivi
vedremo invece come utilizzarli dal punto di vista tecnico.

Panorama
Molte applicazioni native di Windows Phone fanno uso di questo paradigma: quasi tutti
gli hub (People, Photos o Music, ad esempio) utilizzano un panorama nella schermata
principale. Di cosa si tratta? Di una vista in cui diversi elementi vengono disposti
orizzontalmente, occupando uno spazio maggiore della dimensione dello schermo.
L’utente, trascinando il dito sullo schermo verso destra o verso sinistra (in gergo tecnico
questa gesture si chiama swipe), può sfogliare i vari contenuti; il nome “panorama” deriva
dal fatto che una vista di questo tipo può avere un’immagine di sfondo molto larga, che si
muove con una velocità diversa rispetto agli elementi testuali durante lo swipe.
La particolarità del panorama è che la vista corrente non occupa tutto lo spazio
disponibile: il margine destro è infatti occupato da un’anteprima dei contenuti della vista
successiva.

Figura 1.1 - Un esempio di panorama utilizzato nell’hub People.

Il controllo panorama viene utilizzato solitamente nella schermata iniziale delle


applicazioni, come punto di accesso alle funzioni principali: ogni vista di un panorama
può infatti contenere elementi diversi tra loro, che non devono avere, per forza di cose, un
legame.
Prendiamo come esempio un’applicazione per leggere le ultime notizie: potrebbe
contenere una vista con gli ultimi articoli pubblicati, una con l’elenco delle categorie di
notizie, una con le opzioni configurabili dall’utente ecc.
Il concetto chiave da tenere a mente è che il controllo panorama non deve essere un
contenitore di dati, ma un punto di accesso alle funzioni della nostra applicazione: per
questo motivo, inserire troppi contenuti in un panorama non è corretto, non solo per una
questione di esperienza d’uso, ma anche di performance.
Riprendendo l’esempio di prima, è giusto inserire in una vista del panorama le ultime 10
notizie e poi proporre un pulsante per accedere all’elenco completo; non è corretto
utilizzare una vista come punto di accesso a tutte le notizie, mostrando un numero molto
elevato di elementi.
Il controllo panorama è ciclico: nel momento in cui l’utente ha raggiunto l’ultima vista ed
effettua uno swipe, verrà riportato alla prima.
Le guideline prevedono l’utilizzo del panorama con un numero massimo di cinque viste.

Pivot
Il controllo pivot offre un’esperienza d’uso simile a quella del panorama: con uno swipe
l’utente può spostarsi da una vista all’altra, visualizzando contenuti diversi. In questo caso,
però, i contenuti hanno a disposizione tutta la dimensione dello schermo: infatti non viene
mostrata un’anteprima dei contenuti della vista successiva, bensì solamente del titolo. Il
titolo della vista corrente viene visualizzato in bianco, mentre quelli delle viste successive
in grigio, così da dare immediatamente all’utente la percezione di dove si trova e di cosa
c’è dopo.
Figura 1.2 - L’utilizzo di un controllo pivot in un’applicazione meteorologica.

Questo paradigma è solitamente utilizzato per mostrare la stessa tipologia di informazioni,


ma filtrata in maniera diversa o per contesti differenti, oppure informazioni diverse, ma
legate allo stesso contesto.
L’applicazione mostrata in Figura 1.2 ci dà un esempio del primo approccio: il pivot viene
utilizzato per mostrare sempre lo stesso tipo di informazioni (le previsioni del tempo), ma
relative a città differenti.
Il dettaglio di un contatto all’interno dell’hub People ci dà invece un esempio del secondo
approccio: in questo caso sono mostrate informazioni disomogenee tra di loro (le
informazioni di contatto, gli aggiornamenti sui social network, le fotografie ecc.), ma
legate allo stesso contesto: la persona.
Anche il controllo pivot offre un’esperienza d’uso ciclica: raggiunto l’ultimo elemento, lo
swipe riporta l’utente alla prima vista.

L’application bar
L’application bar è una barra che si trova in fondo allo schermo, nella quale vengono
posizionati pulsanti ed elementi testuali che consentono di accedere a funzioni specifiche
dell’applicazione, tipicamente legate al contesto.
Ad esempio, l’hub Messaging include nell’application bar i pulsanti per creare un nuovo
messaggio o per impostare il nostro stato su Messenger; nel momento in cui ci spostiamo
nella schermata per comporre il messaggio, i pulsanti cambiano per dare accesso alle
funzionalità di invio sms o di dettatura vocale.
Le guideline prevedono un numero massimo di quattro pulsanti, identificati da un’icona
tonda con all’interno un simbolo. L’application bar, sul margine destro, mostra però tre
puntini, che fanno comprendere all’utente che si può interagire con la stessa: toccandola,
infatti, essa si espande, mostrando altre opzioni sotto forma di etichette di testo.

Figura 1.3 - L’application bar di Windows Phone.

Le tile
Le tile sono sicuramente una delle caratteristiche del Microsoft Design style che
maggiormente contraddistinguono le applicazioni Windows Phone rispetto alla
concorrenza. Come abbiamo visto nei paragrafi introduttivi, le tile non sono
semplicemente un vezzo estetico, ma consentono di comunicare informazioni all’utente
senza che questi sia costretto ad aprire l’applicazione.
Windows Phone 8 ha introdotto numerose novità per quanto riguarda le tile, partendo dai
formati disponibili fino ai template che le applicazioni possono utilizzare per
personalizzarli. Vedremo i dettagli implementativi nel Capitolo 11, per ora è importante
sottolineare i formati disponibili, dato che a questi è collegata anche la configurazione
della nostra applicazione, che affronteremo nel Capitolo 2.
Fino a Windows Phone 7 esistevano due formati di tile: Medium (quadrato) e Wide
(rettangolare). In realtà, come sviluppatori, potevamo utilizzare solo il formato Medium:
solo alcune applicazioni native (come Photos e Calendar) erano in grado di utilizzare il
formato Wide. In più, era l’applicazione ad avere il controllo del formato di tile, non
l’utente: se avessimo scelto di mantenere in home la tile del calendario, saremmo stati
costretti a mantenere la tile rettangolare, in grado di occupare tutta la larghezza a
disposizione.
A partire da Windows Phone 8, invece, le possibilità per lo sviluppatore e per l’utente
sono aumentate, in primis, con l’introduzione di un nuovo formato, Small, equivalente a
1/4 di una tile tradizionale quadrata. Come sviluppatori, abbiamo finalmente la possibilità
di interagire con tutti e tre i formati; come utenti, invece, possiamo scegliere il formato
che preferiamo tra quelli supportati dall’applicazione.

Figura 1.4 - Le tile di Windows Phone 8.

Un nuovo approccio per i device


Prima dell’arrivo di Windows Phone, i produttori di sistemi operativi mobile avevano a
disposizione sostanzialmente due approcci: farsi carico anche dello sviluppo dell’hardware
(è il caso di Apple) oppure vendere il software ai tanti produttori sul mercato, lasciando a
loro l’incarico di produrre i device (è l’approccio di Android o di Microsoft con Windows
Mobile).
Entrambi gli scenari hanno dei punti di forza e di debolezza: l’approccio di Apple
consente all’azienda di Cupertino di avere il pieno controllo dell’intero ecosistema e di
avere minimi problemi di frammentazione, in virtù della limitata varietà di device in
commercio (la media è di un nuovo modello di iPhone ogni anno). Di contro, però, questa
scelta limita la libertà degli utenti, che sono vincolati a un solo form factor, a dimensioni
dello schermo fisse ecc.
Viceversa, il mondo Android offre una varietà di device praticamente illimitata in termini
di forma, dimensione e dotazione hardware. Tale varietà, però, viene pagata sia dagli
utenti che dagli sviluppatori: dai primi perché questa politica rende difficoltosa la
procedura di aggiornamento alle nuove versioni del sistema operativo, che deve essere
gestita direttamente dal produttore del telefono; inoltre, non è garantita la compatibilità
universale delle applicazioni, a causa delle caratteristiche tecniche (e, di conseguenza, di
performance) molto diverse tra loro; dai secondi perché si trovano nelle condizioni di
dover sviluppare applicazioni per dispositivi che possono avere una potenza di calcolo e
risoluzioni molto differenti, rendendo perciò difficoltoso il testing. Microsoft, che
storicamente da sempre si occupa di software (se escludiamo le parentesi Xbox e Surface),
ha cercato un punto di incontro tra questi due approcci, allo scopo di trovare un equilibrio:
molteplicità di dispositivi, ma allo stesso tempo garanzia di affidabilità, di distribuzione
puntuale degli aggiornamenti e di qualità delle applicazioni.
Per questo motivo, Microsoft ha deciso di imporre ai produttori di device una
configurazione minima, che deve essere rispettata per la produzione di tutti gli smartphone
dotati di Windows Phone. Questa scelta semplifica notevolmente il lavoro degli
sviluppatori: non dovremo preoccuparci di capire se il device su cui è in esecuzione la
nostra applicazione sia dotato, ad esempio, di GPS o di accelerometro, perché Microsoft
ha imposto la presenza di questi sensori su tutti i dispositivi.
Tale configurazione minima è stata aggiornata più o meno a ogni revisione del sistema
operativo: si è passati, ad esempio, dai processori single core della prima generazione al
supporto ai multi core con Windows Phone 8.
Ho usato più volte il termine “configurazione minima”: i produttori di device hanno infatti
la facoltà di migliorare tale configurazione, offrendo, ad esempio, una fotocamera di
migliore qualità, un processore più veloce o un quantitativo di storage maggiore.
Tabella 1.1 - Requisiti minimi per la produzione di smartphone dotati di Windows
Phone.

Caratteristica Requisiti minimi e descrizione


La risoluzione minima supportata è 800x480. Fino a
Windows Phone 7.5 era l’unica risoluzione supportata;
Risoluzione
Windows Phone 8 ha introdotto anche le risoluzioni
1280x728 e 1280x720.
Lo schermo deve offrire un touch screen di tipo
Touch screen capacitivo, con il supporto ad almeno quattro punti di
contatto.
È richiesta la presenza di almeno una fotocamera sul
Fotocamera
retro. È opzionale la fotocamera frontale.
I device devono essere dotati di un sensore A-GPS per
GPS
usufruire dei servizi di geolocalizzazione.

È obbligatoria la presenza di questo sensore, che


Accelerometro
permette di rilevare la posizione del device nello spazio.
Questo sensore permette di capire l’orientamento del
Bussola
device rispetto al nord magnetico.
Questo sensore permette di rilevare in automatico le
Sensore di luminosità condizioni di luce e di adattare la luminosità dello
schermo di conseguenza.
Questo sensore consente al device di capire la vicinanza
del telefono al volto della persona, così da bloccare
Sensore di prossimità
l’utilizzo del touch screen, ad esempio, durante una
telefonata.
Il device deve essere dotato di tre pulsanti hardware
obbligatori: Start, Back e Search. In più, tutti i device
Pulsanti hardware Windows Phone sono solitamente dotati di un tasto per il
blocco e per l’accensione, uno per l’accesso alla
fotocamera e due per la regolazione del volume.
I dispositivi Windows Phone devono essere dotati di un
ricevitore Wi-Fi e del supporto alla connessione dati
Wi-Fi e rete dati
tramite rete cellulare, per garantire la connettività a
Internet.
I device Windows Phone devono essere dotati di almeno
256 MB di RAM: in realtà, tutti i device della prima e
della seconda generazione sono equipaggiati con un
Memoria RAM
quantitativo superiore, ovvero 512 MB di RAM. I primi
device dotati di 256 MB di RAM sono comparsi sul
mercato verso la metà del 2012.
Un dispositivo Windows Phone deve avere almeno 8 GB
Storage di memoria flash per la memorizzazione dei dati:
applicazioni, musica, video ecc.
Le specifiche minime iniziali prevedevano un processore
da almeno 1 GHz: tali specifiche sono state abbassate
con l’introduzione di Windows Phone 7.5 Refresh, per
Processore
cui sono consentiti anche dispositivi con processori da
800 MHz. In più, Windows Phone 8 ha aggiunto il
supporto ai processori multi core.

In questo ecosistema si inserisce anche la scelta di Microsoft di non consentire ai


produttori di personalizzare l’interfaccia del sistema, come invece era possibile con
Windows Mobile. L’obiettivo della software house di Redmond è quello di dare una forte
identità al proprio prodotto e offrire un’esperienza unica a tutti gli utenti,
indipendentemente dal modello di smartphone. Inoltre questa scelta consente di poter
gestire in maniera centralizzata gli aggiornamenti del sistema operativo e di non dipendere
dai produttori.
Un’altra caratteristica importante per i device Windows Phone è la presenza di tre tasti
fisici obbligatori, al contrario della tastiera, che è opzionale (la maggior parte dei device
sul mercato è dotata solamente di touch sceen, ma esistono anche alcuni modelli che
includono una tastiera fisica). Questi tre tasti sono:
1. il tasto Back, che ha lo scopo di permettere all’utente di tornare in qualsiasi
momento alla schermata precedente, sia all’interno della stessa applicazione sia tra
applicazioni diverse. A partire da Windows Phone 7.5, questo pulsante permette
anche di attivare il task switcher, che consente all’utente di spostarsi tra le ultime
applicazioni aperte, tramite una pressione prolungata;
2. il tasto Start, identificato dal logo di Windows: questo pulsante porta l’utente, in
qualsiasi momento e indipendentemente dall’applicazione attiva, alla home di
Windows Phone. L’utilizzo di questo pulsante causa la sospensione dell’applicazione
corrente: approfondiremo questo concetto nel Capitolo 4;
3. il pulsante Search, identificato da una lente di ingrandimento: questo pulsante porta
l’utente in qualsiasi momento alla pagina di ricerca di Bing, che consente di lanciare
una ricerca sul web e di utilizzare alcune delle nuove feature introdotte in Windows
Phone 7.5, come Bing Audio e Bing Vision. È importante sapere che, come
sviluppatori, non abbiamo la possibilità di interagire con questo pulsante per
utilizzarlo, ad esempio, come pulsante di ricerca nelle nostre applicazioni.
Windows Phone 7.5: un aggiornamento importante
Windows Phone si è rivelata da subito una buona piattaforma, con delle caratteristiche
innovative e interessanti: i tempi molto stretti, però, non hanno consentito a Microsoft di
implementare dall’inizio tutte le funzionalità desiderate, lasciando così alcune lacune
rispetto alle piattaforme concorrenti.
Microsoft ha continuato, però, a lavorare duramente sulla piattaforma subito dopo il
rilascio di Windows Phone 7, e qualche mese dopo, al Mobile World Congress di
Barcellona (nel febbraio 2011), ha ufficialmente presentato il primo frutto: Mango, ovvero
il nome in codice del nuovo importante aggiornamento che sarebbe stato rilasciato
nell’autunno dello stesso anno.
Questa nuova versione (il cui nome ufficiale è Windows Phone 7.5) ha introdotto
tantissime novità sia per gli utenti sia per gli sviluppatori. I primi hanno potuto beneficiare
dell’integrazione con nuovi social network e con la chat di Facebook e Messenger, del
supporto ai gruppi di contatti, di un browser più moderno e in grado di supportare le più
recenti tecnologie web ecc.
I secondi, invece, hanno potuto iniziare a sviluppare applicazioni più complete, grazie a
circa 1500 nuove API e al supporto a nuove feature prima mancanti: multitasking,
database relazionali, socket, tile multiple e tanto altro ancora. Nel corso di questo libro
esploreremo tutte queste funzionalità e impareremo a utilizzarle per rendere ancora più
utili e interessanti le nostre applicazioni.
Windows Phone 7.5 è stato rilasciato nell’autunno 2011 come aggiornamento gratuito per
tutti i device disponibili sul mercato. In più, i principali produttori (come HTC e Samsung)
hanno iniziato a mettere in commercio dispositivi di seconda generazione, dotati di nuovi
accessori (come la fotocamera frontale) e di processori più potenti, con preinstallata la
nuova versione del sistema operativo.
Inoltre, nel corso della primavera 2012, Microsoft ha presentato Windows Phone 7.5
Refresh, un aggiornamento del sistema operativo creato con lo scopo di penetrare nel
mercato dei device economici: con questo update, infatti, Windows Phone è stato
ulteriormente ottimizzato, per consentire l’utilizzo anche su telefoni dotati di
caratteristiche hardware inferiori (un processore meno potente, una minor quantità di
memoria RAM ecc.) e, per questo motivo, più economici.
L’accordo con Nokia
Una forte spinta alla diffusione di Windows Phone è arrivata grazie a Nokia, che ha siglato
con Microsoft uno storico accordo per l’utilizzo di Windows Phone come sistema
operativo principale per gli smartphone di nuova generazione, prodotti dalla casa
finlandese, con lo scopo di rimpiazzare Symbian, sistema operativo, a tutt’oggi molto
valido per i dispositivi più economici, ma che inizia a mostrare qualche limite rispetto alla
concorrenza nel mercato degli smartphone di fascia alta.
Nell’autunno 2010 Nokia ha presentato ufficialmente i primi frutti di questo accordo: il
Nokia Lumia 800 e il Nokia Lumia 710, due telefoni dotati di un’ottima configurazione
hardware e dal form factor particolare e ricercato. A questi due modelli è seguito il Nokia
Lumia 900, destinato al mercato americano e presentato nel corso del CES di Las Vegas.
Nokia è stata, inoltre, la prima compagnia a puntare sui device di fascia bassa,
annunciando il Lumia 610, uno dei primi telefoni con preinstallato Windows Phone 7.5
Refresh. Il punto di forza dell’accordo con Nokia è sicuramente la capacità della casa
finlandese di saper fare breccia nel mercato: la campagna promozionale di questi modelli è
stata imponente, contribuendo a creare un rinnovato interesse verso la nuova piattaforma
mobile di Microsoft.
In più, nel corso degli anni, Nokia è riuscita a realizzare dispositivi top di gamma,
caratterizzati da caratteristiche tecniche pari, se non superiori, a quelle della concorrenza:
celebre è il Lumia 920, dotato della miglior fotocamera disponibile attualmente (al
momento della stesura del libro) nel mercato degli smartphone.
Infine, un altro ambito in cui Nokia si è sempre contraddistinta è il supporto alla
piattaforma: molte delle migliori applicazioni disponibili (come Nokia Music o Nokia
Drive) sono state prodotte direttamente dalla casa finlandese e vengono costantemente
aggiornate nel tempo, segno dell’attenzione e dell’interesse di Nokia verso la piattaforma.
Windows Phone 8: un nuovo punto di partenza
Nel corso dell’estate 2012 Microsoft ha ufficialmente presentato, in un evento tenutosi a
San Francisco, Windows Phone 8, la nuova versione della piattaforma, rilasciata poi a fine
ottobre.
In questo caso le novità introdotte sono ancora più sostanziose, tanto che Windows Phone
8 si può considerare quasi una nuova piattaforma.
Il cambiamento più grande è dietro le quinte: Windows Phone 7, pur essendo un sistema
totalmente diverso da Windows Mobile, ne condivideva le fondamenta, visto che era
basato su WinCE. Windows Phone 8, invece, ha un kernel totalmente nuovo, condiviso
con quello di Windows 8, molto più efficiente e adatto per gestire dispositivi come gli
smartphone di nuova generazione. Il vantaggio è che ci troviamo davanti a una
piattaforma molto più moderna e versatile, per gli utenti ma soprattutto per gli
sviluppatori.
Lo svantaggio è che, a causa del nuovo kernel, Windows Phone 8 non è disponibile come
aggiornamento per i device dotati di Windows Phone 7.5. Per non “abbandonare” gli
utenti che hanno dato fiducia alla prima generazione di smartphone, Microsoft ha
preparato un update chiamato Windows Phone 7.8, che ha portato alcune delle novità
consumer di Windows Phone 8 sulla vecchia piattaforma.
Dal punto di vista hardware, Microsoft ha cambiato ulteriormente le specifiche,
introducendo il supporto a tre differenti risoluzioni: oltre alla classica 480x800, i
produttori di device sono in grado di realizzare telefoni con schermi dalla risoluzione
768x1280 (WXGA) e 720x1280 (720p).
Ci sono novità anche per quanto riguarda i processori, che possono essere multi core,
garantendo così migliori prestazioni per le applicazioni più impegnative, come i giochi.
Molte novità sono state introdotte anche per quanto riguarda le caratteristiche del sistema
operativo: una delle più importanti è la presenza di una nuova start screen, in grado di
occupare tutto lo spazio a disposizione dello schermo (è stata rimossa, infatti, la banda
nera laterale con l’icona della freccia, ora spostata in fondo). Come conseguenza, le tile
possono assumere tre differenti dimensioni: 159x159 (small), 336x336 (medium) e
691x336 (wide).
Anche il browser ha ricevuto un aggiornamento consistente, che, al pari del mondo
desktop/tablet, è stato allineato alla versione 10, migliorando ulteriormente il supporto alle
novità di HTML 5. Tra le novità più importanti di Windows Phone 8 troviamo:
• una nuova applicazione, chiamata Wallet, per memorizzare in maniera sicura i dati
delle nostre carte di credito, membership card e così via. In combinazione con la
tecnologia NFC, è possibile utilizzare questa applicazione per effettuare pagamenti
direttamente tramite il telefono;
• la collaborazione tra Nokia e Microsoft vede i suoi frutti in questa nuova versione del
sistema operativo: Bing Maps è stato sostituito da Nokia Maps, la tecnologia
cartografica finlandese. Di conseguenza, tutti i possessori di un device Windows
Phone 8 (e non solo di quelli prodotti da Nokia) potranno beneficiare di
caratteristiche come le mappe offline e il navigatore turn by turn;
• un miglior supporto per il mondo enterprise, sia in termini di sicurezza (grazie al
criptaggio dei dati tramite BitLocker, a funzionalità come secure boot e alla
possibilità di gestione da remoto), sia in termini di supporto alle applicazioni
aziendali, grazie al nuovo Company Hub. Si tratta di un nuovo hub al cui interno gli
amministratori saranno in grado di preinstallare tutte le applicazioni aziendali;
• supporto automatico ai backup sul cloud: ora è possibile effettuare il backup dei
propri dati (sms, contatti, elenco delle applicazioni installate ecc.) sul cloud tramite il
proprio account Microsoft;
• supporto a nuove lingue, incluse quelle orientali con il verso di scrittura da destra a
sinistra;
• supporto alla tecnologia NFC, che consente lo scambio di dati tra dispositivi sia di
tipo attivo (un altro telefono, un tablet ecc.) sia di tipo passivo (totem, adesivi ecc.)
semplicemente con il tocco;
• supporto alle microSD, per espandere lo storage che potrà essere usato per
memorizzare foto, musica e video.
Molte, moltissime novità sono state introdotte anche e soprattutto per gli sviluppatori,
partendo da un nuovo runtime di sviluppo, allineato con quello di Windows 8, e da un
gran numero di nuove API, per integrarsi con tutte le nuove funzionalità introdotte (come
NFC o l’applicazione Wallet).
Anche l’ambiente di sviluppo è stato aggiornato, per allinearsi alle ultime tecnologie
Microsoft, ovvero Windows 8 e Visual Studio 2012.
Dal punto di vista della compatibilità, le applicazioni Windows Phone 7 sono in grado di
funzionare senza limitazioni anche su Windows Phone 8: non è possibile, però, il
contrario.
Avremo la necessità, perciò, di mantenere due progetti differenti se vogliamo rendere
compatibile la nostra applicazione con entrambe le versioni.
Lo sviluppo di applicazioni
I framework in Windows Phone 7.5
Per quanto riguarda Windows Phone 7.5, per consentire lo sviluppo di applicazioni di
terze parti Microsoft ha deciso di riutilizzare e adattare due tecnologie già conosciute e
apprezzate dagli sviluppatori: Silverlight e XNA.
Silverlight è un framework creato da Microsoft per lo sviluppo di applicazioni RIA (Rich
Internet Application), sia web sia client. Silverlight è infatti un plugin da installare nel
browser che consente l’utilizzo di queste applicazioni direttamente nel browser stesso. In
più, Silverlight ha introdotto, a partire dalla versione 3, il supporto alla modalità out of
browser, ovvero la possibilità per gli utenti di installare le applicazioni web sul proprio
computer, come se fossero delle vere e proprie applicazioni desktop.
Le applicazioni Windows Phone 7.5 sono basate su uno specifico subset di Silverlight
chiamato Silverlight Runtime for Windows Phone: si tratta di una versione in tutto e per
tutto equivalente a Silverlight, con la differenza che sono state aggiunte una serie di API
che permettono di interagire con il telefono.
Concettualmente, le applicazioni Windows Phone sono applicazioni Silverlight che
vengono eseguite in modalità out of browser.
Il Silverlight Runtime for Windows Phone ha seguito, però, un ciclo di sviluppo separato
da quello di Silverlight: la versione 7.0 di Windows Phone era allineata con la versione 3
di Silverlight, mentre la versione 7.5 è allineata con la versione 4. La versione di
Silverlight più recente, invece, è la 5. Questo perché non sempre le feature presenti in
Silverlight possono essere portate su un dispositivo mobile (pensiamo, ad esempio, alle
API per la stampa).
XNA è un framework dedicato allo sviluppo di videogiochi ed è già utilizzato da
Microsoft per creare videogame per Windows e per Xbox 360; consente l’accesso a tutte
le risorse che tipicamente sono utilizzate in un videogioco, come grafica 3D, audio,
interazioni avanzate con il touch screen ecc.
Il modello di programmazione con XNA è molto diverso da quello di Silverlight: se
quest’ultimo offre un approccio più familiare, basato su eventi, XNA è invece basato sul
concetto di “ciclo”. A intervalli di tempo molto frequenti (che coincidono con i frame per
secondo) l’applicazione ciclicamente si occupa di elaborare tutte le informazioni: riceve
gli input dell’utente, aggiorna la schermata, riproduce l’audio ecc.
Il vantaggio di XNA per Windows Phone consiste nel fatto di condividere le stesse API
della versione per Windows e Xbox 360: nell’ottica di portare un gioco da una piattaforma
all’altra, perciò, sono minime le modifiche che dovremo fare, principalmente per adattare i
controlli di gioco all’utilizzo del touch screen.
XNA è un framework molto potente, ma anche complesso e di uso meno comune rispetto
a Silverlight: lo sviluppo di videogiochi, infatti, seppure molto affascinante, è uno scenario
meno frequente rispetto allo sviluppo di applicazioni. Per questo motivo, non tratteremo lo
sviluppo di videogiochi e l’utilizzo di XNA nel corso di questo libro. Se siete interessati a
questo argomento, su Internet potete trovare molti articoli e tutorial; inoltre sono
disponibili diversi libri su XNA (soprattutto in inglese), alcuni dei quali specifici per
Windows Phone.
Nel corso del libro, tuttavia, capiterà talvolta di citare XNA: questo perché alcune librerie
di XNA possono essere utilizzate anche all’interno di applicazioni Silverlight, come quelle
per la riproduzione audio, per accedere al microfono o alla libreria multimediale
dell’utente. È sufficiente, infatti, aggiungere la libreria Microsoft.Framework. Xna per
poter accedere alle API appartenenti a questo Framework.
Una delle novità introdotte in Windows Phone 7.5 è l’interoperabilità tra XNA e
Silverlight: XNA da solo, infatti, è un framework molto potente per gestire grafica 3D,
audio ecc., ma risulta poco versatile per gestire elementi testuali, bottoni, menu ecc., dato
che lo sviluppatore deve realizzare manualmente ogni singolo elemento. Spesso, perciò, si
investe quasi più tempo per realizzare il “contorno” del gioco (menu, classifiche, opzioni
ecc.) che per il gioco stesso; al contrario, Silverlight semplifica notevolmente la
realizzazione di interfacce, offrendo in dotazione un gran numero di controlli ed eventi già
pronti.
Grazie a questa nuova feature è possibile realizzare applicazioni che combinino il meglio
di entrambi i mondi: all’interno di una pagina Silverlight è possibile includere un’area che
invece viene gestita da XNA. Le due componenti saranno gestite in maniera
completamente indipendente: la parte in Silverlight permetterà l’inserimento di controlli e
la gestione degli eventi standard (come il tap), semplificando la creazione di menu e
schermate di opzioni; la parte in XNA, invece, sarà aggiornata ciclicamente per
renderizzare gli elementi grafici, l’audio e le interazioni.

I framework in Windows Phone 8


Con Windows Phone 8 Microsoft ha cambiato un po’ le carte in tavola, allo scopo di
allineare l’ambiente di sviluppo con le tecnologie più recenti e dando la possibilità agli
sviluppatori di portare con più facilità le loro applicazioni per il mondo desktop/tablet di
Windows 8.
Ecco, perciò, che il Silverlight Runtime for Windows Phone è stato sostituito dal
Windows Runtime for Windows Phone, un subset del runtime creato da Microsoft per lo
sviluppo di applicazioni Windows 8.
La differenza principale tra i due runtime è l’approccio nella scrittura di codice asincrono,
dato che WinRT fa uso intensivo del nuovo approccio basato sulle keyword async e await,
introdotte nella quinta revisione di C#, e che approfondiremo nel prossimo capitolo.
Grazie a una serie di accorgimenti (che vedremo in dettaglio nel prossimo paragrafo), lo
sforzo necessario per sviluppare applicazioni per Windows Phone 8, avendo già
esperienza con lo sviluppo su Windows Phone 7, è molto basso. Il team, infatti, ha fatto sì
che la conoscenza maturata sulla versione precedente della piattaforma possa essere
riciclata praticamente al 100% con la nuova versione.
Per chi si occupa di realizzare videogiochi la possibilità di scrivere applicazioni in codice
nativo è sicuramente gradita: C++ è sicuramente il linguaggio più diffuso in questo campo
e, fino a questo momento, Windows Phone era l’unica piattaforma a non dare questa
possibilità. Questa nuova caratteristica, inoltre, apre le porte a tantissime applicazioni che,
fino a oggi, non venivano portate su Windows Phone per motivi di costo: grazie
all’utilizzo di codice nativo è infatti più semplice creare una codebase comune tra tutte le
principali piattaforme mobile (come iOS e Android).
Farà dispiacere, invece, sapere che XNA non è più supportato: è garantita la compatibilità,
per consentire l’esecuzione dei giochi realizzati per Windows Phone 7, ma non è possibile
realizzare un gioco Windows Phone 8 appoggiandosi a questo framework. È sempre
possibile realizzare un’applicazione Windows Phone 7 utilizzando XNA, che funzionerà
senza problemi sia sui vecchi sia sui nuovi device: l’unico svantaggio è che non saremo in
grado di sfruttare le nuove feature di Windows Phone 8.

I linguaggi
Così come la maggior parte delle tecnologie Microsoft, sono due i linguaggi principali per
sviluppare applicazioni per Windows Phone: C# e VB.NET. Il primo è un linguaggio che
deriva da C come sintassi ed è il più diffuso; il secondo, invece, è l’evoluzione di Visual
Basic per il framework .NET e mantiene a tutt’oggi una discreta base utenti, in virtù della
grande diffusione di Visual Basic in passato.
Dato che C# è il linguaggio più utilizzato per lo sviluppo con tecnologie Microsoft, gli
esempi di codice presentati in questo libro faranno ricorso a esso. Non sarà trattata, però,
la teoria necessaria per apprenderlo; è quindi richiesta una conoscenza base dello stesso
per poter utilizzare e comprendere il codice che sarà presentato nel corso dei prossimi
capitoli.
Se siete sviluppatori alle prime armi, potete trovare tantissimi tutorial su Internet nonché
libri dedicati all’apprendimento di questi linguaggi.
Se siete sviluppatori VB.NET sappiate che tutto quanto è spiegato in questo libro è
utilizzabile anche con questo linguaggio: le nozioni di XAML, le API, i nomi delle classi
ecc. non presentano differenze.
Windows Phone 8 ha introdotto tra i linguaggi supportati anche C++, per creare
applicazioni in codice nativo: è importante, però, sottolineare che, in tal caso, alcune
feature (come i background agent) non sono utilizzabili. L’utilizzo di questo linguaggio
non verrà preso in considerazione in questo libro.

Il Windows Runtime
Il Windows Runtime è il nuovo runtime che Microsoft ha introdotto in Windows 8 e
rappresenta un taglio abbastanza netto rispetto al passato: al contrario del framework
.NET, da sempre pilastro dello sviluppo con tecnologie Microsoft, basato su codice
managed, il Windows Runtime è, infatti, un runtime scritto in codice nativo, in grado di
offrire, perciò, prestazioni superiori. Per dare la possibilità agli sviluppatori di riutilizzare
le loro conoscenze, Microsoft ha creato delle projection, ovvero degli “interpreti” che
consentono di accedere a questo strato di API native utilizzando linguaggi ad alto livello,
come C# e VB.NET.
In Windows Phone 8 è disponibile un subset del Windows Runtime, chiamato Windows
Runtime for Windows Phone: tale subset presenta alcune differenze rispetto al Windows
Runtime originale, in virtù della differenza di alcuni scenari tra le due piattaforme. Per
molti ambiti i due runtime, in termini di namespace, API, nomi di classi e metodi,
coincidono, dando così allo sviluppatore la possibilità di portare con maggiore facilità le
proprie applicazioni su Windows 8.
Una differenza importante tra il Windows Runtime tradizionale e la versione ad hoc per
Windows Phone è l’assenza di projection: i linguaggi supportati sono gli stessi disponibili
sin dalla prima versione di Windows Phone, ovvero C# e VB.NET per la logica e XAML
per l’interfaccia grafica. In più, al contrario di quanto accade nel mondo delle applicazioni
Windows Store per Windows 8, lo XAML è allineato al 100% con quello di Silverlight.
Manca, di conseguenza, una delle novità del Windows Runtime maggiormente apprezzate
dagli sviluppatori web: il supporto a HTML e Javascript. È comunque possibile utilizzare
questi linguaggi per sviluppare applicazioni, ma sfruttando il controllo WebBrowser di
Windows Phone, che consente di includere una vista web (in grado di offrire le stesse
feature a livello di prestazioni e supporto di HTML5 di Internet Explorer 10) all’interno di
un’applicazione mobile. Si tratta di un approccio diverso da quello di Windows 8, dove è
disponibile una libreria, chiamata WinJS, che consente di accedere in maniera nativa alle
funzionalità del Windows Runtime utilizzando Javascript.
Per aiutare gli sviluppatori Windows Phone a riutilizzare il lavoro svolto in precedenza
con Windows Phone 7.5, Microsoft ha introdotto due importanti strumenti:
• la quirk mode è una modalità che consente alle applicazioni Windows Phone 7.5 di
funzionare anche su Windows Phone 8 senza, nella maggior parte dei casi, alcuna
modifica. Ciò è reso possibile dal fatto che tale modalità si occupa di fare un
mapping tra le vecchie API del Silverlight Runtime e quelle nuove del Windows
Runtime. Di conseguenza, anche nel caso in cui ci siano stati dei cambiamenti
importanti da una versione all’altra del sistema operativo, l’applicazione è in grado di
continuare a funzionare senza problemi;
• le .NET API for Windows Phone sono un subset del Windows Runtime che
coincide con le API che erano disponibili nel Silverlight Runtime di Windows Phone
7.5. Di conseguenza, tutto il codice scritto per le applicazioni della precedente
generazione è riutilizzabile anche nel nuovo sistema operativo.
In virtù di questo approccio, in Windows Phone 8 troverete alcune API duplicate, dato che
alcuni ambiti sono disponibili sia nelle .NET API for Windows Phone che nel Windows
Runtime: alcuni esempi sono l’accesso ai dati, l’utilizzo dei sensori di movimento o di
quelli per la geolocalizzazione.
Nel corso di questo libro prenderemo in considerazione solamente le API del Windows
Runtime.
I tool di sviluppo
Il punto di partenza per iniziare a sviluppare applicazioni Windows Phone è il Windows
Phone Dev Center, il portale di Microsoft dedicato agli sviluppatori Windows disponibile
all’indirizzo http://dev.windowsphone.com.
Nel corso di questo libro faremo riferimento all’SDK di Windows Phone 8: questa
versione, infatti, può essere utilizzata anche per lo sviluppo di applicazioni per la versione
7.5. È su questo sito che troverete il link per scaricare i tool di sviluppo, disponibili in due
formati differenti:
• un installer online di pochi MB, che analizza il vostro computer e scarica solamente i
pacchetti necessari (consigliato), disponibile all’indirizzo
http://s.qmatteoq.com/WP8-SDK;
• l’immagine completa in formato CD (si tratta di un file ISO), contenente tutti i
pacchetti facenti parte dei tool di sviluppo: questo approccio è consigliato solo nel
caso dobbiate installare i tool su più computer e vogliate evitare di scaricare da capo i
pacchetti ogni volta.

Attenzione! Anche se questa scelta ha generato un po’ di confusione, Microsoft


ha scelto di utilizzare due numeri di versione differenti per identificare la
precedente versione del sistema operativo a seconda del punto di vista
NOTA (consumer o sviluppatore). Nel primo caso, si parla di Windows Phone 7.5; nel
secondo, di Windows Phone OS 7.1. In entrambi i casi, però, ci si riferisce alla
stessa versione del sistema operativo: l’SDK 8 permette di sviluppare
applicazioni compatibili con Windows Phone 7.5 e Windows Phone 8.

Oltre alla versione base dell’SDK, Microsoft ha rilasciato una patch dedicata
alla versione 7.8 del sistema operativo: tale patch aggiunge semplicemente due
nuove immagini per l’emulatore, al fine di consentire agli sviluppatori di testare
le loro applicazioni su questo aggiornamento dedicato ai dispositivi della
NOTA
precedente generazione. Non ci sono altre novità: Windows Phone 7.8 non ha
introdotto, infatti, alcuna novità per gli sviluppatori, se non il supporto ai nuovi
formati di tile (nell’ultimo capitolo affronteremo l’argomento). Potete scaricare
tale update all’indirizzo http://s.qmatteoq.com/WP-SDK.

I tool di sviluppo sono localizzati in diverse lingue, tra cui anche l’italiano: il consiglio,
però, se non avete problemi con la lingua di Shakespeare, è quello di installare sempre la
versione inglese; questo approccio vi renderà molto più semplice ricercare informazioni
sulla piattaforma e consultare o porre domande sui numerosi forum dedicati allo sviluppo
per Windows Phone presenti in rete.
I tool di sviluppo sono composti da:
• Visual Studio 2012 Express, ovvero la versione gratuita del celebre ambiente di
sviluppo Microsoft. Si tratta di una versione funzionante al 100% e senza alcuna
limitazione per quanto riguarda lo sviluppo di applicazioni per Windows Phone. È
l’ambiente principale, in cui trascorreremo la maggior parte del tempo, e che ci
permette di lavorare sia sull’interfaccia che sulla logica delle applicazioni;
• Blend for Windows Phone: Blend è un’applicazione facente parte della suite
Expression di Microsoft, che include una serie di programmi rivolti in particolar
modo a chi si occupa di grafica e di design. Blend è un editor visuale di XAML, il
linguaggio di markup utilizzato per la definizione dell’interfaccia grafica di
un’applicazione, e offre un approccio più intuitivo per la creazione di tutto ciò che
riguarda l’aspetto visuale di un programma (grafica, animazioni, effetti ecc.). La
versione Express è completa di tutte le funzionalità, ma limitata al supporto alle sole
applicazioni per Windows Phone (mentre la versione completa può essere utilizzata
anche nello sviluppo di applicazioni Silverlight e WPF). Nel corso del libro,
soprattutto nei capitoli dedicati alla realizzazione dell’interfaccia grafica, avremo
occasione di utilizzare questo tool;
• l’SDK, ovvero l’insieme di librerie che ci permetteranno di interagire con le API del
telefono;
• l’emulatore, che ci dà la possibilità di testare le nostre applicazioni anche senza
possedere un dispositivo.
I tool di sviluppo sono compatibili anche con le versioni professionali e a pagamento di
Visual Studio e di Blend: nel caso in cui il pacchetto di installazione rilevi la presenza di
questi tool, si limiterà a installare solamente l’SDK e l’emulatore, i quali andranno a
integrarsi con le versioni già presenti sul vostro computer.
Un requisito fondamentale per poter installare la versione 8.0 dell’SDK è Windows 8 Pro
o Enterprise a 64 bit: questo perché, come vedremo a breve, la tecnologia utilizzata per
l’emulatore è disponibile solamente nella versione più recente del sistema operativo
Microsoft.
Se avete Windows 7, avete comunque la possibilità di installare la versione 7.1 dell’SDK,
che vi consentirà però di sviluppare solamente applicazioni per Windows Phone 7.5.
NuGet
La versione Express di Visual Studio installata con i tool di sviluppo ha una particolarità:
il supporto a NuGet. NuGet è una estensione di Visual Studio e, per tale motivo, non
sarebbe compatibile con la versione Express, dato che non offre il supporto alle estensioni.
L’importanza di NuGet nel panorama dello sviluppo Microsoft è sempre maggiore e, per
questo, il team di sviluppo ha deciso di renderlo disponibile a tutti gli sviluppatori
Windows Phone.
Cos’è NuGet? Si tratta di un package manager che semplifica la procedura di installazione
e di utilizzo di librerie di terze parti all’interno di un progetto.
Prima dell’avvento di NuGet, il procedimento tipico adottato da uno sviluppatore per
utilizzare una libreria all’interno del proprio progetto era il seguente:
1. collegarsi sul sito dello sviluppatore o su un portale dedicato ai progetti open source,
come Codeplex o GitHub;
2. scaricare la libreria e copiare le DLL all’interno di una cartella del proprio progetto
(ad esempio, lib);
3. da Visual Studio, aggiungere un riferimento alla libreria tramite l’opzione Add
reference, che è disponibile nel menu contestuale e che appare cliccando con il tasto
destro su un progetto.
NuGet si occupa al posto nostro di fare tutte queste operazioni, offrendoci una semplice
interfaccia che ci permette di ricercare le librerie all’interno del repository, di installarle e
di mantenerle aggiornate. Il tool è poi in grado di interagire anche con la struttura del
progetto, includendo eventuali altri file necessari alla libreria (immagini, file di
configurazione, classi ecc.)
In realtà, NuGet offre anche altre possibilità: ad esempio, è possibile creare dei repository
privati per la distribuzione di librerie all’interno di un’azienda, oppure interagire con
un’interfaccia a linea di comando, che offre una maggiore flessibilità.
Figura 1.5 - La schermata principale di NuGet.

Utilizzeremo spesso questo tool nel corso del libro per installare librerie di terze parti. Per
accedervi è sufficiente fare clic con il tasto destro su un progetto in Visual Studio e
scegliere la voce Manage NuGet Packages. La finestra prevede, in alto a destra, un
motore di ricerca integrato, per cercare una specifica libreria: come comportamento
predefinito, invece, vengono mostrati i package più scaricati. La schermata di NuGet è
suddivisa in tre sezioni, elencate nella parte sinistra:
• Installed packages vi consente di vedere e gestire tutte le librerie installate nel
progetto corrente;
• Online vi permette di accedere alla libreria online di NuGet (è la vista predefinita);
• Updates vi mostra, se presenti, le librerie per cui è disponibile un aggiornamento: in
tal caso comparirà un pulsante Update che vi permetterà di aggiornare la libreria in
questione all’interno del vostro progetto.

L’emulatore
I tool di sviluppo comprendono, tra le altre cose, un emulatore che ci dà la possibilità di
testare le applicazioni senza possedere un device reale. L’emulatore non vuole e non può
sostituire un telefono: l’esperienza d’uso e le prestazioni sono differenti, perciò è
indispensabile testare l’applicazione anche su un dispositivo reale prima di pubblicarla.
Rimane, però, uno strumento molto importante durante lo sviluppo, grazie alla facilità di
utilizzo e al supporto per il testing di scenari: operazione che, anche con un device reale,
risulterebbe complessa.

Figura 1.6 - L’emulatore di Windows Phone.

Inoltre è lo strumento più adatto se vi state avvicinando per la prima volta allo sviluppo
per Windows Phone e non avete ancora le idee chiare sul progetto che volete realizzare,
ma volete limitarvi a fare qualche esperimento: come vedremo tra poco, anche se siete in
possesso di un dispositivo, dovrete prima sbloccarlo per poterlo utilizzare per il testing.
L’emulatore di Windows Phone è una macchina virtuale basata su Hyper-V, la tecnologia
di virtualizzazione di Microsoft che è stata introdotta, per il mondo consumer, proprio in
Windows 8: prima era disponibile solo nelle edizioni server di Windows.
A causa di questa soluzione, è stato introdotto un requisito hardware che non farà
particolarmente piacere ai possessori di computer non troppo recenti: Hyper-V, infatti, per
funzionare correttamente richiede che il vostro processore supporti una tecnologia
chiamata SLAT, disponibile sui computer di recente generazione (prendendo come
riferimento il mondo Intel, per esempio, parliamo di processori dall’i3 in su). Senza questo
supporto avrete comunque la possibilità di installare i tool di sviluppo, ma non potrete
utilizzare l’emulatore: dovrete necessariamente utilizzare un device vero e proprio per
effettuare i test.
Questa architettura, in compenso, consente all’emulatore di interagire con l’hardware del
vostro computer e di sfruttarlo per offrire la migliore esperienza d’uso possibile. Ad
esempio, se siete in possesso di un monitor touch screen potrete utilizzarlo per simulare il
multi touch dello schermo; se state sviluppando un’applicazione che richiede l’utilizzo del
microfono, potete utilizzare quello integrato nel vostro portatile per simulare quello
incorporato nel telefono, e così via.
Di contro, per questo motivo, l’emulatore non è compatibile con ambienti virtuali: in
questo caso potreste sperimentare notevoli problemi di stabilità e di prestazioni (o, molto
probabilmente, di non funzionamento in generale); è sempre bene, perciò, utilizzare un
computer con un’installazione di Windows fisica per sviluppare applicazioni per Windows
Phone.
L’emulatore viene lanciato in automatico da Visual Studio nel momento in cui eseguite un
progetto di tipo Windows Phone: l’ambiente di sviluppo Microsoft vi dà la possibilità di
scegliere, tramite un menu a tendina, su quale variante testare l’applicazione. È infatti
disponibile una versione dell’emulatore per ognuna delle tipologie di device disponibili
sul mercato, sia come versione del sistema operativo (7.5, 7.8 e 8), sia come caratteristiche
tecniche (risoluzione e quantitativo di memoria RAM).
L’emulatore riproduce fedelmente un generico device Windows Phone, con il supporto ai
tre pulsanti hardware obbligatori di cui abbiamo parlato nel capitolo introduttivo. A
differenza dell’emulatore che era disponibile con le vecchie versioni dei tool, quello di
Windows Phone 8 è dotato di tutte le funzionalità del sistema operativo e di tutte le
applicazioni native.

Figura 1.7 - Le differenti versioni dell’emulatore disponibili in Visual Studio per un progetto Windows Phone 8.

In più, la versione di Internet Explorer presente è fedele a quella disponibile nei device
reali: possiamo perciò utilizzarla per testare, ad esempio, il rendering di un sito mobile da
parte del browser e verificare che si veda correttamente.
Posizionando il cursore del mouse sul lato destro dell’emulatore, comparirà, nell’angolo
superiore, una serie di icone, che permettono di accedere a una serie di opzioni e tool
aggiuntivi. Vediamoli insieme partendo dalla terza icona (le prime due, infatti, sono quelle
classiche di Windows per chiudere e ridurre a icona l’emulatore):
• rotazione a destra o a sinistra: la terza e la quarta icona servono a ruotare
l’emulatore verso destra o verso sinistra, simulando la rotazione del device dalla
modalità portrait alla modalità landscape e viceversa;
• adatta allo schermo: la quinta icona permette di ottimizzare la dimensione
dell’emulatore in relazione alla risoluzione del proprio computer;
• zoom: se la dimensione impostata dall’opzione “Adatta allo schermo” non ci
soddisfa, possiamo agire manualmente sul livello di zoom tramite questa icona. Lo
zoom al 100% coincide con la dimensione reale di un device Windows Phone;
• additional tools: l’ultima icona disponibile permette di aprire una nuova sezione
dell’emulatore, che contiene una serie di tool aggiuntivi per il testing; si tratta di una
novità introdotta con la versione 7.1 dell’SDK, che ora esamineremo in dettaglio.

Accelerometro
Questo tool vi permette di testare le applicazioni che fanno uso dell’accelerometro, senza
necessariamente essere in possesso di un device vero e proprio. Il funzionamento è molto
semplice: la schermata mostra un modello 3D del device, che è possibile ruotare cliccando
e tenendo premuto il tasto destro del mouse.
Nella parte inferiore della schermata potrete vedere in tempo reale le coordinate X, Y e Z
corrispondenti alla posizione del device: tali coordinate verranno inviate all’emulatore e
potranno essere rilevate tramite le API messe a disposizione per interagire con i sensori di
movimento (le vedremo in dettaglio nel Capitolo 6).
Questa sezione include anche due opzioni per impostare la rotazione del device a una delle
posizioni predefinite (landscape o portrait) o per simulare lo “shake”, ovvero una brusca
agitazione del telefono ripetuta per qualche secondo.
Figura 1.8 - Il tool per testare l’uso dell’accelerometro.

Posizione
Questo tool vi permette di testare con semplicità applicazioni che fanno uso dei servizi di
geolocalizzazione disponibili nel telefono. Tale sezione mette a disposizione una mappa,
nella quale possiamo posizionare dei segnaposto con il mouse; le coordinate di ogni
segnaposto (mostrate nella parte inferiore sotto forma di latitudine e longitudine) vengono
inviate all’emulatore, che sarà in grado di rilevarle grazie alle API apposite, trattate nel
Capitolo 6.
Nella parte superiore del tool è presente una toolbar, che ci offre diverse possibilità:
• search: come impostazione predefinita la mappa è posizionata sull’area di Seattle,
dove è localizzata la sede di Microsoft (per la precisione, a Redmond). Tramite
questa casella di testo possiamo lanciare una ricerca per una qualsiasi posizione sulla
mappa (ad esempio, digitando il nome di una città);
• zoom: i due pulsanti con l’icona di una lente di ingrandimento ci permettono di
regolare lo zoom della mappa;
• live: questa modalità (che è abilitata come impostazione predefinita) fa sì che le
coordinate dei segnaposto che inseriamo sulla mappa vengano inviate in tempo reale
all’emulatore. Se la disabilitiamo, invece, possiamo inserire uno o più segnaposto
senza che le API dedicate rilevino il cambiamento di posizione: questa funzione è
utile quando, ad esempio, vogliamo tracciare un percorso;

Figura 1.9 - Il tool per il testing dei servizi di geolocalizzazione.

• modalità pin: anche questa opzione è attiva di default e abilita il posizionamento di


un segnalino a ogni clic del mouse. Quando è disattivata, il clic viene utilizzato per
interagire con la mappa: possiamo tenere premuto il pulsante sinistro del mouse e
trascinarlo per spostarci, oppure fare doppio clic per effettuare lo zoom;
• elimina pin: questo pulsante permette di eliminare tutti i pin che abbiamo
posizionato sulla mappa;
• salva percorso: possiamo sfruttare questo tool non solamente per simulare la
posizione del device in un determinato luogo, ma anche per definire dei percorsi;
questa opzione ci permette di salvare la posizione di tutti i pin che abbiamo inserito
sulla mappa, così da riutilizzarli in un secondo momento tramite il pulsante Load
posizionato nella sezione Recorded data del tool;
• riproduci percorso: una volta che abbiamo posizionato una serie di segnaposto sulla
mappa, possiamo simulare lo spostamento dell’utente da uno all’altro, secondo
l’ordine in cui li abbiamo inseriti (e che possiamo gestire nella schermata inferiore).
Il pulsante Play ci permette di dare avvio alla simulazione: nella casella di testo
contrassegnata dall’etichetta Fire every possiamo specificare l’intervallo di tempo
trascorso il quale l’ipotetico utente verrà spostato al segnaposto successivo.

Screenshot
Il penultimo tool serve per scattare screenshot dell’emulatore e risulta indispensabile nel
momento in cui andremo a pubblicare la nostra applicazione sullo Store: come vedremo in
dettaglio nel Capitolo 12, è richiesto infatti il caricamento di almeno un’immagine
rappresentativa della nostra applicazione.
Il funzionamento è molto semplice: il pulsante Capture scatta lo screenshot, che possiamo
salvare su disco tramite il pulsante Save. Indipendentemente dallo zoom che avete
impostato, lo screenshot sarà sempre scattato alla risoluzione nativa dell’emulatore.

Network
Il quarto e ultimo tool, disponibile solo per Windows Phone 8, consente di avere una serie
di informazioni di diagnostica sulla configurazione di rete dell’emulatore: è utile per
capire cosa c’è che non va in caso di problemi di connettività.

Simulation Dashboard
All’interno di Visual Studio 2012, con l’avvento dell’SDK di Windows Phone 8, è stata
aggiunta una sezione chiamata Simulation Dashboard, accessibile dal menu Tools di
Visual Studio.
Con questo strumento possiamo simulare i seguenti comportamenti:
• le condizioni della rete: tramite due slider possiamo scegliere la velocità (tra 2G, 3G,
4G e Wi-Fi) e la potenza (tra Good, Average e Poor) del segnale;
• il blocco del telefono: tramite un radio button possiamo simulare il lock e l’unlock del
telefono;
• la comparsa di un reminder, tramite il pulsante Trigger reminder.
Figura 1.10 - Il tool Simulation Dashboard.

Lo store
Lo store (conosciuto, prima di Windows Phone 8, con il nome di Marketplace, rinominato
poi Store per allinearsi a Windows 8) rappresenta il punto di accesso per gli utenti per le
applicazioni di terze parti ed è disponibile sia sotto forma di applicazione, sia di sito web
(accessibile all’indirizzo http://www.windowsphone.com/store). Entrambi consentono di
visualizzare il catalogo completo, effettuare ricerche, vedere le classifiche e scaricare le
applicazioni sul proprio telefono.
Lo store è un ecosistema controllato, in quanto le applicazioni, prima di essere pubblicate,
devono superare un processo di certificazione che ne garantisce la qualità dal punto di
vista tecnico e il rispetto delle linee guida della piattaforma, per quanto riguarda sia i
contenuti sia l’aspetto visuale.
Attualmente lo store è disponibile in 191 Paesi del mondo: durante il processo di
pubblicazione avrete la possibilità di scegliere in quali di questi rendere disponibile la
vostra applicazione. Il prezzo di vendita sarà sempre lo stesso: ci penserà lo store a
convertire il costo che avete stabilito nelle varie valute mondiali.
L’iscrizione per gli sviluppatori ha un costo di 79 euro all’anno e consente la
pubblicazione di 100 applicazioni gratuite e di un numero illimitato di applicazioni a
pagamento: in più, permette allo sviluppatore di sbloccare fino a tre device. Cosa
significa? Che normalmente l’unica strada per installare applicazioni di terze parti è
proprio lo store: sbloccando il telefono tramite uno dei tool installati assieme all’SDK, si
ha invece la possibilità di effettuare il deploy direttamente da Visual Studio, dando così la
possibilità di utilizzare un device reale per il testing, al posto dell’emulatore.
Lo store offre molte opportunità per gli sviluppatori, dando l’accesso a report di vendita,
di download e di crash; sono anche disponibili diverse tipologie di pubblicazione, per
venire incontro alle differenti esigenze.
Alla pubblicazione delle applicazioni è dedicato il Capitolo 12, nel quale saranno
affrontati argomenti come il processo di certificazione, le guideline da rispettare, le
tipologie di pubblicazione disponibili e la localizzazione delle applicazioni per i vari
mercati.

Sbloccare un telefono per lo sviluppo


Come anticipato poco fa, non è possibile effettuare il deploy di applicazioni da Visual
Studio su un telefono senza averlo prima sbloccato: per questo scopo l’SDK installa
un’applicazione chiamata Windows Phone Developer Registration, disponibile nel
menu Start di Windows.
Una volta avviata l’applicazione, è necessario collegare il proprio telefono tramite il cavo
USB. Nel momento in cui la connessione è stabilita, possiamo inserire le credenziali
dell’account Microsoft a cui è legata la nostra sottoscrizione come sviluppatori; una volta
data conferma, l’applicazione si collegherà ai server Microsoft e verificherà la validità
dell’account: se tutto è andato a buon fine, un messaggio confermerà l’esito
dell’operazione e, a questo punto, potrete iniziare a utilizzare il vostro telefono per i test.
Figura 1.11 - Il tool per sbloccare un telefono per lo sviluppo.

Caricare manualmente un’applicazione


Lo sblocco di un dispositivo consente anche il caricamento manuale di applicazioni di
terze parti tramite un tool, installando sempre dall’SDK, chiamato Application
Deployment e disponibile anch’esso nel menu Start di Windows.
Il tool richiede lo XAP dell’applicazione, ovvero il pacchetto preparato da Visual Studio
quando si compila un progetto Windows Phone; dopodiché possiamo specificare due
diversi target per il deploy: l’emulatore o il device. Una volta dato l’OK, il tool installerà
l’applicazione sul dispositivo.
Esiste, però, un limite massimo di 10 applicazioni installabili sul telefono utilizzando
questo sistema: tale limite è stato introdotto per prevenire il problema della pirateria ed
evitare perciò che degli utenti possano recuperare in maniera illegale gli XAP delle
applicazioni sullo store e installarli sul telefono.
Figura 1.12 - Il tool per caricare un’applicazione manualmente sul telefono.
Alcune note sul libro
Il libro che state leggendo è dedicato a Windows Phone 8: di conseguenza, questa versione
tratterà solo le API e i controlli che sono stati aggiunti nella nuova versione, tralasciando
quelli che invece sono originali di Windows Phone 7.5 e che sono stati lasciati nella nuova
piattaforma solo per motivi di retro-compatibilità.
Cosa significa? Che, nelle situazioni in cui ci sono delle API duplicate tra il Windows
Runtime e le .NET API for Windows Phone, questo libro prenderà in esame solamente le
API del Windows Runtime. Oppure, nel caso in cui alcuni controlli siano stati rimpiazzati
(come il controllo Bing Maps, sostituito da quello fornito da Nokia), verranno presi in
esame solamente quelli di nuova generazione.
In conclusione
In questo capitolo abbiamo imparato a familiarizzare con il sistema operativo, analizzando
le caratteristiche che lo differenziano dagli altri e tutto ciò che occorre imparare per
sfruttare correttamente le applicazioni.
Abbiamo visto, inoltre, come muovere i primi passi nello sviluppo di applicazioni: quali
sono i tool da scaricare, in cosa consiste il programma per gli sviluppatori e quali sono i
programmi da utilizzare.
Ora siamo pronti a entrare nel vivo dello sviluppo e a iniziare a creare il nostro primo
progetto.
Iniziare a sviluppare per Windows Phone:
le basi di C# e XAML

Uno degli aspetti che sicuramente contribuiscono alla diffusione di un


sistema operativo è la disponibilità di applicazioni di terze parti: da
questo punto di vista, Windows Phone si presenta come una piattaforma
molto interessante per gli sviluppatori, in virtù di un linguaggio e di una
serie di tool di sviluppo di altissimo livello, che permettono di focalizzarsi
su cosa deve fare un’applicazione piuttosto che sul come farlo.
A dimostrazione dell’importanza che hanno gli sviluppatori per Microsoft, tutti i tool
necessari per iniziare a lavorare sono resi disponibili in maniera gratuita, insieme a
tantissime risorse, sotto forma di tutorial, esempi di codice e librerie.
Questo capitolo è rivolto a chi si sta avvicinando per la prima volta a Windows Phone e
vuole conoscerne le basi: creeremo il nostro primo progetto, ne analizzeremo la struttura,
dopodiché affronteremo le basi di C# e XAML, indispensabili per capire come creare
l’interfaccia grafica della nostra applicazione e come interagire con i controlli.
Molte delle conoscenze che apprenderemo nel corso del capitolo sono valide per lo
sviluppo di applicazioni sia per Windows Phone 7 che per Windows Phone 8: anche se,
dietro le quinte, ci sono due runtime diversi (Silverlight nel primo caso, WinRT nel
secondo), le tecnologie alla base sono le medesime.
Creiamo il nostro primo progetto
Per creare il nostro primo progetto faremo ricorso a Visual Studio, l’ambiente di sviluppo
principale che utilizzeremo nella maggior parte dei casi nel corso di questo libro. Una
volta apertolo e selezionata la voce Create new project, sia che si tratti della versione
Express che di quella completa, ci sarà una categoria di template chiamata Windows
Phone. Al suo interno troverete tutti i template base per lo sviluppo di applicazioni e
servizi legati alla piattaforma mobile Microsoft: per questo primo approccio ci limiteremo
a utilizzare il template base, chiamato semplicemente Windows Phone Application.
Una volta scelto un qualsiasi template, la prima cosa che ci sarà richiesta è la versione del
sistema operativo per la quale vogliamo sviluppare l’applicazione:
• Windows Phone OS 7.1, che corrisponde a Windows Phone 7.5;
• Windows Phone OS 8.0, che corrisponde a Windows Phone 8.

Figura 2.1 - La struttura di un progetto Windows Phone.

La scelta della piattaforma da utilizzare dipende dal tipo di applicazione che volete
sviluppare e dal vostro target: è importante, infatti, tenere a mente che le applicazioni per
Windows Phone 8 non sono in grado di funzionare sui device dotati di Windows Phone 7,
mentre è vero il contrario. La scelta della piattaforma dipende perciò dal grado di
complessità del vostro progetto e dal livello di integrazione che prevedete con le nuove
feature di Windows Phone 8 (NFC, notifiche nella lock screen ecc.).
Dopo qualche secondo, Visual Studio avrà creato tutto il necessario per il nostro primo
progetto: tramite il tool Solution Explorer, solitamente posizionato nella parte destra
dello schermo, possiamo vederne la struttura.
Il file di manifest
La prima cosa importante da notare è la presenza di un file XML, chiamato
WMAppManifest.xml e contenuto all’interno della cartella Properties: tale file,
denominato file di manifest, contiene una serie di informazioni sull’applicazione, come il
nome, il numero di versione, l’autore ecc.
A partire dall’SDK di Windows Phone 8, è disponibile un comodo editor visuale che
permette di configurare le varie opzioni della nostra applicazione senza dover modificare
manualmente l’XML (tranne per alcuni casi particolari, che saranno specificati nel corso
del libro).
Vediamo nei dettagli le varie sezioni di cui è composto il file di manifest di
un’applicazione Windows Phone 8.

Application UI
La prima sezione del file di manifest ha lo scopo di configurare tutti gli aspetti visuali di
base dell’applicazione, come il titolo, le tile da utilizzare, le risoluzioni supportate ecc.
Vediamo in dettaglio i parametri che è possibile configurare:
• Display name: è il nome dell’applicazione;
• Description: è la descrizione dell’applicazione;
• NavigationPage: è la prima pagina dell’applicazione, che viene caricata una volta
inizializzata. La pagina predefinita (che viene automaticamente creata con ogni
nuovo progetto) si chiama MainPage.xaml, ma è possibile modificarla a piacimento;
• App icon: è l’icona che identifica la vostra applicazione. Il formato richiesto è
100x100 e l’icona predefinita è costituita dal file ApplicationIcon.png, contenuto
all’interno della cartella Assets;
• Supported resolutions: consente di specificare quale delle tre risoluzioni disponibili
in Windows Phone 8 supporta la vostra applicazione. I valori supportati sono wvga
(480x800), wxga (768x1280) e 720p (720x1280);
• TileTemplate: Windows Phone 8 mette a disposizione tre template differenti per
personalizzare le tile dell’applicazione. Approfondiremo il discorso nel Capitolo 11,
per ora mi limito a elencare le tre tipologie disponibili:
– TemplateFlip: la tile può mostrare informazioni anche sul retro, che viene
visualizzato tramite un effetto di rotazione;
– TemplateCycle: la tile è in grado di mostrare fino a un massimo di nove immagini
a rotazione;
– TemplateIconic: con questo template è possibile creare tile con lo stesso aspetto di
quelle native (ad esempio, Mail e Messaging).
A seconda del tipo di template che abbiamo scelto di utilizzare, la sezione successiva
permetterà di impostare il titolo da mostrare e le immagini da utilizzare come sfondo. In
più, è presente l’opzione Support for large tiles che, se abilitata, consente di utilizzare
anche tile in formato wide (e, di conseguenza, di caricare un’immagine adatta).

Figura 2.2 - La sezione del file di manifest Application UI.

La cartella Assets/Tiles, creata in automatico in ogni nuovo progetto, contiene già alcune
immagini predefinite per le tile. Nel caso dei template Flip e Cycle, i tre formati richiesti
sono:
• 159x159 per la small;
• 336x336 per la medium;
• 691x336 per la large.
Nel caso del template Iconic, invece, dato che l’immagine viene automaticamente
utilizzata e adattata per i vari formati, è sufficiente specificarne due:
• 71x110 per la small;
• 134x202 per la medium e la large.
Capability
Il manifest include una sezione molto importante chiamata Capabilities, in cui gli
sviluppatori hanno il compito di dichiarare le funzionalità utilizzate dall’applicazione: è
importante che questa lista sia corretta e precisa, in quanto la pagina dello store, tramite la
quale l’utente potrà scaricare la vostra applicazione, mostrerà un elenco completo delle
funzionalità proprio in base a quanto dichiarato nel file di manifest. È opportuno, quindi,
non dichiarare funzionalità che non saranno poi utilizzate, in particolar modo se implicano
potenziali rischi per la privacy (accesso all’identità dell’utente, alle informazioni del
telefono, ai servizi di geolocalizzazione o alla rubrica ecc.). In più, se tentate di utilizzare
una funzionalità per la quale non avete dichiarato la specifica capability, sarà sollevata
un’eccezione dal sistema operativo.
Ogni funzionalità è inserita nel file di manifest, dietro le quinte, sotto forma di tag XML,
strutturato nel seguente modo:
<Capability Name="ID_CAP_LOCATION"/>

L’attributo Name contiene l’identificativo della funzionalità.


Ecco l’elenco completo delle capability software:
• ID_CAP_GAMERSERVICES: l’applicazione fa uso dei servizi legati al gioco, offerti da
Xbox Live;
• ID_CAP_IDENTITY_DEVICE: l’applicazione fa uso delle informazioni sul device in uso
(marca, modello, id univoco, quantità di memoria ecc.);
• ID_CAP_IDENTITY_USER: l’applicazione è in grado di utilizzare l’Anonymous Live Id,
ovvero un id univoco basato sul Live Id dell’utente. Questo tipo di informazione
permette di tracciare l’utente senza però violare la sua privacy, in quanto l’indirizzo
mail associato al profilo Live viene mantenuto nascosto;
• ID_CAP_LOCATION: l’applicazione fa uso dei servizi di geolocalizzazione per
identificare la posizione dell’utente;
• ID_CAP_MEDIALIB_AUDIO: l’applicazione è in grado di accedere alla libreria audio
dell’utente;
• ID_CAP_MEDIALIB_VIDEO: l’applicazione è in grado di accedere alla libreria video
dell’utente;
• ID_CAP_MEDIALIB_PHOTO: l’applicazione è in grado di accedere alle gallerie
fotografiche dell’utente;
• ID_CAP_MEDIALIB_PLAYBACK: l’applicazione è in grado di accedere alle informazioni
sulla traccia audio o video correntemente in riproduzione;
• ID_CAP_MICROPHONE: l’applicazione fa uso del microfono integrato nel telefono;
• ID_CAP_NETWORKING: l’applicazione accede alla rete per ricevere o inviare dati;
• ID_CAP_PHONEDIALER: l’applicazione è in grado di avviare una telefonata per conto
dell’utente;
• ID_CAP_PUSH_NOTIFICATIONS: l’applicazione fa uso di notifiche push, siano esse locali
(generate dall’applicazione stessa) o remote (spedite da un’applicazione esterna
tramite il Microsoft Push Notification Service di Microsoft);
• ID_CAP_SENSORS: l’applicazione fa uso dei sensori di movimento (accelerometro,
bussola e giroscopio, se presente);
• ID_CAP_WEBBROWSERCOMPONENT: l’applicazione fa uso del controllo WebBrowser, che
permette di includere un’istanza di Internet Explorer per visualizzare pagine web;
• ID_CAP_ISV_CAMERA: l’applicazione accede alla fotocamera (o alle fotocamere, in caso
di device dotati di camera frontale);
• ID_CAP_CONTACTS: l’applicazione è in grado di accedere (in sola lettura) ai contatti
contenuti nell’hub People;
• ID_CAP_APPOINTMENTS: l’applicazione è in grado di accedere (in sola lettura) agli
appuntamenti memorizzati nel calendario;
• ID_CAP_NFC_PROXIMITY: l’applicazione fa uso dei servizi per l’utilizzo delle tecnologia
NFC e Bluetooth;
• ID_CAP_REMOVABLE_STORAGE: l’applicazione è in grado di accedere ai dati
memorizzati sulla scheda micro SD (se presente);
• ID_CAP_RINGTONE_ADD: l’applicazione è in grado di aggiungere una suoneria tra quelle
di sistema;
• ID_CAP_SPEECH_RECOGNITION: l’applicazione è in grado di utilizzare le API per il
riconoscimento vocale;
• ID_CAP_VOIP: se stiamo sviluppando un’applicazione VoIP, possiamo integrarla con il
sistema utilizzando le apposite API e dichiarando questa capability nel manifest;
• ID_CAP_WALLET: l’applicazione è in grado di accedere all’applicazione di sistema
Wallet, per salvare, aggiornare e modificare le informazioni sui deal e le carte fedeltà;
• ID_CAP_WALLET_PAYMENTINSTRUMENTS: l’applicazione è in grado di accedere alle
informazioni per il pagamento (come i dati delle carte di credito) memorizzate
nell’applicazione Wallet;
• ID_CAP_WALLET_SECUREELEMENT: l’applicazione è in grado di effettuare pagamenti
tramite NFC utilizzando i dati di pagamento memorizzati nell’applicazione Wallet.
Figura 2.3 - La sezione del file di manifest dedicata alle capability.
Requirements
La sezione Requirements ha lo stesso scopo di quella precedente, con la differenza che,
in questo caso, possiamo specificare i requisiti hardware indispensabili (utilizzo di sensori,
memoria necessaria ecc.) affinché l’applicazione funzioni correttamente.
Ecco l’elenco dei requisiti disponibili:
• ID_REQ_FRONTCAMERA: l’applicazione fa uso della fotocamera frontale;
• ID_REQ_REARCAMERA: l’applicazione fa uso della fotocamera posteriore;
• ID_REQ_NFC: l’applicazione fa uso della tecnologia NFC;
• ID_REQ_MAGNETOMETER: l’applicazione fa uso della bussola;
• ID_REQ_GYROSCOPE: l’applicazione fa uso del giroscopio.

Figura 2.4 - La sezione del file di manifest dedicata ai requisiti.


Packaging
La quarta e ultima sezione contiene tutte le informazioni necessarie per la distribuzione
dell’applicazione sullo store, ovvero:
• autore, publisher e numero di versione;
• Product ID e Publisher ID: sono due valori fissi e univoci per l’applicazione (nel
primo caso) e per sviluppatore (nel secondo caso);
• lingua di default dell’applicazione;
• elenco delle lingue supportate dall’applicazione (nei capitoli finali vedremo come
localizzare un’applicazione).

Figura 2.5 - La sezione del file di manifest Packaging.


La splash screen
La splash screen è l’immagine che viene mostrata all’utente nel momento in cui viene
lanciata un’applicazione e che rimane visibile fintanto che questa non è stata caricata
completamente e non è pronta per essere utilizzata.
Grazie alle migliori performance del Windows Runtime e alla maggiore potenza dei
device di nuova generazione, i tempi di caricamento delle applicazioni si sono ridotti
notevolmente, tanto che in Windows Phone 8 la splash screen non è più obbligatoria e
non è più presente tra i file di default di un nuovo progetto.
Avete comunque la facoltà di specificarne una, nel caso in cui il tempo di startup della
vostra applicazione non sia così istantaneo: è sufficiente includere un’immagine dal nome
SplashScreenImage.jpg con risoluzione 768x1280 nella root del progetto. L’immagine
sarà automaticamente scalata in base alla risoluzione del device.
Se volete avere, invece, pieno controllo su come la splash screen sarà visualizzata a ogni
risoluzione, avete la possibilità di includere, sempre nella root del progetto, un file per
ogni risoluzione supportata, con la seguente denominazione:
• SplashScreenImage.screen-WVGA.jpg per la risoluzione 480x800;
• SplashScreenImage.screen-720p.jpg per la risoluzione 720x1280;
• SplashScreenImage.screen-WXGA.jpg per la risoluzione 768x1280.
Lo XAML e il code behind
XAML è l’acronimo di Extensible Application Markup Language ed è un linguaggio di
markup (analogo ad HTML come concezione), introdotto da Microsoft con la tecnologia
WPF e, successivamente, adottato anche in Silverlight e, infine, in Windows Phone e
Windows 8.
Lo XAML è il linguaggio di markup con il quale si definiscono le interfacce grafiche di
un’applicazione: controlli, animazioni ed effetti vengono tradotti in tag XML, ciascuno
con vari attributi che ne definiscono le proprietà e gli eventi.
Le applicazioni Windows Phone sono basate su pagine, ognuna delle quali rappresenta un
contenitore di contenuti: possiamo quindi avere, ad esempio, una pagina con una lista di
elementi, una pagina con il dettaglio di un elemento, una pagina con le informazioni per il
supporto, e così via. Approfondiremo la struttura di una pagina e le modalità di
navigazione tra di esse nei capitoli successivi.
Per ora è importante sapere che ognuna di queste pagine è composta da un file XAML,
che ne definisce il layout, e da un file di codice, che ne definisce il comportamento e la
logica.
La definizione del layout è contenuta nel file principale con estensione .xaml; il codice è
contenuto nel file (chiamato di code behind) che ha lo stesso nome del file .xaml, con
l’aggiunta dell’estensione .cs, se state utilizzando il linguaggio C#, o .vb, se state
utilizzando invece VB.NET.
Visual Studio vi mostrerà i file di code behind, all’interno della struttura ad albero del
Solution Explorer, come figli del file XAML: dovrete cliccare sulla freccetta posizionata
a sinistra del file per visualizzarlo.
Di default, un progetto Windows Phone contiene già una pagina, che è quella che viene
caricata all’avvio dell’applicazione: il suo nome è MainPage.xaml.
Nel progetto è presente anche un’altra pagina, chiamata App.xaml, che, pur mantenendo
la medesima struttura che abbiamo appena trattato, non è una vera e propria pagina: se
aprite il file XAML non troverete, infatti, controlli visuali.
Questo perché l’App.xaml rappresenta il punto di partenza di ogni applicazione Windows
Phone, che si occupa di caricare tutte le risorse necessarie e che poi lascia il controllo alla
pagina iniziale (se vi ricordate, è quella che è definita nel campo NavigationPage del file
di manifest).
Un’altra caratteristica importante è che questa classe rimane in vita per tutto il tempo di
utilizzo dell’applicazione: come vedremo nei prossimi capitoli, è infatti all’interno del file
App.xaml.cs che sono gestiti gli eventi legati al ciclo di vita dell’applicazione (avvio,
sospensione, chiusura ecc.).
In più, il file App.xaml contiene tutte le risorse di layout (stili, brush, template ecc.) di
tipo globale, ovvero che possono essere utilizzate all’interno di qualsiasi pagina
dell’applicazione. Approfondiremo l’argomento nel corso di questo capitolo.
Lo XAML
Lo XAML, essendo basato sull’XML, ne segue le stesse regole e convenzioni: tag, nodi,
attributi ecc.
L’ambiente di sviluppo mette a disposizione un buon numero di controlli già pronti, che
sono rappresentati nello XAML da un nodo XML.
Pertanto, se vogliamo inserire un’etichetta di testo dobbiamo inserire un controllo TextBlock
<TextBlock />

Oppure, se vogliamo inserire un pulsante, abbiamo a disposizione il controllo Button:


<Button />

Ovviamente un controllo inserito in questo modo è di scarsa utilità: per interagire con lo
stesso abbiamo a disposizione diverse proprietà, che variano da controllo a controllo e
sono rappresentate da attributi del nodo XML.
Se, ad esempio, vogliamo visualizzare del testo all’interno del controllo TextBlock, abbiamo
a disposizione la proprietà Text:
<TextBlock Text="Questo è un testo" />

L’Intellisense di Visual Studio ci viene in aiuto in fase di creazione del markup,


suggerendoci le proprietà disponibili per quel controllo, caratterizzate dall’icona di una
mano che regge un foglio.

Figura 2.6 - L’Intellisense di Visual Studio ci suggerisce la proprietà Text.

Una proprietà molto importante dei controlli è x:Name, che rappresenta il nome
identificativo del controllo e che, per tale motivo, deve essere univoco tra tutti quelli della
pagina: è tramite questo nome che potremo utilizzarlo nel code behind e interagire con lo
stesso.
<TextBlock x:Name="Title"/>

Gli attributi non servono solamente per specificare le proprietà, ma anche per gestire gli
eventi, ovvero le interazioni dell’utente o dell’applicazione stessa con il controllo:
l’Intellisense di Visual Studio li evidenzia con il simbolo di un fulmine e sono necessari
per interagire con il controllo stesso. Ad esempio, un controllo di tipo Button espone
l’evento Tap per gestire la pressione dello stesso da parte dell’utente:
<Button Tap="Button_Tap" />

Il valore dell’attributo rappresenta il nome della funzione che, nel code behind, viene
invocata allo scatenarsi dell’evento.

Figura 2.7 - La rappresentazione di un evento da parte dell’Intellisense di Visual Studio.

Un modo alternativo per esplorare le proprietà e gli eventi di un controllo consiste


nell’utilizzare l’editor visuale di Visual Studio e accedere alla finestra Properties di
Visual Studio: per passare da una vista all’altra possiamo utilizzare i due tab posti a lato
della finestra.

Figura 2.8 - I pulsanti per passare dalla modalità design a quella codice.

In questo editor il controllo viene renderizzato in tempo reale, mostrandone l’aspetto che
avrà nell’applicazione finale (a meno che non subisca delle modifiche a runtime nel corso
dell’esecuzione): cliccando su uno dei controlli e aprendo la finestra Properties (che
dovrebbe essere visibile di default, in caso contrario possiamo attivarla dal menu View),
avremo una rappresentazione visuale di tutte le proprietà, con la possibilità di modificarne
il valore. Visual Studio genererà in automatico il codice XAML necessario.
Un’altra caratteristica importante dello XAML, ereditata direttamente dall’XML, è la
possibilità di includere nodi all’interno di altri nodi per definirne la gerarchia.
<StackPanel>
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

In questo esempio, preso direttamente dal template standard delle pagine Windows Phone,
i controlli TextBlock sono nidificati all’interno di un controllo StackPanel: questo significa che
le caselle di testo saranno contenute all’interno di questo controllo, che ne definirà il
layout e il posizionamento.

Figura 2.9 - Le proprietà di un controllo TextBlock.

La gerarchizzazione dei tag nello XAML può essere utilizzata anche per definire le
proprietà di un controllo.
<TextBlock>
<TextBlock.Text>
Questo è un testo
</TextBlock.Text>
</TextBlock>

Questo esempio rappresenta un modo alternativo per valorizzare la proprietà Text di un


controllo TextBlock: ovviamente, in casi come questi, questa sintassi non è consigliata, dato
che è molto più verbosa. In alcuni casi, però, è l’unico modo per definire alcune proprietà
complesse, come i template di alcuni controlli (che vedremo nel corso di questo capitolo).
I namespace
I namespace sono una feature del linguaggio di programmazione che consente di
organizzare meglio il proprio codice: definire un namespace significa definire un percorso
logico all’interno del quale è definita una classe. Come comportamento predefinito, Visual
Studio crea in automatico i namespace in base alla struttura dei file e delle cartelle: se il
namespace base di un’applicazione è WindowsPhoneApplication, ad esempio, le classi
contenute all’interno della cartella Converters apparterranno in automatico al namespace
WindowsPhoneApplication.Converters.
L’utilizzo dei namespace permette anche di avere classi con lo stesso nome all’interno
dello stesso progetto: in tal caso, per utilizzarle, sarà necessario specificare il percorso
completo al posto del semplice nome (ad esempio,
WindowsPhoneApplication.Converters.MyConverter).
I namespace che sono più frequentemente utilizzati nel codice possono essere aggiunti nel
code behind, in cima alla classe, tramite la keyword using: in questo modo è possibile
accedere alla classe contenuta in un namespace senza doverne specificare tutte le volte il
percorso completo.
Se, ad esempio, includiamo la dichiarazione:
using WindowsPhone.Converters;

potremo utilizzare la classe MyConverter semplicemente in questo modo:


MyConverter converter = new MyConverter();

A volte, però, si ha la necessità, ad esempio quando si devono utilizzare risorse o controlli


definiti in librerie esterne, di dichiarare i namespace anche nello XAML. In questo caso
devono essere aggiunti come attributi del nodo principale phone:PhoneApplicationPage,
utilizzando la sintassi xmlns: seguita dal nome che vogliamo dare al namespace. Ecco un
esempio di definizione di un namespace per accedere alle classi interne del progetto:
xmlns:converters="clr-namespace:WindowsPhone.Converters"

Mentre quello che segue è il namespace relativo a una libreria esterna:


xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

A questo punto, quando vogliamo accedere a uno degli oggetti presenti nel namespace,
non dobbiamo fare altro che utilizzare come prefisso il nome che abbiamo dato al
namespace. Ad esempio, per utilizzare il nostro MyConverter presente nel namespace
WindowsPhone.Converters useremo la seguente sintassi:
<converters:MyConverter />
I brush
I brush sono degli oggetti che permettono di disegnare e riempire delle aree: ad esempio,
ogni volta che impostiamo il colore di un testo o lo sfondo di un pulsante stiamo in realtà
utilizzando un brush.
Esistono, però, brush più complessi, che permettono di utilizzare immagini oppure di
ottenere effetti avanzati come i gradienti. Vediamoli in dettaglio.

SolidColorBrush
È la tipologia più semplice: l’area viene riempita con il colore specificato.
<Rectangle Fill="Red" />

Possiamo definirlo anche utilizzando la sintassi estesa:


<Rectangle Width="2⊘⊘" Height="2⊘⊘" x:Name="RectangleElement" >
<Rectangle.Fill>
<SolidColorBrush x:Name="RectangleColor" Color="Blue" />
</Rectangle.Fill>
</Rectangle>

LinearGradientBrush
Permette di definire un gradiente tra due o più colori, che viene realizzato utilizzando una
linea come separatore:
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Blue" Offset="⊘" />
<GradientStop Color="Red" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>

RadialGradiantBrush
Permette, anch’esso, di definire un gradiente, il cui effetto viene però realizzato partendo
da un punto focale per proseguire in maniera circolare:
<Rectangle>
<Rectangle.Fill>
<RadialGradientBrush>
<GradientStop Color="Blue" Offset="⊘" />
<GradientStop Color="Red" Offset="1" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>

ImageBrush
Viene utilizzata un’immagine per riempire l’area:
<Rectangle>
<Rectangle.Fill>
<ImageBrush ImageSource="brush.png" />
</Rectangle.Fill>
</Rectangle>

VideoBrush
Viene utilizzato un controllo MediaElement (in grado di riprodurre video):
<Rectangle>
<Rectangle.Fill>
<VideoBrush SourceName="video.wmv" />
</Rectangle.Fill>
</Rectangle>
Stili, risorse e template
Così come nell’HTML abbiamo la possibilità di definire degli stili, grazie ai CSS, che
possono essere riutilizzati in più parti del codice, anche lo XAML permette di determinare
stili e risorse, identificati da una chiave univoca, che possono essere utilizzati all’interno
dell’applicazione.
Entrambi sono inseriti all’interno della proprietà Resources, che è esposta da tutti i controlli
XAML, incluse le pagine e l’intera applicazione. Per capire il funzionamento di questa
proprietà dobbiamo ricordarci il concetto di XML e di nidificazione: una volta che
abbiamo specificato alcuni stili e risorse per un controllo, questi saranno accessibili dal
controllo stesso e da tutti quelli figli.
Tipicamente, però, stili e risorse vengono definiti a livello di pagina (per essere utilizzati
da tutti i controlli presenti nella vista) o a livello di applicazione (per essere utilizzati da
tutte le pagine).
Per essere utilizzati all’interno di una pagina devono essere definiti all’interno della
proprietà Resources della classe PhoneApplicationPage, in un punto qualsiasi dello XAML che sia
contenuto all’interno del nodo principale, rappresentato dal tag <phone:PhoneApplicationPage>.
<phone:PhoneApplicationPage.Resources>
<!-- stili e risorse -->
</phone:PhoneApplicationPage.Resources>

Se, invece, apriamo il file App.xaml, troveremo un contenitore già pronto (anche se
inizialmente vuoto) per le nostre risorse globali:
<Application.Resources>
<!-- stili e risorse -->
</Application.Resources>

Sia gli stili che le risorse sono identificati univocamente dal valore della proprietà x:Key,
che deve essere utilizzata in combinazione con la markup extension chiamata
StaticResource. Le markup extension sono espressioni complesse che, normalmente, sarebbe
necessario esprimere con del codice: sono racchiuse tra parentesi graffe e, nel corso di
questo capitolo, ne incontreremo delle altre.
Per assegnare al valore di una proprietà di un controllo uno stile o una risorsa si usa la
markup extension StaticResource seguita dal valore dell’attributo x:Key che abbiamo definito.
<TextBlock x:Name="ApplicationTitle" Text="Windows Phone" Style="
{StaticResource PhoneTextNormalStyle}"/>

Nell’esempio riportato il sistema operativo andrà a cercare uno stile identificato dal nome
PhoneTextNormalStyle e lo applicherà alla proprietà Style del controllo TextBlock.

Le risorse
Le risorse sono oggetti di qualsiasi tipo definiti direttamente nello XAML, che possono
essere valorizzati e riutilizzati da più controlli.
<phone:PhoneApplicationPage.Resources>
<System:String x:Key="Name">Windows Phone</System:String>
</phone:PhoneApplicationPage.Resources>

<TextBlock Text="{StaticResource Name}" />

Nell’esempio abbiamo definito come risorsa un oggetto di tipo string, il cui valore viene poi
passato alla proprietà Text di un controllo TextBlock tramite la markup extension StaticResource,
seguita dal nome univoco della risorsa (Name). Il risultato sarà la visualizzazione a video
della stringa Windows Phone.

Gli stili
Gli stili sono l’equivalente dei CSS in HTML e permettono di agire su una o più proprietà
di un controllo: nel momento in cui lo stile viene applicato al controllo, tutte quelle
proprietà assumeranno il valore che abbiamo definito.
Nel momento in cui definiamo uno stile, abbiamo la possibilità di specificare a quale
tipologia di controlli vogliamo applicarlo tramite la proprietà TargetType. Se impostiamo
come target una classe padre da cui derivano più controlli (ad esempio, la classe Control, da
cui derivano tutti i controlli XAML), potremo applicare lo stile a più controlli di diverso
tipo.
<phone:PhoneApplicationPage.Resources>
<Style x:Key="CustomText" TargetType="TextBlock">
<Setter Property="FontSize" Value="24" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</phone:PhoneApplicationPage.Resources>

<TextBlock Style="{StaticResource CustomText}" />

Una volta definito il target, possiamo specificare le proprietà su cui vogliamo agire tramite
il tag Setter, che richiede due attributi:
• Property, ovvero la proprietà da valorizzare;
• Value, ovvero il valore che dovrà avere la proprietà.
Una volta definito lo stile, possiamo utilizzarlo valorizzando la proprietà Style di un
controllo, sempre tramite la markup extension StaticResource.

Gli stili impliciti


Windows Phone 7.5 ha introdotto il supporto agli stili impliciti, ovvero stili “anonimi” che
non hanno una proprietà x:Key che li identifica univocamente.
In questo caso lo stile sarà applicato a tutti i controlli che soddisfano il TargetType, in base
alla posizione in cui questo è stato definito: a livello di controllo, di pagina o di
applicazione.
<phone:PhoneApplicationPage.Resources>
<StyleTargetType="TextBlock">
<Setter Property="FontSize" Value="24" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</phone:PhoneApplicationPage.Resources>
In questo esempio tutti i controlli di tipo TextBlock inseriti all’interno della pagina
adotteranno lo stile dichiarato.

I template
I controlli XAML sono caratterizzati da un template che ne definisce l’aspetto visuale;
ognuno di essi è dotato di un template di default, che non contiene solo gli elementi visuali
che lo rappresentano, ma anche la definizione dei vari stati in cui si può trovare. Grazie
allo XAML siamo in grado di ridefinire il template di ogni controllo, sfruttando la
proprietà Template.
Il modo in cui viene definito un template è analogo a quello di una risorsa o di uno stile: si
definisce, all’interno delle risorse, un ControlTemplate che contiene, al suo interno, lo XAML
con cui deve essere visualizzato. In più, abbiamo a disposizione il controllo ContentPresenter,
che funge da contenitore per il valore assegnato alla proprietà Content del controllo.
<phone:PhoneApplicationPage.Resources>
<ControlTemplate x:Key="CustomTemplate" TargetType="Button">
<StackPanel HorizontalAlignment="Center">
<ContentPresenter />
</StackPanel>
</ControlTemplate>
</phone:PhoneApplicationPage.Resources>

In questo esempio abbiamo creato un template per i pulsanti che si limita a mostrare il
valore della proprietà Content allineato centralmente, senza aggiungere, ad esempio, il bordo
previsto dal template di default.
Per utilizzarlo non dobbiamo fare altro che valorizzare la proprietà Template per utilizzare la
risorsa identificata dal valore della proprietà x:Key.
<Button Template="{StaticResource CustomTemplate}" Content="Click me!" />

Gestire gli stati di un controllo


Nel processo di creazione di un template ci viene in aiuto Blend, il tool di design di
Microsoft. Il template che abbiamo definito nell’esempio precedente è piuttosto semplice
e non prende in considerazione un aspetto molto importante di un controllo: gli stati.
Un controllo, infatti, si può trovare in diversi stati, a seconda dell’interazione dell’utente,
e, di conseguenza, può avere un aspetto differente: l’esempio più semplice ci è dato dal
controllo Button, che presenta uno stile diverso a seconda che il bottone sia premuto o
meno.
Grazie a Blend siamo in grado di creare una copia del template base di un controllo
(inclusi tutti gli stati) e di lavorare su quella. Vediamo, ad esempio, come, grazie a Blend,
siamo in grado di cambiare l’effetto che viene applicato quando il pulsante viene premuto:
il colore bianco di cui è riempito il controllo diventerà rosso.
Visual Studio ci offre una comoda scorciatoia per aprire Blend: facendo clic con il tasto
destro sul progetto, troveremo l’opzione Open In Expression Blend. Si aprirà
l’interfaccia dell’applicazione con precaricato il progetto stesso. Ora inseriamo un
controllo Button all’interno di una pagina (tramite XAML o trascinandolo dalla sezione
Controls del menu Assets); facendo clic con il tasto destro su di esso, troviamo l’opzione
Edit Template, che ci offre due possibilità:
1. Create a copy è la funzionalità maggiormente utilizzata, in quanto crea una copia del
template di default, dandoci l’opportunità di modificare solo le proprietà desiderate;
2. Create empty crea invece un template completamente vuoto.
In entrambi i casi ci sarà chiesto dove vogliamo definire il template: a livello applicativo,
nella pagina stessa o in un file di risorse esterno.
Nel nostro caso utilizziamo la funzionalità Create a copy: indipendentemente da dove
abbiamo generato il nostro template, Blend ci mostrerà automaticamente una
rappresentazione visuale del controllo. Se guardiamo lo XAML che è stato generato,
noteremo un nuovo stile, con al suo interno tutte le proprietà che definiscono l’aspetto di
default di un bottone, tra cui il template.
Possiamo notare subito che la definizione della proprietà Template è piuttosto complessa e
passa attraverso l’utilizzo di un VisualStateManager: questa classe rappresenta uno dei punti di
forza dello XAML e consente di gestire l’aspetto visuale che avrà un controllo nei vari
stati. Nel momento in cui il controllo cambia stato, è il VisualState-Manager a variarne
automaticamente l’aspetto.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="⊘" Value="{StaticResource PhoneBackgroundBrush}"/>
</ObjectAnimationUsingKeyFrames><ObjectAnimationUsingKeyFrames Storyboard.
TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="⊘"
Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
<DiscreteObjectKeyFrame KeyTime="⊘"
Value="{StaticResource PhoneForegroundBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Il VisualStateManager contiene una serie di VisualState, ognuno dei quali rappresenta uno stato
(che possono eventualmente essere raggruppati in gruppi dal nome VisualStateGroup).
Quando uno stato non contiene alcuna definizione al suo interno, significa che deve essere
utilizzato il template di default: nell’esempio, quando il pulsante si trova nello stato Normal
(ovvero l’utente non sta interagendo con esso), non viene applicata alcuna modifica.
Quando, invece, troviamo uno Storyboard al suo interno, significa che viene messa in atto
un’animazione, che va a modificare alcune proprietà del controllo. Nell’esempio,
l’animazione va ad agire sulle proprietà Foreground e Background, assegnando loro uno stile
differente: testo bianco con sfondo nero quando non è premuto, testo nero su sfondo
bianco quando l’utente ci fa tap sopra.
Come potete notare, il codice generato è piuttosto articolato e difficile da ricordare: ecco
perché Blend ci viene in aiuto, semplificandoci il lavoro. Nel momento in cui siamo in
modalità di modifica di un template, tramite la finestra States, possiamo visualizzare tutti
gli stati possibili (suddivisi in eventuali gruppi): cliccando su uno di essi, ci viene mostrato
in tempo reale l’aspetto del controllo e, nella finestra Objects & Timeline, la
rappresentazione ad albero dei controlli e delle animazioni incluse in quello stato.

Figura 2.10 - L’editing del template di un controllo in Blend.

Se ci posizioniamo nello stato Pressed ed espandiamo la struttura ad albero della sezione


Objects & Timeline, potremo vedere una rappresentazione visuale delle animazioni che
abbiamo visto poco fa nello XAML. Dato che abbiamo deciso di cambiare lo sfondo del
pulsante da bianco a rosso, l’animazione che ci interessa è quella che coinvolge la
proprietà Background: clicchiamoci sopra e, nel pannello laterale delle proprietà, ci sarà il
Value, ovvero il valore che deve avere quella specifica proprietà alla fine dell’animazione.
Cambiamo il valore che è inserito in Red e il gioco è fatto: se andiamo a vedere lo XAML
generato per il pulsante da cui siamo partiti per creare un nuovo template, questo sarà stato
adattato per utilizzarlo in automatico.
<Button Content="Click me" Style="{StaticResource ButtonStyle1}" />

I DataTemplate
Un tipo particolare di template sono i DataTemplate, utilizzati in tutti quei controlli che
consentono la visualizzazione di collezioni di elementi, come la ListBox. Il template, infatti,
definisce il layout che deve avere un singolo elemento e che sarà poi ripetuto
automaticamente per ognuno degli elementi facenti parte della collezione. Una possibilità
è quella di definire il template inline, ovvero direttamente nella definizione del controllo:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="Questo è un testo" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Questo approccio può però rendere più difficoltoso da leggere il markup di una pagina,
soprattutto in presenza di template molto complessi. In altri casi, abbiamo la necessità di
riutilizzare lo stesso template in più controlli; abbiamo perciò la possibilità di definire un
template come risorsa riutilizzabile, esattamente come abbiamo fatto in precedenza per gli
stili.
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="PersonTemplate">
<stackPanel>
<TextBlock Text="Questo è un testo" />
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>

A questo punto, la definizione della ListBox vista in precedenza può diventare la seguente:
<ListBox ItemTemplate="{StaticResource PersonTemplate}" />

Il template in questo esempio, in realtà, è di dubbia utilità, dato che contiene un testo fisso,
che sarebbe ripetuto per ognuno degli elementi della lista. Vedremo a breve un esempio
reale di utilizzo di un template: prima, però, dobbiamo introdurre il concetto di binding,
che sarà trattato nel paragrafo successivo.

Creare un file di risorse esterno


Nell’ottica di rendere il codice di un’applicazione più facile da mantenere è possibile
definire stili, risorse e template, non solo all’interno di una pagina o dell’applicazione, ma
anche in un file esterno, che può essere poi importato in fase di inizializzazione.
Tecnicamente, questo file prende il nome di ResourceDictionary: si tratta semplicemente di un
file XML, il cui nodo principale è appunto ResourceDictionary, che al suo interno contiene la
definizione di stili, risorse e template.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2⊘⊘6/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2⊘⊘6/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2⊘⊘8"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2⊘⊘" mc:Ignorable="d">

<Style x:Key="CustomText" TargetType="TextBlock">


<Setter Property="FontSize" Value="24" />
<Setter Property="FontWeight" Value="Bold" />
</Style>

</ResourceDictionary>

Per aggiungere un file di risorse al progetto è sufficiente fare clic con il tasto destro,
scegliere Add new item e selezionare la tipologia XML file, all’interno del quale dovrete
includere un blocco identificato dal tag <ResourceDictionary> (come mostrato nell’esempio di
codice sopra riportato). A questo punto possiamo includere il file di risorse nella pagina o
a livello di applicazione, sfruttando il tag ResourceDictionary e specificando nell’attributo
Source il percorso del file, come nell’esempio:
<phone:PhoneApplicationPage.Resources>
<ResourceDictionary Source="Resources.xaml" />
</phone:PhoneApplicationPage.Resources>

I file di risorse esterni da includere, in alcuni casi, potrebbero essere più di uno: spesso gli
sviluppatori, per organizzare al meglio il proprio codice, creano, ad esempio, un file
separato per risorse, stili e template. In tal caso dobbiamo affidarci a una proprietà dei
ResourceDictionary chiamata MergedDictionaries. Utilizzando questo meccanismo, tutti i file di
risorsa definiti all’interno di tale proprietà, in fase di esecuzione, saranno uniti e caricati in
memoria: il risultato è che la pagina (o l’intera applicazione, a seconda del contesto) sarà
in grado di accedere a tutte le risorse, agli stili e ai template definiti nei vari file.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml" />
<ResourceDictionary Source="Styles.xaml" />
<ResourceDictionary Source="Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

Le trasformazioni
Le trasformazioni in XAML consentono di applicare degli effetti a un controllo,
cambiando il modo in cui esso viene rappresentato sullo schermo. Il vantaggio delle
trasformazioni è che permettono di agire sullo stesso controllo senza doverne cambiare
tutte le proprietà, ma solamente quelle che ci interessano.
Tramite la proprietà RenderTransform, disponibile in ogni controllo, possiamo applicare una
delle tante trasformazioni disponibili:
<Rectangle Width="2⊘⊘" Height="2⊘⊘" Fill="Blue">
<Rectangle.RenderTransform>
<RotateTransform Angle="4⊘" />
</Rectangle.RenderTransform>
</Rectangle>

In questo esempio a un rettangolo viene applicata una RotateTransform, che fa sì che il


controllo venga ruotato dell’angolo specificato.
Figura 2.11 - Un rettangolo a cui viene applicata una rotazione.

Esistono diverse tipologie di trasformazione.

RotateTransform
È la trasformazione di cui abbiamo appena visto un esempio; applica una rotazione
secondo uno specifico angolo.

SkewTransform
Questa trasformazione applica un effetto obliquo, dati i due angoli X e Y su cui agire.
<Rectangle Width="2⊘⊘" Height="2⊘⊘" Fill="Blue">
<Rectangle.RenderTransform>
<SkewTransform AngleX="-3⊘" AngleY="11"/>
</Rectangle.RenderTransform>
</Rectangle>

Figura 2.12 - Una trasformazione di tipo SkewTransform.

ScaleTransform
Questa trasformazione consente di variare le dimensioni X e Y del controllo.
<Rectangle Width="2⊘⊘" Height="2⊘⊘" Fill="Blue">
<Rectangle.RenderTransform>
<ScaleTransform ScaleX="1.85" ScaleY="1.⊘5"/>
</Rectangle.RenderTransform>
</Rectangle>

Figura 2.13 - Una trasformazione di tipo ScaleTransform.

TranslateTransform
Questa trasformazione consente di muovere il controllo nello spazio, dato lo spostamento
rispetto agli assi X e Y.
<Rectangle Width="2⊘⊘" Height="2⊘⊘" Fill="Blue">
<Rectangle.RenderTransform>
<TranslateTransform X="75" Y="1⊘⊘"/>
</Rectangle.RenderTransform>
</Rectangle>
Figura 2.14 - Una trasformazione di tipo TranslateTransform.

CompositeTransform
È possibile, infine, sommare insieme più trasformazioni, applicando così più effetti
contemporaneamente a un controllo.
Questa trasformazione espone una serie di proprietà per specificare i valori delle
trasformazioni viste in precedenza.
<Rectangle Width="2⊘⊘" Height="2⊘⊘" Fill="Blue">
<Rectangle.RenderTransform>
<CompositeTransform Rotation="14" SkewY="16" SkewX="19" TranslateX="-19"
TranslateY="6⊘"/>
</Rectangle.RenderTransform>
</Rectangle>

Nell’esempio, al rettangolo vengono applicate tre trasformazioni: RotationTransform,


SkewTransform e TranslateTransform.
Figura 2.15 - Tramite una CompositeTransform vengono applicate tre trasformazioni differenti.

Creare trasformazioni con Blend


Anche in questo caso, Blend può essere un valido aiuto: dal pannello delle proprietà di un
controllo abbiamo infatti accesso a una sezione chiamata Transform, dove, in maniera
visuale, possiamo applicare le trasformazioni che abbiamo visto; queste saranno effettuate
in tempo reale, consentendoci così di vedere il risultato finale senza dover testare
l’applicazione sull’emulatore o sul device.

Figura 2.16 - Il pannello di gestione delle trasformazioni di Blend.

Ognuno dei tab disponibili corrisponde a una tipologia di trasformazione: cliccando su una
delle proprietà (ad esempio, Angle in una RotateTransform) e muovendo il mouse, tenendo
contemporaneamente premuto il tasto sinistro, il valore sarà cambiato in tempo reale.
Le animazioni
Un altro punto di forza dello XAML è la capacità di gestire animazioni, le quali vengono
espresse tramite una Storyboard che permette di andare a specificare per un controllo:
• la proprietà che vogliamo modificare;
• il valore di partenza e quello finale;
• la durata dell’animazione.
<Storyboard x:Name="Animation">
<DoubleAnimation Storyboard.TargetName="RectangleElement"
Storyboard.TargetProperty="Width"
From="2⊘⊘"
To="4⊘⊘"
Duration="⊘⊘:⊘⊘:⊘4" />
</Storyboard>

In questo esempio abbiamo definito un’animazione che va a cambiare il valore della


proprietà Width (tramite l’attributo Storyboard.TargetProperty) di un controllo chiamato
RectangleElement (tramite l’attributo Storyboard.TargetName). La durata dell’animazione è di
quattro secondi: durante questo intervallo di tempo, il valore della proprietà Width del
rettangolo sarà cambiata da 200 a 400.
DoubleAnimation è una delle tipologie di animazioni disponibili, che si applica alle proprietà
di un controllo che sono espresse tramite un valore numerico (la larghezza, l’altezza, la
trasparenza ecc.).
Un’altra tipologia di animazione è la ColorAnimation, che permette di agire sul colore di
un controllo.
<Rectangle Width="20⊘" Height="2⊘⊘" x:Name="RectangleElement" >
<Rectangle.Fill>
<SolidColorBrush x:Name="RectangleColor" Color="Blue" />
</Rectangle.Fill>
</Rectangle>

Nell’esempio abbiamo definito il colore di riempimento del rettangolo tramite un


SolidColorBrush, associato alla proprietà Fill.

Possiamo, di conseguenza, applicare un’animazione di tipo ColorAnimation al brush per fargli


cambiare colore:
<Storyboard x:Name="Animation">
<ColorAnimation Storyboard.TargetName="RectangleColor"
Storyboard.TargetProperty="Color"
To="Red"
Duration="⊘⊘:⊘⊘:⊘4"
/>
</Storyboard>

Nell’esempio, il colore del rettangolo viene cambiato da blu a rosso tramite un’animazione
della durata di quattro secondi.
Esistono, infine, le PointAnimation, che si applicano a tutte le proprietà di tipo vettoriale che
definiscono le coordinate di un punto. Queste animazioni consentono di cambiare le
coordinate di un punto, simulandone perciò il movimento.
<Path Fill="Red">
<Path.Data>
<EllipseGeometry x:Name="Ellipse"
Center="5⊘,5⊘"
RadiusX="2⊘"
RadiusY="2⊘"
/>
</Path.Data>
</Path>

In questo esempio è stata definita un’ellisse di colore rosso, posizionata in un punto


preciso dello schermo: le coordinate sono 50,50, definite nella proprietà Center. Tramite una
PointAnimation possiamo variare il valore di questa proprietà, muovendo perciò l’ellisse sullo
schermo:
<Storyboard x:Name="Animation">
<PointAnimation Storyboard.TargetName="Ellipse"
Storyboard.TargetProperty="Center"
From="5⊘, 5⊘"
To="1⊘⊘, 1⊘⊘"
Duration="⊘⊘:⊘⊘:⊘4" />
</Storyboard>

Come comportamento predefinito, le animazioni applicano la trasformazione in maniera


proporzionale rispetto alla durata della stessa. Abbiamo, però, la possibilità di interagire
con l’animazione per definire un comportamento diverso, determinando il valore che la
proprietà dovrà avere in un dato frame dell’animazione.
In questo caso, ognuna delle tipologie di animazione che abbiamo visto in precedenza
espone un equivalente con il suffisso UsingKeyFrames (ad esempio, DoubleAnimation-
UsingKeyFrames). Invece di specificare il valore iniziale, il valore finale e la durata
dell’animazione, si utilizzano una serie di KeyFrame per indicare il valore che avrà la
proprietà in un determinato momento.
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="RectangleElement"
Storyboard.TargetProperty="Width">
<LinearDoubleKeyFrame KeyTime="⊘⊘:⊘⊘:⊘⊘" Value="2⊘⊘" />
<LinearDoubleKeyFrame KeyTime="⊘⊘:⊘⊘:⊘2" Value="25⊘" />
<LinearDoubleKeyFrame KeyTime="⊘⊘:⊘⊘:⊘4" Value="5⊘⊘" />
</DoubleAnimationUsingKeyFrames>

Nell’esempio agiamo sempre sulla proprietà Width di un rettangolo, ma definendo come


sarà l’animazione: il valore iniziale della proprietà sarà 200; al secondo 2 sarà invece 250;
al termine dell’animazione (dopo quattro secondi) sarà 500. All’interno di uno storyboard
possiamo specificare più animazioni differenti: in tal caso, saranno eseguite tutte quante in
parallelo. Se, ad esempio, creassimo uno storyboard di questo tipo:
<Storyboard x:Name="Animation">
<DoubleAnimation Storyboard.TargetName="RectangleElement"
Storyboard.TargetProperty="Width"
From="2⊘⊘"
To="4⊘⊘"
Duration="⊘⊘:⊘⊘:⊘4" />
<ColorAnimation Storyboard.TargetName="RectangleColor"
Storyboard.TargetProperty="Color"
To="Red"
Duration="⊘⊘:⊘⊘:⊘4"
/>
</Storyboard>

nell’arco di quattro secondi, il rettangolo diventerebbe più largo di 200px e, allo stesso
tempo, cambierebbe colore da blu a rosso.

Creare le animazioni con Blend


Blend ci viene in aiuto nella creazione di animazioni grazie a una rappresentazione visuale
dello Storyboard: lungo una linea temporale sono disposti i vari frame chiave, nei quali
possiamo andare a modificare l’aspetto del controllo. In automatico Blend genererà il
codice XAML necessario per rappresentare l’animazione.
Vediamo un esempio, sempre facendo riferimento al controllo Rectangle usato nelle demo
precedenti.
Subito sotto l’header del pannello Objects and Timeline troviamo un menu a tendina che
ci permette di accedere a tutte le animazioni definite nella pagina o nell’applicazione. Con
il pulsante + posizionato nella parte destra possiamo andare a creare un nuovo storyboard.

Figura 2.17 - La gestione degli storyboard.

Una volta creato, lo storyboard apparirà nella sezione Objects and Timeline, la timeline
dell’animazione, con in cima l’indicazione dei secondi trascorsi. Ora possiamo
posizionare il cursore giallo in un punto qualsiasi della timeline, e quindi cambiare il
valore di una qualsiasi delle proprietà di un controllo. In automatico Blend definirà
l’animazione necessaria per cambiare il valore della proprietà da quello iniziale a quello
specificato.
Ad esempio, proviamo a cliccare sul rettangolo blu e a cambiare la proprietà Width dopo
aver posizionato il cursore sui tre secondi: Blend creerà un’animazione della durata di tre
secondi che cambierà la larghezza del rettangolo in base ai valori che abbiamo impostato.
Tramite i controlli di riproduzione posti in cima alla timeline possiamo avviare
l’animazione direttamente dentro Blend, così da poter vedere il risultato finale senza dover
avviare l’applicazione. In più, spostando il cursore giallo lungo la timeline, vedremo il
controllo nella vista principale cambiare per mostrare lo stato che avrà in quello specifico
frame dell’animazione.
Figura 2.18 - La timeline di Blend per gestire le animazioni.

Controllare le animazioni
Finora abbiamo semplicemente definito le animazioni: se avviassimo l’applicazione,
questa non verrebbe eseguita.
Il modo più semplice per avviare un’animazione è da codice: l’animazione è identificata
da un nome univoco, esattamente come tutti gli altri controlli, definito nella proprietà
x:Name. Dal code behind abbiamo perciò accesso all’oggetto che rappresenta l’animazione,
che espone diversi metodi per gestirne lo stato.
private void Button_Click(object sender, RoutedEventArgs e)
{
Animation.Begin();
}

Nell’esempio, alla pressione di un pulsante, diamo avvio all’animazione definita nello


XAML tramite il metodo Begin. Abbiamo a disposizione anche i metodi Pause e Stop per
mettere in pausa o terminare anticipatamente l’animazione in corso.
Esiste, però, anche un altro meccanismo che ci permette di non dover scrivere del codice
nel code behind: i behavior, ovvero un oggetto che viene associato a un controllo e che è
in grado di eseguire delle operazioni in automatico al verificarsi di un determinato evento.
Esistono behavior per far cambiare stato al Visual State Manager di un controllo, per
navigare da una pagina a un’altra, per riprodurre un suono ecc.
Uno dei behavior disponibili si chiama ControlStoryboardAction e permette di controllare la
riproduzione di un’animazione in seguito al verificarsi di un determinato evento. Per
utilizzare i behavior dobbiamo aggiungere una reference alle librerie
System.Windows.Interactivity e Microsoft.Expression.Interactions, presenti nell’SDK di Windows Phone.
Poi occorre dichiarare nello XAML i namespace per accedere ai behavior:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:eim="clr-namespace:Microsoft.Expression.Interactivity.
Media;assembly=Microsoft.Expression.Interactions"

Ecco un esempio di come controllare un’animazione tramite un behavior:


<Rectangle Width="2⊘⊘" Height="2⊘⊘" x:Name="RectangleElement">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<eim:ControlStoryboardAction Storyboard="{StaticResource Animation}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>

All’interno del nodo Triggers viene definito il trigger a cui deve essere associato il behavior,
ovvero la tipologia di evento, tramite la proprietà EventName. Nell’esempio, l’evento
MouseLeftButtonDown corrisponde al tap dell’utente sul rettangolo. Se non specifichiamo alcun
valore per la proprietà EventName, il behavior sarà automaticamente eseguito all’avvio.
Dopodiché, all’interno dell’EventTrigger, definiamo il behavior vero e proprio, nel nostro
caso un ControlStoryboardAction, al quale passiamo nella proprietà Storyboard l’animazione che
dobbiamo eseguire (l’oggetto Storyboard definito in precedenza come risorsa).

Gestire i behavior con Blend


Anche in questo caso Blend può creare lo XAML necessario a definire un behavior per
noi. Nella finestra Assets troviamo infatti, tra le voci disponibili, la categoria Behaviors,
con l’elenco di tutti i behavior disponibili.

Figura 2.19 - I behavior disponibili in Blend.


Figura 2.20 - Le proprietà di un behavior di tipo ControlStoryboardAction.

Per associare un behavior a un controllo è sufficiente utilizzare il drag and drop e


trascinare il behavior desiderato sopra il controllo nella finestra Objects and timeline.
Nella struttura ad albero il behavior sarà mostrato come elemento contenuto all’interno del
controllo stesso: cliccando sul behavior, il pannello Properties ci permetterà di definirne il
comportamento.
Nella sezione Trigger del pannello possiamo scegliere dal menu a tendina EventName
l’evento che vogliamo associare al behavior; nella sezione Common Properties possiamo
specificare, invece, le proprietà del behavior. Nel caso di un ControlStoryboardAction (come
quello mostrato in Figura 2.16), possiamo definire quale storyboard deve essere gestito
con il behavior e quale azione dello storyboard deve essere eseguita.
Il binding
Il binding è una delle caratteristiche concettualmente più complesse da capire per chi
arriva da altri framework di sviluppo, ma, allo stesso tempo, anche una delle più potenti
del linguaggio XAML.
Il binding è la possibilità di collegare tra loro controlli con oggetti nel codice (o con altri
controlli), creando così un flusso di comunicazione che fa sì che, a ogni variazione
dell’uno, i cambiamenti siano rispecchiati dall’altro, e viceversa.
Il binding viene espresso tramite una markup extension: utilizzare la markup extension
Binding per valorizzare la proprietà di un controllo significa creare un canale di
comunicazione tra quella proprietà e gli oggetti nel codice. In un’espressione di binding
vengono coinvolti un source (la sorgente dati: ad esempio, un oggetto dichiarato nel
codice) e il target (il controllo della UI che interagisce con la sorgente).

Le modalità di binding
Esistono tre modalità diverse per controllare il canale che viene creato in un binding,
tramite l’attributo Mode:
• OneWay: è il comportamento di default; ciò significa che, quando si usa questa
modalità, è possibile omettere l’attributo Mode. In questo tipo di binding le
modifiche apportate alla sorgente dati (il source) vengono intercettate dal controllo
target, ma non viceversa;
• TwoWay: in questa modalità viene creato un canale bidirezionale tra il source e il target:
le modifiche apportate a uno vengono intercettate dall’altro, e viceversa.
<TextBox Text="{Binding Name, Mode=TwoWay}" />

In questo esempio il valore della proprietà Name viene visualizzato in una casella di testo:
grazie alla modalità TwoWay, però, se l’utente modifica il contenuto della casella di testo,
sarà modificata anche la proprietà Name collegata;
• OneTime: in questa modalità l’espressione di binding viene valutata una volta sola;
eventuali modifiche successive alla sorgente dati non saranno intercettate dal
controllo.

Il binding tra controlli


Vediamo un semplice esempio di come questo approccio semplifichi la scrittura di
applicazioni: ipotizziamo di avere un controllo Slider, che l’utente può trascinare con il dito
per selezionare in maniera visuale un valore. Il nostro obiettivo è quello di mostrare sullo
schermo il valore selezionato dall’utente.
Figura 2.21 - Il controllo slider.

Normalmente, dovremmo svolgere l’operazione su due fronti: lo XAML e il code behind.


Nello XAML andremmo a inserire i due controlli richiesti:
<StackPanel>
<Slider x:Name="Volume" ValueChanged="Volume_ValueChanged" />
<TextBlock x:Name="SliderValue" />
</StackPanel>

Ci siamo sottoscritti all’evento ValueChanged del controllo, che viene scatenato ogni
qualvolta l’utente interagisce con lo slider e ne cambia il valore.
Nel code behind, andiamo a gestire questo evento:
private void Volume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
SliderValue.Text = Volume.Value.ToString();
}

Viene recuperato il valore dello slider (tramite la proprietà Value), mostrato sullo schermo
tramite il controllo TextBlock inserito.
Possiamo ottenere lo stesso risultato utilizzando il binding e senza scrivere una riga di
codice nel code behind:
<StackPanel>
<Slider x:Name="Volume" />
<TextBlock x:Name="SliderValue" Text="{Binding ElementName=Volume, Path=Value}" />
</StackPanel>

Nell’espressione di binding specifichiamo il nome del controllo a cui ci vogliamo


collegare tramite l’attributo ElementName, dopodiché, tramite l’attributo Path, specifichiamo
qual è la proprietà del controllo che vogliamo recuperare. Il tutto viene inserito all’interno
della proprietà Text del controllo TextBlock, che sarà perciò valorizzata con il risultato di
questa espressione e, di conseguenza, mostrata a video.

Il binding tra controlli e oggetti


Una delle funzionalità più utili del binding, però, è la possibilita di collegare i controlli
della UI con oggetti definiti nel nostro codice; questo è reso possibile dalla proprietà
DataContext, che rappresenta il contesto del binding: una volta dichiarato, ad esempio, un
oggetto come DataContext di un controllo, il controllo stesso e tutti quelli contenuti al suo
interno saranno in grado di accedere alle proprietà pubbliche esposte dall’oggetto.
Vediamo un esempio, ipotizzando di voler mostrare le informazioni relative a una persona
tramite alcuni blocchi di testo. Normalmente assegneremmo un nome tramite la proprietà
x:Name ai controlli TextBlock, dopodiché, dal code behind, accederemmo alla proprietà Text e
la valorizzeremmo con l’informazione che vogliamo mostrare.
Vediamo, invece, come ottenere lo stesso risultato con il binding: innanzitutto dobbiamo
creare delle classi per gestire le informazioni sulla persona.
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}

La classe Person espone due proprietà testuali di tipo string e una di tipo Address, che è
un’altra classe (definita sotto) ed è utilizzata per memorizzare la città e l’indirizzo della
persona. A questo punto, andiamo a creare nel costruttore della pagina una nuova istanza
di questa classe, inserendo dei valori fittizi:
public MainPage()
{
InitializeComponent();

Person person = new Person();


person.Name = "Matteo";
person.Surname = "Pagani";

Address address = new Address();


address.City = "Como";
address.Street = "Una via a caso";
person.Address = address;

Author.DataContext = person;
}

La “magia” è creata dall’ultima riga di codice, in cui la proprietà DataContext di un controllo


di nome Author (vedremo a breve di cosa si tratta) viene valorizzata con l’istanza della
classe Person appena creata. Ed ecco, infine, lo XAML dell’interfaccia grafica:
<StackPanel x:Name="Author">
<TextBlock Text="Nome" />
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="Cognome" />
<TextBlock Text="{Binding Path=Surname}" />
<TextBlock Text="City" />
<TextBlock Text="{Binding Path=Address.City}" />
</StackPanel>

Author è un controllo di tipo StackPanel: valorizzando la proprietà DataContext con l’oggetto


person, abbiamo dato la possibilità a tutti i controlli inseriti al suo interno di accedere alle
proprietà esposte dalla classe tramite binding, sfruttando l’attributo Path. In realtà, in questi
casi è possibile anche omettere l’attributo Path e specificare solamente la proprietà, come
nell’esempio seguente:
<TextBlock Text="{Binding Name}" />

Il risultato sarà assolutamente identico.


Le espressioni di binding che vediamo nell’esempio vanno a recuperare il valore della
specifica proprietà del nostro oggetto e lo inseriscono nella proprietà Text di un controllo
TextBlock, facendo sì che il testo sia visualizzato sullo schermo.

L’interfaccia INotifyPropertyChanged
Quando si parla di binding, non si può non menzionare l’interfaccia INotifyProperty-Changed,
che costituisce l’attore principale quando si vuole far sì che i cambiamenti degli elementi
nel source del binding (la sorgente dati) siano automaticamente trasmessi al controllo
target, senza alcun intervento da parte nostra.
Prendiamo in considerazione l’esempio che abbiamo appena visto: il codice che abbiamo
scritto funziona in quanto l’oggetto viene creato in fase di inizializzazione della pagina,
perciò quando lo XAML viene renderizzato ed è già pronto per essere utilizzato. Se
avessimo inserito un pulsante per variare una delle proprietà dell’oggetto in un secondo
momento (ad esempio, il nome della persona), la modifica non si sarebbe propagata fino al
controllo: il nome mostrato sullo schermo sarebbe perciò rimasto invariato. Questo perché
la classe Person non implementa l’interfaccia INotifyProperty-Changed, la quale permette di
gestire un evento che dobbiamo scatenare ogni qualvolta il valore di una proprietà è stato
modificato.
Ecco come cambia la classe Person in seguito a questa modifica:
public class Person: INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
NotifyPropertyChanged("Name");
}
}

}
public string Surname { get; set; }
public Address Address { get; set; }

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(string info)


{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}

Dopo aver implementato l’interfaccia INotifyPropertyChanged, abbiamo a disposizione un


handler chiamato PropertyChanged, che dobbiamo invocare ogni qualvolta viene cambiato il
valore di una proprietà. Per raggiungere questo scopo definiamo un metodo, chiamato
NotifyPropertyChanged, che accetta come parametro il nome della proprietà che è stata
cambiata.
Nell’esempio la proprietà Name è stata modificata per implementare questo meccanismo di
notifica: ogni qualvolta ne viene cambiato il valore (nel blocco di codice identificato dalla
parola chiave set) andiamo a scatenare l’evento NotifyPropertyChanged. Quando si verifica
questa condizione, a tutti i controlli che sono in binding con la proprietà che è cambiata
viene notificato il cambiamento e, quindi, essi sono automaticamente aggiornati per
utilizzare il nuovo valore.
Ora che abbiamo modificato opportunamente il nostro codice, lo scenario
precedentemente ipotizzato (il pulsante per variare il nome della persona) funzionerà
correttamente.

Il binding di collezioni
Il binding viene usato frequentemente anche quando si lavora con controlli in grado di
gestire collezioni, come le ListBox. Questi controlli sono in grado, dato il template con cui
dovrà essere visualizzato un singolo elemento, di gestire la visualizzazione delle
collezioni, indipendentemente dal numero di elementi da cui sono composte.
Queste tipologie di controlli mettono a disposizione la proprietà ItemsSource, che rappresenta
la collezione di dati che è in binding e che deve essere visualizzata.
Abbiamo già visto come si definiscono i template: se ricordate, però, l’esempio riportato
non poteva essere utilizzato in uno scenario reale, dato che conteneva semplicemente un
testo fisso; non era prevista la possibilità di recuperare informazioni dagli oggetti della
collezione.
Ecco che, anche in questo caso, entra in gioco il binding: nel momento in cui definiamo la
proprietà ItemsSource, la collezione di dati diventa il data context del controllo. Nel template
possiamo perciò accedere alle proprietà esposte dal singolo elemento della collezione.
Riprendendo l’esempio precedente, ipotizziamo di voler visualizzare tramite una ListBox
una collezione di oggetti di tipo Person:
Person person1 = new Person();
person1.Name = "Mario";
person1.Surname = "Rossi";

Person person2 = new Person();


person2.Name = "Carlo";
person2.Name = "Bianchi";

List<Person> persons = new List<Person>();


persons.Add(person1);
persons.Add(person2);

Persons.ItemsSource = persons;

Nel codice abbiamo creato due oggetti di tipo Person e li abbiamo aggiunti all’interno di
una collezione di tipo List<Person>, che abbiamo assegnato alla proprietà ItemsSource del
controllo ListBox. A questo punto, la classe Person diventa il data context del template che
definiamo nello XAML, il quale potrà quindi accedere tramite binding alle proprietà
esposte.
<ListBox x:Name="Persons">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="Nome" />
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="Cognome" />
<TextBlock Text="{Binding Path=Surname}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Il risultato sarà la visualizzazione delle proprietà Name e Surname per ognuno degli oggetti di
tipo Person contenuti nella collezione (nell’esempio saranno due, Mario Rossi e Carlo
Bianchi).

La collezione ObservableCollection
Nel mondo XAML si usa frequentemente un tipo particolare di collezione, chiamato
ObservableCollection: per quanto riguarda l’utilizzo, si tratta di una normale collezione dati,
che si comporta esattamente come la collezione List. È una collezione fortemente tipizzata
(ovvero, tramite generics, occorre specificare il tipo di dati che andremo a inserire) e
implementa l’interfaccia IEnumerable: è possibile, perciò, tramite LINQ, eseguire query più o
meno complesse per effettuare operazioni sui dati.
La particolarità, però, è che essa implementa anche l’interfaccia INotifyPropertyChanged: ogni
qualvolta andremo ad apportare una modifica qualsiasi alla lista (aggiunta o rimozione di
un elemento, modifica dell’ordine dei dati ecc.), il controllo in binding con essa si
aggiornerà automaticamente per riflettere i cambiamenti.
public partial class MainPage : PhoneApplicationPage
{
private ObservableCollection<Person> persons;

// Constructor
public MainPage()
{
InitializeComponent();

persons = new ObservableCollection<Person>();

Persons.ItemsSource = persons;
}

private void OnAddElementClicked(object sender, RoutedEventArgs e)


{
Person person = new Person();
person.Name = "Mario";
person.Surname = "Rossi";

persons.Add(person);
}
}

In questo esempio, in fase di inizializzazione della pagina, creiamo una istanza della classe
ObservableCollection<Person> e la colleghiamo alla ListBox tramite la proprietà ItemsSource.

Nell’interfaccia grafica inseriamo, quindi, un pulsante che, al clic, va a creare un nuovo


oggetto di tipo Person e ad aggiungerlo alla collezione.
Il controllo ListBox si aggiornerà automaticamente per mostrare l’elemento appena inserito,
senza alcun intervento da codice da parte nostra.
I converter
In alcune situazioni può capitare che, in un’espressione di binding, il valore restituito dalla
sorgente debba essere manipolato prima di essere passato al controllo target. Il caso più
comune è la visualizzazione di date: gli oggetti di tipo DateTime, se assegnati così come
sono alla proprietà Text di una TextBlock tramite un’espressione di binding, vengono
visualizzati nel formato esteso, comprensivo di data, ore, minuti, secondi e millisecondi.
Nella maggior parte dei casi si tratta di un’informazione fin troppo precisa: a volte è
sufficiente solo la data, oppure si vuole utilizzare la rappresentazione testuale.
In questi (e in molti altri casi) entrano in gioco i converter, ovvero degli oggetti che
intercettano un’espressione di binding e ne manipolano il valore prima di restituire il
risultato al controllo target.
Per definire un converter occorre creare una classe che implementi l’interfaccia
IValueConverter, la quale richiederà l’implementazione di due metodi:

• Convert viene invocato quando deve essere modificato il valore passato dalla sorgente
dati al controllo target;
• ConvertBack, viceversa, viene invocato quando a essere modificato è il valore restituito
dal controllo target alla sorgente dati. Se si tratta di un binding di tipo OneWay, questo
metodo non sarà mai invocato.
Entrambi i metodi ci restituiscono, tramite parametri, quattro informazioni:
• il risultato dell’espressione di binding che deve essere trattato (è un generico object,
in quanto il binding può essere utilizzato con oggetti di qualsiasi tipo);
• il tipo del controllo target;
• un parametro opzionale, che può essere passato nell’espressione di binding;
• la culture corrente.
Ecco un esempio di implementazione di un converter che, ricevuto in ingresso un oggetto
di tipo DateTime, ne restituisce la visualizzazione breve della data (ad esempio,
01/01/2011).
Viceversa, nel metodo ConvertBack la stringa viene nuovamente convertita in un oggetto di
tipo DateTime e restituita alla sorgente dati.
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{

if (value != null)
{
DateTime date = (DateTime) value;
return date.ToShortDateString();
}

return string.Empty;
}
public object ConvertBack(object value, Type targetType, object
parameter, CultureInfo culture)
{
if (value != null)
{
DateTime date = DateTime.Parse(value.ToString());
return date;
}

return DateTime.Now;
}
}

Per utilizzare un converter in un’espressione di binding dobbiamo prima dichiararlo come


risorsa, locale o a livello di applicazione.
<phone:PhoneApplicationPage.Resources>
<converters:DateConverter x:Key="DateConverter" />
</phone:PhoneApplicationPage.Resources>

A questo punto, possiamo dichiararlo nell’espressione di binding sfruttando l’attributo


Converter, come nell’esempio seguente:

<TextBlock Text="{Binding Path=BirthDate, Converter={StaticResource DateConverter}}"


Lo sviluppo multithreading
Nel corso degli anni, con l’aumentare della potenza di calcolo dei computer, ha preso
sempre più piede il concetto di programmazione asincrona, in grado di sfruttare la capacità
dei processori di eseguire più operazioni contemporaneamente e su thread separati.
Windows Phone è un ambiente fortemente orientato al multi threading, con lo scopo di
offrire un’esperienza d’uso il più fluida e piacevole possibile. Sicuramente ci sarà capitato
di utilizzare vecchie applicazioni che non fanno uso di codice asincrono: la tipica
esperienza d’uso è che l’applicazione, nel momento in cui viene avviata un’operazione,
rimane bloccata fino a quando questa non è terminata; l’utente non può fare altro che
aspettare, dato che l’interfaccia non è più in grado di reagire alle interazioni.
Uno scenario di questo tipo non è accettabile su un dispositivo mobile dove l’esecuzione
di più operazioni contemporaneamente è all’ordine del giorno e dove l’utente non può
permettersi di aspettare a rispondere a una telefonata perché l’applicazione in uso ha
bloccato l’interfaccia.
Moltissime delle API disponibili sono strutturate in modo da sfruttare questo approccio
alla programmazione, che cambia notevolmente la struttura del codice che andremo a
scrivere.
Due sono gli approcci disponibili: il primo è quello delle callback; il secondo, invece, di
recente introduzione, è basato sulle keyword async e await ed è stato introdotto in C# 5.
Le callback
Per capire meglio il concetto, prendiamo in esame la classe WebClient, di cui parleremo
approfonditamente nel Capitolo 5. Per ora vi basti sapere che tale classe permette di
scaricare dati dalla rete e che non è esclusiva di Windows Phone, ma fa parte del
framework .NET.
Il metodo che utilizzeremo si chiama DownloadString() e permette di scaricare una risorsa
testuale (un file di testo, un XML ecc.) da Internet.
In un’applicazione web o desktop tradizionale potremmo utilizzare il codice seguente per
raggiungere il nostro scopo:
WebClient client = new WebClient();
string download = client.DownloadString("http://www.qmatteoq.com");
Debug.WriteLine(download);

Questo codice è sincrono: fintanto che il metodo DownloadString() non ha completato il


download della risorsa richiesta, l’applicazione sarà bloccata; la riga successiva (che
scrive nella Output Window di Visual Studio il testo scaricato) sarà eseguita solo a
operazione conclusa.
In Windows Phone, invece, questo scenario non è possibile: la classe WebClient espone
solamente il metodo DownloadStringAsync(), che è asincrono. Proprio per supportare questo
meccanismo la classe stessa espone un evento che viene scatenato nel momento in cui il
download è completato e possiamo quindi elaborare il risultato, che si chiama
DownloadStringCompleted.

Per facilitare allo sviluppatore la sottoscrizione di un evento entra in azione Visual Studio:
dopo aver scritto la keyword +=, premendo il tasto TAB della tastiera, Visual Studio
genererà in automatico il codice necessario, creando una nuova istanza dell’event handler
relativo.
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_
DownloadStringCompleted);
client.DownloadStringAsync("http://www.qmatteoq.com");
Debug.WriteLine("Download avviato");

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)


{
Debug.WriteLine(e.Result);
}

La differenza, rispetto a prima, è che le operazioni per elaborare il risultato del download
vengono delegate a un evento separato, che tra i parametri restituisce l’informazione
richiesta. Essendo questo codice asincrono, la riga di codice che stampa nella Output
Window il testo “Download avviato” viene eseguita immediatamente: non si dovrà
attendere il completamento dell’operazione di download.
Con l’introduzione del nuovo pattern basato sulle keyword async e await questo approccio
è caduto un po’ in disuso, dato che tutte le nuove API del Windows Runtime for Windows
Phone sono state aggiornate per non utilizzare più le callback.
È importante, però, conoscere questo meccanismo perché alcune API ne fanno ancora uso,
in particolar modo quelle utilizzate per la rilevazione di dati: ad esempio, le API per i
sensori di movimento espongono un meccanismo basato su callback per rimanere in
ascolto dei sensori e ricevere i dati aggiornati sulla posizione del device nello spazio.
Async e await
Il codice che abbiamo appena visto utilizza un approccio non troppo complesso da
implementare, ma poco lineare e difficile da capire e da testare. Il limite più grande,
infatti, è che tale codice non è lineare: la riga che stampa del testo nella Output Window
viene eseguita subito dopo aver avviato l’operazione di download, ma, a un certo punto,
l’esecuzione del flusso principale viene interrotta per lasciare spazio al codice dichiarato
all’interno dell’event handler e, più precisamente, nel momento in cui il download è
completato.
Le keyword async e await, introdotte in C# 5 e nella versione 4.5 del framework .NET,
permettono di scrivere codice asincrono pur mantenendo il vecchio approccio, ovvero la
scrittura di codice sequenziale, in cui ogni riga viene eseguita una dopo l’altra, così come
nel primo esempio in cui abbiamo utilizzato i metodi sincroni della classe WebClient.
Dietro le quinte, nel momento in cui eseguiamo un’operazione asincrona, il controllo
viene restituito al thread principale, lasciando perciò la UI fluida e reattiva. Viene
mantenuto, però, una sorta di “segnaposto”, per ricordare al sistema qual è la riga da cui
riprendere l’esecuzione nel momento in cui l’operazione asincrona è terminata. Il risultato
sarà che avremo gli stessi vantaggi delle callback (applicazione responsiva), mantenendo
però la scrittura di codice sequenziale.
Quando utilizziamo questo nuovo approccio, in realtà utilizziamo una classe delle API
chiamata Task, che rappresenta un’operazione eseguita in maniera asincrona. Un metodo
asincrono può restituire due tipi di risultati differenti:
• Task, quando si tratta di un metodo di tipo void, ovvero che non restituisce alcun
risultato ma che deve semplicemente eseguire della logica. Ritornando un oggetto di
tipo Task, possiamo far sì che l’esecuzione del nostro codice prosegua solo nel
momento in cui le operazioni definite nel metodo siano state completate;
• Task<T>, quando si tratta di un metodo che restituisce un risultato, il cui tipo è T. In
questo caso il metodo eseguirà le operazioni definite e, una volta che queste saranno
state completate, restituirà il risultato al chiamante: solo a quel punto l’esecuzione
delle righe di codice successive sarà ripresa.
Vediamo un esempio concreto: avremo bisogno, però, di installare un pacchetto da NuGet,
chiamato Bcl.Async. Fate clic con il tasto destro sul vostro progetto dal Solution Explorer,
selezionate Manage NuGet packages e cercate e installate il pacchetto chiamato Async
for .NET Framework 4, Silverlight 4 and 5 and Windows Phone 7.5.
Lo scopo reale di questa libreria è di aggiungere il supporto ad async e await anche a
tecnologie che non sono basate su C# 5 e che quindi non potrebbero usufruire di questo
approccio.
In realtà, essa è di grande utilità anche per le applicazioni Windows Phone 8: per i motivi
di retro-compatibilità di cui abbiamo già parlato nel Capitolo 1, alcune API sono state
mantenute anche nella nuova versione senza apportare modifiche. Il risultato è che classi
come WebClient continuano a esporre solo i metodi per utilizzare l’approccio basato su
callback: tramite questa libreria, invece, saranno aggiunti degli extension methods,
ovvero delle estensioni che vi metteranno a disposizione dei nuovi metodi per effettuare
operazioni utilizzando le keyword async e await.
Vediamo come, utilizzando questa libreria, siamo in grado di modificare l’esempio
precedente con la classe WebClient per utilizzare il nuovo approccio:
public async void Download()
{
WebClient client = new WebClient();
string result = await client.DownloadStringTaskAsync("http://www.qmatteoq.com");
Debug.WriteLine(result);
}

Come potete notare, grazie alla libreria Microsoft.Bcl.Async, ora la classe WebClient espone
un nuovo metodo chiamato DownloadStringTaskAsync(), che permette di scaricare una risorsa
testuale dalla rete con il nuovo approccio. Tale metodo, infatti, restituisce un oggetto di
tipo Task<string>: questo significa che, dietro le quinte, il metodo eseguirà tutte le operazioni
necessarie per scaricare la risorsa e, una volta che avrà terminato, restituirà al chiamante il
contenuto sotto forma di stringa. La “magia” è resa possibile dall’utilizzo di due keyword:
la prima, async, è quella che viene utilizzata per marcare il metodo Download() e serve a
specificare che, al suo interno, faremo uso di uno o più metodi che restituiscono un
oggetto di tipo Task o Task<T>; la seconda, await, viene apposta come prefisso della
chiamata al metodo asincrono e, letteralmente, fa sì che questo venga “aspettato” fino a
che non è terminato prima di continuare l’esecuzione. Come vedete, grazie all’utilizzo
della parola chiave await l’esistenza della classe Task è completamente trasparente: anche
se il metodo DownloadStringTaskAsync() restituisce un oggetto di tipo Task<string>, siamo in grado
di assegnare il risultato direttamente a una stringa senza che sia necessario fare alcuna
elaborazione.
Se ora rileggete la descrizione del funzionamento di questo meccanismo, tutto dovrebbe
esservi più chiaro: nel momento in cui viene chiamato il metodo DownloadStringTaskAsync()
viene impostato un “segnaposto” e il controllo viene restituito al thread principale.
L’utente è perciò in grado di interagire con l’applicazione e di effettuare altre operazioni;
nel momento in cui il download è terminato, l’applicazione, grazie al segnaposto
precedentemente impostato, è in grado di riprendere il flusso lì dove lo avevamo lasciato:
dalla riga successiva, ovvero quella in cui il testo scaricato viene stampato nella Output
Window di Visual Studio.
Il dispatcher
Molte delle API di Windows Phone sono basate non solo su codice sincrono, ma anche sul
concetto di multi thread: il thread principale di un’applicazione Windows Phone è quello
che gestisce la UI e, per questo motivo, deve essere lasciato il più libero possibile, affinché
l’interfaccia sia sempre reattiva e fluida.
Per questo motivo, molte API che eseguono operazioni che possono richiedere un tempo
di elaborazione più o meno lungo utilizzano thread differenti: solo nel momento in cui è
terminata l’elaborazione i risultati devono essere riportati nel thread principale, per essere
accessibili dai controlli dell’interfaccia.
I thread secondari non hanno però accesso diretto alla UI: se da una funzione che viene
eseguita in un thread secondario cerchiamo infatti di accedere direttamente a un controllo,
otterremo una eccezione di tipo UnauthorizedAccessException con il messaggio Invalid cross-
thread access.

Figura 2.22 - L’eccezione che si verifica quando si tenta di accedere alla UI da un thread secondario.

Per questi scenari abbiamo a disposizione una classe chiamata Dispatcher, che permette di
interagire con il thread principale della UI direttamente da un thread secondario.
Dispatcher.BeginInvoke(() =>
{
txtName.Text = "Matteo";
});

All’interno del blocco tra parentesi graffe vengono racchiuse tutte le operazioni che hanno
la necessità di interagire con i controlli (nell’esempio, l’accesso alla proprietà Text di un
controllo TextBlock).
Tipicamente, negli eventi che sono gestiti in thread secondari, si procede a elaborare i
risultati dell’operazione e, solo alla fine, tramite il Dispatcher si riportano alla UI.
In conclusione
In questo capitolo abbiamo affrontato le basi necessarie per iniziare a sviluppare per
Windows Phone: siamo partiti dalla struttura di un progetto per arrivare alla scrittura di
codice asincrono, indispensabile per scrivere applicazioni fluide e reattive.
Nel mezzo, abbiamo imparato a scrivere codice XAML, il linguaggio di markup che
permette di definire l’interfaccia di un’applicazione, sfruttandone tutte le potenzialità: stili,
risorse e template, ovvero i meccanismi messi a disposizione dal linguaggio per strutturare
al meglio il nostro codice e dare un aspetto visuale uniforme a tutta l’applicazione.
Abbiamo poi affrontato il tema del binding, forse il più complesso da comprendere, ma
anche quello che offre le potenzialità maggiori: con questa caratteristica dello XAML
possiamo creare dei canali di comunicazione tra controlli e codice, semplificando
notevolmente la visualizzazione dei dati della nostra applicazione.
Infine, abbiamo concluso con una delle tematiche più importanti per scrivere applicazioni
fluide e responsive: la scrittura di codice asincrono, per eseguire operazioni, anche lunghe
(come il download di un file), senza che queste impattino sulla reattività dell’interfaccia
grafica.
I controlli di Windows Phone

Uno dei punti di forza di Windows Phone è la grande quantità di controlli


messi a disposizione, che possono essere usati per definire l’interfaccia
utente della nostra applicazione e gestire le interazioni con l’utente.
In questo capitolo andremo a vedere i principali controlli che abbiamo a disposizione per
interagire con l’utente e per organizzare il layout della nostra applicazione.
In più vedremo come alcune librerie di terze parti possano semplificare il nostro lavoro,
offrendoci un gran numero di controlli già pronti per essere utilizzati.
Definire il posizionamento dei controlli
Lo XAML mette a disposizione diversi controlli “contenitore” che permettono di
posizionarne altri al loro interno per definirne il layout. Lo StackPanel che abbiamo visto nel
Capitolo 2 è uno di questi. Analizziamo ora i principali controlli disponibili e utilizzati con
maggiore frequenza.

Il controllo Grid
Il controllo Grid permette di realizzare delle griglie, composte da righe e colonne,
all’interno delle quali andare a posizionare altri elementi. Ciò è reso possibile da due
proprietà, RowDefinitions e ColumnDefinition, che permettono di specificare il numero e l’altezza
delle righe e la larghezza delle colonne.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5⊘" />
<RowDefinition MaxHeight="1⊘⊘" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>

In questo esempio vediamo la definizione di una griglia, composta da due colonne e due
righe. Possiamo notare le varie modalità che abbiamo a disposizione per specificarne le
dimensioni:
• Height e Width permettono di dare una dimensione fissa;
• MaxHeight e MaxWidth permettono alla cella di adattarsi al contenuto fino a raggiungere
la dimensione massima impostata;
• l’asterisco utilizzato come valore di una proprietà permette alla cella di adattare
automaticamente la propria dimensione in base a quella delle celle precedenti.
Per inserire un controllo all’interno di una cella specifica abbiamo a disposizione le
proprietà Grid.Row e Grid.Column, che accettano come valore il numero della riga e della
colonna in cui posizionare l’elemento (partendo da 0). Gli elementi da posizionare nella
griglia devono comunque essere inseriti all’interno del controllo Grid.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5⊘" />
<RowDefinition MaxHeight="1⊘⊘" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2⊘⊘" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Questo è un testo" Grid.Row="⊘" Grid.Column="1" />
</Grid>

Nell’esempio è stato inserito un controllo TextBlock nella seconda colonna della prima riga
della griglia. Esattamente come nelle tabelle del mondo HTML, possiamo anche far sì che
una cella occupi lo spazio di più righe o più colonne, tramite le proprietà Grid. RowSpan e
Grid.ColumnSpan.

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5⊘" />
<RowDefinition MaxHeight="1⊘⊘" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2⊘⊘" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Questo è un testo" Grid.Row="⊘" Grid.Column="1" Grid.ColumnSpan="2" />
</Grid>

In questo esempio il controllo TextBlock è posizionato sempre nella seconda colonna della
prima riga, ma la cella in cui è inserito occupa lo spazio di due colonne.

Il controllo StackPanel
Questo “contenitore” ha una funzione molto semplice, ma allo stesso tempo molto utile:
impilare i controlli. Gli elementi che sono inseriti all’interno di uno StackPanel sono
semplicemente disposti uno sotto l’altro, occupando il massimo spazio disponibile
concesso dall’elemento.
<StackPanel>
<TextBlock Text="Primo testo" />
<TextBlock Text="Secondo testo" />
</StackPanel>

Nell’esempio i due testi saranno visualizzati uno sotto l’altro.


Figura 3.1 - Uno StackPanel in modalità standard.

È possibile anche utilizzare lo StackPanel in modalità orizzontale, così da allineare gli


elementi allo stesso modo, ma disposti uno di fianco all’altro. Per fare questo occorre
agire sulla proprietà Orientation, come nell’esempio:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Primo testo" />
<TextBlock Text="Secondo testo" />
</StackPanel>
Figura 3.2 - Uno StackPanel in modalità orizzontale.

Il controllo Canvas
Canvas in italiano significa “tela”: si tratta, infatti, di un contenitore che lascia la massima
libertà e non tenta di organizzare in alcun modo il layout degli elementi inseriti al suo
interno.
Il posizionamento degli elementi viene affidato all’utilizzo della proprietà Canvas.Top e
Canvas.Left, che definiscono la distanza in pixel, rispettivamente, dal margine superiore e da
quello sinistro del canvas, considerando le coordinate 0,0 come il margine superiore
sinistro del controllo.
<Canvas Width="64⊘" Height="48⊘" Background="White">
<RectangleCanvas.Left="3⊘" Canvas.Top="3⊘" Fill="red" Width="2⊘⊘" Height="2⊘⊘" />
</Canvas>

Ecco come appare a video questo codice di esempio:


Figura 3.3 - Un rettangolo inserito all’interno di un canvas.

Il controllo ScrollViewer
Questo controllo, in realtà, si differenzia da quelli che abbiamo appena visto, in quanto
non serve per definire il layout degli elementi, ma per consentire lo scrolling e permettere,
così, di inserire contenuti più lunghi della dimensione dello schermo.
Il layout degli elementi deve comunque essere definito da uno dei controlli visti in
precedenza, che possono essere inseriti all’interno di uno ScrollViewer.
<ScrollViewer Height="6⊘⊘">
<StackPanel>
<TextBlock Text="Primo testo" />
<TextBlock Text="Secondo testo" />
</StackPanel>
</ScrollViewer>

In questo esempio lo ScrollViewer ha un’altezza di 600 pixel: nel momento in cui gli elementi
al suo interno dovessero superare questa altezza (nell’esempio, due testi), sarà abilitato
automaticamente lo scrolling l’utente, tramite lo sfioramento dello schermo con il dito,
potrà scorrere i testi e visualizzare le parti nascoste.
Visualizzare i testi
Lo XAML mette a disposizione diversi controlli per visualizzare informazioni testuali
nella vostra applicazione. Vediamoli in dettaglio.
Il controllo TextBlock serve per visualizzare del testo semplice, contenuto nella proprietà
Text. In più, abbiamo a disposizione una serie di proprietà (comuni a molti altri controlli)
per modificare l’aspetto visivo, come il colore (Foreground) o le caratteristiche del font
(FontSize, FontWeight, FontFamily ecc.).
Se si ha la necessità di formattare porzioni di testo in maniera differente, possiamo
utilizzare i tag Run e LineBreak, come nell’esempio seguente:
<TextBlock>
<Run Text="Testo normale" />
<LineBreak />
<Run Text="Testo in grassetto" FontWeight="Bold" />
</TextBlock>

In questo esempio il testo viene disposto su due righe: il primo sarà visualizzato
normalmente, il secondo in grassetto.
Nella versione 7.1 dell’SDK è stato introdotto un nuovo controllo, chiamato RichTextBlock,
che permette di formattare il testo con una serie di tag che ricordano le possibilità offerte
dall’HTML. Possiamo inserire paragrafi (Paragraph), testi in corsivo (Italic) e grassetto (Bold),
link (Hyperlink) oppure andare a capo (LineBreak).
<RichTextBox>
<Paragraph>
<Bold>Questo è un paragrafo</Bold>
</Paragraph>
<Paragraph>
<Italic>Questo è un altro paragrafo</Italic>
<LineBreak />
<Hyperlink NavigateUri="AboutPage.xaml" />
</Paragraph>
</RichTextBox>

Figura 3.4 - Il controllo RichTextBox.


Ricevere l’input dell’utente
TextBox è un controllo di input, che permette all’utente di inserire del testo. Anche in questo
caso, il contenuto viene gestito dalla proprietà Text, che può essere utilizzata sia in lettura
(per leggere il testo inserito dall’utente), sia in scrittura (per assegnare un contenuto
predefinito alla casella).
Nel momento in cui l’utente fa tap su un controllo TextBox, viene automaticamente aperta la
tastiera virtuale di Windows Phone, per dare la possibilità all’utente di immettere il testo.
L’aspetto più interessante è che, come sviluppatori, possiamo controllare l’aspetto della
tastiera virtuale, in base al tipo di input che ci aspettiamo: se, ad esempio, la casella di
testo serve per inserire un numero, possiamo visualizzare una tastiera con soli numeri; se
serve per inserire indirizzi e-mail, possiamo visualizzare una versione della tastiera
alfabetica che fornisca un accesso veloce a simboli come la chiocciola.

Figura 3.5 - La tastiera virtuale di Windows Phone.

Questa opzione viene gestita tramite la proprietà InputScope, i cui valori disponibili sono
numerosissimi.
Ecco quelli di uso più comune:
• Text: per la digitazione di testo generico con il supporto al correttore ortografico;
• Number: per l’inserimento di numeri (è quella che compare quando nella tastiera
tradizionale premiamo il pulsante &123);
• TelephoneNumber: per la digitazione di numeri di telefono (è lo stesso tipo di tastiera che
compare quando utilizzate l’applicazione nativa Phone per fare una chiamata);
• EmailNameOrAddress: per la digitazione di indirizzi e-mail; include un tasto per la
chiocciola e un tasto per il suffisso .com;
• Url: per la digitazione di indirizzi di siti web; include un tasto per il suffisso .com.
<TextBox x:Name="txtDescription" InputScope="Text" />

Se ricordate quanto spiegato nel Capitolo 2, è possibile valorizzare le proprietà di un


controllo anche con una modalità più verbosa e che, per tale motivo, vi avevo sconsigliato.
Per quanto riguarda la proprietà TextBox, invece, vale il consiglio contrario: solo utilizzando
la sintassi estesa, infatti, si attiverà l’Intellisense di Visual Studio, mostrandovi l’elenco di
tutte le tipologie di tastiera disponibili.
<TextBox x:Name="txtDescription">
<TextBox.InputScope>
<InputScope>
<InputScopeName NameValue="Text"></InputScopeName>
</InputScope>
</TextBox.InputScope>
</TextBox>

Quando inserite un controllo TextBox, anche nel caso in cui l’input da voi
richiesto non esiga un tipo particolare di tastiera virtuale perché è semplice
NOTA testo, ricordatevi sempre di impostare la proprietà InputScope a Text. In
caso contrario, la tastiera virtuale visualizzata non offrirà supporto
all’autocompletamento e al correttore ortografico.

L’SDK include anche un controllo specifico per l’inserimento di password, chiamato


PasswordBox: il suo comportamento e il suo funzionamento sono identici a quelli del
controllo TextBox, con la differenza che il testo inserito viene automaticamente mascherato
per impedirne la lettura da parte di altre persone che stanno guardando lo schermo del
device.
Interagire con l’utente
Lo XAML mette a disposizione diversi controlli per interagire con l’utente, come Button,
CheckBox o HyperlinkButton. La loro caratteristica principale è quella di offrire una proprietà
Content, che ne definisce l’aspetto visuale: il contenuto di un controllo di questo tipo può
essere semplicemente un testo, oppure dello XAML più complesso.
Possiamo, ad esempio, inserire un pulsante con una semplice etichetta:
<Button Content="Click me" />

Figura 3.6 - Un pulsante con un semplice contenuto testuale.

Possiamo, invece, realizzare qualcosa di più complesso, inserendo, ad esempio,


un’immagine al suo interno:
<Button>
<Button.Content>
<StackPanel>
<Image Source="windows_phone_logo1.jpg" Height="2⊘⊘" />
<TextBlock Text="Click me" />
</StackPanel>
</Button.Content>
</Button>

Figura 3.7 - Un pulsante con un contenuto personalizzato.

L’interazione con questa tipologia di controlli passa per l’evento Click (o Tap), che viene
scatenato nel momento in cui l’utente fa tap sul controllo. Tramite l’event handler che sarà
generato da Visual Studio nel code behind potremo gestire l’interazione.
Altri controlli, invece, offrono delle proprietà specifiche: ad esempio, i controlli CheckBox e
RadioButton offrono la proprietà IsChecked, che vi comunica se l’elemento è stato selezionato o
meno.
Mostrare liste di elementi: il controllo LongListSelector
Una delle necessità più frequenti è quella di mostrare liste di elementi all’interno di
un’applicazione: per questo motivo Windows Phone offre una serie di controlli che sono
in grado, dato il template di un singolo elemento, di mostrare in automatico tutti gli
elementi che fanno parte di una collezione.
Abbiamo già visto, nel Capitolo 2, il meccanismo che lo XAML ci mette a disposizione
per definire un template e come, tramite il binding, possiamo associare elementi del
template alle proprietà degli elementi che dobbiamo visualizzare.
Questi controlli sono caratterizzati da alcune proprietà importanti:
• la proprietà ItemTemplate, che definisce il template da utilizzare per visualizzare il
singolo elemento;
• la proprietà ItemsSource, che rappresenta la collezione di elementi che deve essere
visualizzata;
• la proprietà SelectedItem, che contiene l’elemento della lista che è stato selezionato
dall’utente.
In più, questi controlli espongono, solitamente, un evento chiamato SelectionChanged che
viene invocato nel momento in cui un elemento è stato selezionato e può esserci utile per
gestire l’interazione dell’utente (ad esempio, la navigazione verso una pagina di dettaglio
che mostri maggiori informazioni sull’elemento scelto).
Windows Phone, per questo scopo, ha messo a disposizione, sin dalla prima versione, un
controllo chiamato ListBox: esso è stato però rimpiazzato, in Windows Phone 8, da un altro
controllo, chiamato LongListSelector, che, originariamente, era presente nel Phone Toolkit per
Windows Phone, di cui parleremo in maniera approfondita più avanti. ListBox è ancora
presente tra i controlli di Windows Phone, soprattutto per motivi di retrocompatibilità: se
state sviluppando un nuovo progetto esclusivo per Windows Phone 8, il controllo
consigliato da Microsoft è LongListSelector. Quali sono i vantaggi?
• migliori performance, ambito in cui, invece, il controllo ListBox mostrava qualche
limite, soprattutto in presenza di molti elementi;
• supporto alla virtualizzazione, ovvero la possibilità di caricare in memoria solo gli
elementi correntemente visualizzati, così da ottimizzare le performance in caso di
liste molto lunghe;
• supporto ai gruppi: è possibile raggruppare gli elementi di una lista e fornire una
jump list, ovvero una scorciatoia per passare velocemente da un gruppo all’altro.
Potete trovare un esempio di questa funzionalità nell’hub People: i contatti sono
raggruppati per l’iniziale del nome e, facendo tap su una singola lettera, viene aperta
una schermata che consente di passare velocemente da una lettera all’altra.
Figura 3.8 - L’hub People di Windows Phone, in cui i contatti vengono raggruppati per iniziale.

Il controllo LongListSelector può essere utilizzato come una normale lista (senza sfruttare il
raggruppamento), valorizzando solamente la proprietà ItemTemplate con il template che
rappresenta il singolo elemento e impostando la proprietà IsGroupingEnabled uguale a false,
come nell’esempio:
<phone:LongListSelector x:Name="List"
IsGroupingEnabled="False"
ItemTemplate="{StaticResource ListTemplate}" />

Se, invece, vogliamo raggruppare gli elementi, dobbiamo effettuare alcune modifiche, sia
nel codice sia nello XAML.
È necessario intervenire sul codice per cambiare il modo in cui passiamo i dati alla lista:
non una semplice collezione di elementi, ma una collezione di gruppi, ognuno dei quali
contiene gli elementi appartenenti a quel gruppo.
Andremo a vedere due esempi: uno relativo alla creazione di un elenco raggruppato in
base all’ordine alfabetico e uno, invece, per creare una collezione di dati raggruppata in
base a un gruppo personalizzato (ad esempio, il genere di un film).

Raggruppare per ordine alfabetico


Raggruppare per ordine alfabetico richiede un approccio differente rispetto al
raggruppamento tradizionale: questo perché, anche se concettualmente l’operazione è la
stessa (creare una collezione di gruppi invece che di elementi singoli), abbiamo la
necessità di ordinare gli elementi e di far sì che venga creato un gruppo per ogni lettera
dell’alfabeto, anche per quelle per le quali non c’è un elemento.
In questo modo, infatti, siamo in grado di creare un’esperienza utente analoga a quella
delle applicazioni native: la jump list contiene l’elenco di tutte le lettere dell’alfabeto, ma
solo quelle per cui esistono degli elementi sono attive.
Per raggiungere questo scopo andiamo a creare una classe chiamata AlphaKeyGroup<T>, la cui
implementazione ci viene suggerita direttamente dalla documentazione ufficiale
Microsoft: ogni oggetto di tipo AlphaKeyGroup<T> rappresenta una lettera dell’alfabeto e
contiene all’interno tutti gli elementi (il cui tipo è T) che iniziano per tale lettera.
public class AlphaKeyGroup<T> : List<T>

/// <summary>
/// The delegate that is used to get the key information.
/// </summary>
/// <param name="item">An object of type T</param>
/// <returns>The key value to use for this object</returns>
public delegate string GetKeyDelegate(T item);

/// <summary>
/// The Key of this group.
/// </summary>
public string Key { get; private set; }

/// <summary>
/// Public constructor.
/// </summary>
/// <param name="key">The key for this group.</param>
public AlphaKeyGroup(string key)
{
Key = key;
}

/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="slg">The </param>
/// <returns>Theitems source for a LongListSelector</returns>
private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg)
{
List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>();

foreach (string key in slg.GroupDisplayNames)


{
list.Add(new AlphaKeyGroup<T>(key));
}

return list;
}

/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="items">The items to place in the groups.</param>
/// <param name="ci">The CultureInfo to group and sort by.</param>
/// <param name="getKey">A delegate to get the key from an item.</param>
/// <param name="sort">Will sort the data if true.</param>
/// <returns>An items source for a LongListSelector</returns>
public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items,
CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
List<AlphaKeyGroup<T>> list = CreateGroups(slg);

foreach (T item in items)


{
int index = ⊘;
if (slg.SupportsPhonetics)
{
//check if your database has yomi string for item
//if it does not, then do you want to generate Yomi or ask the
user for this item.
//index = slg.GetGroupIndex(getKey(Yomiof(item)));
}
else
{
index = slg.GetGroupIndex(getKey(item));
}
if (index >= ⊘ && index < list.Count)
{
list[index].Add(item);
}
}

if (sort)
{
foreach (AlphaKeyGroup<T> group in list)
{
group.Sort((c⊘, c1) => { return ci.CompareInfo.Compare(getKey(c⊘),
getKey(c1)); });
}
}

return list;
}
}

Non descriverò punto per punto il funzionamento di questa classe, ma mi limiterò a


metterne in evidenza alcune caratteristiche:
• eredita da List<T>, di conseguenza serve a rappresentare una lista di elementi;
• ha una proprietà chiamata Key, che rappresenta la chiave che identifica il gruppo (nel
nostro caso, la lettera dell’alfabeto);
• fa uso di un tipo speciale di collezione chiamato SortedLocaleGrouping, che permette di
creare dei gruppi ordinati in base alle impostazioni regionali del telefono (in modo
che l’ordinamento in base a numeri e lettere segua la lingua corrente).
La classe espone poi un metodo, chiamato CreateGroups(), che è quello che ci servirà
all’interno della nostra applicazione per trasformare una collezione di elementi
tradizionale in una collezione di gruppi, suddivisi in base alla lettera iniziale.
Vediamo un esempio di utilizzo per mostrare e raggruppare un elenco di contatti.
Innanzitutto ci serve una classe per rappresentare il singolo contatto:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
Dopodiché, in fase di inizializzazione della pagina, creiamo un elenco fittizio di contatti:
List<Person> people = new List<Person>
{
new Person
{
Name = "Matteo",
Surname = "Pagani"
},
new Person
{
Name = "Roberto",
Surname = "Freato"
},
new Person
{
Name = "Ugo",
Surname = "Lattanzi"
}
};

Quello che abbiamo in questo momento è una lista “piatta” composta da tre elementi: se
valorizzassimo direttamente la proprietà ItemsSource di un controllo LongListSelector con questa
lista, essa si limiterebbe a visualizzare gli elementi uno di seguito all’altro, utilizzando il
template definito nella proprietà ItemTemplate, come nell’esempio:
<DataTemplate x:Key="PeopleItemTemplate">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Surname}" />
</StackPanel>
</DataTemplate>

Per passare a una visualizzazione a gruppi, abbiamo invece bisogno, sfruttando la classe
AlphaKeyGroup<T> creata in precedenza, di trasformare la nostra lista piatta in una lista
raggruppata:
List<AlphaKeyGroup<Person>> list = AlphaKeyGroup<Person>.CreateGroups(people,
Thread.CurrentThread.CurrentUICulture, p => p.Name, true);

Il metodo CreateGroups() necessita di quattro parametri:


• il primo è la collezione “piatta” da trasformare, nel nostro esempio people;
• il secondo è la culture di riferimento da utilizzare per la generazione dell’alfabeto:
nell’esempio, tramite la proprietà Thread.CurrentThread.CurrentUICulture, passiamo la culture
corrente del device (ad esempio, se l’utente sta utilizzando il telefono in italiano,
questa sarà it-IT);
• il terzo viene espresso tramite una lambda expression e rappresenta la proprietà del
singolo elemento della collezione da utilizzare come chiave per il raggruppamento e
l’ordinamento; in questo esempio, i gruppi saranno creati in base all’iniziale del
nome;
• il quarto è una proprietà booleana che dice se vogliamo applicare anche
l’ordinamento degli elementi in ordine alfabetico.
Quella che ci viene restituita è una collezione di tipo List<AlphaKeyGroup<Person>>: questo
perché ogni AlphaKeyGroup<Person> rappresenta una singola lettera dell’alfabeto (con i suoi
elementi), di conseguenza il valore di ritorno del metodo CreateGroups() è una collezione a
sua volta contenente tutte le lettere dell’alfabeto.
Ora possiamo assegnare il nostro risultato alla proprietà ItemsSource del controllo
LongListSelector:

People.ItemsSource = list;

Ci manca, però, un ultimo passaggio: definire l’aspetto visuale del LongListSelector. Dato che
questo controllo ha diverse modalità di visualizzazione (come la jump list o l’intestazione
che viene aggiunta in testa a ogni gruppo), non è sufficiente specificare la proprietà
ItemTemplate.

È necessario, infatti, valorizzare altre due importanti proprietà: la prima si chiama


GroupHeaderTemplate e rappresenta il template con cui viene rappresentata l’intestazione di
ogni gruppo. Ecco un esempio:
<DataTemplate x:Key="PeopleGroupHeaderTemplate">
<Border Background="Transparent" Padding="5">
<Border Background="{StaticResource PhoneAccentBrush}"
BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" Width="62"
Height="62" Margin="⊘,⊘,18,⊘" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}" Foreground="{StaticResource
PhoneForegroundBrush}" FontSize="48" Padding="6"
FontFamily="{StaticResource PhoneFontFamilySemiLight}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</Border>
</DataTemplate>

Questo template mira a ricreare lo stile dei gruppi nativi di Windows Phone: la lettera è
inserita all’interno di un rettangolo, il cui colore di sfondo coincide con il colore del tema
scelto dall’utente (possiamo notare che ad alcune proprietà, come il Background del controllo
Border, viene applicata la risorsa PhoneAccentBrush, che rappresenta, per l’appunto, il colore
del tema correntemente utilizzato).
Possiamo notare anche il binding con la proprietà Key, che abbiamo definito in precedenza
nella classe AlphaKeyGroup<T>: in questo modo, ogni rettangolo conterrà al suo interno la
chiave identificativa del gruppo, nel nostro caso la lettera dell’alfabeto.
La seconda e ultima proprietà che occorre valorizzare si chiama JumpListStyle e, al contrario
di quelle precedenti, non è un template, ma uno stile. Ecco un esempio di definizione:
<phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/>
<phone:JumpListItemForegroundConverter x:Key="ForegroundConverter"/>
<Style x:Key="PeopleJumpListStyle" TargetType="phone:LongListSelector">
<Setter Property="GridCellSize" Value="113,113"/>
<Setter Property="LayoutMode" Value="Grid" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border Background="{Binding Converter={StaticResource
BackgroundConverter}}" Width="113" Height="113" Margin="6" >
<TextBlock Text="{Binding Key}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="48" Padding="6"
Foreground="{Binding Converter={StaticResource ForegroundConverter}}"
VerticalAlignment="Center"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

Anche in questo caso, tale stile mira a ricreare lo stesso aspetto delle liste native di
Windows Phone: le lettere dell’alfabeto, che corrispondono ai vari gruppi, vengono poste
l’una accanto all’altra, all’interno di un riquadro. Possiamo notare l’utilizzo di due
converter di sistema, che sono presenti all’interno del namespace Microsoft.Phone.
Controls: il loro scopo è quello di dare un aspetto differente ai gruppi per i quali sono
presenti degli elementi rispetto a quelli per i quali invece non ce ne sono. L’uso di questi
converter fa sì che anche l’interazione dell’utente sia impedita: se l’utente fa tap su una
lettera per cui non ci sono elementi, non accadrà nulla. Se, invece, fa tap su una lettera
attiva, sarà portato in automatico alla posizione corretta della lista.

Figura 3.9 - Il controllo LongListSelector con raggruppamento per lettera.

Questi due converter utilizzano in automatico il colore del tema selezionato dall’utente: se
vogliamo cambiare questo comportamento (ad esempio, perché vogliamo utilizzare il
colore del tema che abbiamo dato alla nostra applicazione), dobbiamo creare un nostro
converter personalizzato e utilizzarlo al posto di quelli di sistema.
Ecco come appare la definizione del controllo LongListSelector dopo che abbiamo definito
tutti gli stili e i template necessari:
<phone:LongListSelector
x:Name="People"
GroupHeaderTemplate="{StaticResource
PeopleGroupHeaderTemplate}"
ItemTemplate="{StaticResource PeopleItemTemplate}"
JumpListStyle="{StaticResource PeopleJumpListStyle}"
IsGroupingEnabled="True"
HideEmptyGroups="True"
LayoutMode="List"
/>

Raggruppamento personalizzato
Non sempre si ha la necessità di raggruppare una lista in base all’iniziale: in alcuni casi si
vuole adottare un raggruppamento personalizzato, ad esempio per categoria. Ipotizziamo
di aggiungere alla classe Person, vista in precedenza, un nuovo campo City e di voler
raggruppare la lista in base alla città di provenienza della persona. Ecco la nuova
definizione della classe Person:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public string City { get; set; }
}

In questo caso la classe AlphaKeyGroup<T> che abbiamo usato in precedenza non è adatta al
nostro scopo, perché nasce specificamente per raggruppare gli elementi in base
all’iniziale. Ci serve, perciò, una classe che vada a sostituire AlphaKeyGroup<T> per il
raggruppamento: l’implementazione questa volta è molto più semplice, dato che quello
che ci serve è solamente un modo per creare dei gruppi, ognuno dei quali identificato da
una chiave e con all’interno una serie di elementi.
Ecco un esempio di implementazione della classe Group<T>:
public class Group<T> : List<T>
{
public Group(string name, IEnumerable<T> items)
: base(items)
{
this.Key = name;
}

public string Key


{
get;
set;
}
}

Come potete notare, l’implementazione è molto più semplice rispetto a prima: la classe
eredita da List<T>, perciò è anch’essa una collezione, dove ogni elemento che la compone è
caratterizzato da una chiave (la proprietà Key) e da una serie di sotto elementi (la proprietà
items che viene passata come parametro al costruttore).

A questo punto, grazie a LINQ, diventa piuttosto semplice sfruttare questa classe per
raggruppare la lista in base a una chiave, grazie a un metodo come quello seguente:
private List<Group<T>> GetItemGroups<T>(IEnumerable<T> itemList, Func<T,
string> getKeyFunc)
{
IEnumerable<Group<T>> groupList = from item in itemList
group item by getKeyFunc(item) into g
orderby g.Key
select new Group<T>(g.Key, g);
return groupList.ToList();
}

Tale metodo accetta come parametro la lista “piatta” da raggruppare e, tramite una lambda
expression, la proprietà del singolo elemento da utilizzare come chiave. Nel costruttore
della nostra pagina possiamo quindi ottenere una lista raggruppata nel modo seguente:
List<Group<Person>> groups = GetItemGroups(people, x => x.City);
PeopleByCity.ItemsSource = groups;

Ipotizzando di riutilizzare l’elenco di persone definito nell’esempio del raggruppamento


alfabetico, lo passiamo come parametro al metodo GetItemGroups() insieme al valore da
utilizzare come chiave per raggruppare gli elementi: la proprietà City della classe Person.
Per quanto riguarda l’aspetto visuale, possiamo riutilizzare i template visti in precedenza,
anche se sarà necessaria una piccola modifica: i riquadri utilizzati, infatti, erano adatti per
visualizzare una singola lettera, mentre in questo caso noi vogliamo mostrare l’intero
nome della città. Dobbiamo quindi agire sulla proprietà Width dell’oggetto Border (sia per
quanto riguarda la proprietà GroupHeaderTemplate sia per quella JumpListStyle), come negli
esempi seguenti:
<DataTemplate x:Key="PeopleCityGroupHeaderTemplate">
<Border Background="Transparent" Padding="5">
<Border Background="{StaticResource PhoneAccentBrush}"
BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" Width="18⊘"
Height="62" Margin="⊘,⊘,18,⊘" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}" Foreground="{StaticResource
PhoneForegroundBrush}" FontSize="48" Padding="6"
FontFamily="{StaticResource PhoneFontFamilySemiLight}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</Border>
</DataTemplate>

<Style x:Key="PeopleCityJumpListStyle" TargetType="phone:LongListSelector">


<Setter Property="GridCellSize" Value="113,113"/>
<Setter Property="LayoutMode" Value="List" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border Background="{Binding Converter={StaticResource
BackgroundConverter}}" Width="18⊘" Height="113" Margin="6" >
<TextBlock Text="{Binding Key}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="48" Padding="6"
Foreground="{Binding Converter={StaticResource ForegroundConverter}}"
VerticalAlignment="Center"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>

Se osservate bene il codice dello stile PeopleCityJumpListStyle, noterete una differenza: la


proprietà LayoutMode è impostata a List invece che a Grid. Tale valore fa sì che gli elementi
mostrati nella jump list non siano disposti a griglia (come le lettere dell’esempio
precedente, che erano una affiancata all’altra), ma a lista (ogni elemento sotto l’altro).
Questo perché la jump list, contenendo non più una semplice lettera ma il nome completo
della città, avrebbe dei problemi di visualizzazione mantenendo gli elementi affiancati.
Anche in questo caso entrambi gli stili utilizzano dei converter per sfruttare l’accent color
del telefono e per gestire la possibilità che esistano dei gruppi senza elementi.
A questo punto ci basta definire il controllo LongListSelector, come abbiamo fatto in
precedenza:
<phone:LongListSelector
x:Name="PeopleByCity"
ItemTemplate="{StaticResource PeopleItemTemplate}"
GroupHeaderTemplate="{StaticResource
PeopleCityGroupHeaderTemplate}"
JumpListStyle="{StaticResource PeopleCityJumpListStyle}"
IsGroupingEnabled="True"
LayoutMode="List"
/>

Figura 3.10 - Il controllo LongListSelector con raggruppamento personalizzato.


I controlli specifici di Windows Phone
Fin qui abbiamo fatto una panoramica di alcuni controlli base che sono disponibili: ora
vedremo in dettaglio, invece, alcuni controlli specifici di Windows Phone e non
disponibili in altre piattaforme, come Panorama, Pivot e ApplicationBar.

Il controllo Panorama
Abbiamo già parlato di questo controllo nel Capitolo 1, in cui abbiamo analizzato il suo
funzionamento e gli scenari d’uso. In questo paragrafo vedremo, dal punto di vista
tecnico, come inserire e gestire un panorama all’interno dell’applicazione.
Il controllo Panorama fa parte della libreria Microsoft.Phone.Controls e, per utilizzarlo, è
necessario dichiarare nello XAML della pagina il seguente namespace:
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"

Tale controllo è composto da un contenitore, chiamato Panorama, al cui interno vengono


inseriti uno o più PanoramaItem, che rappresentano le varie schermate.
<controls:Panorama Title="Panorama">
<controls:PanoramaItem Header="Primo elemento">
<StackPanel>
<TextBlock Text="Pagina 1" />
</StackPanel>
</controls:PanoramaItem>
<controls:PanoramaItem Header="Primo elemento">
<StackPanel>
<TextBlock Text="Pagina 2" />
</StackPanel>
</controls:PanoramaItem>
</controls:Panorama>

Il controllo Panorama ha una proprietà Title che rappresenta il titolo che viene mostrato in
cima alla pagina: è una proprietà di tipo Content e può contenere, perciò, al suo interno dello
XAML più complesso per personalizzare l’aspetto visivo.
In più, ogni vista all’interno di un panorama può avere un suo specifico titolo,
rappresentato dalla proprietà Header.
Come abbiamo appreso nel Capitolo 1, spesso un panorama è contraddistinto da
un’immagine di sfondo: a tale scopo abbiamo a disposizione la proprietà Background, che
accetta uno dei tanti brush che abbiamo imparato a usare nel Capitolo 2.
<controls:Panorama Title="Panorama">
<controls:Panorama.Background>
<ImageBrush ImageSource="Background.png" />
</controls:Panorama.Background>
</controls:Panorama>

Un’altra caratteristica di un PanoramaItem consiste nel poter essere disposto orizzontalmente


e quindi nell’occupare lo spazio di due o più viste; per ottenere questo risultato occorre
inserire un contenuto più largo della dimensione dello schermo e impostare la proprietà
Orientation del singolo PanoramaItem su Horizontal.

<controls:Panorama Title="Panorama">
<controls:PanoramaItem Header="Primo elemento" Orientation="Horizontal">
<Grid Width="6⊘⊘">

</Grid>
</controls:PanoramaItem>
</controls:Panorama>

Sfruttando l’evento SelectionChanged e la proprietà SelectedIndex, siamo in grado di capire


quando l’utente si sposta da un PanoramaItem all’altro e di determinare esattamente in quale
si trova in un dato momento: allo scopo di migliorare le performance e il tempo di startup
dell’applicazione, possiamo sfruttare questo meccanismo, ad esempio, per caricare i dati
contenuti in un PanoramaItem solo nel momento in cui questo viene visualizzato.

Il controllo Pivot
Anche di questo controllo ci siamo occupati nel Capitolo 1: dal punto di vista tecnico, il
suo utilizzo è uguale a quello del controllo Panorama.
Anch’esso è incluso nella libreria Microsoft.Phone.Controls e richiede un’apposita
dichiarazione nei namespace della pagina per essere utilizzato.
Il controllo Pivot funge da contenitore di uno o più PivotItem, che rappresentano le varie viste
il cui titolo è definito dalla proprietà Header.
<controls:Pivot Title="Pivot">
<controls:PivotItem Header="Primo elemento">
<StackPanel>
<TextBlock Text="Pagina 1" />
</StackPanel>
</controls:PivotItem>
<controls:PivotItem Header="Seccondo elemento">
<StackPanel>
<TextBlock Text="Pagina 2" />
</StackPanel>
</controls:PivotItem>

</controls:Pivot>

Analogamente a quanto avviene per il controllo panorama, anche in questo caso abbiamo
a disposizione l’evento SelectionChanged e la proprietà SelectedIndex per essere notificati ogni
volta che l’utente passa da un PivotItem all’altro e determinare la vista corrente.
La ApplicationBar
La ApplicationBar è un elemento un po’ anomalo all’interno dell’architettura dei controlli di
Windows Phone: se apriamo una pagina qualsiasi di Windows Phone capiremo perché.
In fondo troveremo, infatti, commentato, il codice necessario per mostrare una application
bar all’interno della pagina corrente: la prima particolarità è che tale elemento è al di fuori
della griglia principale che rappresenta la vista della nostra applicazione, tanto da essere
dichiarata come proprietà della classe PhoneApplicationPage.
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
..
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

La particolarità della ApplicationBar è che, al contrario di tutti gli altri elementi visual, non
deriva da FrameworkElement, ovvero la classe base da cui derivano tutti i controlli XAML e
che espone le proprietà base che vengono condivise da tutti, come DataContext o x:Name.
Da questa scelta derivano alcune differenze nelle modalità di interazione con la stessa: è
importante sottolineare la mancanza di supporto al binding o la possibilità di interagire
da codice con gli elementi all’interno della application bar sfruttandone il nome definito
nella proprietà x:Name.
Prima di affrontare l’argomento, vediamo l’anatomia di una application bar: il cuore è il
controllo ApplicationBar, che al suo interno può contenere due tipologie di elementi:
• ApplicationBarIconButton rappresenta un pulsante mostrato nella barra, che è caratterizzato
da una icona (la proprietà IconUri) e da una etichetta (la proprietà Text). Vi ricordo che
una application bar può contenere al massimo quattro elementi di questo tipo;
• ApplicationBar.MenuItems è, a sua volta, un contenitore per i pulsanti testuali che vengono
mostrati quando la application bar viene espansa. Tali pulsanti sono rappresentati dal
controllo ApplicationBarMenuItem, che mette a disposizione la sola proprietà Text per
personalizzare l’etichetta.
Entrambe le tipologie di controlli permettono di sottoscrivere l’evento Click, che viene
invocato alla pressione del pulsante da parte dell’utente.
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png"
Text="Button 1" Click="ApplicationBarIconButton_Click" />
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="MenuItem 1" Click="ApplicationBarMenuItem_Click"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Un’altra proprietà importante comune a entrambi i controlli è IsEnabled: quando viene


messa a false, il pulsante viene oscurato, per non dare all’utente la possibilità di utilizzarlo.
A volte, infatti, si possono verificare delle condizioni che fanno sì che l’operazione
collegata a quel pulsante non sia disponibile.
Abbiamo appena detto, però, che, da codice, non siamo in grado di interagire con i
pulsanti tramite la proprietà x:Name. Dobbiamo perciò usare l’oggetto ApplicationBar,
accessibile dal code behind, che rappresenta la barra inclusa nella pagina corrente (dato
che ce ne può essere soltanto una). Tra le varie proprietà esposte da questo oggetto
abbiamo le collezioni Buttons (i pulsanti principali) e i MenuItems (i pulsanti testuali).
Essendo delle collezioni, possiamo specificare l’indice dell’elemento che vogliamo
recuperare, effettuare il cast al tipo specifico del pulsante e infine andare a modificare la
proprietà che ci serve:
ApplicationBarIconButton button = this.ApplicationBar.Buttons[⊘]
as ApplicationBarIconButton;
button.Text = "Pulsante";
button.IsEnabled = false;
ApplicationBarMenuItem item = this.ApplicationBar.MenuItems[⊘]
as ApplicationBarMenuItem;
item.Text = "Menu item";

Una caratteristica introdotta a partire da Windows Phone 7.5 è la possibilità di


minimizzare completamente la barra, lasciando all’utente il compito di espanderla per
visualizzarla.
Per raggiungere questo scopo è sufficiente impostare a Minimized la proprietà Mode del
controllo: in questo modo saranno presenti solamente i tre puntini sul lato destro, per
avvisare l’utente della presenza di una application bar.
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" Mode="Minimized">
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Figura 3.11 - Una application bar in modalità minimale.

Infine, è possibile agire anche sull’opacità della barra, che può essere opaca o
semitrasparente. Nel primo caso, il limite inferiore del contenuto sarà dato dalla
dimensione della barra; nel secondo, il contenuto potrà occupare tutta la dimensione dello
schermo, dato che sarà visualizzato sotto la barra.
Per ottenere questo risultato occorre impostare la proprietà Opacity del controllo, che accetta
un valore da 0 (completamente trasparente) a 1 (opaca). Per ottenere la semitrasparenza
occorre impostare il valore 0.5.
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" Opacity="⊘.5">
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Una ApplicationBar alternativa


In rete esistono molte implementazioni alternative della ApplicationBar che cercano di
ovviare ai limiti menzionati poco sopra, limiti che, soprattutto nel caso in cui utilizziate un
pattern di sviluppo come Model-View-ViewModel (riguardo al quale troverete qualche
cenno nell’appendice, al termine del libro), possono diventare molto fastidiosi.
Una delle implementazioni più interessanti è contenuta nella libreria Phone7.Fx,
disponibile su NuGet e su Codeplex all’indirizzo http://phone7.codeplex.com/.
Tra le varie componenti di questo toolkit c’è un controllo chiamato BindableApplicationBar, che
si comporta in tutto e per tutto come una application Bar, con la differenza che pulsanti e
menu item supportano il binding.
Ecco un esempio di BindableApplicationBar con un pulsante e un menu item:
<Preview:BindableApplicationBar>
<Preview:BindableApplicationBarIconButton IconUri="/Icons/Save.png"
Text="{Binding Path=ButtonName}" />
<Preview:BindableApplicationBarMenuItem Text="{Binding Path=MenuItemName}" />
</Preview:BindableApplicationBar>

Una volta installata la libreria, per utilizzare questo controllo è necessario includere il
seguente namespace nel vostro XAML:
xmlns:Preview="clr-namespace:Phone7.Fx.Controls;assembly=Phone7.Fx"

Fate attenzione che questa speciale application bar, al contrario di quella standard, non va
dichiarata al di fuori della Grid principale (nel template standard è identificata dal nome
LayoutRoot), ma al suo interno.
System tray
In realtà la system tray non è tanto un controllo quanto un’area dello schermo in cui
Windows Phone visualizza una serie di informazioni base sullo stato del telefono, come la
batteria, l’intensità del segnale della rete cellulare, l’orario ecc.

Figura 3.12 - La system tray e le varie icone disponibili.

Come sviluppatori abbiamo la possibilità di scegliere se rendere quest’area visibile o


meno: in alcuni casi, infatti, la visualizzazione della barra nera che contiene le varie icone
può mal adattarsi al layout e ai colori utilizzati nella vostra applicazione, rendendo
l’aspetto visivo meno piacevole.
È importante, però, tenere a mente che, senza la system tray, l’utente non ha la possibilità
di accedere a informazioni importanti come lo stato di carica della batteria o la potenza del
segnale, senza uscire dall’applicazione: soprattutto nel caso in cui la vostra applicazione
acceda a Internet (motivo per cui l’utente potrebbe voler controllare l’intensità del segnale
e la presenza o meno di connettività 3G), è sconsigliato nascondere la system tray.
La scelta se abilitare o meno la system tray viene fatta a livello di pagina, tramite uno
degli attributi della classe PhoneApplicationPage, chiamato SystemTray.IsVisible.
<phone:PhoneApplicationPage
x:Class="AlarmsReminders.MainPage"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">

Quando questa proprietà è a True, la barra è visibile, quando è a False viene invece nascosta.
È possibile anche personalizzare l’aspetto della system tray modificandone i colori di
background e foreground, tramite due metodi messi a disposizione dalla classe SystemTray.
SystemTray.SetBackgroundColor(this, Colors.Blue);
SystemTray.SetForegroundColor(this, Colors.Green);

A partire da Windows Phone 7.5 la system tray ha la possibilità di includere una progress
bar, per mostrare lo stato di avanzamento di un’operazione: questa novità è mutuata
direttamente dal comportamento di molte applicazioni native che, durante una fase di
caricamento, mostrano in cima alla system tray una progress bar per indicare che c’è
un’operazione in corso (ad esempio, l’hub People durante la fase di caricamento degli
ultimi aggiornamenti dai social network).
A questo scopo l’SDK include una classe chiamata ProgressIndicator, che espone tre proprietà
fondamentali per personalizzare il comportamento:
• Text: rappresenta un testo opzionale che può essere mostrato prima della barra (ad
esempio, Downloading);
• IsVisible: se la barra deve essere visibile o meno;
• IsIndeterminate: le progress bar hanno due modalità di funzionamento: quella standard
(quindi questa proprietà è a false) viene utilizzata per le operazioni di caricamento
per cui è possibile determinare la percentuale di completamento (ad esempio, un
download). Quando questa proprietà è a false, invece, la barra viene utilizzata per
dare all’utente un feedback visivo del fatto che c’è un’operazione in corso, senza
però mostrarne lo stato di avanzamento preciso.

Figura 3.13 - Le due modalità di funzionamento di una progress bar: sopra indeterminata, sotto tradizionale.

Quando la progress bar è usata per mostrare un avanzamento, dovete interagire con la
proprietà Value, che accetta un valore da 0 a 1.
ProgressIndicator progress = new ProgressIndicator();
progress.Text = "Downloading";
progress.IsVisible = true;
progress.IsIndeterminate = true;

SystemTray.SetProgressIndicator(this, progress);

Una volta creata l’istanza della classe ProgressIndicator è possibile assegnarla alla system tray
passandola come parametro del metodo SetProgressIndicator.
Il Phone Toolkit for Windows Phone
L’SDK di Windows Phone manca di molti dei controlli utilizzati in numerose applicazioni
native, come i date picker (per selezionare una data) o gli switch (per le opzioni booleane,
del tipo on /off).
Questo perché Microsoft ha preferito renderli disponibili attraverso un toolkit open source
disponibile su Codeplex e su Nuget chiamato Phone Toolkit for Windows Phone. In
questo modo, il toolkit può seguire un ciclo di vita separato da quello dell’SDK, più
agevole e con aggiornamenti più frequenti, dato che si tratta semplicemente di una libreria
da installare nel nostro progetto.
Il modo più semplice per farlo è utilizzare Nuget: in alternativa, è possibile trovare sul sito
http://phone.codeplex.com il codice sorgente completo, corredato da un progetto con
esempi di codice. Il namespace da includere nello XAML per utilizzare i controlli inclusi
nel toolkit è il seguente:
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=
Microsoft.Phone.Controls.Toolkit"
Il controllo ToggleSwitch
Questo controllo viene utilizzato soprattutto nelle pagine di impostazioni e permette
all’utente di effettuare una scelta di tipo booleano: visivamente viene rappresentato,
infatti, come un interruttore, che può assumere solamente gli stati on e off.

Figura 3.14 - Il controllo Switch.

Il nome del controllo è ToggleSwitch e ha una proprietà Header per personalizzare l’etichetta
che viene mostrata sopra il pulsante.
<toolkit:ToggleSwitch Header="Wi Fi networking"/>

Per conoscere lo stato del controllo abbiamo a disposizione, esattamente come per i
CheckBox, la proprietà IsChecked.

if (WiFi.IsChecked.Value)
{
//eseguo le operazioni
}

Notate l’utilizzo della proprietà IsChecked.Value: questo perché IsChecked è un nullable


boolean. Nel framework .NET il tipo nullable permette di assegnare valori nulli anche a
tipi che normalmente non supporterebbero questo valore, come i DateTime o i boolean.
Gli oggetti di tipo nullable espongono una proprietà HasValue (che indica se sia presente un
valore o meno) e la proprietà Value (con il valore vero e proprio contenuto nell’oggetto).
Il controllo ContextMenu
Il controllo ContextMenu permette di realizzare menu contestuali, che appaiono dopo una
pressione prolungata dell’utente su un elemento. Se vogliamo vedere un esempio di
ContextMenu è sufficiente portarci nell’elenco delle applicazioni installate sul nostro telefono
e tenere il dito premuto per qualche secondo su un elemento della lista: comparirà un
menu contestuale con diverse opzioni, come quella per aggiungere la tile dell’applicazione
in home.

Figura 3.15 - Il controllo ContextMenu.

Questo controllo ha la particolarità di dover essere inserito in altri controlli per poter
essere utilizzato: il menu deve essere infatti legato a un contesto, sul quale fare tap per
poterlo espandere.
<TextBlock Text="Context Menu">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="menu item 1" Click="MenuItem_Click"/>
<toolkit:MenuItem Header="menu item 2" Click="MenuItem_Click"/>
<toolkit:MenuItem Header="menu item 3" Click="MenuItem_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBlock>

In questo esempio, è stato inserito all’interno di un controllo TextBlock: sarà su questo


elemento che l’utente dovrà fare tap per visualizzare il menu contestuale.
All’interno del controllo ContextMenu viene dichiarato un MenuItem per ognuna delle voci di
cui sarà composto il menu: la proprietà Header rappresenta il testo visualizzato, mentre
l’evento Click è disponibile per intercettare la scelta dell’utente.
Da codice possiamo utilizzare l’oggetto sender dell’evento Click per determinare quale
elemento è stato scelto.
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((sender as MenuItem).Header.ToString());
}

Nell’esempio recuperiamo la proprietà Header della voce di menu selezionata e la


mostriamo sullo schermo.
I controlli DatePicker e TimePicker
Questi due controlli permettono la selezione di data e ora all’interno dell’applicazione e
offrono un’esperienza d’uso uguale a quella del sistema operativo quando, nella sezione
impostazioni, vi viene data la possibilità di configurare la data e l’ora di sistema.

Figura 3.16 - I controlli DatePicker e TimePicker.

Il comportamento di questi controlli è lo stesso: entrambi espongono un evento chiamato


ValueChanged, che viene scatenato nel momento in cui è stato selezionato un valore dal
picker.
<toolkit:DatePicker ValueChanged="DatePicker_ValueChanged"/>

<toolkit:TimePicker ValueChanged="TimePicker_ValueChanged"/>

Nell’evento ValueChanged ci viene restituito, in entrambi i casi, un oggetto di tipo


DateTimeValueChangedEventArgs, che contiene due proprietà:

• OldDateTime: quando un picker viene attivato contiene sempre un valore; nel caso sia la
prima volta che viene utilizzato, conterrà la data o l’ora corrente; in caso contrario, il
valore che era inserito precedentemente. Questa proprietà contiene un oggetto di tipo
DateTime con questa informazione;
• NewDateTime: questa proprietà di tipo DateTime contiene il valore effettivamente
selezionato dall’utente.
private void DatePicker_ValueChanged(object sender, DateTimeValueChangedEventArgs e)
{
string message = string.Format("Old date: {⊘} - New date: {1}", e.OldDateTime.
Value.ToShortDateString(),
e.NewDateTime.Value.ToShortDateString());
MessageBox.Show(message);
}
Il controllo WrapPanel
Il controllo WrapPanel rientra tra quelli utilizzabili per definire il layout di un’applicazione,
in quanto si va ad aggiungere ai controlli StackPanel, Grid e Canvas che abbiamo visto all’inizio
del capitolo.
Al contrario degli altri controlli per il layout, un WrapPanel è in grado di disporre in
automatico gli elementi contenuti al suo interno su più righe e colonne nel caso non ci sia
più spazio a disposizione.

Figura 3.17 - Le due tipologie di layout che si ottengono con un WrapPanel.

A seconda del valore della proprietà Orientation, gli elementi saranno disposti in più righe o
colonne: inoltre, tramite le proprietà ItemsHeight e ItemsWidth possiamo definire la dimensione
dell’area che conterrà ogni singolo elemento.
<toolkit:WrapPanel ItemHeight="1⊘⊘" ItemWidth="1⊘⊘" Height="3⊘⊘">
<Rectangle Fill="Aqua" Height="8⊘" Width="8⊘"/>
<Rectangle Fill="Pink" Height="8⊘" Width="8⊘"/>
<Rectangle Fill="Green" Height="8⊘" Width="8⊘"/>
<Rectangle Fill="YellowGreen" Height="8⊘" Width="8⊘"/>
<Rectangle Fill="Red" Height="8⊘" Width="8⊘"/>
</toolkit:WrapPanel>

Se non specifichiamo il valore della proprietà Orientation, viene utilizzato di default


l’orientamento orizzontale (disposto, perciò, su colonne).
Ecco come appare visivamente la differenza tra le due modalità:
Il controllo AutoCompleteBox
Questo controllo è un tipo di particolare di TextBox che visualizza alcuni suggerimenti
all’utente mentre sta digitando un testo.

Figura 3.18 - Il controllo AutoCompleteBox.

Dal punto di vista dell’interazione, il controllo si comporta esattamente come una TextBox:
tramite la proprietà Text possiamo accedere al valore inserito dall’utente.
<toolkit:AutoCompleteBox x:Name="Name" />

Per determinare l’elenco di parole che saranno utilizzate per i suggerimenti occorre
valorizzare la proprietà ItemsSource con una collezione di elementi di tipo string, come
nell’esempio:
List<string> words = new List<string>();
words.Add("Mario");
words.Add("Marco");
words.Add("Stefano");
words.Add("Angela");
Name.ItemsSource = words;

Nel momento in cui l’utente inizierà a digitare del testo, se questo coincide con uno o più
degli elementi facenti parte della collezione, comparirà a video, sopra la casella di testo,
l’elenco dei suggerimenti.
Il controllo ListPicker
Questo controllo permette all’utente di selezionare un elemento tra una possibile scelta di
valori e può operare in due modalità, a seconda del numero di elementi:
• da uno a cinque, gli elementi vengono visualizzati all’interno del controllo stesso;
• da cinque in su, gli elementi vengono visualizzati in una pagina separata, di cui
possiamo personalizzare l’aspetto visuale.
L’utilizzo base del controllo è molto semplice. Una volta dichiarato nello XAML:
<toolkit:ListPicker x:Name="Picker" />

possiamo sfruttare la proprietà ItemsSource per specificare gli elementi che saranno
visualizzati nella lista, come nell’esempio:
List<string> words = new List<string>();
words.Add("Mario");
words.Add("Marco");
words.Add("Stefano");
words.Add("Angela");

Picker.ItemsSource = words;

Ci penserà il controllo in automatico, a seconda del numero di elementi, a utilizzare la


modalità di visualizzazione più appropriata.
Il template di default che viene utilizzato, però, è molto semplice e si limita a visualizzare
il testo dell’elemento: in alcuni casi si vuole avere maggiore controllo sulla
personalizzazione della lista, come avviene, ad esempio, nell’elenco mostrato in Figura
3.16, dove il nome del colore è preceduto da un quadrato riempito del colore stesso.
Figura 3.19 - Le due modalità di utilizzo di un controllo ListPicker.

Per questo scopo abbiamo la possibilità di personalizzare due tipologie di template:


• ItemTemplate rappresenta il template utilizzato quando il numero di elementi è inferiore
a cinque;
• FullModeItemTemplate rappresenta invece il template utilizzato quando il numero di
elementi è superiore a cinque.
<toolkit:ListPicker Header="accent color" FullModeHeader="ACCENTS">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding}" Width="24" Height="24"/>
<TextBlock Text="{Binding}" Margin="12 ⊘ ⊘ ⊘"/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="16 21 ⊘ 2⊘">
<Rectangle Fill="{Binding}" Width="43" Height="43"/>
<TextBlock Text="{Binding}" Margin="16 ⊘ ⊘ ⊘" FontSize="43"
FontFamily="{StaticResource PhoneFontFamilyLight}"/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>

In questo esempio vediamo il codice XAML necessario per realizzare i due controlli
ListPicker mostrati in Figura 3.19.

Impostando la proprietà SelectionMode a Multiple, è possibile attivare un tipo speciale di lista,


in cui ogni elemento è preceduto da un checkbox per consentire la selezione multipla.
In tal caso possiamo utilizzare la proprietà SelectedItems per determinare quali elementi sono
stati selezionati.
Il TiltEffect
In questo caso non parliamo di un vero e proprio controllo, ma di un helper che vi aiuterà
a implementare uno degli effetti più utilizzati nelle applicazioni Windows Phone: il tilt
effect.
Il tilt effect è l’effetto che viene applicato nel momento in cui fate tap su un elemento
dell’interfaccia, il quale si inclinerà leggermente per dare un feedback della pressione
all’utente.
Il modo più semplice per applicarlo è aggiungere l’attributo TiltEffect.IsEnabled al nodo
PhoneApplicationPage, valorizzandolo a true:

<phone:PhoneApplicationPage
x:Class="SilverlightToolkit.MainPage"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.
Controls.Toolkit"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
toolkit:TiltEffect.IsTiltEnabled="True"
>

In questo modo, automaticamente, tutti i controlli presenti nella pagina in grado di


interagire con l’utente implementeranno l’effetto tilt. Nel caso in cui volessimo
disabilitarlo su alcuni controlli, è sufficiente aggiungere la proprietà TiltEffect.SuppressTilt
nella dichiarazione del controllo, come nell’esempio:
<Button Content="Click me" toolkit:TiltEffect.SuppressTilt="True" />
Il controllo ExpanderView
Il controllo ExpanderView è stato introdotto in una delle release più recenti del toolkit e
consente di ricreare l’effetto utilizzato dall’applicazione nativa Outlook per gestire le
conversazioni: un elemento che contiene al suo interno altri elementi, che vengono espansi
al tap dell’utente.

Figura 3.20 - Il controllo ExpanderView.

Ecco un esempio di dichiarazione di un controllo Expander nello XAML:


<toolkit:ExpanderView x:Name="Exp"
IsNonExpandable="False"
Header="Mails"
Expander="Some mails to read"
NonExpandableHeader="Just 1 mail to read"
/>

Vediamo in dettaglio cosa rappresentano le varie proprietà:


• la proprietà IsNonExpandable è di tipo booleano e serve per definire se il controllo in
questione può essere espanso o meno. Tipicamente si tratta di una proprietà che
occorre valutare dinamicamente in base al numero di elementi da visualizzare: ad
esempio, se la lista di elementi contiene un solo item, allora la proprietà andrà
impostata a True;
• la proprietà Header contiene il testo che viene visualizzato come header dell’intero
controllo (in Outlook, coincide con il nome del mittente dell’ultima mail facente
parte della conversazione);
• la proprietà Expander contiene il testo che viene visualizzato appena sotto l’header
quando il controllo può essere espanso e comprende più elementi. In Outlook, questa
proprietà viene utilizzata per visualizzare l’oggetto e il numero di mail racchiuse
all’interno della conversazione;
• la proprietà NonExpandableHeader contiene, invece, il testo che viene visualizzato come
header del controllo nel momento in cui la proprietà IsExpandable è impostata a
false (tipicamente, quando c’è un solo elemento).
Ognuna di queste proprietà è di tipo Content, perciò, esattamente come abbiamo visto per i
Button, abbiamo la possibilità di inserire XAML e non solamente stringhe di testo, per
rendere visivamente più completo l’aspetto finale.

Figura 3.21 - Le varie proprietà che costituiscono il controllo ExpanderView.

Infine, come per tutti gli oggetti pensati per gestire collezioni, abbiamo a disposizione la
proprietà ItemsSource per specificare la collezione dati sorgente e la proprietà ItemTemplate per
definire il template di un singolo elemento.
Il controllo MultiSelectList
Il controllo MultiSelectList è un’alternativa alla ListBox che offre la possibilità all’utente
di selezionare più elementi della lista. Possiamo ritrovare questo approccio in Outlook: nel
momento in cui l’utente fa tap a sinistra di una mail, compaiono una serie di checkbox di
fianco a ognuna di esse, dando la possibilità di selezionarne più di una e di eseguire
un’operazione (ad esempio, la cancellazione) su tutti gli elementi scelti.

Figura 3.22 - Il controllo MultiSelectList.

<toolkit:MultiselectList x:Name="EmailList"
SelectionChanged="EmailList_SelectionChanged"
IsSelectionEnabledChanged="EmailList_IsSelectionEnabledChanged"
/>

Il comportamento predefinito del controllo è quello appena descritto: solo nel momento in
cui l’utente fa tap a sinistra di un elemento si abilita la modalità di selezione multipla. È
possibile, però, forzare tale modalità da codice: ad esempio, il client di posta di Windows
Phone include nella application bar un pulsante per ottenere lo stesso risultato. Per farlo
occorre agire sulla proprietà IsSelectionEnabled: il controllo espone anche un evento, chiamato
IsSelectionEnabledChanged, che viene scatenato ogni qualvolta viene attivata o disattivata la
modalità di selezione multipla; tale evento può essere utilizzato per cambiare alcuni
aspetti dell’interfaccia in base al contesto.
Anche in questo caso il client di posta ci offre un esempio: nel momento in cui abilitiamo
la modalità di selezione, i pulsanti nella application bar cambiano, per lasciare spazio a
operazioni legate alla possibilità di selezionare più mail.
Infine, la proprietà SelectedItems contiene la collezione di elementi che sono stati selezionati.
Il controllo PhoneTextBox
Il controllo PhoneTextBox è una casella di testo che offre diverse funzionalità avanzate
rispetto al controllo TextBox base.

Figura 3.23 - Alcuni dei possibili utilizzi del controllo PhoneTextBox.

Le nuove proprietà a disposizione sono:


• Hint: permette di specificare un placeholder per indicare lo scopo della textbox (ad
esempio, “Inserisci il nome”). Tale placeholder viene inserito con un colore più
chiaro all’interno della casella di testo e sparisce in automatico nel momento in cui si
inizia a inserire del testo;
• MaxLength: è possibile limitare la lunghezza del testo che si può inserire nella casella
assegnando a questa proprietà il numero massimo di caratteri;
• LimitIndicatorVisible: nel caso abbiate abilitato un limite al numero di caratteri sfruttando
la proprietà MaxLength, è possibile attivare un contatore (analogo a quello che compare
quando state scrivendo un sms) che mostra il numero di caratteri inseriti e il massimo
numero di caratteri consentiti;
• LengthIndicatorThreshold: è possibile rendere visibile il contatore di caratteri solo quando si
supera il numero di caratteri specificato in questa proprietà. Se non la si imposta e
LimitIndicatorVisible è a true, il contatore sarà invece sempre visualizzato;
• DisplayedMaxLength: di default il contatore di caratteri mostra come numero di caratteri
massimo il valore della proprietà MaxLength. È possibile personalizzare questo
valore assegnando un numero a questa proprietà;
• AcceptsReturn: quando questa proprietà è impostata a true, è possibile andare a capo con
il testo premendo il pulsante Return della tastiera virtuale;
• ActionIcon: è possibile inserire un’icona alla fine della TextBox, con la quale l’utente
potrà interagire per eseguire un’azione. Ad esempio, una TextBox dedicata alla
ricerca potrebbe contenere l’icona di una lente d’ingrandimento che, quando viene
toccata, lancia la funzione di ricerca;
• ActionIconTapped: è l’evento scatenato nel momento in cui si fa tap sulla ActionIcon.
Ecco qualche esempio di utilizzo del controllo:
<!-- casella di testo che accetta un testo con massimo 4⊘ caratteri;
dopo l'inserimento di 1⊘ caratteri, viene mostrato il contatore-->

<toolkit:PhoneTextBox Hint="Last Name"


MaxLength="4⊘"
LengthIndicatorVisible="True"
LengthIndicatorTheshold="1⊘" />

<toolkit:PhoneTextBox ActionIcon="/Images/Search.png"
MinHeight="15⊘"
TextWrapping="Wrap" AcceptsReturn="True" />

<!-- casella di testo con un pulsante per la ricerca -->


<toolkit:PhoneTextBox ActionIcon="/Images/Search.png"
MinHeight="150"
TextWrapping="Wrap" AcceptsReturn="True"
ActionIconTapped="PhoneTextBox_ActionIconTapped"
/>
Il controllo HubTile
Il controllo HubTile permette di inserire all’interno di un’applicazione degli elementi con lo
stesso look & feel delle tile, inclusa l’animazione di rotazione, che viene attivata dopo
qualche secondo.
Ecco un esempio del codice XAML necessario a inserire questo controllo:
<toolkit:HubTile Title="Seattle"
Notification="Washington"
DisplayNotification="True"
Source="/Images/Seattle.jpg"
GroupTag="Cities"
/>

Vediamo ora nel dettaglio cosa rappresentano queste proprietà:


• Title: è il titolo della tile, che viene visualizzato sul lato principale con un font più
grosso e alternato con un’animazione all’immagine;
• Notification: è il titolo mostrato sul retro della tile. Quando la tile viene ruotata, il valore
di questa proprietà viene visualizzato come titolo con un font più grosso, mentre il
valore della proprietà Title viene visualizzato in fondo alla tile con un font più piccolo;
• Source: l’immagine che viene mostrata sul lato principale della tile;
• DisplayNotification: si tratta di una proprietà booleana che permette di abilitare o meno la
visualizzazione del retro della tile. Se viene impostata a false, non sarà mai mostrato
il retro dell’immagine;
• GroupTag: grazie a questa proprietà è possibile raggruppare più tile all’interno di un
gruppo identificato da un nome univoco. Grazie alla classe HubTileService introdotta nel
toolkit, è possibile effettuare operazioni su un intero gruppo di tile in base al nome
specificato in questa proprietà.
Figura 3.24 - L’aspetto di un controllo HubTile con le relative proprietà.

Nello specifico, la classe HubTileService consente di bloccare l’animazione di una singola tile
o di un intero gruppo. Nel primo caso, si usa il metodo FreezeHubTile seguito dal riferimento
all’oggetto di tipo HubTile; nel secondo, invece, si utilizza il metodo FreezeGroup seguito dal
nome del gruppo. Tutte le tile la cui proprietà GroupTag è uguale al nome cesseranno di
animarsi.
Se, invece, vogliamo gestire lo stop dell’animazione direttamente dal controllo stesso,
abbiamo a disposizione la proprietà di tipo booleano IsFrozen.
Dal punto di vista dell’interazione, il controllo HubTile si comporta come un pulsante:
possiamo gestire il tap dell’utente tramite l’evento Tap.
Il controllo CustomMessageBox
Uno dei controlli che abbiamo già visto nel corso dei capitoli precedenti è il MessageBox, che
consente di mostrare sullo schermo dei messaggi di conferma.
Tale controllo è molto semplice da utilizzare, ma, allo stesso tempo, anche molto scarno:
possiamo specificare solamente un titolo, un testo descrittivo e se vogliamo a disposizione
solamente il pulsante Ok o anche il pulsante Cancel. Dopodiché, da codice, ci limitiamo a
mostrarlo ed, eventualmente, a determinare quale dei due pulsanti sia stato premuto.
Il controllo CustomMessageBox incluso nel toolkit consente invece di creare MessageBox
personalizzati, così da ricreare lo stesso look & feel di alcuni messaggi mostrati dal
sistema operativo stesso.
La classe base è CustomMessageBox, che supporta una serie di proprietà per personalizzare il
messaggio:
• Caption rappresenta il titolo del messaggio;
• Message rappresenta il testo del messaggio;
• Content rappresenta l’eventuale contenuto aggiuntivo (si vedano gli esempi successivi);
• LeftButtonContent e RightButtonContent consentono di personalizzare il testo dei pulsanti
sinistro e destro inclusi nel messaggio;
• IsFullScreen permette di specificare se il messaggio deve occupare tutta la dimensione
dello schermo, oppure solamente quella del contenuto.
Ecco un esempio di definizione di un oggetto di tipo CustomMessageBox:
CustomMessageBox messageBox = new CustomMessageBox()
{
Caption = "Titolo",
Message = "Descrizione",
LeftButtonContent = "Ok",
RightButtonContent = "Annulla",
IsFullScreen = true
};
Figura 3.25 - Un controllo CustomMessageBox a tutto schermo.

Una volta definito l’oggetto, possiamo sottoscriverci all’evento Dismissed, che viene
invocato nel momento in cui l’utente ha interagito con uno dei pulsanti e ha chiuso la
schermata.
messageBox.Dismissed+=messageBox_Dismissed;

void messageBox_Dismissed(object sender, DismissedEventArgs e)


{
switch (e.Result)
{
case CustomMessageBoxResult.LeftButton:
// È stato premuto il pulsante sinistro
break;
case CustomMessageBoxResult.RightButton:
// È stato premuto il pulsante destro
break;
case CustomMessageBoxResult.None:
// Non è stato restituito alcun risultato
break;
default:
break;
}
}
Il parametro che viene restituito all’evento è di tipo DismissedEventArgs e contiene una
proprietà Result, che è di tipo CustomMessageBoxDefault: si tratta di un enumeratore, che può
assumere tre valori:
• LeftButton, nel caso in cui l’utente abbia premuto il pulsante sinistro;
• RightButton, nel caso in cui l’utente abbia premuto il pulsante destro;
• None, nel caso in cui la schermata sia stata chiusa senza che l’utente abbia premuto
uno dei due pulsanti.
È possibile, inoltre, personalizzare ulteriormente il contenuto della schermata, inserendo
degli altri controlli con il quale l’utente può interagire, sfruttando la proprietà Content. È
sufficiente creare i controlli da codice e poi assegnarli a tale proprietà.

Figura 3.26 - Un controllo CustomMessageBox con un link al suo interno.

Ad esempio, è possibile inserire un link sfruttando il controllo Hyperlink, come nell’esempio


che segue:

HyperlinkButton hyperlinkButton = new HyperlinkButton()
{
Content = "Vai al blog",
Margin = new Thickness(0, 28, ⊘, 8),
HorizontalAlignment = HorizontalAlignment.Left,
NavigateUri = new Uri("http://www.qmatteoq.com/", UriKind.Absolute)
};

CustomMessageBox messageBox = new CustomMessageBox()


{
Caption = "Titolo",
Message = "Descrizione",
Content = hyperlinkButton,
LeftButtonContent = "Ok",
RightButtonContent = "Annulla",
IsFullScreen = true
};

Figura 3.27 - Un controllo CustomMessageBox con un checkbox al suo interno.

Oppure inserire un Checkbox, con il quale l’utente possa interagire per attivare o meno una
determinata opzione:
CheckBox checkBox = new CheckBox()
{
Content = "Non chiederlo nuovamente",
Margin = new Thickness(⊘, 14, ⊘, -2)
};
CustomMessageBox messageBox = new CustomMessageBox()
{
Caption = "Titolo",
Message = "Descrizione"
Content = checkBox,
LeftButtonContent = "Ok",
RightButtonContent = "Annulla",
IsFullScreen = true
};
II controllo Rating
Il controllo Rating consente di dare all’utente la possibilità di lasciare un voto. Le possibilità
sono molteplici: un’applicazione di e-commerce potrebbe consentire all’utente di lasciare
un voto al prodotto acquistato; un’applicazione di gestione della propria libreria potrebbe
consentire di dare un voto ai libri ecc.
I voti sono espressi tramite stelle: l’utente ha la possibilità di fare tap su una stella per
selezionare il voto corrispondente. Automaticamente, il numero di stelle che corrisponde
al voto scelto cambierà colore, per dare un feedback all’utente.
Ecco un esempio di dichiarazione del controllo nello XAML:
<toolkit:Rating x:Name="RatingControl" RatingItemCount="5" ShowSelectionHelper="False"
AllowSelectingZero="False" AllowHalfItemIncrement="False" />

Le proprietà principali sono:


• RatingItemCount rappresenta il massimo voto che l’utente può dare, ovvero il numero di
stelle che viene visualizzato;
• ShowSelectionHelper: quando questa proprietà è impostata a true, l’utente ha la possibilità
di tenere premuto su una stella qualsiasi e, trascinando il dito, visualizzare il voto
numerico corrispondente alla stella scelta;
• AllowSelectingZero permette di specificare se l’utente ha la possibilità o meno di dare
voto 0 (nessuna stella);
• AllowHalfItemIncrement permette di specificare se l’utente ha la possibilità di dare mezzi
voti (la stella viene selezionata a metà) oppure solo voti interi.
Il voto espresso viene valorizzato nella proprietà Value. Nell’esempio, da codice,
recuperiamo tale valore e lo mostriamo a schermo:
private void OnRatingButtonClicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("Il voto selezionato è " + RatingControl.Value);
}
Figura 3.28 - Il controllo Rating.
Le transizioni
Il Phone Toolkit include anche una serie di animazioni che permettono di implementare un
effetto di transizione nel momento in cui si passa da una pagina all’altra dell’applicazione.
La gestione degli effetti di transizione è a carico dal frame dell’applicazione, ovvero la
classe che rappresenta il contenitore di tutte le pagine. Se apriamo il file App.xaml.cs,
all’interno della region chiamata Phone application initialization troviamo il metodo
InizializePhoneApplication, che viene chiamato al primo avvio e ha il compito di inizializzare
tutto il necessario affinché l’applicazione funzioni correttamente.
Tra questi c’è anche l’inizializzazione del frame dell’applicazione, tramite le seguenti
righe di codice:
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;

Il PhoneApplicationFrame è la classe standard di Windows Phone che rappresenta il frame che


contiene le varie pagine, il quale però non offre il supporto nativo alle transizioni.
Il Phone Toolkit include una classe, chiamata TransitionFrame, che estende il
PhoneApplicationFrame con il supporto alle animazioni. La prima cosa da fare, perciò, è
modificare l’inizializzazione nel seguente modo:
RootFrame = new TransitionFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;

A questo punto, nello XAML delle nostre pagine possiamo utilizzare alcuni controlli
presenti nel toolkit per gestire le animazioni.
Ecco un esempio di codice XAML che possiamo inserire nelle nostre pagine, subito dopo
il nodo principale e prima dell’inizio dei controlli visuali veri e propri:
<toolkit:TransitionService.NavigationInTransition>
<toolkit:NavigationInTransition>
<toolkit:NavigationInTransition.Backward>
<toolkit:TurnstileTransition Mode="BackwardIn"/>
</toolkit:NavigationInTransition.Backward>
<toolkit:NavigationInTransition.Forward>
<toolkit:TurnstileTransition Mode="ForwardIn"/>
</toolkit:NavigationInTransition.Forward>
</toolkit:NavigationInTransition>
</toolkit:TransitionService.NavigationInTransition>

<toolkit:TransitionService.NavigationOutTransition>
<toolkit:NavigationOutTransition>
<toolkit:NavigationOutTransition.Backward>
<toolkit:TurnstileTransition Mode="BackwardOut"/>
</toolkit:NavigationOutTransition.Backward>
<toolkit:NavigationOutTransition.Forward>
<toolkit:TurnstileTransition Mode="ForwardOut"/>
</toolkit:NavigationOutTransition.Forward>
</toolkit:NavigationOutTransition>
</toolkit:TransitionService.NavigationOutTransition>

I due controlli utilizzati si chiamano TransitionService.NavigationInTransition e


TransitionService.NavigationOutTransition: il primo contiene le animazioni utilizzate quando ci
spostiamo verso la pagina corrente; il secondo quelle utilizzate quando ci spostiamo dalla
pagina corrente verso un’altra pagina.
Dopodiché, per ognuno dei due casi possiamo specificare due tipologie di animazione:
• Backward: è l’animazione utilizzata quando si arriva alla pagina corrente attraverso la
pressione del tasto Back;
• Forward: è l’animazione utilizzata quando si arriva alla pagina corrente tramite il
normale flusso di navigazione.
Infine, all’interno del blocco Backward e Forward possiamo specificare la vera e propria
animazione, ognuna delle quali è rappresentata da un controllo.
Vediamo i principali:
• RollTransition: viene applicato un effetto di rotazione standard;
• RotateTransition: viene applicato un effetto di rotazione, personalizzabile impostando
l’attributo Mode (esempi di valori supportati: In18⊘Clockwise, Out9⊘Counterclockwise ecc.);
• SlideTransition:
viene applicato un effetto di “scivolamento”, personalizzabile
impostando l’attributo Mode (esempi di valori supportati: SlideDownFadeIn,
SlideUpFadeIn);

• SwivelTransition: viene applicato un effetto di rotazione sull’asse verticale, anch’esso


personalizzabile tramite l’attributo Mode (esempi di valori supportati: BackwardIn,
ForwardIn);

• TurnstileTransition: viene applicato un effetto stile “pagine di un libro che vengono


sfogliate”, personalizzabile tramite l’attributo Mode (esempi di valori supportati:
BackwardIn, ForwardOut);

• TurnstileFeatherTransition: come il precedente, ma con la possibilità di personalizzare


l’entrata degli elementi che compongono la pagina.
Per dare alla vostra applicazione un look coerente, è importante scegliere una sola
tipologia di animazione e utilizzarla in tutte le pagine. Questa regola vale ancora di più se
stiamo parlando di due pagine collegate: in questo caso l’utilizzo di due animazioni
diverse per l’entrata e per l’uscita rischierebbe di generare confusione nell’utente.

L’animazione TurnstileFeatherTransition
Come già anticipato, il TurnstileFeatherTransition si comporta come il TurnstileTransition, creando
perciò l’effetto di sfoglio della pagina di un libro. Utilizzando questa animazione, però,
abbiamo la possibilità, con una serie di attributi, di personalizzare il comportamento degli
elementi che compongono la pagina, applicando tale effetto anche ai singoli controlli e
determinandone l’ordine di entrata.
Il primo passo per realizzare questo obiettivo è applicare alla pagina un effetto di tipo
TurnstileFeatherTransition, come nell’esempio seguente:

<toolkit:TransitionService.NavigationInTransition>
<toolkit:NavigationInTransition>
<toolkit:NavigationInTransition.Backward>
<toolkit:TurnstileFeatherTransition Mode="BackwardIn"/>
</toolkit:NavigationInTransition.Backward>
<toolkit:NavigationInTransition.Forward>
<toolkit:TurnstileFeatherTransition Mode="ForwardIn"/>
</toolkit:NavigationInTransition.Forward>
</toolkit:NavigationInTransition>
</toolkit:TransitionService.NavigationInTransition>

<toolkit:TransitionService.NavigationOutTransition>
<toolkit:NavigationOutTransition>
<toolkit:NavigationOutTransition.Backward>
<toolkit:TurnstileFeatherTransition Mode="BackwardOut"/>
</toolkit:NavigationOutTransition.Backward>
<toolkit:NavigationOutTransition.Forward>
<toolkit:TurnstileFeatherTransition Mode="ForwardOut"/>
</toolkit:NavigationOutTransition.Forward>
</toolkit:NavigationOutTransition>
</toolkit:TransitionService.NavigationOutTransition>

A questo punto è possibile aggiungere la proprietà TurnstileFeatherEffect.FeatheringIndex ai


controlli dei quali vogliamo personalizzare l’entrata: tale proprietà richiede un valore
numerico che funge da indice. I vari controlli della pagina entreranno sullo schermo
nell’ordine stabilito da questa proprietà. Ecco un semplice esempio:
<StackPanel>
<TextBlock Text="First element" toolkit:TurnstileFeatherEffect.
FeatheringIndex="⊘"></TextBlock>
<TextBlock Text="Second element" toolkit:TurnstileFeatherEffect.
FeatheringIndex="1"></TextBlock>
<TextBlock Text="Third element" toolkit:TurnstileFeatherEffect.
FeatheringIndex="2"></TextBlock>
</StackPanel>

Una volta terminata l’animazione di entrata della pagina, entreranno a schermo i tre
controlli di tipo TextBlock nell’ordine determinato dalla proprietà
TurnstileFeatherEffect.FeatheringIndex: nell’esempio, a partire dal primo all’ultimo.
Altri toolkit disponibili sul web
Su Internet esistono molte altre librerie, sia gratuite sia a pagamento, che contengono
numerosi controlli personalizzati per migliorare l’esperienza d’uso delle nostre
applicazioni.
Uno dei toolkit più interessanti tra quelli gratuiti è il Coding4Fun Toolkit, un progetto
open source di Microsoft disponibile su NuGet e ospitato su Codeplex all’indirizzo
http://coding4fun.codeplex.com/.
Tra i controlli più utili disponibili in questo toolkit troviamo:
• pulsanti con template personalizzati (ad esempio, circolari come quelli della
application bar);
• TimeSpanPicker, simile al TimePicker del Phone Toolkit, che però può essere
utilizzato per selezionare un intervallo temporale (ad esempio, 10 minuti);
• una serie di controlli popup, per mostrare contenuti in overlay alla pagina corrente;
• un controllo Toast, per simulare le notifiche toast all’interno dell’applicazione.
In più la libreria non include solamente controlli, ma anche una serie di helper e converter
già pronti di grande utilità.
Un altro toolkit di grande qualità, ma a pagamento, è quello fornito da Telerik, società
molto conosciuta nel mondo Microsoft, dato che produce librerie di controlli praticamente
per qualsiasi tecnologia facente parte di .NET, da quelle web (come ASP.NET e ASP.NET
MVC) a quelle client (come Windows Forms e WPF).
L’indirizzo di riferimento è http://www.telerik.com/products/windows-phone.aspx, dove è
pubblicata una panoramica di tutti i controlli disponibili. In più sullo Store è disponibile
un’applicazione dimostrativa per poterli provare in prima persona.
Tra le feature più interessanti del toolkit troviamo:
• un frame completo di numerose animazioni;
• controlli per realizzare grafici di vario tipo;
• controlli diagnostici per consentire l’invio di log da parte dell’utente nel caso di
errori;
• helper per la creazione di tile dall’aspetto personalizzato;
• MessageBox più complete, con la possibilità di inserire vari pulsanti;
• un controllo per gestire il supporto alla versione trial nella vostra applicazione.
In conclusione
In questo capitolo abbiamo analizzato in dettaglio tutti i principali controlli a disposizione
dello sviluppatore per creare l’interfaccia grafica della propria applicazione e per poter
interagire con l’utente.
La panoramica è iniziata con i controlli base, come le caselle di testo, i pulsanti e le
etichette. Poi abbiamo visto in dettaglio i controlli esclusivi di Windows Phone, come
Panorama, Pivot e ApplicationBar.
Microsoft ha deciso, però, di non includere tutti i controlli standard all’interno dell’SDK,
ma di distribuirli tramite un progetto open source su Codeplex, chiamato Phone Toolkit
for Windows Phone. Nel corso del capitolo abbiamo visto, uno per uno, tutti i controlli
facenti parte di questa libreria, data la loro importanza e utilità.
Il capitolo si è concluso con qualche suggerimento su alcuni ottimi toolkit disponibili in
rete.
I concetti chiave per sviluppare
un’applicazione Windows Phone

Come abbiamo detto nei capitoli introduttivi, le tecnologie utilizzate per lo


sviluppo di applicazioni Windows Phone sono XAML e C#: chi ha già una
buona conoscenza di questa piattaforma parte sicuramente
avvantaggiato, in quanto l’architettura di base è la medesima.
Se avete già avuto l’occasione di lavorare in passato con Silverlight e WPF, troverete
molte similitudini con il mondo Windows Phone. Ci sono, però, alcuni concetti chiave che
sono esclusivi, dovuti al fatto che stiamo parlando di una piattaforma mobile e non
web/client. Altri aspetti, come la navigazione, sono invece già noti a chi conosce queste
tecnologie, ma è bene soffermarsi ad analizzarli, poiché essi sono indispensabili per poter
sviluppare un’applicazione Windows Phone completa in tutti i suoi aspetti.
Ecco, perciò, che in questo capitolo vedremo in dettaglio quali sono questi concetti chiave,
come il ciclo di vita delle applicazioni, la navigazione e la gestione della rotazione del
dispositivo.
La navigazione
Le applicazioni Windows Phone sono strutturate in pagine, all’interno delle quali vengono
inseriti tutti gli elementi visuali che compongono l’interfaccia grafica. A livello di API, le
pagine sono identificate dalla classe PhoneApplicationPage: se apriamo lo XAML di una pagina
qualsiasi, noteremo infatti che l’elemento radice che contiene tutto il resto è
<phone:PhoneApplicationPage>.

Le pagine sono ospitate all’interno di un frame che funge da contenitore e che è unico per
ogni applicazione: viene, infatti, inizializzato dalla classe App ed è quello che si occupa di
gestire la navigazione tra la pagina e le eventuali transizioni.
Le probabilità che la vostra applicazione sia composta da un’unica pagina sono molto
basse: tipicamente, un’applicazione complessa è composta da più pagine, ognuna delle
quali corrisponde a una vista diversa. Diventa indispensabile, perciò, avere la possibilità di
navigare tra quelle di un’applicazione e di passare dati da una vista all’altra. La classe che
l’SDK mette a disposizione per gestire la navigazione si chiama NavigationService ed è un
singleton: ne esiste un’unica istanza statica condivisa da tutta l’applicazione.
Questa classe è mutuata direttamente da Silverlight, dove la navigazione tra viste funziona
come su Windows Phone. Infatti, la classe NavigationService espone anche alcune proprietà e
metodi originari di Silverlight, che non sono supportati da Windows Phone (come il
metodo GoForward(), che scatena un’eccezione, dato che Windows Phone mantiene
solamente la cronologia delle pagine precedenti).

Navigare da una pagina all’altra


Per spostarsi verso un’altra pagina si utilizza il metodo Navigate, che accetta un URI come
parametro in ingresso.
Gli URI delle pagine in un’applicazione Windows Phone sono espressi come relativi
rispetto alla radice del progetto. Ecco un esempio di navigazione verso una pagina
chiamata DetailPage.xaml, presente nella root:
NavigationService.Navigate(new Uri("/DetailPage.xaml", UriKind.Relative));

La navigazione tra pagine scatena degli eventi, che vengono gestiti direttamente dalla
classe PhoneApplicationPage.
I due principali (che utilizzeremo anche nel prossimo paragrafo, dedicato al ciclo di vita)
sono i seguenti:
• OnNavigatedFrom, che viene scatenato nel momento in cui stiamo lasciando la pagina
corrente per spostarci verso un’altra pagina;
• OnNavigatedTo, che viene scatenato nel momento in cui abbiamo raggiunto la pagina di
destinazione in seguito a una navigazione.
Questi eventi sono spesso utilizzati per gestire informazioni il cui ciclo di vita è
strettamente legato alla vista corrente e che non è necessario mantenere in memoria nel
momento in cui ci spostiamo verso un’altra pagina (ad esempio, le classi per accedere ai
sensori di movimento o alla fotocamera).
Dato che questi eventi sono definiti all’interno della classe base PhoneApplicationPage, per
utilizzarli all’interno della nostra pagina è necessario farne l’override, come nell’esempio
che segue:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(System.Windows.Navigation.


NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
}

Il passaggio di parametri
Grazie alle classi messe a disposizione dall’SDK per gestire la navigazione, è possibile
passare informazioni da una pagina all’altra con lo stesso meccanismo utilizzato nel
mondo web: l’aggiunta di parametri all’URL sfruttando le query string.
Nel mondo web è possibile passare delle informazioni a una pagina in GET, ovvero
aggiungendole all’URL, come nell’esempio:
http://www.qmatteoq.com/news.aspx?id=501
L’ipotetica pagina news.aspx sarà in grado di recuperare il valore del parametro id e di
utilizzarlo per mostrare la specifica news richiesta.
Lo stesso meccanismo può essere utilizzato in Windows Phone e, di conseguenza, scrivere
del codice di questo tipo:
NavigationService.Navigate(new Uri(”/DetailPage.xaml?id=5⊘1”, UriKind.Relative));

Esattamente come nell’esempio web di poco fa, la pagina DetailPage.xaml sarà in grado di
recuperare il valore del parametro id sfruttando la classe NavigationContext.
L’operazione di lettura dei parametri viene fatta solitamente sfruttando gli eventi di
navigazione che abbiamo visto poco fa, nello specifico l’evento OnNavigatedTo:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("id"))
{
int id = int.Parse(NavigationContext.QueryString[«id»]);
MessageBox.Show(«The id is « + id);
}
}

Nell’esempio andiamo a verificare se la collezione QueryString esposta dal NavigationContext


contiene un parametro identificato dalla chiave id: in caso affermativo, lo recuperiamo e lo
mostriamo a video sfruttando una MessageBox.
Questo meccanismo è adatto, però, solamente nelle situazioni in cui il parametro da
passare all’altra pagina sia “piatto”: un numero, una stringa, un carattere. Ci possono
essere situazioni, però, in cui il dato da passare è un oggetto complesso: ad esempio,
l’elemento selezionato in una ListBox.
Come fare? In questo caso, gli strumenti messi a disposizione dal NavigationService non sono
sufficienti e dobbiamo appoggiarci a un’altra classe per poter memorizzare l’oggetto. Un
valido alleato è la classe App: dato che la sua istanza rimane viva per tutto l’utilizzo
dell’applicazione, può essere usata per memorizzare informazioni che abbiamo la
necessità di mantenere da una pagina all’altra.
L’istanza corrente della classe App è accessibile, infatti, in qualsiasi pagina grazie al
singleton Application.Current: l’unica accortezza è che va effettuato un cast alla classe App,
dato che viene restituita l’istanza corrente della classe Application, che è quella di base da cui
eredita App. Senza il cast, avremmo accesso solamente ai metodi e alle proprietà
generiche di Application e non a quelle specifiche definite in App.
Fatta questa premessa, possiamo definire una proprietà nel file App.xaml.cs nella quale
memorizzare l’elemento che ci serve mantenere da una pagina all’altra, come
nell’esempio:
public object SelectedItem { get; set; }

A questo punto, per recuperare il valore di tale proprietà all’interno di un’altra pagina,
dobbiamo semplicemente utilizzare questo codice:
var selectedItem = (Application.Current as App).SelectedItem;

Quello che abbiamo appena effettuato è un cast, ovvero una conversione da un


tipo all’altro del framework .NET. Il metodo appena utilizzato è definito safe
NOTA
cast: se la conversione fallisce, non viene sollevata alcuna eccezione, ma
avremo solamente, come risultato, un oggetto null.

Dato che ci appoggiamo a una classe esterna, per quanto riguarda la navigazione non
bisogna adottare nessun accorgimento particolare. Nel nostro esempio di passaggio di
valori da una ListBox, il flusso sarebbe il seguente:
• si recupera l’elemento selezionato della ListBox e si memorizza nella proprietà
SelectedItem definita nella classe App;

• si naviga utilizzando il NavigationService alla pagina di dettaglio;


• nella pagina di dettaglio si recupera il valore della proprietà SelectedItem della classe
App, dopodiché si applica la logica necessaria per mostrare le informazioni richieste.

La navigazione circolare: un errore da evitare


Il pulsante Back è uno degli aspetti centrali della navigazione: l’utente si aspetta sempre
che esso lo porti alla vista precedente e, nel caso si trovi nella pagina principale
dell’applicazione, lo riporti alla home o all’applicazione utilizzata in precedenza.
Il pulsante Back non fa altro che scorrere lo stack delle pagine: ogni volta che utilizziamo
il metodo Navigate del NavigationService non facciamo altro che aggiungere una nuova pagina
allo stack.
Bisogna, perciò, fare attenzione perché in alcuni casi l’utilizzo di questo metodo rischia di
creare una navigazione circolare, che va a rovinare l’esperienza d’uso dell’utente (è infatti
una delle ragioni per cui l’applicazione può essere rifiutata in fase di certificazione dallo
Store).
Facciamo un esempio concreto: ipotizziamo di avere un’applicazione con una vista
principale e, nella application bar, un pulsante per accedere a una pagina di impostazioni.
Anche questa pagina contiene una application bar, con un pulsante per confermare le
modifiche: quando viene premuto, le impostazioni vengono salvate e l’utente viene
riportato alla vista principale.
L’istinto sarebbe quello di usare il metodo Navigate per riportare l’utente alla vista
principale, una volta salvate le impostazioni, come nell’esempio seguente:
private void Button_Click(object sender, RoutedEventArgs e)
{
//salvo le impostazioni
NavigationService.Navigate(new Uri(«/MainPage.xaml», UriKind.Relative));
}

Cosa succede, invece, in questo caso? La pagina delle impostazioni viene nuovamente
aggiunta allo stack e l’utente viene riportato alla home: nel momento in cui preme il
pulsante Back, si aspetterebbe di uscire dall’applicazione; purtroppo, dato che la pagina
più recente nello stack è quella delle impostazioni, l’utente viene riportato nuovamente a
quella vista, rovinando di fatto la user experience attesa dall’utente. Le guideline
prevedono, infatti, che la pressione del pulsante Back nella pagina principale
dell’applicazione ne provochi sempre la chiusura.
Come gestire, perciò, questi casi? Ci viene in aiuto il NavigationService, offrendo una serie di
metodi per muoversi all’interno dello stack delle pagine senza aggiungerne una nuova
ogni volta.
Nel nostro esempio, il metodo corretto da utilizzare è GoBack, che forza la navigazione
verso la pagina precedente, simulando la pressione del pulsante Back. Il codice visto in
precedenza, corretto, è quindi:
private void Button_Click(object sender, RoutedEventArgs e)
{
//salvo le impostazioni
NavigationService.GoBack();
}

In realtà, a partire da Windows Phone 7.5 il NavigationService espone diversi nuovi metodi e
proprietà che permettono di manipolare lo stack, per rimuovere, ad esempio, l’ultima
pagina inserita. Li vedremo in dettaglio nel Capitolo 11 dedicato alle tile: queste novità
sono state, infatti, introdotte per supportare i nuovi paradigmi di navigazione nati grazie
alla possibilità data agli sviluppatori di creare più tile in home legate alla stessa
applicazione.
Un’alternativa al NavigationService: il controllo HyperlinkButton
Windows Phone include un controllo, chiamato HyperlinkButton, che permette di realizzare
collegamenti sia verso l’esterno (una pagina web), sia verso altre pagine dell’applicazione.
Il suo funzionamento è molto semplice: basta inserirlo nella pagina, valorizzando la
proprietà NavigationUri con l’indirizzo della pagina di destinazione.
<HyperlinkButton NavigateUri="AboutPage.xaml" Content="Informazioni" />

Questo approccio risulta molto utile quando ci serve semplicemente un punto di accesso a
una pagina dell’applicazione, senza la necessità di passare parametri o interazioni
particolari. Un esempio potrebbe essere costituito da un pulsante che conduca l’utente a
una pagina di informazioni con tutti i dettagli sull’applicazione (autore, indirizzo di
contatto per il supporto, numero di versione ecc.).
La classe UriMapper
Ci sono molteplici situazioni in cui un’applicazione Windows Phone può essere aperta
senza che l’utente abbia fatto tap sull’icona principale: può aver utilizzato una tile
secondaria; può aver aperto un file che l’applicazione è in grado di gestire; può aver fatto
tap su una notifica toast.
In tutti questi casi, come sviluppatori, dobbiamo gestire il fatto che l’applicazione sia stata
aperta da un punto di accesso secondario.
Il modo migliore per farlo è utilizzare la classe UriMapper, che consente di intercettare
tutte le navigazioni da una pagina all’altra dell’applicazione per verificare la presenza di
eventuali parametri che ci fanno capire che questa non è stata aperta dalla tile principale.
Utilizzare questa meccanismo è molto semplice: innanzitutto dobbiamo aggiungere una
nuova classe al nostro progetto, dandogli un nome a piacimento. L’essenziale è che questa
erediti dalla classe UriMapperBase, la quale ci obbligherà a implementare il metodo MapUri(),
che viene scatenato ogni qualvolta si verifica una navigazione e si vuole verificare l’URL
di destinazione della navigazione.
public class MyUriMapper: UriMapperBase
{
public override Uri MapUri(Uri uri)
{
// gestisco la navigazione
}
}

Il parametro che ci viene fornito è l’Uri di destinazione: sta a noi, in base allo scenario che
stiamo implementando, andare alla ricerca dei parametri che ci aspettiamo di trovare e
gestire la navigazione verso la pagina appropriata, il cui Uri deve essere restituito come
valore di ritorno del metodo.
Nel corso del libro vedremo diversi esempi di come utilizzare questa classe negli scenari
più comuni.
Una volta definita la classe di tipo UriMapper, è necessario “istruire” l’applicazione che
deve farne uso: dobbiamo, perciò, aprire il file App.xaml.cs e portarci all’interno del
metodo InitializePhoneApplication(), che solitamente è “nascosto” all’interno della region dal
titolo Phone application initialization.
A questo punto, dopo che l’oggetto di tipo RootFrame è stato inizializzato, è necessario
associare una nuova istanza del nostro UriMapper alla proprietà UriMapper dell’oggetto,
come nell’esempio:
RootFrame = new PhoneApplicationFrame();
RootFrame.UriMapper = new MyUriMapper();
Il ciclo di vita delle applicazioni
Le applicazioni Windows Phone hanno un ciclo di vita diverso dalle applicazioni
tradizionali: questo perché le applicazioni mobile devono tenere conto di una serie di
requisiti (ad esempio, il consumo di batteria) che non sono invece necessari nel mondo
web/desktop. Inoltre, il team di sviluppo di Windows Phone ha cercato di ottimizzare il
più possibile l’esecuzione delle applicazioni, affinché non si verificassero i problemi di
consumo di memoria e di CPU che affliggevano Windows Mobile.
Windows Mobile era dotato, infatti, di un multitasking reale: era possibile aprire più
applicazioni, le quali poi rimanevano effettivamente in esecuzione ed erano in grado di
continuare a eseguire operazioni anche quando non erano più in primo piano. Questo
approccio aveva il vantaggio di garantire alle applicazioni molta libertà, ma allo stesso
tempo riduceva drasticamente la durata della batteria.
In più, se troppe applicazioni erano aperte, si verificavano problemi di consumo eccessivo
di memoria, che andavano a intaccare le prestazioni sia delle altre applicazioni in
esecuzione sia del sistema operativo stesso.
Windows Phone, che si basa sul concetto chiave “al centro l’utente”, non poteva
permettersi un’architettura di questo tipo: scarsa reattività, interfacce lente e programmi
bloccati sono all’opposto di quella che viene considerata un’esperienza d’uso
soddisfacente.
Ecco, perciò, che il team di Windows Phone ha ideato un’architettura in grado di
soddisfare il più possibile entrambe le necessità: la possibilità di passare velocemente da
un’applicazione all’altra senza perdere dati e un consumo equilibrato delle risorse del
telefono.

Il ciclo di vita delle applicazioni Windows Phone 7.0


Nel passaggio dalla versione 7.0 alla versione 7.5 di Windows Phone, il ciclo di vita delle
applicazioni è cambiato, per venire incontro alle esigenze degli utenti e ovviare ad alcuni
limiti riscontrati nell’utilizzo della prima versione del sistema operativo.
Per comprenderne fino in fondo il funzionamento, partiamo dalle basi, analizzando il ciclo
di vita originale, per evidenziare poi come è stato modificato nella nuova versione del
sistema operativo.
Ecco un grafico che riassume il ciclo di vita:
Figura 4.1 - Il ciclo di vita delle applicazioni Windows Phone.

L’applicazione, nel momento in cui è in esecuzione, può essere sospesa in qualsiasi


momento dall’utente: l’evento di sospensione si verifica nel momento in cui l’utente
preme il pulsante Start per tornare alla home oppure quando interagisce con un evento
esterno come una notifica (ad esempio, ha ricevuto un sms e, facendo tap sulla notifica,
viene portato direttamente all’hub Messaging).
In Windows Phone 7.0 la sospensione porta l’applicazione in uno stato chiamato
tombstoned: in questo stato, il processo è terminato e non è più residente in memoria.
Questo meccanismo risolve i problemi di cui abbiamo parlato in precedenza: essendo
terminato, il processo non occupa memoria o cicli di CPU, lasciando tutte le risorse
disponibili al sistema operativo e alle applicazioni aperte in seguito.
Grazie all’utilizzo del pulsante Back, l’utente è però in grado di tornare a un’applicazione
aperta in precedenza: il problema, però, è che, essendo il processo terminato, lo stato
dell’applicazione è andato completamente perso.
Ecco, perciò, che l’SDK mette a disposizione un meccanismo che consente agli
sviluppatori di essere notificati nel momento in cui l’applicazione viene sospesa, e un’area
di memoria in cui poter salvare lo stato dell’applicazione. In questo modo, il processo
viene comunque inizializzato nuovamente da zero, ma avremo la possibilità di recuperare
lo stato salvato in precedenza, in modo da dare all’utente l’illusione che l’applicazione
non sia mai stata chiusa.
Alcuni esempi classici di salvataggio dello stato sono i dati all’interno di un form o la
posizione dello scrolling di una lista. Vedremo nei paragrafi successivi quali sono,
tecnicamente, i meccanismi che hanno a disposizione gli sviluppatori per salvare lo stato.
Il sistema operativo si fa carico solamente di gestire la navigazione tra pagine, in modo
che l’applicazione venga ripristinata esattamente sulla vista che ha lasciato l’utente: tutto
il resto è a carico dello sviluppatore, dato che è l’unico a conoscere la logica della sua
applicazione e quali dati è necessario salvare.

Il ciclo di vita delle applicazioni Windows Phone 7.5


Il meccanismo visto in precedenza riusciva a garantire gli obiettivi prefissati dal team di
Windows Phone, ovvero stabilità e performance: aveva, però, un limite che, con il passare
del tempo, si è dimostrato abbastanza fastidioso. Dato che il processo veniva terminato a
ogni sospensione, l’applicazione doveva essere inizializzata da zero a ogni ripresa: se si
trattava di un problema di poco conto per le applicazioni semplici, per quelle più
complesse (come i giochi) si traduceva in un tempo abbastanza lungo (nell’ordine dei 10
secondi) prima che l’utente fosse in grado di tornare esattamente dov’era prima. Microsoft
ha risolto questo inconveniente in Windows Phone 7.5, introducendo il Fast Application
Switching, che si traduce in un nuovo stato, come possiamo vedere nello schema
sottostante.

Figura 4.2 - Il ciclo di vita delle applicazioni Windows Phone 7.5.

Quando le applicazioni vengono sospese, vengono portate in uno stato chiamato dormant,
nel quale il processo non è più terminato, ma viene “congelato”: vengono solamente messi
in atto una serie di processi (che a breve vedremo in dettaglio) affinché l’impatto sul
consumo di memoria, di CPU e di batteria sia minimo.
Ciò significa che, nel momento in cui l’applicazione viene ripristinata, il processo non
deve essere inizializzato da capo, ma semplicemente riattivato: questo si traduce nel
passaggio pressoché istantaneo da un’applicazione all’altra. In più, in questo caso gli
sviluppatori non hanno la necessità di recuperare lo stato dell’applicazione: siccome il
processo non è stato terminato, i dati non sono andati perduti.
Ma cosa succede dietro le quinte? Ecco uno schema che riassume gli interventi messi in
atto in maniera automatica da parte del sistema operativo quando l’applicazione viene
sospesa:

Audio con API XNA Messo in pausa


Sensori Soppressione delle notifiche
Connessioni di rete Annullate
Socket Disconnesse
Media Element Disconnessi
Fotocamera Effettuato il dispose

Vengono interrotti, inoltre, gli eventuali thread secondari in esecuzione e vengono


terminate tutte le operazioni in corso: un’applicazione sospesa, infatti, non può eseguire
operazioni in background; per questo scopo esistono delle API dedicate che verranno
trattate nel Capitolo 10.
Come si può notare dallo schema, la maggior parte delle risorse è semplicemente
disconnessa: fa eccezione la fotocamera, poiché si tratta di una risorsa ad accesso
esclusivo (se l’utente aprisse un’altra applicazione in grado di usare la fotocamera, si
verificherebbe un conflitto di risorse).
Quando l’applicazione viene riattivata, viene applicato uno schema simile a quello
precedente, anche se con qualche differenza:

Audio con API XNA Si riprende la riproduzione


Sensori Riattivazione delle notifiche
Connessioni di rete Completate con stato “Canceled”
Socket -
Media Element -
Fotocamera -

Per quanto riguarda l’audio e i sensori, non è necessario alcun intervento da parte dello
sviluppatore: ci pensa il sistema operativo a riportare lo stato a quello precedente.
Le connessioni di rete, invece, vengono completate, ma lo stato della risposta viene
impostato a Canceled: questo perché l’operazione di rete, in fase di sospensione, viene
annullata, e quindi, al ripristino, i dati richiesti non sono disponibili. Starà a noi, perciò,
implementare, ad esempio, un meccanismo che faccia partire nuovamente la richiesta in
caso di cancellazione.
Il ripristino di socket, Media Element e fotocamera, invece, sono completamente a carico
dello sviluppatore; essi devono essere riattivati manualmente (ad esempio, deve essere
nuovamente impostata la sorgente del controllo Media Element, oppure deve essere creata
una nuova istanza della fotocamera).
Una delle novità portate dall’introduzione del Fast Application Switching è il task
switcher, ovvero la possibilità di navigare tra le applicazioni aperte in precedenza e di
passare velocemente dall’una all’altra (in Windows Phone 7.0 era possibile solamente
tornare all’applicazione utilizzata in precedenza mediante il pulsante Back). Per attivare il
task switcher è sufficiente tenere premuto il pulsante Back: avremo la possibilità di vedere
un’anteprima delle ultime applicazioni aperte e, con un tap, di spostarci direttamente a
quella selezionata.
Nel grafico avrete sicuramente notato che lo stato tombstoned è ancora presente: questo
perché, nonostante gli accorgimenti visti in precedenza, un’applicazione sospesa occupa
comunque memoria. Può succedere, perciò, soprattutto nei device a basso costo, dotati di
un quantitativo di RAM inferiore, che il sistema operativo vada a corto di memoria: in
questo caso è in grado di terminare le applicazioni più vecchie, mandandole appunto nello
stato tombstoned.

Figura 4.3 - Il task switcher in azione.

In tal caso, il ciclo di vita delle applicazioni torna a essere identico a quello di Windows
Phone 7.0: il processo è terminato e, in caso di ripristino, viene inizializzato da capo. Ecco
perché, come potete vedere nello schema, la procedura di salvataggio dello stato è
comunque richiesta in fase di sospensione: perché l’applicazione potrebbe essere stata
terminata dal sistema operativo senza preavviso.
Come vedremo nel paragrafo successivo, come sviluppatori abbiamo modo di capire se la
nostra applicazione è stata ripristinata da uno stato suspend o da uno stato tombstoned e,
quindi, di comportarci di conseguenza.
Visual Studio, inoltre, ci viene in aiuto per testare entrambi gli scenari, dato che a priori
non abbiamo modo di sapere se e quando il sistema operativo porterà un’applicazione
dallo stato tombstoned allo stato suspended: ciò è reso possibile da una nuova opzione
nelle proprietà del progetto, che permette di forzare il tombstoning dell’applicazione nel
momento in cui viene sospesa.
Tale opzione è accessibile facendo clic con il tasto destro sul progetto, selezionando la
voce Properties e accedendo alla sezione Debug: attivando il checkbox Tombstone upon
deactivation while debugging ogni volta che sospenderemo l’applicazione sull’emulatore
o sul telefono, questa verrà sempre mandata in stato tombstoned.

Figura 4.4 - L’opzione da attivare in Visual Studio per simulare il tombstoning.

Gestire il ciclo di vita all’interno di un’applicazione


Finora abbiamo visto la teoria sulla quale si basa la gestione del ciclo di vita di
un’applicazione da parte del sistema operativo: ora scendiamo nel concreto e vediamo
quali sono gli strumenti che ci mette a disposizione l’SDK per gestire i vari stati in cui si
può trovare l’applicazione.
Il cuore della gestione del ciclo di vita è classe App: ricordate quando, nel Capitolo 2,
abbiamo parlato di questa classe, dicendo che la sua istanza rimane in vita fintanto che
l’applicazione è in esecuzione? Per questo motivo, essa rappresenta il punto ideale in cui
gestire gli eventi legati al ciclo di vita. Se apriamo il file App.xaml.cs, troveremo al suo
interno la dichiarazione dei seguenti eventi:
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}

// Code to execute when the application is activated (brought to foreground)


// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}

// Code to execute when the application is deactivated (sent to background)


// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
}

Application_Launching è l’evento che viene invocato nel momento in cui l’applicazione si sta
avviando da zero; questo avviene quando l’utente la apre dall’icona, sia essa la tile in
home o nell’elenco delle applicazioni.
È importante sottolineare come l’apertura dell’applicazione in questo modo forzi sempre
la creazione di una nuova istanza, anche se questa era stata aperta in precedenza. Questo
significa che l’applicazione verrà riaperta dalla prima pagina e gli eventuali stati salvati in
precedenza saranno eliminati.
Application_Activated è l’evento che viene invocato nel momento in cui l’applicazione viene
ripresa da una sospensione, tipicamente perché l’utente ha premuto il pulsante Back
oppure ha usato il task switcher.
A partire da Windows Phone 7.5, l’oggetto di tipo ActivatedEventArgs che viene restituito
dall’evento contiene una proprietà chiamata IsApplicationInstancePreserved: se questa è a true,
vuol dire che l’applicazione proviene da uno stato suspend. In tal caso, non dovremo
effettuare alcuna operazione, dal momento che i dati sono stati mantenuti in memoria; se,
invece, è a false, ci troviamo nel caso in cui l’applicazione è stata mandata in tombstone dal
sistema operativo a causa dell’esaurimento della memoria: dobbiamo, pertanto, recuperare
lo stato memorizzato in precedenza.
Application_Deactivated è l’evento che viene invocato nel momento in cui l’applicazione viene
sospesa, dove, come abbiamo visto in precedenza, per sospensione si intende la chiusura
dell’applicazione tramite la pressione del pulsante Start o per l’apertura di un’altra
applicazione in seguito a una notifica.
Questo è l’evento che tipicamente viene utilizzato per salvare lo stato dell’applicazione: vi
ricordo che è importante farlo sempre, dato che non avete modo di sapere a priori se la
vostra applicazione sarà mantenuta in stato suspend o portata in stato tombstoned.
Application_Closing è l’evento invocato nel momento in cui l’applicazione viene terminata.
L’unico modo per chiudere definitivamente un’applicazione è tramite la pressione del
pulsante Back nella prima pagina: non è previsto, infatti, che le applicazioni abbiano un
pulsante per la chiusura della stessa (infatti nell’SDK non esiste alcun metodo per forzare
la chiusura di un’applicazione).
Dall’analisi degli stati possiamo evincere come questi siano mutuamente esclusivi:
all’avvio può verificarsi l’evento Launching o Activated; alla chiusura può verificarsi Deactivated
o Closing. Non possono mai verificarsi entrambi nello stesso momento.
In realtà, questi eventi possono essere sfruttati anche per la gestione di dati permanenti,
che vengono memorizzati, ad esempio, nello storage o nel cloud: gli eventi
Application_Deactivated e Application_Closing possono essere utilizzati anche per salvare questi
dati, per far sì che al successivo avvio (sia che si tratti di un ripristino sia di una nuova
istanza) i dati siano ancora presenti.

Il tombstoning
In questo paragrafo tratteremo i meccanismi che l’SDK ci mette a disposizione per gestire
il tombstoning: in realtà, l’operazione di salvataggio dello stato non ha delle regole fisse,
in quanto dipende molto dal tipo di applicazione che state sviluppando e dai dati che
dovete salvare.
Come abbiamo visto in precedenza, viene definito tombstoned lo stato in cui si trova
un’applicazione dopo che è stata sospesa e terminata dal sistema operativo, a causa della
limitata quantità di memoria disponibile. Nel momento in cui avviene la sospensione, lo
sviluppatore ha la possibilità di salvare lo stato dell’applicazione, per essere poi in grado
di recuperarlo in fase di ripristino.
Ma dove possiamo salvare questo stato?
Il sistema operativo mette a disposizione due collezioni di tipo Dictionary<string, object>, che
possono contenere oggetti di qualsiasi tipo, purché siano serializzabili (affronteremo
questo argomento in dettaglio nel capitolo dedicato al salvataggio dei dati). Queste due
collezioni sono poste in due classi differenti e, pur condividendo la medesima struttura,
hanno scopi differenti:
• le pagine di Windows Phone ereditano dalla classe PhoneApplicationPage, la quale
contiene una proprietà chiamata State che rappresenta la nostra collezione; questa
collezione segue lo stesso ciclo di vita delle pagine e serve allo sviluppatore per
memorizzare lo stato relativo alla pagina corrente: la posizione dello scrolling di una
lista, i valori inseriti all’interno di un form ecc. Tipicamente, siccome il
mantenimento di questi dati è legato al ciclo di vita delle pagine, il salvataggio viene
effettuato sfruttando gli eventi di navigazione OnNavigatedTo e OnNavigatedFrom;
• nella classe App, e, più precisamente, nello XAML, è dichiarata la classe
PhoneApplicationService, che segue lo stesso ciclo di vita della classe App e, di
conseguenza, è accessibile da qualsiasi pagina e rimane attiva fintanto che
l’applicazione è in esecuzione. L’istanza di tale classe (accessibile tramite
PhoneApplicationService.Current) contiene una proprietà State, che rappresenta la collezione
in cui salvare i dati. Il suo scopo è tipicamente quello di memorizzare dati relativi
all’intera sessione di utilizzo: ad esempio, ipotizziamo di avere un’applicazione che
mostra delle notizie che vengono scaricate di volta in volta da Internet tramite un
feed RSS. Se vogliamo che, al restore dell’applicazione, le notizie non vengano
scaricate nuovamente, possiamo memorizzarle in questa collezione, sfruttandola
come una sorta di cache per recuperare i dati al successivo ripristino: il salvataggio e
il ripristino di questi viene effettuato solitamente sfruttando gli eventi legati al ciclo
di vita delle applicazioni definiti nella classe App, che abbiamo visto in precedenza
(Application_Activated e Application_Deactivated).
È importante sottolineare come entrambe le collezioni servano esclusivamente per salvare
dati temporanei, da mantenere solamente nell’intervallo di tempo trascorso da una
sospensione alla successiva riattivazione.
Se, infatti, l’utente decidesse di riaprire l’applicazione passando per l’icona e non tramite
il pulsante Back o tramite il task switcher, entrambe le collezioni State verrebbero
inizializzate da zero, causando la perdita dello stato memorizzato in precedenza.
Quando parliamo di dati permanenti (ad esempio, le impostazioni dell’applicazione o un
database), dobbiamo ricorrere ad altre soluzioni, come l’Isolated Storage o il cloud,
affinché vengano mantenuti nel corso del tempo, da un’esecuzione all’altra.

Un esempio di utilizzo della collezione PhoneApplicationPage.State


In questo esempio vedremo come utilizzare la collezione PhoneApplicationPage.State per salvare
il valore di una TextBox di un ipotetico form, così da recuperarlo al ripristino.
Per farlo, sfrutteremo gli eventi legati alla navigazione già visti in precedenza, ovvero
OnNavigatedTo e OnNavigatedFrom. Il primo si verifica nel momento in cui stiamo lasciando la
pagina corrente: anche la sospensione causa questo evento, perciò possiamo utilizzarlo per
memorizzare il contenuto della casella di testo. Il secondo si verifica quando torniamo alla
pagina corrente, non solo nel caso in cui proveniamo da un’altra pagina, ma anche in
seguito a un ripristino: possiamo, quindi, utilizzare questo evento per recuperare i dati
salvati in precedenza.
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
if (PhoneApplicationService.Current.State.ContainsKey("Name"))
{
PhoneApplicationService.Current.State.Remove("Name");
}
PhoneApplicationService.Current.State.Add("Name", txtName.Text);
}

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)


{
if (PhoneApplicationService.Current.State.ContainsKey("Name"))
{
txtName.text = PhoneApplicationService.Current.State[«Name»];
PhoneApplicationService.Current.State.Remove(«Name»);
}
}

Nell’evento OnNavigatedFrom aggiungiamo il contenuto della nostra casella di testo alla


collezione, verificando prima che non esista già e, in caso affermativo, rimuovendo il
valore precedente.
Nell’evento OnNavigatedTo verifichiamo che ci sia un elemento contrassegnato dalla chiave
Name e, in caso affermativo, lo usiamo per valorizzare il contenuto della casella di testo;
dopodiché, lo rimuoviamo dalla collezione, per evitare che venga utilizzato
impropriamente (ad esempio, perché siamo giunti alla pagina corrente da un’altra pagina e
non da un evento di riattivazione).
Questo codice contiene, però, una lacuna: non tiene conto del Fast Application Switching.
Come abbiamo detto in precedenza, lo stato deve essere infatti ripristinato solo se
l’applicazione proviene da uno stato tombstoned: lo stato suspend non causa il termine del
processo, e quindi i dati sono ancora in memoria.
Questa informazione, però, non è accessibile dalla pagina, ma dalla classe App, all’interno
dell’evento Application_Activated. Possiamo sfruttare, perciò, la classe App per propagare
questa informazione fino alla pagina stessa, grazie al fatto che tale classe rimane istanziata
per tutto il tempo che l’applicazione è in uso.
La prima cosa da fare è creare una proprietà pubblicata all’interno della classe App, in
modo che sia accessibile anche dalle altre classi:
public bool IsAppStatePreserved { get; set; }

Poi dobbiamo intercettare l’evento Application_Activated e valorizzare questa proprietà con


quella restituita dall’evento chiamata IsApplicationInstancePreserved:
private void Application_Activated(object sender, ActivatedEventArgs e)
{
IsAppStatePreserved = e.IsApplicationInstancePreserved;
}

In questo modo avremo a disposizione una proprietà pubblica, accessibile da qualsiasi


pagina della nostra applicazione, che ci permetterà di determinare se l’applicazione è stata
ripristinata da uno stato suspend o da uno stato tombstoned.
A questo punto ci serve solamente accedere a tale proprietà dalla nostra pagina, sfruttando
il singleton Application.Current, che ci permette di accedere all’istanza corrente della classe
App e di cui abbiamo parlato in precedenza relativamente al passaggio di parametri
durante la navigazione tra pagine.
Il modo corretto per accedere a tale proprietà da qualsiasi pagina della nostra applicazione
è quindi il seguente:
(Application.Current as App).IsAppStatePreserved

Ecco, perciò, come diventa l’evento OnNavigatedTo alla luce di quanto abbiamo visto:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
bool IsAppStatePreserved = (Application.Current as App).IsAppStatePreserved;
if (!IsAppStatePreserved && PhoneApplicationService.Current.State.ContainsKey("Name"))
{
txtName.Text = PhoneApplicationService.Current.State["Name"].ToString();
....PhoneApplicationService.Current.State.Remove("Text");
}
}

Un esempio di utilizzo della collezione PhoneApplicationService.Current.State


Secondo quanto abbiamo visto nel paragrafo precedente, è molto semplice capire come
utilizzare la collezione State della classe PhoneApplicationService. Il funzionamento è, infatti, lo
stesso: in fase di sospensione aggiungeremo alla collezione PhoneApplicationService.Current.State
uno o più elementi, da recuperare in fase di ripristino. Ricordate la proprietà
IsAppStatePreserved che abbiamo definito nel paragrafo precedente nella classe App in modo
da renderla accessibile a tutte le pagine dell’applicazione?
Lo stesso meccanismo può essere utilizzato anche per altri dati che sono gestiti a livello di
applicazione e che devono essere mantenuti da una pagina all’altra.
Ecco, quindi, che la classe App diventa il luogo naturale dove gestirne il salvataggio e il
recupero in fase di suspend e in fase di restore, utilizzando la collezione
PhoneApplicationService.Current.State, come nell’esempio seguente, dove riprendiamo quello fatto
in precedenza e ipotizziamo l’esistenza di una collezione di tipo List<object> chiamata News
che contiene una lista di notizie scaricate dalla rete.
public List<object> News { get; set; }

private void Application_Activated(object sender, ActivatedEventArgs e)


{
if (PhoneApplicationService.Current.State.ContainsKey("News") && !e.IsApplicatio
nInstancePreserved)
{
News = PhoneApplicationService.Current.State["News"] as List<Item>;
PhoneApplicationService.Current.State.Remove("News");
}
}

private void Application_Deactivated(object sender, DeactivatedEventArgs e)


{
if (PhoneApplicationService.Current.State.ContainsKey("News"))
{
PhoneApplicationService.Current.State.Remove("News");
}
PhoneApplicationService.Current.State.Add("News", News);
}

Nell’evento Application_Activated viene verificata l’esistenza di un elemento identificato dalla


chiave News nella collezione PhoneApplicationService.Current.State. Solo se questo esiste e se
l’applicazione arriva da uno stato tombstoned, allora lo recuperiamo e andiamo a
valorizzare la proprietà pubblica News che abbiamo definito in precedenza. Nell’evento
Application_Deactivated, invece, previa eliminazione di eventuali residui delle sospensioni
precedenti, salviamo il contenuto della proprietà News.
Fast App Resume
L’architettura che abbiamo visto ha, però, un limite: l’unico modo che ha l’utente per
ritornare a un’applicazione sospesa è tramite il task switcher o utilizzando il pulsante
Back. Se, infatti, cerca di riaprirla dalla tile o dall’icona presente nell’elenco delle
applicazioni installate, questa sarà riaperta da capo: l’utente verrà portato alla pagina
principale e l’eventuale stato salvato sarà perso.
Windows Phone 8 ha introdotto il Fast App Resume, ovvero la possibilità per gli
sviluppatori di intercettare l’evento di apertura dell’applicazione e di decidere cosa fare:
continuare con il flusso di navigazione tradizionale (e quindi aprire una nuova istanza
della pagina iniziale) oppure annullare la navigazione e lasciare che l’utente venga portato
all’ultima pagina che stava utilizzando.
Questa funzionalità è molto semplice da implementare, ma richiede un po’ di lavoro da
parte dello sviluppatore per gestire correttamente il flusso della navigazione, dato che sarà
leggermente diverso rispetto a prima.
Attivare il Fast App Resume è molto semplice: occorre aggiungere una dichiarazione
all’interno del file di manifest. Purtroppo, però, si tratta di una di quelle funzionalità non
supportate dall’editor visuale del manifest: dovrete, perciò, fare clic con il tasto destro sul
file WMAppManifest.xml e scegliere l’opzione View Code.
In questo modo potrete vedere e modificare la struttura del file di manifest: a questo punto
dovete cercare la riga che definisce la pagina che viene caricata, identificata dal nodo
DefaultTask, e aggiungere il parametro ActivationPolicy=”Resume”, come
nell’esempio:
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" ActivationPolicy="Resume"/>

Per verificare il comportamento del Fast App Resume aggiungiamo un’altra pagina alla
nostra applicazione, facendo clic con il tasto destro sul progetto, scegliendo Add New
Item e aggiungendo un nuovo file di tipo Windows Phone Portrait Page, chiamandolo
Page1.xaml. Ora inseriamo un pulsante nella pagina MainPage.xaml e gestiamone
l’evento Tap: utilizzeremo il NavigationService per portare l’utente alla pagina
Page1.xaml.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,⊘,12,⊘">
<StackPanel>
<Button Content="Vai alla pagina 1" Click="OnGoToPage1ButtonClicked" />
</StackPanel>
</Grid>

private void OnGoToPage1ButtonClicked(object sender, RoutedEventArgs e)


{
NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
}

Ora portiamoci nel file App.xaml.cs, all’interno del quale sono gestiti gli eventi di
navigazione globali della pagina. All’interno del metodo InitializePhoneApplication() viene
sottoscritto l’evento Navigated del RootFrame, scatenato ogni qualvolta l’utente “completa” la
transizione verso un’altra pagina.
Nel progetto standard di Visual Studio l’evento viene associato al metodo Clear-
BackStackAfterReset(), così definito:

private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)


{
// Unregister the event so it doesn't get called again
RootFrame.Navigated -= ClearBackStackAfterReset;

// Only clear the stack for 'new' (forward) and 'refresh' navigations
if (e.NavigationMode != NavigationMode.New && e.NavigationMode !=
NavigationMode.Refresh)
return;

// For UI consistency, clear the entire page stack


while (RootFrame.RemoveBackEntry() != null)
{
; // do nothing
}
}

Cosa succede nel momento in cui l’applicazione viene riaperta dalla tile principale ed è
attivato il Fast App Resume? La proprietà NavigationMode dell’oggetto di tipo
NavigationEventArgs viene impostata su Reset e l’Uri che indica la pagina verso cui stiamo
navigando corrisponde alla pagina principale dell’applicazione (nel nostro caso, Main-
Page.xaml).
Altri possibili valori della proprietà NavigationMode sono New (quando si sta navigando verso
una nuova pagina che non è presente nello stack) o Back (quando, invece, stiamo
navigando a ritroso nello stack delle pagine).
Cosa succede dietro le quinte quando il Fast App Resume è attivato e l’utente fa tap sulla
tile principale dell’applicazione? Il sistema operativo, in realtà, scatena non una, ma due
navigazioni:
• la prima verso l’ultima pagina che è stata aperta dall’utente;
• la seconda verso la pagina principale: questo perché la tile principale è associata,
come Uri di navigazione, alla MainPage.xaml.
Le possibilità sono quindi due: lasciare il flusso di navigazione intatto e portare l’utente
alla pagina principale, oppure annullare la seconda navigazione e lasciare che l’utente
venga portato all’ultima pagina che stava visitando prima della sospensione.
Un esempio del primo caso ci viene dal metodo ClearBackStackAfterReset() che abbiamo appena
visto. In questo caso la navigazione non viene cancellata, ma prosegue normalmente:
viene adottato, però, un accorgimento, ovvero quello di eliminare tutte le pagine presenti
nel back stack dell’applicazione sfruttando il metodo RemoveBackEntry() della classe RootFrame.
Se ricordate quanto abbiamo detto riguardo la navigazione circolare, dovreste capirne il
motivo: nel momento in cui l’applicazione viene riaperta, viene eseguito un comando di
navigazione verso la pagina MainPage.xaml. Dato che l’applicazione era già aperta, lo
stack conteneva già le pagine visitate dall’utente in precedenza: se le lasciassimo al loro
posto, nel momento in cui l’utente dovesse premere il pulsante Back nella pagina
principale, verrebbe portato all’ultima pagina vista invece che alla start screen del
telefono, come si aspetterebbe.
Se lanciamo l’applicazione senza fare alcuna modifica, vedremo questo comportamento in
azione: dalla pagina principale spostiamoci, tramite il pulsante, nella Page1. xaml e
sospendiamo l’applicazione. Se ora la riapriamo dalla tile principale, verremo portati
nuovamente alla MainPage.xaml: premendo nuovamente il pulsante Back, l’applicazione
sarà correttamente chiusa, dato che lo stack delle pagine è stato preventivamente svuotato.
Il secondo caso, invece, ci richiede di intercettare l’evento di navigazione da uno stato
Reset per annullarlo e lasciare che l’utente venga portato all’ultima pagina utilizzata: per
farlo dobbiamo apportare alcune modifiche al codice che abbiamo visto, dato che lo scopo
della configurazione standard del progetto Windows Phone è quello di mantenere il ciclo
di vita originale anche in caso di Fast App Resume.
La prima cosa da fare è intercettare l’evento RootFrame.Navigated, che useremo per
memorizzare in una variabile globale della classe App il tipo di navigazione in cui ci
troviamo.
Dichiariamo, perciò, all’interno del file App.xaml.cs, una variabile booleana:
private bool reset;

Dopodiché andiamo a ridefinire il metodo RootFrame_Navigated nel modo seguente:


void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
reset = e.NavigationMode == NavigationMode.Reset;
}

Il successivo e ultimo passaggio è quello di andare a gestire l’evento RootFrame.Navigating,


che viene scatenato nel momento in cui è stata avviata una navigazione, ma non è ancora
stata completata. Andiamo, perciò, sempre all’interno del metodo Initialize PhoneApplication(),
dopo che la classe RootFrame è stata inizializzata, a sottoscriverci per tale evento:
RootFrame.Navigating += RootFrame_Navigating;
void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
{
if (reset && e.IsCancelable && e.Uri.OriginalString == "/MainPage.xaml")
{
e.Cancel = true;
reset = false;
}
}

La prima cosa che possiamo notare è che l’evento porta con sé un parametro di tipo
NavigatingCancelEventArgs: questo perché, dato che la navigazione non è ancora stata
completata, siamo ancora in tempo ad annullarla. Ecco, perciò, che, se ci troviamo in uno
stato di Reset e l’Uri richiesto è quello della pagina principale, allora vuol dire che
l’utente ha riaperto l’applicazione dalla tile o dall’icona presente nell’elenco: in questo
caso dobbiamo annullare la navigazione verso la pagina MainPage.xaml, impostando a
true la proprietà Cancel dell’oggetto di tipo NavigatingCancelEventArgs.

Se ora lanciamo nuovamente l’applicazione, avremo un comportamento diverso: anche


riaprendola, dopo la sospensione, dalla tile principale, torneremo infatti alla pagina
Page1.xaml, e lo stack delle pagine sarà ancora intatto. La pressione del pulsante Back,
infatti, riporterà l’utente alla pagina MainPage.xaml e, solamente utilizzando nuovamente
il tasto Back, l’applicazione sarà chiusa e riapparirà la start screen di Windows Phone.
Qual è lo scopo di avere a disposizione entrambi gli scenari? Non sarebbe più semplice,
una volta attivato il Fast App Resume, consentire all’utente di ritornare sempre all’ultima
pagina utilizzata indipendentemente dal punto di accesso all’applicazione (task switcher,
pulsante back o tile principale)?
Il motivo è la flessibilità; alcune applicazioni, ad esempio, potrebbero voler implementare
un meccanismo di resume basato su condizioni temporali: se l’utente ha utilizzato per
l’ultima volta l’applicazione meno di un giorno fa, allora possiamo sfruttare il Fast App
Resume e far sì che possa continuare le operazioni che stava svolgendo; se l’utente,
invece, non la utilizza da diversi giorni, probabilmente si sarà dimenticato cosa stava
facendo l’ultima volta, perciò è preferibile non modificare il flusso di navigazione e
lasciare che l’applicazione venga riaperta dall’inizio.
Ecco, perciò, che, nell’evento RootFrame_Navigating, potreste aggiungere una condizione
temporale al controllo, oltre alla modalità di navigazione e all’Uri della pagina richiesta.
Un suggerimento, se decidete di sfruttare il Fast App Resume nelle vostre applicazioni, è
quello di dare all’utente la possibilità di tornare velocemente alla pagina principale,
indipendentemente da dove si trova. Questo consiglio è tanto più prezioso quanto più la
vostra applicazione è complessa: l’obiettivo è evitare che l’utente che vuole tornare al
punto di partenza sia costretto a premere il pulsante Back numerose volte prima di
raggiungere il suo scopo.
La rotazione
Uno dei sensori di cui sono dotati tutti gli smartphone moderni è l’accelerometro: nel
Capitolo 6 vedremo come utilizzarlo per rendere più interattiva la nostra applicazione. In
realtà, però, l’accelerometro può essere utilizzato anche per uno scopo molto semplice:
determinare l’orientamento del telefono; molte applicazioni sono infatti in grado di
sfruttare questa informazione per adattare il layout di conseguenza, così da utilizzare lo
spazio a disposizione.
In Windows Phone abbiamo due modi per gestire l’orientamento: automaticamente o
adattando manualmente il layout da codice.

La gestione automatica dell’orientamento


Una volta che abbiamo definito quali sono le posizioni supportate da una pagina,
Windows Phone è in grado di adattare il contenuto automaticamente in base al layout.
Ovviamente, l’interfaccia grafica deve essere costruita con una serie di regole che le
permettano di essere flessibile: ad esempio, l’utilizzo di Grid come contenitore, con righe e
colonne dalle dimensioni dinamiche.
Per abilitare questa modalità è sufficiente definire nello XAML di una pagina quali sono
gli orientamenti supportati, precisamente grazie alle proprietà SupportedOrientations e Orientation
del nodo principale PhoneApplicationPage.
SupportedOrientations="Portrait" Orientation="Portrait"

La prima proprietà permette di definire gli orientamenti supportati: di default è impostata


su Portrait; questo significa che la pagina non ruoterà mai anche se il telefono viene ruotato
in orizzontale. Viceversa, possiamo impostarla su Landscape se vogliamo che la pagina non
sia mai visualizzata in portrait. Infine, il valore più interessante è PortraitOrLandscape: questo
significa che la pagina supporta entrambi gli orientamenti e verrà quindi abilitata la
rotazione automatica.
La proprietà Orientation, invece, serve per definire quale sarà l’orientamento iniziale: in
questo caso i valori possibili sono di più, poiché sono contemplate tutte le possibili
rotazioni (ad esempio, PortraiUp o PortraitDown, oppure LandscapeLeft o LandscapeRight).

La gestione manuale dell’orientamento


Non sempre, però, la rotazione applicata da Windows Phone si adatta alla nostra
applicazione, soprattutto se l’interfaccia grafica è complessa. In questo caso abbiamo la
possibilità di intervenire manualmente, grazie a un evento esposto dalla classe
PhoneApplicationPage che viene scatenato ogni qualvolta il device viene ruotato in una delle
posizioni predefinite (portrait o landscape, con le relative varianti).
L’evento si chiama OnOrientationChanged e restituisce un oggetto di tipo
OrientationChangedEventArgs che contiene una proprietà Orientation con la nuova posizione del
device.
public MainPage()
{
InitializeComponent();
this.OrientationChanged += new EventHandler<OrientationChangedEventArgs>(MainPage
_OrientationChanged);
}

void MainPage_OrientationChanged(object sender, OrientationChangedEventArgs e)


{
if (e.Orientation == PageOrientation.PortraitUp || e.Orientation ==
PageOrientation.PortraitDown)
{
//gestisci l'orientamento verticale
}
else if (e.Orientation == PageOrientation.LandscapeLeft || e.Orientation ==
PageOrientation.LandscapeRight)
{
//gestisci l'orientamento orizzontale
}
}

In questo evento possiamo, ad esempio, manipolare i controlli presenti nella pagina per
adattarli al nuovo layout. Ipotizziamo di avere un controllo StackPanel con al suo interno tre
pulsanti: come comportamento predefinito, il controllo StackPanel impila i controlli uno
sotto l’altro.
<StackPanel x:Name="Buttons">
<Button Content=»Button 1» x:Name=»Button1» Height=»75» />
<Button Content=»Button 2» x:Name=»Button2» Height=»75» />
<Button Content=»Button 3» x:Name=»Button3» Height=»75» />
</StackPanel>

Ora facciamo sì che, quando la pagina viene ruotata, i pulsanti non vengano più disposti
verticalmente, ma orizzontalmente (sfruttando la proprietà Orientation); inoltre variamo la
loro altezza. Quando la pagina viene, invece, riportata in modalità portrait, ripristiniamo
tutto come all’inizio.
public MainPage()
{
InitializeComponent();
this.OrientationChanged += new EventHandler<OrientationChangedEventArgs>(MainPage_
OrientationChanged);
}

void MainPage_OrientationChanged(object sender, OrientationChangedEventArgs e)


{
if (e.Orientation == PageOrientation.Landscape || e.Orientation ==
PageOrientation.LandscapeLeft || e.Orientation == PageOrientation.LandscapeRight)
{
Buttons.Orientation = System.Windows.Controls.Orientation.Horizontal;
Button1.Height = 1⊘⊘;
Button2.Height = 1⊘⊘;
Button3.Height = 1⊘⊘;
}
else
{
Buttons.Orientation = System.Windows.Controls.Orientation.Vertical;
Button1.Height = 75;
Button2.Height = 75;
Button3.Height = 75;
}
}
Supportare risoluzioni multiple
Come già anticipato nel Capitolo 1, una delle novità introdotte in Windows Phone 8 è il
supporto a nuove risoluzioni: oltre alla 480x800, che era quella predefinita di Windows
Phone 7, sono state introdotte le risoluzioni WXGA (768x1280) e 720p (720x1280).
Questa novità ha aperto una serie di scenari per gli sviluppatori che non era necessario
tenere in considerazione nelle applicazioni per Windows Phone 7: la necessità di adattare
l’interfaccia affinché si veda al meglio indipendentemente dalla risoluzione del device.
In realtà, l’obiettivo è abbastanza semplice quando si tratta delle risoluzioni WVGA e
WXGA: entrambe, infatti, mantengono le medesime proporzioni, ovvero 15:9. Di
conseguenza, il fattore di scala fa sì che i controlli abbiano le stesse proporzioni su
entrambe le risoluzioni: l’unico accorgimento da adottare è utilizzare immagini che
abbiano una risoluzione sufficientemente alta da risultare nitide anche su device WXGA.
Diverso è il discorso per i dispositivi dotati di risoluzione 720p, che hanno una
proporzione di 16:9: in questo caso il fattore di scala è differente e, di conseguenza, la
risoluzione effettiva risulta essere più alta di 53px rispetto alle altre. Ecco, perciò, che, se
non si usano i dovuti accorgimenti, i controlli possono essere posizionati in maniera
differente e le immagini possono essere distorte.

Figura 4.5 - Le differenti risoluzioni di Windows Phone 8.

Vediamo come gestire correttamente entrambi i casi.

Adattare il layout
Nel Capitolo 3 abbiamo imparato a usare i controlli di Windows Phone; a volte, quando li
usiamo (o magari li posizioniamo sfruttando l’editor visuale di Visual Studio oppure
Blen), andiamo a definire la proprietà Margin, che permette di impostare in senso assoluto
il posizionamento degli stessi. I margini sono sempre relativi al controllo contenitore: i
valori espressi al suo interno sono definiti in pixel e vengono calcolati partendo dal
controllo padre (se inseriamo quest’ultimo direttamente all’interno del tag
PhoneApplicationPage, saranno relativi all’intera pagina).
Ad esempio, la seguente casella di testo:
<TextBox Margin="12, 2⊘, ⊘, 5" />

viene posizionata a una distanza di:


• 0 pixel partendo da sinistra;
• 20 pixel partendo dall’alto;
• 0 pixel partendo da destra;
• 5 pixel partendo dal basso.
Tale approccio permette di definire la posizione di un controllo in maniera molto precisa,
ma mal si adatta a supportare risoluzioni multiple: il posizionamento è infatti assoluto e
non è in grado di adattarsi alle differenti risoluzioni.
La soluzione consiste nell’utilizzare il meno possibile i margini e nell’affidarsi, invece, a
controlli che permettono di definire un layout “fluido”, come Grid. Come abbiamo visto
nel Capitolo 3, infatti, il controllo Grid consente di specificare le dimensioni di righe e
colonne usando valori come Auto e *, che fanno sì che queste siano in grado di adattarsi
automaticamente al contenuto.
In questo modo, indipendentemente dalla risoluzione, il layout sarà in grado di sfruttare lo
spazio disponibile e di adattarsi automaticamente.

Gestire le immagini
Un’altra problematica da considerare è la gestione delle immagini; se in Windows Phone
7.5 era sufficiente inserire immagini a una risoluzione massima di 480x800, ora questo
non è più vero: tali immagini perderebbero di qualità se visualizzate su un device dalla
risoluzione più alta.
In generale, è sufficiente inserire un’immagine alla risoluzione più alta possibile per un
device Windows Phone (ovvero 768x1280): il sistema operativo la scalerà in automatico
alla risoluzione corretta in base al dispositivo.
In alcuni casi, però, il risultato finale potrebbe non essere ottimale, soprattutto sui telefoni
dotati di risoluzione 720p, in virtù delle proporzioni differenti: alcune immagini
potrebbero risultare distorte e creare un effetto poco piacevole.
In questo caso è possibile realizzare un converter da applicare a ogni controllo di tipo
Image: in tal modo avremo la possibilità di specificare come valore della proprietà
Source semplicemente il nome dell’immagine; ci penserà il converter a restituire la
versione corretta in base alla risoluzione.
Per raggiungere questo scopo ci viene in aiuto la documentazione ufficiale Microsoft, che
suggerisce la creazione di una classe che funge da helper per gestire al meglio le differenti
risoluzioni. Vediamone il codice:
public enum Resolutions { WVGA, WXGA, HD720p };

public static class ResolutionHelper


{
private static bool IsWvga
{
get
{
return App.Current.Host.Content.ScaleFactor == 1⊘⊘;
}
}

private static bool IsWxga


{
get
{
return App.Current.Host.Content.ScaleFactor == 16⊘;
}
}

private static bool Is72⊘p


{
get
{
return App.Current.Host.Content.ScaleFactor == 15⊘;
}
}

public static Resolutions CurrentResolution


{
get
{
if (IsWvga) return Resolutions.WVGA;
else if (IsWxga) return Resolutions.WXGA;
else if (Is72⊘p) return Resolutions.HD72⊘p;
else throw new InvalidOperationException("Unknown resolution");
}
}

public static Uri GetImagePath(string filename)


{
string name = Path.GetFileNameWithoutExtension(filename);
string extension = Path.GetExtension(filename);

switch (CurrentResolution)
{
case Resolutions.HD72⊘p:
return new Uri(string.Format("Assets/{⊘}.screen-72⊘p{1}", name,
extension), UriKind.Relative);
case Resolutions.WXGA:
return new Uri(string.Format("Assets/{⊘}.screen-wxga{1}", name,
extension), UriKind.Relative);
case Resolutions.WVGA:
return new Uri(string.Format("Assets/{⊘}.screen-wvga{1}", name,
extension), UriKind.Relative);
default:
throw new InvalidOperationException("Unknown resolution type");
}
}
}

La classe ResolutionHelper
è una classe statica che espone pubblicamente la proprietà
CurrentResolution, la quale restituisce uno dei tre valori dell’enumeratore Resolutions dichiarato
nella classe stessa: WVGA, WXGA o HD720p.
Tale classe è in grado di recuperare questa informazione sfruttando la proprietà ScaleFactor
fornita dalla classe App.Current.Host.Content: dato che a ogni risoluzione corrisponde un fattore
di scala differente, tramite questo valore siamo in grado di capire qual è la risoluzione
corrente.
Rispetto all’esempio mostrato nella documentazione MSDN, ho aggiunto un metodo
chiamato GetImagePath(), che riceve in ingresso il nome dell’immagine che vogliamo
mostrare e lo converte nel percorso reale in base alla risoluzione del dispositivo.
Per questo scopo utilizzeremo la stessa convenzione che il sistema operativo usa per la
gestione della splash screen, che abbiamo imparato a utilizzare nel Capitolo 2. Andremo a
inserire nel nostro progetto, all’interno della cartella Assets, tre immagini così definite:
• una con il suffisso .screen-720p per la risoluzione 720p (ad esempio, image. screen-
720p.jpg);
• una con il suffisso .screen-wxga per la risoluzione WXGA (ad esempio, image.
screen-wxga.jpg);
• una con il suffisso .screen-wvga per la risoluzione WVGA (ad esempio, image.
screen-wvga.jpg).
Il metodo GetImagePath()
sfrutta alcuni helper di sistema facenti parte della classe
System.IO.Path (che permettono di lavorare con i file) per recuperare il nome e l’estensione
dell’immagine, da riutilizzare in seguito. Infine, utilizza la proprietà CurrentResolution definita
in precedenza per verificare la risoluzione corrente, in base alla quale è in grado di ricreare
il percorso reale dell’immagine corretta, che restituisce come valore di ritorno del metodo.
Il risultato sarà che, a fronte dell’assegnazione della proprietà Source del controllo Image, ad
esempio, del semplice valore image.jpg, dietro le quinte sarà restituito il percorso reale
all’immagine che meglio si adatta alla risoluzione del device su cui è in esecuzione
l’applicazione.
In questo modo diventa semplice realizzare un converter per gestire la risoluzione corretta
per noi:
public class ResolutionConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (value != null)
{
Uri url = value as Uri;
return ResolutionHelper.GetImage(url.ToString());
}

return null;
}

public object ConvertBack(object value, Type targetType, object parameter,


CultureInfo culture)
{
throw new NotImplementedException();
}
}
return null;
}
Per applicare il converter dobbiamo rifarci alla teoria vista nel Capitolo 2. Dobbiamo,
innanzitutto, dichiararlo come risorsa all’interno della pagina o dell’applicazione:
<phone:PhoneApplicationPage.Resources>
<Converters:ResolutionConverter x:Key="ResolutionConverter" />
</phone:PhoneApplicationPage.Resources>

Infine, dovremo applicarlo a tutte le espressioni di binding che coinvolgono la


visualizzazione di un’immagine:
<Image Source="{Binding Path=ImageUrl, Converter={StaticResource ResolutionConverter}}" />

La proprietà ImageUrl, nell’esempio, dovrà contenere solamente il nome dell’immagine: il


percorso completo reale sarà calcolato dal converter.
Se avessimo la necessità di eseguire la stessa operazione senza sfruttare il binding (quindi
assegnando da codice il valore alla proprietà Source del controllo Image), sarebbe sufficiente
utilizzare la classe ResolutionHelper direttamente all’interno del codice della nostra pagina,
come nell’esempio:
string imageName = "image.png";
BitmapImage image = new BitmapImage(ResolutionHelper.GetImage(imageName));
myImage.Source = image;

L’utilizzo è lo stesso che abbiamo visto applicato dal converter: viene creato un nuovo
oggetto di tipo BitmapImage, passando come parametro il percorso reale dell’immagine,
ottenuto grazie all’helper. Infine, direttamente da codice valorizziamo con questo oggetto
la proprietà Source di un controllo Image.
In conclusione
In questo capitolo abbiamo trattato alcuni concetti chiave dello sviluppo di
un’applicazione Windows Phone, che vi saranno sicuramente utili per comprendere
appieno gli argomenti che verranno trattati nei capitoli successivi.
Innanzitutto abbiamo parlato di navigazione, un concetto fondamentale, dato che la
maggior parte delle applicazioni è composta da più pagine: abbiamo visto, perciò, come
spostarsi da una pagina all’altra, passando eventuali dati tra di esse.
Dopodiché abbiamo introdotto il ciclo di vita, indispensabile per poter realizzare
un’applicazione Windows Phone che rispetti le linee guida e che offra all’utente
un’esperienza d’uso soddisfacente: abbiamo visto come Windows Phone permetta di
gestire il passaggio veloce da un’applicazione all’altra e quali siano i meccanismi messi in
atto per consentire il salvataggio e il ripristino dello stato.
Infine, abbiamo parlato dell’orientamento del telefono, un altro aspetto importante per
offrire all’utente l’esperienza d’uso migliore e sfruttare al massimo lo spazio messo a
disposizione dallo schermo di Windows Phone.
Interagire con il mondo esterno

La caratteristica principale degli smartphone moderni è quella di offrire


una connessione alla rete: e-mail e social network sono solo due esempi
tra le attività più comuni che utilizzano una connessione a Internet.
Windows Phone non fa eccezione, anzi, possiamo tranquillamente dire che
senza connettività l’esperienza d’uso rimane fortemente limitata:
pensiamo all’hub People, in grado di reperire informazioni in tempo reale
sui nostri contatti dai social network; o, ancora, all’hub Pictures, in grado
di visualizzare gli album che abbiamo caricato su Skydrive o su Facebook.
Ecco perché, di conseguenza, le applicazioni di terze parti non sono da meno e hanno la
possibilità di collegarsi alla rete sfruttando gli strumenti messi a disposizione dal device,
ovvero la connessione Wi-Fi e la connessione dati 3G.
In questo capitolo vedremo come interagire con la rete per recuperare dati, per interrogare
servizi e per scaricare file. Vedremo come sfruttare le tecnologie più comuni con cui
vengono messi a disposizione i dati nella rete, come i servizi WCF, i servizi REST ecc.
Vedremo, infine, alcune delle novità introdotte in Windows Phone 8 per comunicare con
altri dispositivi senza utilizzare la rete: Bluetooth e NFC.
Verificare la connessione a Internet
Il fatto che i device Windows Phone siano dotati di connettività non significa che questa
sia sempre disponibile: l’utente potrebbe trovarsi in una zona dove non c’è campo, oppure
all’estero e aver disabilitato il roaming, oppure ancora aver deciso di disattivare i dati per
risparmiare sulla batteria.
L’SDK di Windows Phone mette a disposizione un’API che permette di determinare non
solo la presenza di connettività, ma anche la tipologia (3G o Wi-Fi): in questo modo, ad
esempio, possiamo far sì che il download di un file particolarmente grande venga avviato
solo se il telefono è collegato in Wi-Fi.
La classe di riferimento si chiama DeviceNetworkInformation e appartiene al namespace
Microsoft.Phone.Net.NetworkInformation. Si tratta di una classe statica, che espone una serie di
proprietà e metodi che permettono di recuperare le informazioni sullo stato della
connessione:
• CellularMobileOperator() è un metodo che restituisce una stringa con il nome dell’operatore
mobile correntemente utilizzato dall’utente;
• IsCellularDataEnabled indica se la rete dati cellulare è abilitata o meno;
• IsCellularDataRoamingEnabled indica se l’utente ha abilitato il roaming dati, ovvero la
possibilità di utilizzare la connessione dati anche quando non si è collegati con il
proprio operatore (soprattutto all’estero);
• IsWiFiEnabled indica se la connessione Wi-Fi è abilitata o meno;
• IsNetworkAvailable è forse la proprietà più importante e utilizzata, in quanto indica se è
disponibile o meno una connessione a Internet. È buona prassi verificare sempre il
valore di questa proprietà prima di eseguire qualsiasi operazione che richieda
connettività, come nell’esempio:
if (DeviceNetworkInformation.IsNetworkAvailable)
{
// scarica un file dalla rete
}

La classe DeviceNetworkInformation espone anche un evento, chiamato NetworkAvalaibilityChanged,


che viene scatenato ogni qualvolta lo stato della connessione cambia e possiamo utilizzare
per gestire, ad esempio, l’improvvisa disconnessione durante l’esecuzione di
un’operazione di rete.
Tale evento restituisce un oggetto di tipo NetworkNotificationEventArgs che contiene la proprietà
NotificationType; questa ci permette di sapere con precisione cosa è cambiato nella
connessione di rete: se il device si è disconnesso (NetworkNotificationType. InterfaceDisconnected),
se è connesso (NetworkNotificationType.InterfaceConnected) oppure è cambiato lo stato, ad esempio
si è passati in roaming (NetworkNotificationType.CharacteristicUpdate).
public MainPage()
{
InitializeComponent();

DeviceNetworkInformation.NetworkAvailabilityChanged += new
EventHandler<NetworkNotificationEventArgs>(DeviceNetworkInformation_NetworkAvailabilityChanged);
}

void DeviceNetworkInformation_NetworkAvailabilityChanged(object sender,


NetworkNotificationEventArgs e)
{
if (e.NotificationType == NetworkNotificationType.InterfaceDisconnected)
{
MessageBox.Show("Disconnected");
}
else if (e.NotificationType == NetworkNotificationType.InterfaceConnected)
{
MessageBox.Show("Connected");
}
}
Determinare il tipo di connessione
All’inizio del paragrafo abbiamo citato la possibilità di determinare il tipo di connessione
corrente, così da poter effettuare alcune operazioni solo in presenza di uno specifico tipo
di connettività. Considerando che i piani dati solitamente hanno un limite di traffico, è
buona norma, ad esempio, evitare di scaricare file molto grossi in presenza di una
connessione 3G.
Possiamo recuperare questa informazione grazie alla classe NetworkInterface (appartenente
sempre al namespace Microsoft.Phone.Net.NetworkInformation), che espone la proprietà
NetworkInterfaceType.

Tale proprietà può assumere tantissimi valori, dato che sono supportati diversi tipi di
connessione; i principali a cui dovete fare riferimento sono i seguenti:
• Ethernet, quando il telefono è collegato al computer e sfrutta la connessione a Internet
del computer;
• MobileBroadbandGsm e MobileBroadbandCdma, quando il telefono è collegato alla rete
cellulare. Ho riportato entrambi i valori, dato che, se distribuite la vostra applicazione
anche all’estero, è possibile che questa venga installata su device CDMA, ovvero una
tipologia di rete cellulare diffusa soprattutto in America;
• Wireless80211 quando il telefono è collegato tramite rete Wi-Fi.
if (NetworkInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
{
txtNetworkType.Text = "Ethernet";
}
else if (NetworkInterface.NetworkInterfaceType == NetworkInterfaceType.
MobileBroadbandGsm || NetworkInterface.NetworkInterfaceType == NetworkInterfaceType.
MobileBroadbandCdma)
{
txtNetworkType.Text = "Mobile";
}
else if (NetworkInterface.NetworkInterfaceType==NetworkInterfaceType.Wireless80211)
{
txtNetworkType.Text = "Wi-Fi";
}
Scaricare file dalla rete: la classe WebClient
In Windows Phone sono molteplici gli approcci per poter scaricare o caricare file e risorse
dalla rete tramite il protocollo HTTP. La classe WebClient rappresenta il sistema più
semplice per farlo, anche se:
• è quella che ha l’impatto maggiore sulle performance dell’applicazione, in quanto
utilizza lo stesso thread che gestisce la UI, riducendo perciò la reattività
dell’interfaccia, soprattutto in caso di operazioni lunghe;
• offre un set limitato di opzioni per interagire con il protocollo HTTP.
La classe WebClient offre due set di proprietà e metodi, a seconda che la risorsa che si sta
scaricando o caricando sia di tipo testuale oppure un file binario (un’immagine, un PDF
ecc.).

Download e upload di dati testuali


Questa funzionalità serve quando dobbiamo scaricare o caricare una risorsa esposta su
Internet costituita da un file di testo, ovvero qualcosa che possiamo salvare in una
variabile di tipo string. I file XML sono un esempio classico e, probabilmente, una delle
casistiche più frequenti per cui si utilizza questa classe.
La classe WebClient non è una novità per gli sviluppatori .NET: da tempo fa parte, infatti, di
questo framework. La differenza è che in Windows Phone vengono esposti solamente i
metodi per effettuare il download e l’upload in asincrono, in modo che l’interfaccia utente
non venga bloccata fino a quando l’operazione non è terminata.
Per il download di dati testuali la classe WebClient espone il metodo DownloadString Async() , che
accetta come parametro in ingresso l’indirizzo della risorsa da scaricare sotto forma di Uri.
Prima dobbiamo, però, sottoscrivere l’evento DownloadStringCompleted, che viene scatenato nel
momento in cui il download è completato: l’oggetto che viene restituito è di tipo
DownloadStringCompletedEventArgs e contiene il testo scaricato nella proprietà Result, che è di tipo
string.

public MainPage()
{
InitializeComponent();
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_
DownloadStringCompleted);
client.DownloadStringAsync(new Uri("http:/www.qmatteoq.com", UriKind.
Absolute));

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)


{
txtHtml.Text = e.Result;
}

In questo esempio facciamo puntare il metodo DownloadStringAsync() all’URL di un sito: il


risultato è che verrà scaricato l’HTML della pagina richiesta. Nell’evento
DownloadStringCompleted prendiamo questo risultato e lo mostriamo sullo schermo tramite un
controllo TextBlock.
Per effettuare l’upload di dati testuali il metodo da utilizzare è invece UploadStringAsync().
Come comportamento predefinito, il dato viene inviato sfruttando il comando POST del
protocollo HTTP: questo significa che l’indirizzo che dovremo passare come parametro di
input del metodo deve essere una pagina in grado di elaborare una richiesta di questo tipo.
Se vogliamo sfruttare un altro dei comandi del protocollo HTTP possiamo specificarlo
come parametro (opzionale) del metodo. Infine, l’unico altro parametro obbligatorio è,
ovviamente, la stringa che deve essere inviata.
L’evento da sottoscrivere e che viene scatenato nel momento in cui l’upload è terminato si
chiama UploadStringCompleted e restituisce un oggetto di tipo UploadStringCompletedEventArgs.
Anche in questo la proprietà più importante è Result, sempre di tipo string, che contiene la
risposta del server alla richiesta.
public MainPage()
{
InitializeComponent();
WebClient client = new WebClient();
client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_
UploadStringCompleted);
client.UploadStringAsync(new Uri("http://www.example.com/Upload. aspx",
UriKind.Absolute), "test");
}

void client_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)


{
MessageBox.Show(e.Result);
}

In questo esempio viene inviata la stringa “test” alla pagina


http://www.example.com/Upload.aspx: la risposta viene intercettata dall’evento
UploadStringCompleted e mostrata a video tramite una MessageBox.

Download e upload di file binari


Il meccanismo per gestire il download e l’upload di file binari è molto simile a quello
appena visto: abbiamo due metodi (uno per il download e uno per l’upload) e due eventi
che vengono scatenati in seguito al termine delle operazioni.
Il metodo per il download si chiama OpenReadAsync() e accetta come parametro l’URL della
risorsa da scaricare; l’evento da sottoscrivere che viene scatenato al termine del download
si chiama, di conseguenza, OpenReadAsyncCompleted. A differenza del download di file
testuali, però, la proprietà Result dell’oggetto restituito da questo evento (di tipo
OpenReadCompletedEventArgs) è di tipo Stream, che possiamo poi elaborare per ricostruire il file
nel formato più adatto.
public MainPage()
{
InitializeComponent();
WebClient client = new WebClient();

client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);


client.OpenReadAsync(new Uri("http://wpsauce.com/wp-content/uploads/2011/11/
windows_phone_logo.jpg", UriKind.Absolute));
}
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
BitmapImage image=new BitmapImage();
image.SetSource(e.Result);
Logo.Source = image;
}

In questo esempio utilizziamo il metodo OpenReadCompleted per scaricare un’immagine con il


logo di Windows Phone. Dopo aver invocato il metodo passando come parametro l’URL
dell’immagine, attendiamo il verificarsi dell’evento OpenReadCompleted, all’interno del quale
lo stream (contenuto nella proprietà e.Result) viene convertito in una BitmapImage e infine
assegnato alla proprietà Source di un controllo Image (qui identificato dal nome Logo).

Figura 5.1 - Il logo di Windows Phone scaricato tramite la classe WebClient.

L’upload funziona, invece, in maniera un po’ diversa: abbiamo, anche in questo caso, un
metodo OpenWriteAsync() e un evento OpenWriteCompleted. La particolarità, però, è che tale
evento non viene scatenato nel momento in cui l’upload è completato, ma quando la
connessione verso l’URL che abbiamo specificato è aperta e pronta a ricevere lo stream di
dati.
È in questo evento che dovremo, perciò, gestire l’operazione di upload vera e propria: se
guardiamo, infatti, la firma del metodo OpenWriteAsync() , non troveremo alcuna variante che
accetta tra i parametri in ingresso il file da caricare.
Per sapere quando l’operazione di upload è terminata, abbiamo un altro evento da
sottoscrivere: WriteStreamClosed.
Vediamo un esempio concreto:
private const int BLOCK_SIZE = 4⊘96;
private Stream photo;

// Constructor
public MainPage()
{
InitializeComponent();
photo = …
Uri uri = new Uri("http://www.example.com/Upload.aspx", UriKind.Absolute);

WebClient wc = new WebClient();


wc.AllowReadStreamBuffering = true;
wc.AllowWriteStreamBuffering = true;

wc.OpenWriteCompleted += new OpenWriteCompletedEventHandler(wc_OpenWriteCompleted);

wc.WriteStreamClosed += new WriteStreamClosedEventHandler(wc_WriteStreamClosed);

wc.OpenWriteAsync(uri, "POST");
}

Ipotizziamo di aver caricato lo stream di un’immagine nella variabile photo, di tipo Stream e
dichiarata a livello globale.
Dopodiché definiamo l’URL a cui inviare il file (un’ipotetica pagina in grado di accettare
una richiesta HTTP di tipo POST) e ci sottoscriviamo ai due eventi OpenWriteCompleted e
WriteStreamClosed.

Infine, chiamiamo il metodo OpenWriteAsync specificando l’URL e il comando HTTP da


utilizzare.
void wc_OpenWriteCompleted(object sender, OpenWriteCompletedEventArgs e)
{
using (BinaryReader br = new BinaryReader(photo))
{
using (BinaryWriter bw = new BinaryWriter(e.Result))
{
long bCount = ⊘;
long fileSize = photo.Length;
byte[] bytes = new byte[BLOCK_SIZE];
do
{
bytes = br.ReadBytes(BLOCK_SIZE);
bCount += bytes.Length;
bw.Write(bytes);
} while (bCount < fileSize);
}
}

Come spiegato in precedenza, nell’evento OpenWriteCompleted si svolge l’upload vero e


proprio: usando le classi messe a disposizione dal framework per operare con i file binari
(BinaryReader e BinaryWriter), effettuiamo l’upload verso l’URL specificato in precedenza.
void wc_WriteStreamClosed(object sender, WriteStreamClosedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Send complete");
}
else
{
MessageBox.Show("An error occurred");
}
}

Nell’evento WriteStreamClosed, che viene scatenato invece quando l’upload vero e proprio è
terminato, mostriamo un messaggio con l’esito dell’operazione.

Mostrare lo stato dell’operazione


Soprattutto nel caso in cui ci si trova a effettuare operazioni su file molto grandi, è buona
prassi prevedere nell’interfaccia grafica un controllo ProgressBar, in grado di mostrare lo
stato del download o dell’upload.
La classe WebClient espone due eventi, uno per il download e uno per l’upload, che sono
indipendenti dal tipo di file che si sta scaricando: vengono, infatti, scatenati sia nel caso
vengano utilizzati i metodi per il download e l’upload di file di testo, sia in quello in cui
vengano impiegati i metodi per il download e l’upload di file binari.
L’evento, per quanto riguarda le operazioni di download, si chiama DownloadProgressChanged e
viene scatenato ogni qualvolta lo stato del download cambia. Tale evento restituisce un
oggetto di tipo DownloadProgressChangedEventArgs. Per la gestione di un controllo ProgressBar è
sufficiente sfruttare la proprietà ProgressPercentage, che specifica la percentuale di
avanzamento dell’operazione e che può essere assegnata direttamente alla proprietà Value
di una ProgressBar.
Se poi si vuole accedere a statistiche più avanzate sul download, sono disponibili anche le
proprietà BytesReceived (la quantità di byte già scaricati) e TotalBytesToReceive (la dimensione
totale del file da scaricare).
public MainPage()
{
InitializeComponent();
WebClient client = new WebClient();
client.DownloadProgressChanged+=new DownloadProgressChangedEventHandler(client_
DownloadProgressChanged);
client.OpenReadAsync(new Uri("http://wpsauce.com/wp-content/uploads/2⊘11/11/
windows_phone_logo.jpg", UriKind.Absolute));
}

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)


{
txtStatus.Text = string.Format("Download status: {⊘} / {1}",
e.BytesReceived, e.TotalBytesToReceive);

DownloadProgress.Value = e.ProgressPercentage;
}

Per quanto riguarda le operazioni di upload, il meccanismo è molto simile: l’evento di


callback da sottoscrivere, in questo caso, che si chiama UploadProgressChanged, restituisce un
oggetto di tipo UploadProgressChangedEventArgs che, anche in questo caso, espone la proprietà
ProgressPercentage con la percentuale di completamento dell’operazione.
Analogamente a quanto visto per il download, abbiamo poi le proprietà BytesSent (con il
totale di byte già inviati) e TotalBytesToSend (con la dimensione totale del file che si sta
inviando).
public MainPage()
{
InitializeComponent();
WebClient client = new WebClient();

client.UploadProgressChanged+=new UploadProgressChangedEventHandler(clie
nt_UploadProgressChanged);
client.OpenWriteAsync(new Uri("http://www.example.com/Upload.aspx",
UriKind.Absolute));
}

void client_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)


{
txtStatus.Text = string.Format("Upload status: {0} / {1}", e.BytesSent,
e.TotalBytesToSend);
UploadProgress.Value = e.ProgressPercentage;
}

Occhio alla cache!


La classe WebClient (e, di conseguenza, anche la classe HttpWebRequest che vedremo nel
paragrafo successivo) implementa un meccanismo di caching tale per cui, se vengono fatte
due richieste successive alla stessa risorsa, questa sarà scaricata solamente la prima volta:
per la seconda richiesta saranno utilizzati i dati memorizzati in cache da quella precedente.
Se state quindi eseguendo dei test con la vostra applicazione e notate qualche
comportamento strano nel download dei file (ad esempio, modifiche alla risorsa scaricata
che non vengono rilevate dall’applicazione Windows Phone), il motivo è proprio l’utilizzo
della cache. Purtroppo non esiste un modo per controllarla; la soluzione più semplice per
evitare il caching è quella di passare all’indirizzo della risorsa un parametro casuale. In
questo modo, dato che la cache viene creata in base all’URL della risorsa, sarà sempre
scaricata una nuova versione del file.
Ecco un semplice esempio:
WebClient client = new WebClient();
client.DownloadProgressChanged+=new DownloadProgressChangedEventHandler(client_
DownloadProgressChanged);
client.DownloadStringAsync(new Uri("http://www.example.com/feed.xml?guid=" + Guid.NewGuid()));

In questo caso viene generato un dato casual di tipo Guid e passato come parametro alla
richiesta.

Il GUID è una stringa esadecimale di 32 caratteri (ad esempio, {F4E09A9A-


EFB6-46C3-89CA-015B4EA2EB5E}) che viene spesso utilizzata come chiave
per identificare in maniera univoca un valore (ad esempio, la chiave primaria di
NOTA
una tabella di un database). Questo perché il numero di combinazioni possibili
è talmente alto che è praticamente impossibile che vengano generate due
stringhe identiche.

Visual Studio stesso include un tool per generare Guid in maniera casuale, raggiungibile
dal menu Tools. In più, come abbiamo appena visto nell’esempio, il framework .NET lo
supporta nativamente tramite il tipo Guid.
Scaricare file dalla rete: la classe HttpWebRequest
La classe HttpWebRequest utilizza un approccio sicuramente più complesso rispetto alla classe
WebClient, ma ha dei vantaggi di performance e di versatilità non indifferenti. Innanzitutto,
perché le operazioni di download e upload sono eseguite su thread differenti da quello
della UI: come vedremo tra poco nell’esempio, infatti, dovremo usare il Dispatcher per
poter interagire con i controlli presenti nella pagina.
Inoltre, essa offre molte più opzioni rispetto alla classe WebClient: possiamo, ad esempio,
interagire con i cookie oppure specificare uno user agent diverso da quello di default
(grazie alla proprietà UserAgent). Analogamente alla classe WebClient, l’operazione di
download si svolge in due fasi: la prima, in cui viene avviato il download tramite la classe
HttpWebRequest, e la seconda, in cui si ricevono i dati richiesti, tramite la classe
HttpWebResponse. In questo caso non ci sono, però, eventi di callback da sottoscrivere, ma il
tutto viene gestito tramite operazioni asincrone.
Vediamo come avviare un download, in questo caso dell’HTML di una pagina web:
request = HttpWebRequest.CreateHttp("http://www.qmatteoq.com");
request.BeginGetResponse(ParseResponse, null);

Una volta creata la richiesta di tipo HttpWebRequest, possiamo avviarne il download con il
metodo BeginGetResponse(): il parametro richiesto non è, in realtà, una variabile, ma una
funzione, la quale riceverà il risultato della risposta e la elaborerà.
private void ParseResponse(IAsyncResult result)
{
HttpWebResponse response = request.EndGetResponse(result) as HttpWebResponse;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string html = reader.ReadToEnd();
Dispatcher.BeginInvoke(() => txtHtml.Text = html);
}
}

Questa funzione riceve in ingresso un parametro di tipo IAsyncRsult, che non contiene il file
scaricato, ma semplicemente il risultato dell’operazione asincrona. Per ottenere il file vero
e proprio dobbiamo ricorrere al metodo EndGetResponse esposto sempre dalla classe
HttpWebRequest: è importante, perciò, che la richiesta (nell’esempio, la variabile request)
venga definita a livello di classe, così da potervi accedere anche nella callback. Il metodo
EndGetResponse ci restituisce una WebResponse (che nell’esempio trasformiamo in una
HttpWebResponse), la quale contiene tutte le informazioni dettagliate sulla risposta HTTP
ricevuta: i cookies (la proprietà Cookies), la dimensione e il tipo di contenuto ricevuto
(ContentLength e ContentType), lo status della richiesta HTTP (StatusCode e StatusDescription) ecc.
La classe HttpWebResponse espone, però, anche un metodo chiave, chiamato GetResponseStream,
che restituisce lo stream vero e proprio del file scaricato (nel nostro caso, l’HTML della
pagina richiesta). Si tratta, però, di un oggetto di tipo Stream, ovvero di un dato grezzo:
dobbiamo poi manipolarlo con le classi messe a disposizione dal framework.
Nell’esempio, utilizziamo la classe StreamReader per ottenere una stringa (dato che abbiamo
richiesto un file testuale) e visualizzarla in un controllo TextBlock (utilizzando il Dispatcher,
come spiegato in precedenza).
Utilizzare l’async targeting pack
Nel Capitolo 2 abbiamo parlato dell’Async Targeting Pack, che permette di utilizzare
alcune classi con il nuovo approccio per l’esecuzione di operazioni asincrone basato sulle
keyword async e await. Le classi che abbiamo appena visto (WebClient e HttpWebRequest)
rientrano in questa categoria: installando, perciò, l’apposito package da NuGet, come
spiegato nel Capitolo 2, avrete la possibilità di effettuare operazioni di rete utilizzando
questo nuovo approccio.
Scaricare file dalla rete: i background transfer
Queste API sono state introdotte in Windows Phone 7.5 e fanno parte delle novità relative
al multitasking: a differenza dei metodi che abbiamo visto in precedenza, infatti, le API
per la gestione dei background transfer sono in grado di continuare le operazioni di
download e upload anche quando l’applicazione viene chiusa.
Abbiamo già sottolineato in precedenza come, nella maggior parte dei casi, i piani dati che
gli utenti possono sottoscrivere offrano un quantitativo di dati limitato: proprio per questo
motivo le API per la gestione dei background transfer hanno dei limiti impliciti a seconda
del tipo di connessione.
Se siamo collegati in 3G, potremo scaricare file non più grandi di 20 MB, che possono
diventare 100 nel caso di connessione Wi-Fi. È possibile superare questo limite solo nel
caso in cui, oltre alla disponibilità di una connessione wireless, il device sia in ricarica,
collegato perciò alla rete elettrica. Per quanto riguarda l’upload, invece, il limite è di 5
MB, indipendentemente dal tipo di connessione e alimentazione.
Un altro concetto che sta molto a cuore al team di Windows Phone e di cui abbiamo
parlato più volte è “al centro l’utente”: l’obiettivo è quindi quello di preservare il più
possibile la durata della batteria e le performance del sistema operativo.
Per questo motivo una singola applicazione non può aggiungere alla coda dei trasferimenti
in background più di 25 download, per un totale di un massimo di 500 download tra tutte
le applicazioni installate sul telefono. Il sistema operativo è in grado di eseguire al
massimo due trasferimenti contemporaneamente.
Dopo questa doverosa premessa sulle condizioni da rispettare per poter usare un
trasferimento in background, vediamo quali sono le classi che l’SDK ci mette a
disposizione.
Un trasferimento in background è identificato da un oggetto di tipo BackgroundTransferRequest,
che espone una serie di proprietà (per configurare il trasferimento) e di metodi (per tenere
traccia dello stato dell’operazione).
Una caratteristica importante dei background transfer è che, come sviluppatori, possiamo
decidere quali sono le condizioni per cui il trasferimento debba essere avviato: per
impostare la modalità più adatta alle nostre esigenze dobbiamo agire sulla proprietà
TransferPreferences, che accetta uno dei valori dell’omonimo enumeratore:

• None è l’impostazione di default, il trasferimento viene avviato solamente se il device


si trova in ricarica e collegato a una rete Wi-Fi;
• AllowBattery consente il trasferimento indipendentemente dal tipo di alimentazione
(batteria o corrente), purché il device sia collegato a una rete Wi-Fi;
• AllowCellular consente il trasferimento indipendentemente dal tipo di connessione (3G o
Wi-Fi), purché il device si trovi in ricarica;
• AllowCellularAndBattery è l’impostazione meno restrittiva in assoluto, in quanto consente
l’avvio del trasferimento in qualunque condizione, sia di connettività che di
alimentazione.

Avviare e gestire lo stato di un trasferimento


Al contrario delle altre classi che abbiamo visto, le operazioni di download e upload
sfruttano gli stessi metodi per gestirne lo stato e vengono avviate allo stesso modo.
La classe BackgroundTransferRequest espone, infatti, due eventi di callback che è possibile
sottoscrivere per gestire lo stato del trasferimento.
L’evento TransferStatusChanged viene scatenato nel momento in cui lo stato del trasferimento
cambia: tale stato è reso disponibile dalla proprietà TransferStatus della classe
BackgroundTransferRequest, che può assumere uno dei valori dell’enumeratore TransferStatus.

Gli stati principali sono Completed (quando il trasferimento è completato), Transferring (quando
il trasferimento è in corso) e Paused (quando il trasferimento è in pausa). In più, ci sono
tutta una serie di stati di tipo Waiting che indicano che, al momento, non sono rispettate le
condizioni che abbiamo definito nella proprietà TransferPreferences, e che, quindi, il download
è in uno stato di attesa (ad esempio, WaitingForExternalPower nel caso in cui sia richiesta
l’alimentazione elettrica, oppure WaitingForWiFi nel caso in cui sia richiesta una connessione
wireless).
L’evento TransferProgressChanged viene scatenato, invece, ogni qualvolta lo stato del
trasferimento cambia: è perciò l’ideale per gestire eventuali ProgressBar che mostrano
visivamente lo stato dell’operazione. Per realizzare questo scopo si ricorre alle proprietà
BytesReceived e TotalBytesToReceive per i download e BytesSent e TotalBytesToSend per gli upload.

Una volta definite tutte le impostazioni, possiamo avviare il trasferimento, anche se il


termine “avviare” non è corretto: non è detto, infatti, che le condizioni che abbiamo
specificato siano soddisfatte. Per questo motivo i trasferimenti in background sono messi
in una coda, che viene poi gestita a livello di sistema operativo: questa coda viene
rappresentata dalla classe BackgroundTransferService. Per aggiungere un download alla coda è
sufficiente usare il metodo Add(), passando come parametro l’oggetto di tipo
BackgroundTransferRequest.

private BackgroundTransferRequest backgroundRequest;

private void Button_Click(object sender, RoutedEventArgs e)


{
backgroundRequest = new BackgroundTransferRequest(sourceUrl, destinationUrl);
backgroundRequest.TransferPreferences = TransferPreferences.
AllowCellularAndBattery;
backgroundRequest.TransferProgressChanged += new EventHandler<BackgroundTransfer
EventArgs>(backgroundRequest_TransferProgressChanged);
backgroundRequest.TransferStatusChanged += new EventHandler<BackgroundTransferEv
entArgs>(backgroundRequest_TransferStatusChanged);
BackgroundTransferService.Add(backgroundRequest);
}

void backgroundRequest_TransferStatusChanged(object sender,


BackgroundTransferEventArgs e)
{
if (backgroundRequest.TransferStatus == TransferStatus.Completed)
{
// elaboro il file scaricato
BackgroundTransferService.Remove(backgroundRequest);
}
}

void backgroundRequest_TransferProgressChanged(object sender,


BackgroundTransferEventArgs e)
{
double received = backgroundRequest.BytesReceived;
double total = backgroundRequest.TotalBytesToReceive;
double percentage = (received/total)*1⊘⊘;

LoadingBar.Value = percentage;
}

In questo esempio avviamo il download di un file in background: nell’evento


TransferStatusChanged andiamo a elaborare il file scaricato solo nel momento in cui il
download è terminato. Dopodiché, andiamo a rimuoverlo dalla coda dei download in
background sfruttando il metodo Remove() del BackgroundTransferService: questo perché i
trasferimenti terminati non vengono automaticamente rimossi dalla coda. È importante
effettuare questa operazione il prima possibile: ricordiamoci, infatti, che una singola
applicazione non può avere in coda più di 25 trasferimenti contemporaneamente.
Nell’evento TransferProgressChanged usiamo, invece, le informazioni sul totale di byte
trasmessi e sulla dimensione del file per calcolare la percentuale del trasferimento e
aggiornare una ProgressBar.
Possiamo notare che, al contrario delle altre classi viste in precedenza, entrambi gli eventi
restituiscono sempre un oggetto di tipo BackgroundTrasferEventArgs, che contiene un riferimento
alla richiesta nella proprietà Request. Tramite tale oggetto è poi possibile accedere alle varie
proprietà. In alternativa, è possibile ricorrere agli anonymous method oppure dichiarare
l’oggetto di tipo BackgroundTransferRequest a livello di classe.

Download di file con i background transfer


Per avviare un download in background è sufficiente creare una nuova istanza della classe
BackgroundTransferRequest, passando come parametri:

• l’URL del file da scaricare;


• la posizione dove scaricare il file.
Entrambe le proprietà sono di tipo Uri: nel primo caso si tratterà di un URL assoluto
(l’indirizzo internet del file), nel secondo di un URL relativo. Da Windows Phone 7.5 è
infatti possibile esprimere un percorso dell’Isolated Storage utilizzando un URL relativo,
senza dover ricorrere necessariamente alle classi disponibili nell’SDK per lavorare con lo
storage.

Importante! Tutti i download gestiti con la classe BackgroundTransferRequest devono


necessariamente essere salvati nella cartella /Shared/Transfers dell’Isolated
Storage, la quale, se non esiste già, verrà creata al primo trasferimento.
NOTA
All’interno di tale cartella possiamo poi andare a creare tutte le sottocartelle che
vogliamo, ma non possiamo salvare un file direttamente al di fuori di questa,
altrimenti sarà sollevata un’eccezione.

In alternativa, è possibile creare l’istanza della classe BackgroundTransferRequest passando


come parametro solamente l’Uri del file da scaricare e valorizzando successivamente la
proprietà DownloadLocation con il percorso dell’Isolated Storage nel quale verrà salvato.
Uri sourceUrl = new Uri("http://wpsauce.com/wp-content/uploads/2011/11/windows_
phone_logo.jpg");
Uri destinationUrl = new Uri("/Shared/Transfers/windows_phone_logo.jpg", UriKind.
RelativeOrAbsolute);

BackgroundTransferRequest backgroundRequest = new BackgroundTransferRequest(sourceU


rl, destinationUrl);
backgroundRequest.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
BackgroundTransferService.Add(backgroundRequest);

In questo esempio avviamo il download di un’immagine (il solito logo di Windows


Phone), che verrà salvata nella cartella /Shared/Transfers dell’Isolated Storage.

Upload di file con i background transfer


Per l’upload di file non possiamo utilizzare lo stesso costruttore con due parametri
impiegato in precedenza, in quanto può essere usato solo per specificare la posizione dove
scaricare il file (e quindi è utilizzabile solo per le operazioni di download).
Nel caso dell’upload il parametro che viene passato al costruttore indica l’indirizzo della
pagina o del servizio in grado di accettare il trasferimento. Dopodiché, occorre valorizzare
la proprietà UploadLocation con il percorso locale nell’Isolated Storage del file da caricare.
Uri destinationUrl = new Uri("http://www.example.com/upload.aspx", UriKind.Absolute);
Uri sourceFile = new Uri("/Shared/Transfers/Image.jpg", UriKind.Relative);

BackgroundTransferRequest backgroundRequest = new BackgroundTransferRequest(destinationUrl);


backgroundRequest.UploadLocation = sourceFile;
backgroundRequest.Method = "POST";
BackgroundTransferService.Add(backgroundRequest);

In questo esempio il file Image.jpg viene trasferito verso un’ipotetica pagina che risponde
all’indirizzo http://www.example.com/upload.aspx tramite il comando POST del
protocollo HTTP.
Gestire il trasferimento in background
La caratteristica principale della classe BackgroundTransferRequest è proprio quella di mantenere
i trasferimenti attivi anche quando l’utente non sta utilizzando l’applicazione: questo apre
una serie di nuovi scenari che non emergevano, invece, con l’utilizzo delle classi WebClient
e HttpWebRequest, dato che il ciclo di vita del trasferimento iniziava e terminava nel corso
dell’utilizzo dell’applicazione stessa.
Dato che il trasferimento rimane in esecuzione anche ad applicazione chiusa, come ci
dobbiamo comportare quando l’utente la riapre? Il trasferimento potrebbe essere
terminato, oppure aver dato un errore.
Per questo motivo, è buona prassi verificare sempre lo stato dell’operazione (tramite la
proprietà TransferStatus) nell’evento OnNavigatedTo, che viene scatenato quando la navigazione
dello stack delle pagine ha portato l’utente alla pagina corrente.
Tipicamente, in tale evento viene eseguito lo stesso codice dell’evento
TransferProgressChanged: si verifica il valore della proprietà TransferStatus e si agisce di
conseguenza (ad esempio, se il valore è Completed, si elabora il file scaricato e si rimuove il
trasferimento dalla coda).
Può essere una buona idea, pertanto, quella di creare una funzione ad hocche esegua
queste operazioni, da richiamare poi in entrambi gli eventi.
private void LoadDownloadedImage()
{
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = storage.OpenFile("/Shared/
Transfers/Image.jpg", FileMode.Open))
{
BitmapImage image = new BitmapImage();
image.SetSource(stream);
Logo.Source = image;
}
}
}

void backgroundRequest_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)


{

if (backgroundRequest.TransferStatus == TransferStatus.Completed)
{
LoadDownloadedImage();
BackgroundTransferService.Remove(backgroundRequest);
}
}

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)


{
if (backgroundRequest != null && backgroundRequest.TransferStatus == TransferStatus.Completed)
{
LoadDownloadedImage();
}
}

In questo esempio è stata definita una funzione LoadImage(), che si occupa di recuperare
dallo storage l’immagine scaricata e di mostrarla sullo schermo sfruttando un controllo
Image.

Tale funzione viene richiamata:


• nell’evento di callback TransferStatusChanged, nel caso in cui lo stato del trasferimento sia
a Completed;
• nell’evento OnNavigatedTo, nel caso in cui l’istanza corrente della classe
BackgroundTransferRequest sia ancora allocata e il trasferimento sia stato completato.

Il tutto funziona correttamente se l’applicazione proviene da uno stato suspend, dato che
nessun dato viene perso e perciò l’istanza della classe BackgroundTransferRequest è ancora
attiva.
Se proveniamo, però, da uno stato tombstone, questo riferimento non è più disponibile: in
tal caso il trucco è quello di sfruttare l’id che viene assegnato automaticamente a ogni
trasferimento, che andrà salvato in fase di sospensione usando i meccanismi che abbiamo
visto nel Capitolo 4.
Con questo identificatore, è poi possibile recuperare il riferimento al trasferimento grazie
al BackgroundTransferService, che espone il metodo Find(): questo, dato un id, restituisce il
corrispondente oggetto di tipo BackgroundTransferRequest.
Interagire con i servizi web
Al giorno d’oggi, nell’architettura delle applicazioni web si tende a sfruttare sempre di più
i servizi che offrono la possibilità di esporre i dati in modo che possano essere consumati
da applicazioni diverse, sviluppate magari con tecnologie e per dispositivi differenti.
La tecnologia di riferimento per il mondo Microsoft è WCF; sono, però, supportati anche
altri tipi di servizio, ad esempio SOAP o il protocollo OData.
La procedura per interagire con un servizio da un’applicazione Windows Phone è la stessa
che si utilizza in altre tipologie di applicazioni del mondo .NET: Visual Studio è infatti in
grado di generare, partendo da un servizio, una classe proxy che permette di interagire con
lo stesso come se si trattasse di una libreria .NET, operando quindi su proprietà e metodi
nel modo abituale.
Il vantaggio è che, trattandosi di un’applicazione Windows Phone, Visual Studio sa già
come generare le classi nella maniera più adatta: ad esempio, verranno create solo versioni
asincrone dei metodi esposti dal servizio.
Per farlo, dobbiamo cliccare con il tasto destro del mouse sul progetto Windows Phone in
Visual Studio e selezionare l’opzione Add Service Reference:
Figura 5.2 - L’opzione per collegare un’applicazione Windows Phone a un servizio.

Nella schermata successiva è possibile specificare l’indirizzo del servizio, oppure andare a
cercare un servizio locale nel caso in cui la soluzione di Visual Studio contenga almeno un
progetto di questo tipo.
Se all’indirizzo specificato viene trovato un servizio valido, la schermata ne mostrerà la
struttura e i metodi disponibili. A questo punto potremo specificare il namespace da
utilizzare per le classi che saranno generate da Visual Studio (di default è ServiceReference1) e
confermare con OK.
Figura 5.3 - La finestra Add Service Reference di Visual Studio.

Dopo qualche secondo vedremo dei nuovi file aggiunti al progetto Visual Studio:
• il file ServiceReferences.clientConfig, che contiene la configurazione del binding e
dell’endpoint. Il binding rappresenta la modalità (canale, protocollo ecc.) con cui la
nostra applicazione comunica con il servizio; l’endpoint contiene invece i vari
parametri relativi alla connessione vera e propria, come l’indirizzo del servizio e la
dimensione dei pacchetti;
• una cartella Service References, con all’interno il riferimento al servizio. Visual Studio
maschererà le classi vere e proprie, ma, se abilitiamo l’opzione Show all files della
finestra Solution explorer, potremo vedere tutti i file e il codice generato.
Se facciamo doppio clic sul servizio, potremo esplorare, con il tool Object Explorer, tutte le
classi generate utilizzando una comoda struttura ad albero: tra queste, ce ne sarà una con il
suffisso client; si tratta dell’oggetto che ci permetterà di interagire con il servizio e con il
quale effettueremo le varie operazioni.
Una volta creata un’istanza di tale classe, potremo interagire con tutti i metodi esposti dal
servizio: per ogni funzione ci sarà un metodo asincrono (con il suffisso Async) e un evento
di callback (con il suffisso Completed) con il quale elaborare il risultato ricevuto grazie alla
proprietà Result restituita.
Per fare qualche esperimento, il sito http://www.webservicex.net mette a disposizione una
serie di web services in maniera totalmente gratuita, generalmente di tipo SOAP.
Nell’esempio successivo useremo uno di quelli pubblicati sul sito, che permette di
convertire valori di peso da un’unità di misura all’altra.
Il servizio risponde all’indirizzo http://www.webservicex.net/convertMetricWeight. asmx?
WSDL, da specificare nella schermata Add Service Reference.
All’indirizzo http://www.webservicex.net/ws/WSDetails.aspx?WSID=33&CATID=13
possiamo vedere la struttura del servizio, che espone un’unica operazione:
ChangeMetricWeightUnit.

Una volta che Visual Studio ha generato le classi proxy, andiamo a cercare quella con il
suffisso client: nel nostro esempio si tratta della classe MetricWeightUnitSoapClient. Vediamone
un esempio di utilizzo:
public MainPage()
{
InitializeComponent();
MetricWeightUnit.MetricWeightUnitSoapClient client = new MetricWeightUnitSoapClient();
client.ChangeMetricWeightUnitCompleted += new EventHandler<ChangeMetricWeightUni
tCompletedEventArgs>(client_ChangeMetricWeightUnitCompleted);

client.ChangeMetricWeightUnitAsync(5, MetricWeights.milligram, MetricWeights.kilogram);


}

void client_ChangeMetricWeightUnitCompleted(object sender,


ChangeMetricWeightUnitCompletedEventArgs e)
{
MessageBox.Show(e.Result.ToString());
}

Come possiamo vedere, Visual Studio ha generato un metodo asincrono per accedere alla
funzione ChangeMetricWeightUnit, identificato dallo stesso nome con l’aggiunta del suffisso
Async. Tale metodo accetta in ingresso tre parametri: il valore da convertire, l’unità di
misura di partenza e quella di destinazione (sfruttando uno dei valori dell’enumeratore
MetricWeights, generato anch’esso in automatico in base alla definizione del servizio).

Prima di invocare tale metodo, però, ci sottoscriviamo all’evento


ChangeMetricWeightUnitCompleted: tutti gli eventi di callback generati da VisualStudio
restituiscono un oggetto con una proprietà Result, che contiene il valore restituito dal
servizio. In questo esempio, ci limitiamo a mostrare sullo schermo del device il risultato
della conversione.

Servizi di test e servizi di produzione


Generalmente, quando i servizi da consumare all’interno di un’applicazione sono
sviluppati da noi stessi, ci troviamo di fronte a due scenari diversi: quello di sviluppo, in
cui utilizziamo una versione locale del servizio per i test, e quella di produzione, in cui il
servizio è ospitato su un server remoto o nel cloud.
Come fare per gestire questa casistica? Ci viene in aiuto il file di configurazione
ServiceReference.ClientConfig: se ricordate quanto detto in precedenza, tale file contiene gli
endpoint, ovvero i vari parametri di configurazione per la connessione al servizio. Nel
momento in cui aggiungete il riferimento a un servizio tramite Visual Studio, il file
contiene un unico endpoint: quello verso l’indirizzo che avete specificato nella schermata
Add Service Reference.
Nulla vi vieta di aggiungere un altro endpoint con un indirizzo diverso: l’importante è che
l’attributo name sia univoco rispetto a tutti gli altri endpoint. Ecco un esempio:
<client>
<endpoint address="http://localhost/convertMetricWeight.asmx"
binding="basicHttpBinding" bindingConfiguration="MetricWeightUnitSoap"
contract="MetricWeightUnit.MetricWeightUnitSoap" name="MetricWeightUnitSoap_Development" />

<endpoint address="http://www.webservicex.net/convertMetricWeight.asmx"
binding="basicHttpBinding" bindingConfiguration="MetricWeightUnitSoap"
contract="MetricWeightUnit.MetricWeightUnitSoap" name="MetricWeightUnitSoap_Production" />
</client>

A questo punto, nel codice, avete la possibilità di specificare quale endpoint utilizzare in
fase di inizializzazione della classe proxy (nell’esempio precedente, la classe
MetricWeightSoapClient): tale classe accetta, infatti, tra i parametri del costruttore il nome
dell’endpoint da utilizzare. Nell’esempio precedente non l’avevamo utilizzato, in quanto,
essendo disponibile un solo endpoint, questo veniva automaticamente utilizzato.
Ecco, perciò, come diventerebbe l’inizializzazione della classe proxy usando le direttive di
compilazione e ipotizzando che, con la compilazione in modalità debug, venga utilizzato
l’endpoint di test, mentre con quella in modalità release l’endpoint di produzione.
#if DEBUG
MetricWeightUnitSoapClient client = new MetricWeightUnitSoapClient("Metric
WeightUnitSoap_Development");
#else
MetricWeightUnitSoapClient client = new MetricWeightUnitSoapClient("Metric
WeightUnitSoap_Production");
#endif

Le direttive di compilazione permettono di modificare il comportamento del


compilatore. Nel nostro caso, possiamo interagire con la modalità con cui verrà
NOTA
compilata l’applicazione e far sì che siano completamente ignorate le righe di
codice incluse nella condizione che non viene soddisfatta.
Interagire con i servizi REST
I servizi di tipo REST sono una realtà sempre più diffusa, grazie anche alla semplicità di
utilizzo indipendentemente dalla tecnologia usata per “consumarli”. Al contrario dei web
service tradizionali (basati su SOAP), i servizi REST si basano sul solo protocollo HTTP e
si rifanno al concetto di risorsa più che a quello di operazione. Cosa significa? Che un web
service tipicamente espone una serie di operazioni, le quali vengono interpretate e
utilizzate dal client grazie al protocollo SOAP; i servizi REST, invece, mappano questa
richiesta sotto forma di risorsa, usando la normale sintassi degli URL del protocollo
HTTP. Ecco, quindi, che un’ipotetica funzione per recuperare le informazioni di un utente
specifico sarebbe tradotta in un web service con il metodo GetUser(int id), mentre in un
servizio REST si tratterebbe semplicemente di richiamare l’URL / User/5 (dove 5 è
l’ipotetico id di un utente).
Il vantaggio di questo meccanismo è evidente: lato client per consumare un web service è
necessaria una libreria che sia in grado di tradurre la chiamata SOAP in qualcosa di
comprensibile per il linguaggio che stiamo utilizzando, a meno che non vogliamo
elaborare manualmente le chiamate SOAP di richiesta e di risposta. È il caso, ad esempio,
di Visual Studio, che genera la classe proxy di cui abbiamo parlato in precedenza. I servizi
REST sono, invece, facilmente utilizzabili con una normale chiamata HTTP, che è
possibile gestire con qualsiasi linguaggio o tecnologia. Il loro utilizzo è molto diffuso, ad
esempio, nel mondo dello sviluppo web: uno dei linguaggi più utilizzati per questo scopo
è Javascript.
Per interagire con questi servizi in un’applicazione Windows Phone è sufficiente una
chiamata HTTP usando una delle due classi viste in precedenza: WebClient o HttpWeb Request.
Quello che si ottiene in risposta è solitamente un XML o un JSON, che va poi elaborato
per ottenere i dati richiesti.
Esiste, però, una libreria di terze parti (ovvero che non è inclusa nell’SDK) chiamata
RestSharp, che semplifica l’utilizzo di servizi REST grazie a una miglior gestione delle
richieste e alla possibilità di personalizzare tutte le proprietà della connessione HTTP
(header, parametri ecc.). Vediamo come installarla e utilizzarla.

Integrare RestSharp all’interno di un progetto Windows Phone


Il sito ufficiale del progetto è http://restsharp.org/, da cui è possibile accedere al codice
sorgente della libreria (ospitato su GitHub) e alla documentazione.
Il metodo più semplice per installare la libreria è tramite NuGet: è sufficiente fare clic con
il tasto destro sul progetto, selezionare l’opzione Manage NuGet Packages, cercare il
pacchetto RestSharp e premere il pulsante Install.
Figura 5.4 - Il pacchetto di RestSharp disponibile su NuGet.

L’utilizzo di RestSharp si basa su tre classi chiave:


• RestRequest è la richiesta che viene inviata verso il servizio e che contiene tutti i dati
necessari (parametri, header ecc.) per ottenere una risposta;
• RestResponse è il risultato che si riceve in seguito a una richiesta;
• RestClient è il client che effettua la connessione vera e propria: si occupa di inviare una
richiesta e di elaborare la conseguente risposta.
Il funzionamento di questa libreria, di conseguenza, è piuttosto semplice: si crea
un’istanza di una RestRequest, si assegnano tutti i parametri necessari per interagire con il
servizio, si invia la richiesta tramite un RestClient (ovviamente in maniera asincrona) e si
elabora il risultato ricevuto.
Vediamo un esempio di utilizzo della libreria per consumare un servizio REST esposto da
Bacon Ipsum (http://baconipsum.com), un sito di generazione casuale di testi, che
vengono solitamente utilizzati quando si crea il prototipo di un’applicazione o di un sito
per simulare l’ingombro. Bacon Ipsum è la versione “goliardica” di Lorem Ipsum (sito
molto più celebre): il nome deriva dal fatto che i testi generati sono basati sulla traduzione
inglese di diversi tagli e tipologie di carni.
Questo sito espone un servizio REST, che risponde all’indirizzo
http://baconipsum.com/api/ e che permette di usufruire della stessa funzionalità del sito,
ovvero la generazione di testi casuali dati una serie di parametri come il numero di
paragrafi. La risposta che si ottiene è in formato JSON e la useremo, nel paragrafo
successivo, per vedere come possiamo manipolare questo formato in un’applicazione
Windows Phone.
La prima cosa da fare è definire la RestRequest, specificando l’indirizzo del servizio e i
parametri. Dalla pagina con la documentazione (http://baconipsum.com/api/) possiamo
vedere che i parametri vengono passati in GET (ovvero come query string dopo l’URL) e
che uno di questi è obbligatorio (type, che indica il database di parole che verrà utilizzato),
mentre due sono facoltativi. Utilizzeremo anche uno di quelli facoltativi, paras, per
specificare che ci interessa generare solo un paragrafo di testo, contro i cinque
dell’impostazione predefinita.
Ecco l’esempio di codice di creazione della richiesta.
RestRequest request = new RestRequest("http://baconipsum.com/api/");
request.AddParameter("type", "all-meat");
request.AddParameter("paras", "1");
request.Method = Method.GET;

Nel costruttore dell’oggetto di tipo RestRequest viene specificato l’URL base del servizio;
dopodiché i parametri vengono aggiunti alla richiesta stessa tramite il metodo AddParameter,
che accetta due valori: il nome del parametro e il valore assegnato. Infine, specifichiamo
che i parametri devono essere passati in GET, valorizzando la proprietà Method.
Ora che abbiamo definito la richiesta, siamo pronti per inviarla sfruttando la classe
RestClient, come nell’esempio:

RestClient client = new RestClient();


client.ExecuteAsync(request, Callback);

Il metodo da utilizzare è ExecuteAsync, che accetta come parametri l’oggetto di tipo


RestRequest definito in precedenza e l’evento nel quale andare a gestire la risposta, che in
questo caso abbiamo chiamato Callback.
private void Callback(RestResponse restResponse)
{
txtText.Text = restResponse.Content;
}

L’evento di callback restituisce un oggetto di tipo RestResponse, con il contenuto della


risposta (memorizzato nella proprietà Content) e le informazioni relative alla richiesta
HTTP (come lo status code, il tipo di contenuto, gli header ecc).
Avremmo potuto anche utilizzare la sintassi degli anonymous method, che ci permette di
definire l’evento di callback direttamente inline senza specificare un metodo a parte:
RestClient client = new RestClient();
client.ExecuteAsync(request, response =>
{
txtText.Text = response.Content;
});

Il risultato, in entrambi i casi, non cambia: il risultato della richiesta sarà visualizzato in un
controllo TextBlock. Il testo visualizzato conterrà, però, una serie di caratteri “strani”
all’inizio e alla fine: questo perché quella che ci viene restituita è la risposta grezza, in
formato JSON, non ancora elaborata sotto forma di oggetti e proprietà.
Lavorare con il formato JSON
Per lavorare con il formato JSON utilizzeremo un’altra libreria di terze parti, chiamata
JSON.NET, il cui sito di riferimento è http://json.codeplex.com/.
Anche JSON.NET è disponibile su NuGet: vi consiglio, perciò, di usare questo tool per
installarla nel vostro progetto.
Tale libreria ha due punti di forza:
• permette di serializzare e deserializzare documenti JSON in oggetti e classi. Il
framework .NET include già un serializzatore, chiamato DataContractJsonSerializer, ma
JSON.NET è più flessibile e supporta molti più scenari, come evidenziato nella
tabella comparativa mostrata sul sito ufficiale;
• include LINQ to JSON, un linguaggio basato su LINQ per manipolare dati in formato
JSON.

Serializzare e deserializzare
Serializzare significa trasformare una serie di oggetti e proprietà complesse in un qualcosa
di “piatto”, memorizzabile utilizzando un formato testuale come XML o JSON.
Tipicamente questa procedura si utilizza quando dobbiamo salvare degli oggetti in
memoria in un file fisico, per una memorizzazione permanente, ad esempio, su disco: la
tratteremo in dettaglio nel Capitolo 9, dedicato alla gestione dei dati.
La deserializzazione è il processo inverso ed è l’argomento che affronteremo in dettaglio
in questo paragrafo, dato che il nostro scopo è quello di trasformare il JSON restituito dal
servizio in una serie di oggetti e proprietà che possiamo manipolare all’interno della nostra
applicazione.
Come funziona la deserializzazione? Il processo è in grado di ricostruire una struttura dati
piatta in un oggetto .NET in maniera automatica, sfruttando una semplice convenzione: i
nomi delle proprietà devono coincidere con i nomi degli attributi del file JSON. Ad
esempio, un’ipotetica classe per gestire una persona:
public class Person
{
public string name { get; set; }
public string surname { get; set; }
}

verrebbe tradotta in un JSON di questo tipo:


{
"name": "Matteo",
"surname": "Pagani"
}

Si può notare come i nomi degli attributi nel file JSON (name e surname) coincidano con le
proprietà della classe Person.

Esistono in rete diversi tool che semplificano questo processo, creando per noi
la classe C# dato un JSON di riferimento, come JSON C# Class Generator
(http://jsonclassgenerator.codeplex.com/) e Json2Csharp
NOTA (http://json2csharp.com/). Il primo è una vera e propria applicazione per
Windows, in grado di generare i file .cs veri e propri; il secondo, invece, è un
sito web, che effettua la conversione online e lascia a noi il compito di creare i
file veri e propri.

In alternativa, JSON.NET mette a disposizione una serie di attributi che consentono di


controllare il processo di serializzazione e deserializzazione; in questo modo possiamo
dare nomi alle proprietà dell’oggetto differenti da quelli del JSON. Si tratta di un
approccio molto utilizzato, dato che spesso la definizione degli oggetti JSON (ereditando
dalle caratteristiche di Javascript) usa un approccio lower case (tutte le lettere in
minuscolo), mentre solitamente in C# le proprietà pubbliche di una classe iniziano con la
lettera maiuscola.
Di conseguenza, ecco come potrebbe essere la definizione della classe utilizzando gli
attributi:
public class Person
{
[JsonProperty("name")]
public string Name { get; set; }

[JsonProperty("lastname")]
public string Surname { get; set; }
}

Questa procedura è molto importante perché, spesso e volentieri, per i server che
manipolano i dati in JSON maiuscole e minuscole sono considerate caratteri differenti.
Ciò significa che, ad esempio, le proprietà Name e name sono diverse.
Ecco come deserializzare il JSON utilizzato nell’esempio per ottenere un oggetto di tipo
Person:
JsonDeserializer json = new JsonDeserializer();
Person person = json.Deserialize<Person>(json);

Dopo aver creato un’istanza della classe JsonDeserializer, si utilizza il metodo Deserialize() , che
richiede, usando la sintassi dei generics, il tipo di oggetto che si desidera ottenere. Come
parametro, infine, è richiesta la stringa contenente il JSON da deserializzare. Ecco un
esempio concreto di come sfruttare questa classe per deserializzare il risultato ottenuto
dalla chiamata al servizio REST di Bacon Ipsum che abbiamo visto in precedenza:
private void Callback(RestResponse restResponse)
{
JsonDeserializer json = new JsonDeserializer();
List<string> result = json.Deserialize<List<string>>(restResponse);
}

Nella callback della richiesta deserializziamo la risposta sotto forma di collezione di


stringhe (List<string>): il servizio restituisce, infatti, un array, dove ogni elemento
corrisponde a uno dei paragrafi generati.

LINQ TO JSON
In alcuni casi il JSON restituito può essere parecchio complesso e non si è in grado di
ricreare una classe in grado di mapparlo con precisione; in altri casi il risultato restituito
dal servizio può contenere molti dati, mentre a noi ne serve solamente uno in particolare.
Per questi scopi la libreria JSON.NET ha introdotto LINQ to JSON, un linguaggio basato
su LINQ per manipolare i dati di un file JSON. Lo stesso framework .NET include un
linguaggio simile, dedicato però ai file XML: LINQ to XML.
La classe di riferimento per iniziare a utilizzare LINQ to JSON si chiama JObject ed espone
una serie di metodi per caricare il file JSON in memoria. Il metodo più utilizzato è
sicuramente Parse, che accetta come parametro di input una stringa: dato che le RestResponse
restituiscono proprio una stringa tramite la proprietà Content, diventa quindi molto semplice
caricare in memoria la risposta restituita da un servizio REST.
private void Callback(RestResponse restResponse)
{
JObject json = JObject.Parse(restResponse.Content);

}

Ecco alcuni esempi di manipolazione di un file JSON con LINQ to JSON:

JSON composto da attributi semplici


In questo caso si utilizza una sintassi molto simile a quella per accedere a un elemento di
una collezione. Ipotizzando di avere questo JSON:
{
"name": "Matteo",
"Surname": "Pagani"
}

e di voler recuperare il valore dell’attributo name , ecco il codice da utilizzare:


JObject json = JObject.Parse(restResponse.Content);
txtText.Text = json["name"].ToString();

Il nome dell’attributo viene specificato tra parentesi quadre, come indice dell’oggetto di
tipo JObject.

JSON composto da attributi complessi


In alcuni casi un attributo JSON può essere complesso, ovvero contenere, a sua volta, dei
sotto attributi, come nell’esempio:
{
"name": "Matteo",
"surname": "Pagani",
"address": {
"street": "Fake address",
"city": "Milan",
"zipcode": "2⊘158"
}
}

In questo caso possiamo utilizzare il metodo SelectToken, che ci permette di navigare la


struttura ad albero del file JSON semplicemente utilizzando un punto quando ci dobbiamo
spostare verso un sotto attributo.
Ecco un esempio di come recuperare l’attributo city, contenuto a sua volta all’interno
dell’attributo address:
JObject json = JObject.Parse(restResponse.Content);
txtText.Text = json.SelectToken("address.city").ToString();

Il JSON potrebbe contenere anche una collezione di dati al suo interno, ad esempio più
attributi address per poter gestire indirizzi diversi associati alla stessa persona, come
nell’esempio:
{
"name": "Matteo",
"surname": "Pagani",
"address":[{
"street": "Fake address",
"city": "Milan",
"zipcode": "2⊘158"
},
{"street": "Fake address",
"city": "Rome",
"zipcode": "⊘⊘118"}]
}

In questo caso la sintassi da utilizzare con il metodo SelectToken() è quella classica di accesso
a un elemento di un array: specificando tra parentesi quadre l’indice di elemento. Ecco un
esempio di come recuperare il valore dell’attributo city del secondo indirizzo, ovvero Rome.
JObject json = JObject.Parse(restResponse.Content);
txtText.Text = json.SelectToken("address[1].city").ToString();

JSON composto da collezioni di dati


Nell’esempio precedente abbiamo visto come un file JSON possa contenere anche una
collezione di dati: l’entità rappresentante una persona conteneva, infatti, più indirizzi.
Per interagire con le collezioni tramite LINQ to JSON è necessario utilizzare il metodo
Children(), che restituisce una collezione di tutti i nodi figli. Tale collezione implementa
l’interfaccia base delle collezioni .NET IEnumerable: è quindi possibile eseguire query LINQ
su di essa per estrarre i dati che ci interessano.
Supponiamo di avere il seguente JSON che, sulla falsariga di quelli visti in precedenza,
contiene una collezione di persone:
{
"people": [
{
"name": "Matteo",
"surname": "Pagani",
"address": {
"street": "Fake address",
"city": "Milan",
"zipcode": "20158"
}
},
{
"name": "Mario",
"surname": "Rossi",
"address": {
"street": "Fake address",
"city": "Rome",
"zipcode": "00118"
}
}
]
}

Il nostro scopo è quello di creare, nell’applicazione Windows Phone, una collezione di


oggetti di tipo Person: secondo la definizione di tale classe che abbiamo visto in
precedenza, però, le uniche informazioni che ci interessano sono il nome e il cognome.
Andremo, perciò, a eseguire una query LINQ to SQL per trasformare il JSON in una
collezione di oggetti .NET di tipo Person, scartando le informazioni che non ci interessano
(in questo caso, l’indirizzo).
JObject json = JObject.Parse(restResponse.Request);
List<Person> persons = json.SelectToken("people").Children()
.Select(x => new Person
{
Name = x["name"].ToString(),
Surname = x["surname"].ToString()
}).ToList();

La prima cosa da fare è utilizzare il metodo già visto SelectToken() per accedere all’attributo
contenente la collezione (in questo caso people): dopodiché, tramite il metodo Children() ,
abbiamo accesso a tutti i nodi figli, nel nostro esempio alle due persone incluse nella lista.
Con la sintassi di LINQ effettuiamo un’operazione di selezione: per ogni elemento
recuperato creiamo un nuovo oggetto di tipo Person, valorizzando le proprietà Name e
Surname con i relativi attributi (anche qui utilizzando la sintassi con le parentesi quadre per
accedere a un attributo specifico, che abbiamo già visto in precedenza).
Il risultato sarà una collezione .NET di tipo List<Person>, che potrà essere comodamente
utilizzata, ad esempio, per popolare una lista sfruttando un controllo ListBox.
Interagire con i servizi OData
OData è un protocollo utilizzato per lo sviluppo di servizi REST e, per questo motivo, i
principi sul quale è basato sono gli stessi visti nel Capitolo 4: l’utilizzo del protocollo
HTTP e di XML e JSON come formato di scambio dati.
La differenza è che OData è in grado di mappare le classiche operazioni che vengono
effettuate sui dati (inserimento, aggiornamento, cancellazione e selezione, in gergo tecnico
si usa l’acronimo CRUD, dalle iniziali inglesi) su specifici comandi del protocollo HTTP.
In più, sono supportati una serie di parametri opzionali per effettuare operazioni sui dati
direttamente tramite la chiamata al servizio, ad esempio applicare filtri o ordinare i
risultati.
Dal punto di vista dell’applicazione Windows Phone, il punto di forza è che i servizi che
implementano il protocollo OData sono supportati in modo nativo: invece di dover
effettuare le chiamate HTTP manualmente (come abbiamo visto negli esempi del
paragrafo precedente, dedicato ai servizi REST), è possibile generare automaticamente
una sorta di classe proxy che ci permette di effettuare operazioni sui dati usando oggetti
.NET e LINQ.
I servizi OData sono basati sul concetto di endpoint, che rappresenta l’entità che è
possibile manipolare tramite il servizio. Gli endpoint vengono elencati accedendo
all’indirizzo del servizio, tramite un documento XML.
Per gli esempi di questo paragrafo utilizzeremo un servizio OData realizzato con la
tecnologia di Microsoft WCF Data Services: tale servizio è esposto dal sito Nerd Dinner
(http://www.nerddinner.com), un portale realizzato da Microsoft come esempio di utilizzo
delle più recenti tecnologie web (come ASP.NET MVC, jQuery ecc.). Se si vuole studiare
il suo funzionamento, infatti, l’intero codice sorgente è disponibile all’indirizzo
http://nerddinner.codeplex.com/.
Lo scopo del sito è quello di aiutare le persone a organizzare cene, offrendo una
piattaforma dove gli utenti possono segnalare un appuntamento o aggregarsi a quelli già
esistenti. Il servizio espone, perciò, le operazioni principali offerte dal sito (ad esempio,
segnalare la propria presenza a una cena), così che sia semplice costruire, ad esempio,
un’applicazione mobile dedicata.
Se apriamo il browser all’indirizzo del servizio (http://www.nerddinner.com/Services/
odata.svc) potremo vedere gli endpoint di cui abbiamo parlato prima:
<service xmlns:atom="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/
app" xmlns="http://www.w3.org/2007/app" xml:base="http://www.nerddinner.com/
Services/OData.svc/">
<workspace>
<atom:title>Default</atom:title>
<collection href="Dinners">
<atom:title>Dinners</atom:title>
</collection>
<collection href="RSVPs">
<atom:title>RSVPs</atom:title>
</collection>
</workspace>
</service>
Come possiamo notare, sono due gli endpoint, ovvero le entità che potremo manipolare:
Dinners (ovvero le cene) e RSVPs (ovvero le conferme di partecipazione a una cena). Ancora
prima di iniziare a interagire con il servizio dalla nostra applicazione Windows Phone,
possiamo avere un’idea di come funziona l’interazione con un servizio OData
semplicemente dal browser: dato che, per effettuare operazioni con lo stesso, è sufficiente
utilizzare comandi HTTP, possiamo usare una semplice GET per recuperare l’elenco di
tutte le cene disponibili richiamando l’indirizzo:
http://www.nerddinner.com/Services/odata.svc/Dinners
Nel browser apparirà un XML molto lungo, composto da molteplici nodi entry, ognuno
dei quali corrisponde a una cena.
<entry>
<id>http://www.nerddinner.com/Services/OData.svc/Dinners(1)</id>
<title type="text"></title>
<updated>2⊘12-⊘4-12T13:51:⊘5Z</updated>
<author>
<name />
</author>
<link rel="edit" title="Dinner" href="Dinners(1)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RSVPs"
type="application/atom+xml;type=feed" title="RSVPs" href="Dinners(1)/RSVPs" />
<category term="NerdDinner.Models.Dinner" scheme="http://schemas.microsoft.com/
ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:DinnerID m:type="Edm.Int32">1</d:DinnerID>
<d:Title>ALT.NERD Dinner</d:Title>
<d:EventDate m:type="Edm.DateTime">2009-02-27T20:00:00</d:EventDate>
<d:Description>Are you in town for the ALT.NET Conference? Are you a .NET
person? Join us at this free, fun, nerd dinner. Well, you pay for your food! But,
still! Come by Red Robin in Redmond Town Center at 8pm Friday.</d:Description>
<d:HostedBy>shanselman</d:HostedBy>
<d:ContactPhone>503-766-2048</d:ContactPhone>
<d:Address>7597 170th Ave NE, Redmond, WA</d:Address>
<d:Country>USA</d:Country>
<d:Latitude m:type="Edm.Double">47.670172</d:Latitude>
<d:Longitude m:type="Edm.Double">-122.1143</d:Longitude>
<d:HostedById>shanselman</d:HostedById>
</m:properties>
</content>
</entry>

Il formato utilizzato per la risposta è XML, dato che di default il browser, nell’header della
richiesta, indica al server di supportare HTML come formato della risposta. Se avessimo
effettuato la chiamata specificando un header differente (ad esempio, utilizzando
Javascript), avremmo ottenuto una risposta in formato JSON.
È possibile anche effettuare query di ricerca più complesse, sempre utilizzando comandi di
tipo GET: ad esempio, è possibile recuperare uno specifico elemento della lista
specificandone l’identificatore univoco tra parentesi:
http://www.nerddinner.com/Services/odata.svc/Dinners(13)
Nell’esempio andiamo a recuperare il solo nodo entry relativo alla cena che ha come
identificatore il numero 13.
Possiamo anche effettuare delle query per ottenere dati su entità relazionate tra di loro:
http://www.nerddinner.com/Services/odata.svc/Dinners(13)/RSVPs
In questo esempio andiamo a recuperare tutte le conferme di partecipazione che sono state
date alla cena identificata dal numero 13.
Infine, il protocollo OData supporta una serie di parametri per filtrare i risultati, da
aggiungere all’indirizzo del servizio sotto forma di query string. Ad esempio, è possibile
definire l’ordinamento dei risultati tramite il parametro $orderby, seguito dal nome
dell’attributo.
http://www.nerddinner.com/Services/odata.svc/Dinners?$orderby=EventDate
In questo esempio l’elenco delle cene viene restituito ordinato in base alla data.
Oppure è possibile recuperare solo un certo numero di risultati tramite il parametro $top,
come nell’esempio:
http://www.nerddinner.com/Services/odata.svc/Dinners?$top=10
È importante conoscere la teoria e il funzionamento del protocollo OData, dato che il suo
punto di forza è la versatilità e potreste trovarvi a utilizzarli in altre tipologie di
applicazioni, sviluppate magari con tecnologie non Microsoft (ad esempio,
un’applicazione iPhone o Android).
In realtà, però, come già anticipato, nel mondo Windows Phone non dovrete preoccuparvi
troppo dei dettagli di funzionamento o di conoscere a memoria tutti i filtri possibili: grazie
alla classe proxy generata da Visual Studio, infatti, potrete manipolare i dati tramite LINQ
come siete abituati a fare con qualsiasi altro tipo di collezione.

Utilizzare un servizio OData in un’applicazione Windows Phone


La prima operazione da fare è analoga a quanto abbiamo visto con i servizi WCF: fare clic
con il tasto destro sul progetto Windows Phone in Solution Explorer e selezionare la voce
Add Service Reference. Nella schermata che comparirà andremo a inserire l’indirizzo del
servizio, ovvero:
http://www.nerddinner.com/Services/odata.svc/Dinners
L’unica differenza, rispetto a quanto visto con i servizi WCF, è che Visual Studio, nella
finestra dedicata alle operazioni disponibili, non ne elencherà alcuna: questo perché, come
abbiamo visto, i servizi REST non espongono operazioni, ma risorse.
Premendo il pulsante Add, saranno generate le varie classi necessarie per interagire con il
servizio, nello specifico una per ogni endpoint (Dinner e RSVP) più una classe client, che
è quella con cui andremo a interagire con il servizio. Anche in questo caso, potremo
specificare un namespace all’interno del quale includere tutte le classi generate.
A questo punto possiamo creare un’istanza della classe client, specificando come
parametro del costruttore l’indirizzo del servizio:
NerdDinners context = new NerdDinners(new Uri("http://www.nerddinner.com/Services/odata.svc"));

Se “sbirciamo” tra le proprietà e i metodi esposti dalla classe NerdDinners noteremo che
avremo accesso alle entità definite dal servizio: ad esempio, troveremo due collezioni
Dinners e RSVPs, per accedere ai rispettivi endpoint.

Non possiamo utilizzarle direttamente, però: dobbiamo ricordarci, infatti, che Windows
Phone è un ambiente asincrono, perciò i dati dal servizio devono essere caricati in maniera
asincrona.
Per ottenere questo risultato si utilizza una DataServiceCollection<T>, che non è altro che una
collezione di oggetti di tipo T che espone, però, metodi per il caricamento asincrono. Una
volta creata un’istanza di tale classe (nel nostro esempio, T equivale a Dinner), ci
sottoscriviamo all’evento LoadCompleted (che viene scatenato nel momento in cui il servizio
ha restituito i dati) e chiamiamo il metodo LoadAsync(), che richiede come parametro di
input la query LINQ da eseguire.
Vediamo un esempio:
private DataServiceCollection<Dinner> result;

// Constructor
public MainPage()
{
InitializeComponent();
NerdDinners context = new NerdDinners(new Uri("http://www.nerddinner.com/
Services/odata.svc"));
var query = from dinner in context.Dinners
where dinner.Country == "Italy"
orderby dinner.EventDate
select dinner;
result = new DataServiceCollection<Dinner>();
result.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(result_LoadCompleted);
result.LoadAsync(query);
}

void result_LoadCompleted(object sender, LoadCompletedEventArgs e)


{
DinnersList.ItemsSource = result;
}

La query LINQ che eseguiamo effettua una selezione di tutte le cene che si svolgeranno in
Italia, ordinate per data. Una volta che l’evento LoadCompleted si è verificato, la collezione
results è popolata con il risultato della richiesta: essendo la classe DataServiceCollection un tipo
di collezione che implementa l’interfaccia standard IEnumerable, possiamo tranquillamente
assegnarla alla proprietà ItemsSource di un controllo ListBox.

Inserire, aggiornare e cancellare dati


Le procedure di inserimento, aggiornamento e cancellazione di oggetti tramite un servizio
oData sono molto semplici: è sufficiente operare sulla collezione che si vuole modificare,
dopodiché chiamare il metodo asincrono esposto dal client BeginSaveChanges().
Se vogliamo modificare un elemento, è sufficiente recuperarlo dalla collezione di tipo
DataServiceCollection<T> e poi modificare le proprietà desiderate.

Dinner dinner = result.Where(x => x.DinnerID == 1);


dinner.EventDate=new DateTime(2012, 04, 12);

In questo esempio viene recuperata la cena il cui id è uguale a 1 e ne viene modificata la


data.
Se vogliamo aggiungere un elemento, dobbiamo creare una nuova istanza dell’oggetto e
aggiungerlo alla collezione di tipo DataServiceCollection<T>.
Dinner dinner = new Dinner
{
Title = "Great dinner",
Country = "Italy"
};
collection.Add(dinner);

Nell’esempio viene creato un nuovo oggetto di tipo Dinner e aggiunto all’oggetto collection,
che è una collezione di tipo DataServiceCollection<Dinner>.
Infine, per cancellare un elemento dobbiamo eliminarlo dalla collezione di tipo
DataServiceCollection<T> tramite il metodo Remove.

Dinner dinner = collection.Where(x => x.DinnerID == 1);


collection.Remove(dinner);

Nell’esempio recuperiamo la cena il cui identificativo è uguale a 1 e la eliminiamo dalla


collezione.
Una volta che abbiamo effettuato una qualsiasi delle operazioni appena viste, dobbiamo
chiamare il metodo BeginSaveChanges() esposto dal client (nel nostro esempio, l’oggetto di
tipo NerdDinners), che utilizza il vecchio meccanismo di gestione degli eventi asincroni, nel
quale dobbiamo definire l’evento di callback in cui finalizzare l’operazione sfruttando il
metodo EndSaveChanges().
public partial class MainPage : PhoneApplicationPage
{
private NerdDinners context;

// Constructor
public MainPage()
{
InitializeComponent();

context = new NerdDinners(new Uri("http://www.nerddinner.com/Services/odata.svc"));


//effettuo le modifiche ai dati

context.BeginSaveChanges(Callback, null);
}

private void Callback(IAsyncResult result)


{
DataServiceResponse response = context.EndSaveChanges(result);
if (response.Any(x => x.Error != null))
{
MessageBox.Show("Error!");
}
}
}

Nell’evento di callback, una volta finalizzata la richiesta asincrona con il metodo


EndSaveChanges() , otteniamo una risposta di tipo DataServiceResponse, che in realtà è una
collezione di tutte le risposte ottenute dal servizio durante la connessione.
In realtà l’evento di callback ci serve solo per verificare se ci sono stati degli errori:
l’operazione di salvataggio delle modifiche è infatti di tipo “fire and forget”, ovvero una
volta lanciata non è più richiesto alcun intervento da parte dello sviluppatore. L’unica
operazione che andiamo a fare è, quindi, verificare se nella collezione ci sono elementi la
cui proprietà Error è diversa da null: in questo caso ciò significa che si è verificato un errore
durante l’operazione, perciò avvisiamo l’utente tramite un messaggio.
Le Proximity API
Una delle novità introdotte in Windows Phone 8 è il supporto alle Proximity API, che è
una famiglia di classi introdotta nel Windows Runtime e già disponibile per le
applicazioni Windows Store di Windows 8.
Le Proximity API consentono di far comunicare due dispositivi tra di loro senza passare
per una connessione a Internet: le tecnologie supportate da Windows Phone 8 sono
Bluetooth e NFC.
La prima è una tecnologia wireless per la comunicazione a breve distanza: la distanza
varia a seconda della classe (esistono tecnologie Bluetooth di classe 1, 2 e 3), ma in linea
di massima si aggira intorno ai 10 metri. Tale tecnologia era disponibile sin dalla prima
versione di Windows Phone 7, ma solo a uso e consumo del sistema operativo: lo scenario
tipico era la connessione del telefono a un auricolare o a un kit vivavoce. Solo a partire da
Windows Phone 8, Microsoft ha dato la possibilità agli sviluppatori di supportare la
tecnologia Bluetooth all’interno delle proprie applicazioni, per comunicare con altri
dispositivi.
NFC è l’acronimo di Near Field Communication ed è una tecnologia che ha iniziato a
diffondersi in tempi più recenti e consente lo scambio di piccole quantità di dati a distanza
molto ravvicinata: i due dispositivi devono praticamente essere a contatto l’uno con
l’altro.
NFC ha preso sempre più piede soprattutto per la sua capacità di funzionare non
solamente con dispositivi attivi (come un altro telefono o un tablet), ma anche con
dispositivi passivi: un totem di un museo, uno sticker, un inserto pubblicitario di una
rivista. In più, all’estero, si sta diffondendo anche come tecnologia per i pagamenti
elettronici che gli utenti possono utilizzare direttamente dal proprio smartphone.
NFC si distingue, oltre che per la breve distanza, anche per la ridotta quantità di dati che è
in grado di trasportare: è possibile utilizzare il chip NFC per condividere un contatto, un
URL, un testo, ma non immagini o grossi file. È interessante mettere in evidenza, però,
come tramite NFC sia possibile creare un canale di comunicazione Bluetooth tra due
dispositivi senza la necessità di effettuare il pairing manuale tramite pin: è il sistema, ad
esempio, utilizzato da Windows Phone 8 quando si sfrutta NFC per scambiare
un’immagine con un altro dispositivo.
In questa sezione andremo a vedere come gestire alcuni degli scenari più comuni con
NFC: lo scambio di messaggi, la scrittura di tag e la comunicazione tra due applicazioni.

Testare applicazioni che fanno uso delle Proximity API


Il modo più semplice ed efficace per testare applicazioni che fanno uso delle Proximity
API è quello di possedere due dispositivi Windows Phone dotati di NFC.
Dato che, come potete immaginare, non si tratta di un requisito così facile da soddisfare, è
stato messo a disposizione su Codeplex un tool non ufficiale (l’uso è suggerito dalla stessa
Microsoft nella documentazione MSDN, ma non è da loro supportato) chiamato
Proximity Tapper, scaricabile all’indirizzo http://proximitytapper.codeplex.com/. Questo
tool vi consente di testare applicazioni che fanno uso del codice che vedremo nel corso del
paragrafo in due modalità:
• con un singolo emulatore, sarete in grado di simulare l’utilizzo di un tag NFC;
• con due emulatori avviati contemporaneamente, sarete in grado di creare un canale di
comunicazione tra gli stessi e di simulare sia lo scambio di messaggi NFC che lo
scambio di dati attraverso un canale Bluetooth.
Vediamo come fare: dopo aver installato l’applicazione, vi troverete davanti a una
schermata come quella in Figura 5.5.

Figura 5.5 - L’applicazione Promixity Tapper per il testing delle Proximity API con l’emulatore.

Nella sezione Devices troverete l’elenco di tutti gli emulatori avviati sul vostro computer.
Cliccando su uno di essi, avrete la possibilità, tramite i due pulsanti sottostanti, di simulare
un tap veloce (il device viene appoggiato vicino a un tag o a un altro device, e poi, dopo
un secondo, rimosso) oppure di mantenere il canale aperto.
Una volta che la connessione è stabilita, la troverete elencata nella sezione sottostante,
chiamata Connections. Selezionandola e utilizzando i pulsanti sottostanti, avrete la
possibilità di simulare l’utilizzo di un tag NFC, per inviare un URL, un messaggio o un
file.
Se, invece, volete collegare tra di loro due emulatori per creare un canale, dovete
innanzitutto avviare due versioni differenti di Windows Phone: non è possibile, infatti,
avere due emulatori uguali in esecuzione; dovrete, perciò, utilizzarne due a risoluzioni
differenti.
Una volta che entrambe le macchine virtuali sono visualizzate all’interno della sezione
Devices, selezionatele facendo clic tenendo premuto il pulsante CTRL e premete il
pulsante Tap selected devices and remain connected. A questo punto la connessione sarà
elencata nell’elenco sottostante e il canale sarà creato.

Lo scambio di messaggi
Lo scambio di messaggi è uno degli scenari più comuni, in virtù del fatto che i dispositivi
Windows Phone sono in grado di interpretare in maniera nativa alcuni tipi di messaggi. Se,
ad esempio, scegliamo di condividere un URL dalla nostra applicazione, un dispositivo
Windows Phone 8 è in grado di intercettarlo senza che ci sia la necessità di utilizzare
un’applicazione dedicata. In più, grazie allo standard NFC Data Exchange, è possibile
scambiare messaggi utilizzando formati standard, che possono essere compresi anche da
dispositivi che non utilizzano Windows Phone come sistema operativo. Ma partiamo dalle
basi: l’utilizzo di NFC è basato sulla classe Proximity, facente parte del namespace
Windows.Networking.Proximity.

È necessario abilitare la capability ID_CAP_PROXIMITY all’interno del file di


NOTA
manifest per utilizzare le Proximity API.

Questa classe funziona in maniera analoga ai sensori di movimento, che vedremo nel
Capitolo 6: per inizializzarla occorre utilizzare il metodo GetDefault(), che restituisce un
riferimento al sensore NFC presente sul telefono. Se tale metodo restituisce un valore
nullo, significa che ci troviamo in presenza di un device non dotato di NFC: è sempre
bene, pertanto, verificare il risultato dell’inizializzazione prima di procedere.
ProximityDevice device = ProximityDevice.GetDefault();

if (device != null)
{

// Inizio la comunicazione tramite NFC


}

Un messaggio è caratterizzato da un protocollo e da una tipologia, separate da un punto.


Ad esempio, la stringa Windows.SimpleMessage identifica un messaggio di tipo
SimpleMessage utilizzando il protocollo Windows.

Esistono diverse tipologie di messaggi che è possibile scambiare utilizzando le API di


Windows Phone:
• messaggi testuali;
• Uri;
• messaggi binari.
Ognuno di questi messaggi ha delle regole ben precise per quanto riguarda il protocollo e
la tipologia. Vediamoli nel dettaglio.

Messaggi testuali
I messaggi testuali contengono al loro interno una semplice stringa, che può essere
ricevuta da un’altra applicazione. Per inviarli si utilizza il metodo PublishMessage(), che
accetta solamente messaggi caratterizzati dal protocollo Windows.
La tipologia può invece essere definita a piacimento, fintanto che la stringa utilizzi i
caratteri alfanumerici e non superi i 250 caratteri. Esempi di messaggi validi sono
Windows.SimpleMessage, Windows.MyMessage, Windows.ThisIsForYou.

L’importante è che l’applicazione ricevente si sottoscriva esattamente a quel tipo di


messaggio per poterlo ricevere.
Vediamo un esempio di invio di un messaggio testuale:
private void OnSendMessageClicked(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();

if (device != null)
{
long Id = device.PublishMessage("Windows.SampleMessage", "Messaggio di prova");
}
}

Una volta inizializzata l’istanza della classe ProximityDevice e verificato che il device sia
dotato di sensore NFC, siamo pronti per pubblicare il messaggio utilizzando il metodo
PublishMessage(), che accetta come parametri la tipologia di messaggio e il testo.

Possiamo notare come il metodo restituisca un numero, di tipo long: si tratta


dell’identificativo univoco del messaggio, che può essere importante mantenere perché ci
consente di interrompere l’invio di quello specifico messaggio.
Vediamo un esempio sfruttando una variante del metodo PublishMessage(), che accetta un
terzo parametro: l’handler di un evento, che viene invocato nel momento in cui il
messaggio è stato pubblicato e ricevuto da un altro dispositivo.
private void OnSendMessageClicked(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
if (device != null)
{
long Id = device.PublishMessage("Windows.SampleMessage", "Messaggio
di prova", MessageSent);
}
}

private void MessageSent(ProximityDevice sender, long messageId)


{
MessageBox.Show("Il messaggio è stato spedito!");
sender.StopPublishingMessage(messageId);
}

In questo esempio, nel momento in cui il messaggio è stato spedito e ricevuto, mostriamo
un messaggio a video e ne interrompiamo la pubblicazione, tramite il metodo
StopPublishingMessage() esposto dalla classe ProximityDevice. Il parametro passato è proprio
l’identificativo del messaggio, che recuperiamo dai parametri del metodo, insieme al
riferimento al sensore NFC.
Come facciamo a ricevere questo messaggio da un’altra applicazione? Dobbiamo
utilizzare il metodo SubscribeForMessage() che, al contrario di PublishMessage (che è specifico per
i messaggi testuali), è generico: lo riutilizzeremo anche per gestire tutti gli altri tipi di
messaggio.
private void OnReadMessageClicked(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();

if (device != null)
{
long Id = device.SubscribeForMessage("Windows.SampleMessage", messageReceived);
}
}

private void messageReceived(ProximityDevice sender, ProximityMessage message)


{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(message.DataAsString);
});
sender.StopSubscribingForMessage(message.SubscriptionId);
}

Il metodo accetta due parametri: la tipologia di messaggio da sottoscrivere (che deve


coincidere con quella inviata dall’altra applicazione, nel nostro caso Windows.SampleMessage) e
l’event handler da eseguire nel momento in cui il messaggio è stato ricevuto. Tale handler
riceve come parametro, oltre al riferimento al sensore NFC, anche il messaggio vero e
proprio, che espone una serie di metodi per manipolarlo. Dato che sappiamo che si tratta
di un messaggio testuale, possiamo usare la proprietà DataAsString per ottenerne il
contenuto in formato testuale. Notiamo l’utilizzo del dispatcher, dato che l’evento viene
gestito in un thread secondario rispetto a quello della UI: ci serve il dispatcher per
mostrare, perciò, il contenuto del messaggio sullo schermo. Infine, analogamente a quanto
abbiamo fatto quando abbiamo inviato il messaggio, fermiamo la ricezione da parte
dell’applicazione tramite il metodo StopSubscribingForMessage().

Nota bene! Il metodo PublishMessage() serve solo per scambiare messaggi con altri
NOTA telefoni: non può essere utilizzato per la scrittura di tag o altri dispositivi NFC
di tipo passivo.

Messaggi contenenti un URL


Un’altra tipologia di messaggi molto diffusa è quella per lo scambio di URL: l’indirizzo di
un sito, un indirizzo di posta elettronica ecc.
Gli URL, inoltre, vengono interpretati da Windows Phone in maniera nativa: non è
necessaria un’applicazione dedicata per riceverli, ma il sistema stesso è in grado di
decodificarli e di proporre all’utente di aprire il browser per andare a visitare il sito.
Figura 5.6 - La ricezione di un messaggio contenente un URL da parte del sistema operativo.

Le API di Windows Phone 8 espongono un metodo specifico per l’invio di URL:


private void OnPublishUriClicked(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
if (device != null)
{
device.PublishUriMessage(new Uri("http://www.qmatteoq.com"));
}
}

Il funzionamento è simile a quello visto in precedenza: in questo caso, però, il metodo si


chiama PublishUrIMessage() e accetta semplicemente l’URL del sito che vogliamo
condividere. Non è necessario specificare il tipo di messaggio perché è già implicito.
Anche in questo caso abbiamo una variante del metodo PublishUriMessage() la quale accetta
un event handler che viene scatenato nel momento in cui il messaggio è stato ricevuto da
un altro dispositivo.
Abbiamo detto che Windows Phone è in grado, in maniera nativa, di ricevere messaggi di
questo tipo. Se, però, volessimo intercettarlo anche all’interno della nostra applicazione?
Possiamo farlo sottoscrivendo un messaggio di tipo UriMessage:
private void OnReceiveUriClicked(object o, RoutedEventArgs e)
{

ProximityDevice device = ProximityDevice.GetDefault();


if (device != null)
{
long id = device.SubscribeForMessage("WindowsUri", messageReceived);
}
}

private void messageReceived(ProximityDevice sender, ProximityMessage message)


{
byte[] array = message.Data.ToArray();
string uri = Encoding.Unicode.GetString(array, 0, array.Length);
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(uri);
});
}

La tipologia di messaggio, in caso di Uri, è WindowsUri: il modo in cui sottoscriviamo la


ricezione è analogo a quello di prima, dato che, come già anticipato, il metodo
SubscribeForMessage() è valido per tutti i tipi di messaggio.

L’unica differenza è che, in questo caso, non ci troviamo in presenza di un messaggio


testuale, ma di un messaggio binario, composto da un array di byte. Per ottenere tale array
utilizziamo la proprietà Data del messaggio all’interno dell’handler che viene scatenato nel
momento in cui il messaggio è stato ricevuto.
Infine, utilizzando la classe Encoding (che permette di gestire la codifica dei testi),
convertiamo l’array di byte in una stringa tramite il metodo GetString().
I messaggi contenenti un URL possono essere usati anche per aprire un’applicazione:
come vedremo in dettaglio nel capitolo dedicato all’accesso ai dati, una delle novità di
Windows Phone è la possibilità, per le applicazioni di terze parti, di registrare un
protocollo personalizzato e di reagire a tutte le chiamate fatte con quel protocollo.
Un’applicazione potrebbe, ad esempio, registrare il protocollo showtext: e, di
conseguenza, rispondere a qualsiasi chiamata da parte di altre applicazioni che facciano
uso di quel protocollo (ad esempio: showtext:ShowDetail/1).
Non approfondiremo in questa sede l’utilizzo di questa funzionalità, che sarà trattata in
maniera esaustiva nel Capitolo 9. Quello che vi serve sapere, al momento, è che questa
funzionalità è basata sull’utilizzo di semplici URL: di conseguenza, è sufficiente inviare
un messaggio tramite NFC contenente un URL di questo tipo perché Windows Phone sia
in grado, in automatico, di aprire un’eventuale applicazione installata sul telefono che si è
registrata per quel protocollo.

I messaggi di tipo NFC Data Exchange Format (NDEF)


NDEF è un formato introdotto per lo scambio di messaggi usando un protocollo standard,
che consenta lo scambio di informazioni anche tra dispositivi diversi.
Di seguito, alcuni scenari previsti dal formato NDEF:
• scambio di dati di geolocalizzazione;
• apertura di un’applicazione o di una sezione delle impostazioni del sistema operativo;
• invio di un sms o di una e-mail;
• avvio di una telefonata;
• sharing di un contenuto su un social network.
I messaggi che utilizzano questo sistema sono caratterizzati dal protocollo NDEF
(ricordate la parte che precede il punto nella definizione di un messaggio?) e molti di
questi sono supportati in maniera nativa da Windows Phone, ma non per quanto riguarda
le possibilità offerte dalle API: è necessario pubblicare, infatti, un messaggio binario, che
deve essere poi elaborato manualmente dall’applicazione ricevente, andando a lavorare
direttamente con i byte che compongono l’array secondo la struttura di un messaggio
NDEF.
A semplificare il lavoro ha provveduto, fortunatamente, Andreas Jakl, lo sviluppatore
della libreria NDEF Library for Proximity API, che espone una serie di API per inviare
e ricevere messaggi basati su NDEF in maniera semplice.
Tale libreria è disponibile sia su NuGet che su Codeplex, all’indirizzo
http://ndef.codeplex.com.
La libreria espone una classe per ognuno dei tipi di messaggio supportati da NDEF:
• NdefAndroidAppRecord: per aprire un’applicazione Android;
• NdefGeoRecord: per condividere le coordinate geografiche di un luogo;
• NdefLaunchAppRecord: per lanciare un’applicazione Windows Phone;
• NdefMailToRecord: per avviare la scrittura di una e-mail;
• NdefNearSpeakRecord: per condividere messaggi vocali utilizzando lo standard Near
Speak;
• NdefNokiaAccessoriesRecord: per interagire con un accessorio Nokia compatibile con NFC;
• NdefSmsRecord: per avviare la scrittura di un sms;
• NdefSocialRecord: per condividere un contenuto su un social network;
• NdefTelRecord: per avviare una telefonata a un numero;
• NdefTextRecord: per condividere un testo;
• NdefUriRecord: per condividere un URL.
Come anticipato poco fa, molti di questi record sono supportati direttamente da Windows
Phone: inviando, ad esempio, un messaggio di tipo NdefTelRecord, verrà proposto in
automatico all’utente di effettuare una chiamata al numero contenuto nel messaggio.
Andremo a vedere proprio questo esempio, tenendo conto che l’utilizzo degli altri tipi di
messaggi è analogo: sono differenti solo le proprietà da valorizzare (per un sms il testo,
per una e-mail il testo della e-mail ecc.).
Vediamo come creare e inviare un messaggio di tipo NdefTelRecord:
private void OnSendNdefMessageClicked(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
if (device != null)
{
NdefTelRecord record = new NdefTelRecord
{
TelNumber = "0123456789"
};
NdefMessage message = new NdefMessage
{
record
};
device.PublishBinaryMessage("NDEF", message.ToByteArray().AsBuffer());
}
}

Rispetto agli altri casi, questa volta dovremo inviare un messaggio di tipo binario, ovvero
“grezzo”, dato che le API di Windows Phone non supportano direttamente i messaggi
NDEF: per questo motivo utilizziamo il metodo PublishBinaryMessage() della classe
ProximityDevice.

La libreria NDEF Library for Proximity API ci aiuta a creare e ricevere messaggi basati
su NDEF senza dover scrivere e leggere manualmente i byte che li compongono: ciò è
reso possibile dalla classe NdefMessage, che rappresenta il messaggio base e che può
includere uno qualsiasi dei record che abbiamo visto in precedenza.
Nell’esempio precedente definiamo un NdefTelRecord e valorizziamo la proprietà TelNumber
con il numero di telefono da chiamare, dopodiché lo “incapsuliamo” all’interno di un
oggetto di tipo NDefMessage.
Infine, lo inviamo utilizzando il metodo PublishBinaryMessage(), passando il valore NDEF come
identificativo del messaggio: dato che stiamo parlando di dati binari, utilizziamo i metodi
ToByteArray() e AsBuffer() per trasformare il messaggio in un array di byte.

Se provassimo l’applicazione in combinazione con un altro dispositivo dotato di NFC in


automatico, il telefono ci proporrebbe di effettuare una chiamata al numero che abbiamo
specificato nella proprietà TelNumber dell’oggetto di tipo NdefTelRecord.
Figura 5.7 - La ricezione di un messaggio contenente un numero di telefono.

Ovviamente, così come abbiamo visto in precedenza per quanto riguarda gli URL,
abbiamo la possibilità di intercettare un record NDEF direttamente dalla nostra
applicazione (anche perché non tutti sono supportati in maniera nativa dal sistema
operativo). Come fare? L’approccio è sempre lo stesso che abbiamo visto nei casi
precedenti, ovvero sfruttare il metodo SubscribeForMessage(), specificando NDEF come
tipologia di messaggio che vogliamo ricevere:
private void OnReceiveNdefMessage(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
if (device != null)
{
device.SubscribeForMessage("NDEF", NdefMessageReceived);
}
}

private void NdefMessageReceived(ProximityDevice sender, ProximityMessage message)


{
NdefMessage receivedMessage = NdefMessage.FromByteArray(message.Data.ToArray());
foreach (NdefRecord record in receivedMessage)
{
if (record.CheckSpecializedType(true) == typeof (NdefTelRecord))
{
NdefTelRecord ndefTelRecord = new NdefTelRecord(record);
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(ndefTelRecord.
TelNumber);
});
}
}
}

Nell’evento che viene invocato nel momento in cui il messaggio è stato ricevuto torniamo
a farci aiutare dalla libreria: utilizzando il metodo FromByteArray() della classe NdefMessage,
siamo in grado di ricostruire il messaggio partendo dai byte, che sono disponibili nella
proprietà Data del messaggio.
Dopodiché, dato che il messaggio potrebbe contenere più record, tramite un ciclo foreach
andiamo a ispezionare tutti gli oggetti di tipo NdefRecord (che rappresenta un generico
record) contenuti al suo interno. Nel nostro caso, ci aspettiamo di trovarne solamente uno,
il cui tipo è NdefTelRecord.
Per ottenere questa informazione dobbiamo chiamare il metodo CheckSpecializedType() sul
record, che ci restituisce la tipologia specifica: se si tratta del tipo NdefTelRecord, allora ci
interessa gestire questo caso.
Tutte le classi che rappresentano una tipologia di record espongono un costruttore in grado
di restituire l’oggetto completo dato il record “generico”: ecco, perciò, che creiamo una
nuova istanza della classe NdefTelRecord, passando come parametro il generico oggetto di
tipo NdefRecord.
A questo punto il gioco è fatto: avendo a disposizione l’oggetto completo, possiamo
semplicemente esplorarne le proprietà e manipolarle in base alle nostre necessità.
Nell’esempio, ci limitiamo a mostrare sullo schermo il numero di telefono contenuto
all’interno del messaggio.

Scrivere un messaggio su un tag (dispositivo passivo)


Finora abbiamo sempre scambiato messaggi tra due dispositivi “attivi”, come due
smartphone. In realtà, una delle caratteristiche più interessanti di NFC è il supporto ai
device passivi, come i tag NFC. All’apparenza sembrano dei normali adesivi: in realtà,
all’interno contengono un piccolo chip, in grado di mantenere in memoria un messaggio,
che viene condiviso ogni qualvolta il tag viene avvicinato a un dispositivo.
Se siete interessati a fare esperimenti, i tag NFC sono disponibili per l’acquisto su molti
siti di e-commerce online, come Amazon.it (http://www.amazon.it): è sufficiente cercare
la parola chiave “tag NFC”. La ricezione di messaggi da tag NFC non presenta differenze
rispetto a ciò che abbiamo visto per quanto riguarda lo scambio di messaggi tra
dispositivi: la procedura è esattamente la stessa.
Quello che cambia è la scrittura di un messaggio su un tag: il meccanismo è lo stesso, ma
è necessario aggiungere il suffisso :WriteTag alla tipologia del messaggio. Ecco come
diventa, quindi, l’esempio precedente per inviare un numero di telefono, affinché
l’informazione venga scritta su un tag invece di essere inviata a un altro telefono:
private void OnSendNdefMessageClicked(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();
if (device != null)
{
NdefTelRecord record = new NdefTelRecord
{
TelNumber = "0123456789"
};
NdefMessage message = new NdefMessage
{
record
};
device.PublishBinaryMessage("NDEF:WriteTag", message.ToByteArray().AsBuffer());
}
}

Se ora provate ad avvicinare il tag NFC sul quale avete scritto questo messaggio al vostro
telefono, si produrrà lo stesso risultato sperimentato in precedenza inviando il messaggio
da un altro device: si aprirà la schermata che vi inviterà a chiamare il numero impostato.

Aprire un’applicazione
Nel momento in cui abbiamo parlato di condivisione di URL tramite messaggi, abbiamo
visto che è possibile sfruttare questa possibilità per aprire un’applicazione installata sul
telefono. In realtà, però, tale meccanismo richiede che l’applicazione abbia implementato
il supporto a uno specifico protocollo: in caso contrario, non avremo mai modo di aprirla
con questo meccanismo.
Tramite NFC, però, è possibile utilizzare un altro approccio per aprire un’applicazione,
senza che questa debba implementare alcuna modifica: tramite l’application id. Ogni
applicazione è identificata da un id univoco, che troviamo nel file di manifest, nella
sezione Packaging, alla voce Product Id. In realtà, grazie alla versione web dello Store,
siamo in grado di scoprire l’application id di qualsiasi applicazione, dato che viene
mostrato nell’URL del browser.
Se andiamo sul sito http://www.windowsphone.com e, nella sezione App e giochi,
cerchiamo, ad esempio, l’applicazione ufficiale del blog Windows Phone Magazine Italia
ed entriamo nella pagina che ne mostra i dettagli, vedremo nel browser il seguente
indirizzo:
http://www.windowsphone.com/it-it/store/app/wpmitalia/6cb78446-ecac-4180-bc03-
070d6dde6ca8
Il Guid finale nell’URL è l’application id dell’applicazione: questo significa che possiamo
sfruttare questo approccio per aprire potenzialmente qualsiasi applicazione presente sullo
store.
Per raggiungere il nostro scopo utilizzeremo nuovamente la libreria NDEF Library For
Proximity API e, nello specifico, il messaggio di tipo NdefLaunchAppRecord.
private void OpenAppAgain(object sender, RoutedEventArgs e)
{
ProximityDevice device = ProximityDevice.GetDefault();

if (device != null)
{
NdefLaunchAppRecord record = new NdefLaunchAppRecord
{
Arguments = "user=default"
};
record.AddPlatformAppId("WindowsPhone", "{6cb78446-ecac-4180-bc03-
070d6dde6ca8}");
NdefMessage message = new NdefMessage
{
record
};
device.PublishBinaryMessage("NDEF", message.ToByteArray().
AsBuffer(), MessageSent);
}
}

Il codice dovrebbe esservi famigliare, dato che il funzionamento è quello standard per
l’utilizzo dei record NDEF. La differenza è che, in questo caso, creiamo un record di tipo
NdefLaunchAppRecord e valorizziamo la proprietà Arguments con la stringa user=default. Si tratta di
un passaggio molto importante: se non lo fate, la procedura non funzionerà. La proprietà
Arguments serve per passare eventuali parametri aggiuntivi, nel caso l’applicazione che state
cercando di aprire li supporti.
L’altra necessità è quella di specificare l’id dell’applicazione da aprire, tramite il metodo
AddPlatformAppId() esposto dal record: il primo parametro è l’id della piattaforma (il valore
fisso è WindowsPhone); il secondo, invece, è l’id dell’applicazione, che abbiamo visto in
precedenza come recuperare.
Figura 5.8 - La ricezione di un messaggio per l’apertura di un’applicazione su un dispositivo dove questa non è
installata.

Se ora lanciamo questa applicazione e la proviamo con un altro telefono, l’applicazione


che abbiamo scelto sarà aperta in automatico: se questa non è installata sul dispositivo,
l’utente sarà invitato ad andare a scaricarla dallo store.
Anche in questo caso è possibile scrivere il messaggio su un tag NFC, semplicemente
apponendo il suffisso :WriteTag al tipo di messaggio (quindi NDEF:WriteTag) che viene passato
al metodo PublishBinaryMessage().

Creare un canale di comunicazione tra due dispositivi tramite NFC


Come ho evidenziato nell’introduzione, NFC è una tecnologia che nasce per lo scambio di
quantità di dati limitate: negli esempi visti finora, abbiamo inviato messaggi contenenti un
numero di telefono, un testo o un indirizzo internet, la cui dimensione è di pochi byte.
Se abbiamo, invece, la necessità di scambiare quantità di dati maggiori (ad esempio,
un’immagine) o di creare un canale di comunicazione stabile tra due dispositivi (ad
esempio, per un gioco o una chat in tempo reale), NFC non è la tecnologia adatta: per
questo motivo le Proximity API consentono di sfruttare anche altre tecniche per scambiare
dati, come Bluetooth o Wi-Fi.
NFC rimane comunque un valido alleato, perché può essere utilizzato per semplificare il
pairing tra due dispositivi. Il pairing è il procedimento richiesto dallo standard Bluetooth
per mettere in comunicazione due dispositivi: se, ad esempio, proviamo a collegare il
telefono con un auricolare Bluetooth, dobbiamo seguire una procedura ben precisa per
raggiungere il nostro scopo; occorre attivare il Bluetooth sul telefono, ricercare altri
dispositivi compatibili e, infine, stabilire la comunicazione (in alcuni casi, previa
autenticazione tramite un PIN).
Tramite NFC, invece, siamo in grado di evitare questo procedimento e di creare
immediatamente un canale di comunicazione tra la stessa applicazione installata su due
dispositivi semplicemente avvicinandoli tra di loro. Vediamo come fare: utilizzeremo
questo meccanismo per mettere in comunicazione la nostra applicazione e useremo il
canale creato per scambiare messaggi testuali.

Per gestisce scenari di comunicazione tra due applicazioni è necessario che


NOTA
anche la capability ID_CAP_NETWORKING sia attiva.

Al cuore di questo meccanismo c’è la classe PeerFinder, che consente di trovare i peer (i
pari, ovvero la stessa applicazione installata su un altro dispositivo).
private void OnFindPeerClicked(object sender, RoutedEventArgs e)
{
PeerFinder.TriggeredConnectionStateChanged += PeerFinder_
TriggeredConnectionStateChanged;
PeerFinder.AllowBluetooth = true;
PeerFinder.AllowInfrastructure = true;
PeerFinder.Start();
}

La classe PeerFinder espone un evento chiamato TriggerConnectionStateChanged, che viene


invocato nel momento in cui lo stato della connessione è cambiato. La prima operazione
da fare è, quindi, quella di sottoscrivere tale evento: solo nel momento in cui la
connessione sarà stabilita potremo creare il canale di comunicazione.
Nell’esempio impostiamo a true le due proprietà AllowBluetooth e AllowInfrastructure: in realtà si
tratta di un’operazione inutile, dato che, come impostazione predefinita, sono già abilitate.
La presenza di queste due proprietà è però l’occasione per parlarvi di come funziona la
comunicazione tra due dispositivi; Windows Phone è in grado di utilizzare due tecnologie
per creare il canale, ovvero Bluetooth e Wi-Fi. Windows Phone, però, al contrario di
Windows 8, supporta solamente l’utilizzo di una infrastruttura Wi-Fi: i due telefoni
devono perciò essere collegati alla medesima rete (ad esempio, un router Wi-Fi). In
Windows 8 è possibile, invece, creare un canale diretto tramite Wi-Fi: se esplorate le
proprietà esposte dalla classe PeerFinder ne troverete una dal nome AllowWiFiDirect, pensata
proprio per questo scenario. In realtà, la presenza di tale proprietà in Windows Phone è
dovuta solamente al fatto che le Proximity API fanno parte del Windows Runtime e sono
condivise con le Windows Store app di Windows 8: su Windows Phone AllowWiFiDirect sarà
sempre a false e non è possibile cambiarne il valore. Come impostazione predefinita,
Windows Phone mantiene abilitate entrambe le proprietà AllowBluetooth e AllowInfrastructure: in
automatico è in grado di utilizzare il canale di comunicazione più adatto, in base alle
condizioni. La connessione Wi-Fi è sicuramente più veloce e stabile, ma non sempre è
utilizzabile, dato che richiede che i telefoni siano connessi alla stessa rete; Bluetooth,
invece, può essere utilizzato in maniera diretta, anche se i dati viaggiano più lentamente.
Se sono entrambe attive, il sistema operativo darà la preferenza a Bluetooth; solo nel caso
in cui questo non fosse disponibile (ad esempio, se entrambi i device sono collegati alla
stessa rete ma solo uno dei due ha Bluetooth attivo), utilizzerà la connessione Wi-Fi.
Se volete forzare l’utilizzo di una tecnologia, è sufficiente impostare a false quella che non
volete utilizzare.
Dopodiché, chiamando il metodo Start() della classe PeerFinder, l’applicazione inizierà la
ricerca e diventerà “l’host”: se la installiamo su entrambi i dispositivi ed eseguiamo questo
codice, una volta avvicinati i due telefoni il dispositivo “ricevente” (il client) si accorgerà
del tentativo di creazione del canale e proporrà all’utente di aprire l’applicazione.
Per confermare la comunicazione e aprire effettivamente il canale dobbiamo chiamare il
metodo PeerFinder.Start() anche dall’altra parte (ovvero dal client) e registrarci comunque
all’evento TriggerConnectionStateChanged. Esiste una strada che ci permette di gestire questo
scenario in maniera automatica, senza dover costringere l’utente, ad esempio, a premere
un pulsante per “accettare” la comunicazione.
Nel momento in cui l’applicazione viene aperta in seguito alla creazione di un canale,
l’URL che viene utilizzato sarà simile a questo:
/MainPage.xaml?ms_nfp_launchargs=Windows.Networking.Proximity.PeerFinder:StreamSocket

Come potete notare dall’URL, al percorso della pagina che viene aperta viene aggiunto un
parametro in query string (evidenziato in grassetto). Possiamo, perciò, sfruttare questa
informazione per inizializzare la ricerca di peer in automatico nel momento in cui
l’applicazione viene aperta, tramite l’evento OnNavigatedTo:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("ms_nfp_launchargs") &&
NavigationContext.QueryString["ms_nfp_launchargs"] ==
"Windows.Networking.Proximity.PeerFinder:StreamSocket")
{
PeerFinder.TriggeredConnectionStateChanged += PeerFinder_
TriggeredConnectionStateChanged;
PeerFinder.Start();
}
}

Ma cosa succede, effettivamente, quando viene scatenato l’evento


TriggeredConnectionStateChanged? Vediamo un esempio di come gestirlo:

private StreamSocket socket;


void PeerFinder_TriggeredConnectionStateChanged(object sender,
TriggeredConnectionStateChangedEventArgs args)
{
switch (args.State)
{
case TriggeredConnectState.Completed:
socket = args.Socket;
StartListeningForMessages();
PeerFinder.Stop();
break;
}
}

Tale evento viene scatenato ogni qualvolta lo stato della connessione cambia. Il parametro
che riceviamo contiene la proprietà State, che rappresenta lo stato corrente e può assumere i
seguenti valori:
• PeerFound: è stato trovato un dispositivo in grado di collegarsi. Da questo momento
possiamo allontanare i telefoni, perché le comunicazioni successive non utilizzeranno
più NFC;
• Listening: questo evento è scatenato dall’applicazione che funge da host, nel momento
in cui ha trovato un altro peer ed è in attesa che l’applicazione client accetti la
comunicazione;
• Connecting: viceversa, questo evento è scatenato dall’applicazione che funge da client,
nel momento in cui ha ricevuto la richiesta di creazione del canale e sta cercando di
stabilire la comunicazione;
• Failed: il tentativo di creazione del canale è fallito; si verifica, ad esempio, se
l’applicazione host rimane in stato Listening per troppo tempo, senza ricevere una
risposta dall’applicazione client;
• Completed: la comunicazione è avvenuta con successo e il canale è stato creato; grazie
alla proprietà Socket del parametro che riceviamo, siamo in grado di inizializzare il
canale e di utilizzarlo per scambiare dati.
Figura 5.9 - Il messaggio che compare nel momento in cui un’applicazione cerca di stabilire una comunicazione con un
altro dispositivo.

È proprio quello che facciamo nell’esempio di codice nel momento in cui lo stato è
Completed: recuperiamo il canale tramite la proprietà Socket e lo memorizziamo in una
variabile globale di tipo StreamSocket. In questo modo, saremo in grado di utilizzare il canale
anche dagli altri metodi (ovvero quelli che andremo a realizzare per ricevere e inviare
messaggi).
Le ultime due operazioni da fare sono mettersi in attesa dell’arrivo di messaggi e chiamare
il metodo Stop() della classe PeerFinder: dato che la comunicazione è stata stabilita, non è più
necessario cercare altri peer tramite NFC.
Come fare per rimanere in ascolto di un messaggio? Vediamo un esempio di codice:
private async Task<string> GetMessage()
{
if (dataReader == null) dataReader = new DataReader(socket.InputStream);
uint bytesRead = await dataReader.LoadAsync(sizeof (uint));
if (bytesRead > 0)
{
uint strLength = (uint) dataReader.ReadUInt32();
bytesRead = await dataReader.LoadAsync(strLength);
if (bytesRead > 0)
{
String message = dataReader.ReadString(strLength);
return message;
}
}

return string.Empty;
}

private bool isListening;

private async void StartListeningForMessages()


{

if (socket != null)
{
if (!isListening)
{
listening = true;
while (isListening)
{
var message = await GetMessage();
if (isListening)
{

if (message != null)
{
Dispatcher.BeginInvoke(() => MessageBox.Show(message));
}
}
}
}
}
}

Il metodo GetMessage() che vediamo nell’esempio è un helper in grado di trasformare lo


stream “grezzo” contenuto all’interno del canale in una stringa (dato che quello che stiamo
implementando è lo scenario di scambi testuali), sfruttando la classe DataReader del
Windows Runtime.
Il metodo StartListeningForMessages() sfrutta il metodo GetMessage() per prendere il messaggio e
mostrarlo a video utilizzando il dispatcher, dato che viene eseguito su un thread differente
da quello della UI. La caratteristica particolare di questo metodo è che la procedura di
conversione da stream a stringa è inclusa all’interno di un ciclo while: in questo modo,
l’applicazione rimane costantemente in ascolto, in attesa che arrivi un messaggio da
elaborare. La proprietà booleana isListening, infatti, rimane impostata a true fintanto che il
canale di comunicazione è attivo.
Come facciamo, infine, a inviare un messaggio? Ecco un esempio di codice:
public async void SendMessage(string message)
{
if (socket != null)
{
DataWriter dataWriter = new DataWriter(socket.OutputStream);

dataWriter.WriteInt32(message.Length);
await dataWriter.StoreAsync();

dataWriter.WriteString(message);
await dataWriter.StoreAsync();
}
}
Anche in questo caso usiamo un’altra classe del Windows Runtime, DataWriter, che ha lo
scopo opposto: scrivere dati in uno stream. Nello specifico, utilizziamo la proprietà
OutputStream del canale e scriviamo due informazioni: prima la lunghezza del messaggio,
dopodiché il messaggio stesso.
A questo punto, ci basta richiamare il metodo SendMessage(), ad esempio, in seguito alla
pressione di un pulsante, passando come parametro il testo del messaggio:
private async void OnSendMessageClicked(object sender, RoutedEventArgs e)
{
await SendMessage("Hello!");
}

Se abbiamo fatto tutto correttamente e proviamo a installare e lanciare l’applicazione su


due differenti dispositivi dotati di NFC, saremo in grado di stabilire un canale di
comunicazione fra entrambi e di scambiare messaggi: vedremo apparire il testo a video nel
momento in cui, sull’altro telefono, avremo premuto il pulsante per l’invio.
Ovviamente, trattandosi di un esempio, ci siamo limitati a scambiare dei semplici
messaggi di testo: dato che, però, quello che mette a disposizione il canale è un vero e
proprio stream, possiamo scambiare dati di qualsiasi tipo, come file, immagini ecc.

Creare un canale di comunicazione tra due dispositivi tramite Bluetooth


Gli esempi che abbiamo appena visto utilizzano Bluetooth come mezzo di comunicazione
tra le due applicazioni: la creazione del canale, però, sfruttava NFC per inizializzare la
connessione.
Abbiamo, però, anche la possibilità di effettuare la stessa operazione direttamente tramite
Bluetooth: in questo modo siamo in grado di supportare, ad esempio, anche i dispositivi
sprovvisti di NFC. In questo caso, però, occorre fare qualche operazione in più anche se lo
scambio dei messaggi vero e proprio sarà assolutamente identico: anche in questo
scenario, infatti, quello che otteniamo come risultato è un canale di tipo StreamSocket,
all’interno del quale inviare i nostri pacchetti.
La classe di riferimento è sempre PeerFinder e, anche in questo caso, dobbiamo inizializzare
la comunicazione da entrambe le parti utilizzando il metodo Start(). Da qui in poi, però,
l’approccio è differente. Vediamo un esempio di codice da eseguire all’interno
dell’applicazione host, ovvero quella che richiede la creazione del canale:
private async void OnFindNearPeers(object sender, RoutedEventArgs e)
{
PeerFinder.Start();
var peers = await PeerFinder.FindAllPeersAsync();
foreach (PeerInformation peerInformation in peers)
{
MessageBox.Show(peerInformation.DisplayName);
}
}

La classe PeerFinder espone un metodo asincrono chiamato FindAllPeersAsync(), che è in grado


di cercare tutti i dispositivi all’interno del raggio d’azione del segnale Bluetooth nei quali
è in esecuzione la stessa applicazione, che a sua volta deve aver inizializzato le Proximity
API con il metodo PeerFinder.Start().
Tale metodo restituisce una collezione di tutti i dispositivi disponibili: ogni device è
rappresentato dalla classe PeerInformation, che contiene diverse informazioni sullo stesso, tra
cui il nome (la proprietà DisplayName). In realtà, l’utente potrebbe avere il ricevitore
Bluetooth disattivato: in tal caso, l’operazione di ricerca dei peer scatenerebbe
un’eccezione generica di tipo Exception con il messaggio The device is not connected e con
codice di errore 0x8007048F. È buona prassi, perciò, includere tale operazione all’interno
di un blocco try/catch per intercettare l’eventuale errore e gestirlo correttamente:
private async void OnFindNearPeers(object sender, RoutedEventArgs e)
{
PeerFinder.Start();
try
{
var peers = await PeerFinder.FindAllPeersAsync();
MessageBox.Show(peerInformation.DisplayName);
}
catch (Exception exc)
{
if ((uint)exc.HResult == 0x8007048F)
{
MessageBox.Show("Il Bluetooth è disattivato!");
}
}
}

Una strategia potrebbe essere quella di invitare l’utente ad andare nelle Impostazioni del
telefono e attivare il Bluetooth, sfruttando l’apposito launcher offerto dalle API, di cui
parleremo in maniera più approfondita nel Capitolo 7.
Nell’esempio di codice appena visto, ci limitiamo al nome del dispositivo rilevato sullo
schermo: nella realtà dovremmo mostrare, tramite un controllo ListBox o LongListSelector,
l’elenco dei device trovati, dando la possibilità all’utente di scegliere a quale di questi
collegarsi.
Per semplificare il nostro esempio, dato che stiamo provando a collegare tra di loro
solamente due device, ci limiteremo a collegarci al primo disponibile (ovvero l’unico che
sarà trovato):
private StreamSocket socket;

private async void OnFindNearPeers(object sender, RoutedEventArgs e)


{
PeerFinder.Start();
var peers = await PeerFinder.FindAllPeersAsync();
foreach (PeerInformation peerInformation in peers)
{
socket = await PeerFinder.ConnectAsync(peerInformation);
StartListeningForMessages();
PeerFinder.Stop();
}
}

Per effettuare questa operazione dobbiamo utilizzare un altro metodo della classe PeerFinder,
chiamato ConnectAsync(). Anch’esso è un metodo asincrono e richiede come parametro il
device a cui vogliamo collegarci, rappresentato dalla classe PeerInformation. Tale metodo ci
restituisce il canale di comunicazione, di tipo StreamSocket: esattamente come facevamo in
precedenza negli esempi che utilizzavano NFC, perciò, lo assegniamo a una variabile
globale, che utilizzeremo per ricevere e inviare messaggi.
Da questo punto in poi, per quanto riguarda l’applicazione host, l’approccio è lo stesso che
abbiamo visto nella demo con NFC: ci mettiamo in ascolto di eventuali messaggi
richiamando il metodo StartListeningForMessages(), la cui definizione è uguale a quella vista in
precedenza e, infine, terminiamo la ricerca di altri dispositivi.
Anche per quanto riguarda l’applicazione client (ovvero quella che riceve la richiesta di
comunicazione), ci sono delle differenze per creare il canale: nel momento in cui questo
sarà disponibile, invece, l’approccio sarà quello già visto.
private void OnListenToConnectionClicked(object sender, RoutedEventArgs e)
{
PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;
PeerFinder.Start();
}

async void PeerFinder_ConnectionRequested(object sender,


ConnectionRequestedEventArgs args)
{
socket = await PeerFinder.ConnectAsync(args.PeerInformation);
StartListeningForMessages();
PeerFinder.Stop();
}

Nel caso dell’applicazione client è necessario sottoscrivere un evento chiamato


ConnectionRequested, il quale viene invocato nel momento in cui è stata ricevuta una richiesta
di connessione. Anche in questo caso, come ricordato in precedenza, dobbiamo chiamare
il metodo PeerFinder.Start(), altrimenti l’applicazione host non sarà in grado di rilevarne la
presenza nel momento in cui cercherà i peer disponibili.
Cosa avviene all’interno dell’evento ConnectionRequested? Rispondiamo alla richiesta di
creazione del canale, utilizzando, anche in questo caso, il metodo ConnectAsync() della classe
PeerFinder: l’informazione su quale sia il device a cui dobbiamo collegarci ci viene restituita
dal parametro dell’evento, nella proprietà PeerInformation. Da qui in poi il comportamento è
lo stesso che abbiamo visto nell’applicazione host: il metodo ci restituisce il canale di
comunicazione, dopodiché ci mettiamo in ascolto di eventuali messaggi in arrivo e
terminiamo la ricerca di altri peer.
Da questo punto in poi il canale di comunicazione è stabilito e potete riutilizzare gli
esempi di codice visti nella demo relativa al pairing tramite NFC per scambiare messaggi
testuali tra i due dispositivi: ovviamente, anche in questo caso, potete utilizzare il canale
per scambiare dati di qualsiasi tipo, trattandosi di uno stream generico.

Comunicazione tra due dispositivi in background


È importante sottolineare come i canali di comunicazione tra due applicazioni (creati sia
tramite NFC sia direttamente tramite Bluetooth) non supportino scenari in background: nel
momento in cui l’applicazione viene terminata o sospesa, il canale sarà immediatamente
interrotto e non sarà più possibile scambiare dati tra le due applicazioni.
In conclusione
Windows Phone è un sistema operativo nato per essere sempre connesso a Internet: in
questo capitolo abbiamo fatto una panoramica piuttosto estesa di tutti i modi in cui uno
sviluppatore può comunicare verso l’esterno da un’applicazione.
Il primo passo è stato quello di determinare, grazie alle nuove API introdotte in Windows
Phone 7.5, lo stato e la tipologia di connettività.
Dopodiché abbiamo visto quali sono le modalità offerte dall’SDK per poter gestire
trasferimenti da e verso la rete: la prima è stata la classe WebClient, che offre un approccio
molto semplice, ma con qualche limite dal punto di vista delle performance; la seconda è
stata la classe HttpWebRequest, un po’ più complessa da utilizzare, ma anche più flessibile e
potente; infine, abbiamo visto i background transfer, una delle novità di Windows Phone
7.5, che permettono di mantenere attivi i trasferimenti anche quando l’applicazione è
chiusa.
Abbiamo chiuso il capitolo passando in rassegna un altro aspetto dell’interazione con
Internet: i servizi che al giorno d’oggi vengono sempre più utilizzati nella definizione
dell’architettura di un software o di un sito Internet. Le tipologie di servizi che abbiamo
affrontato e imparato a utilizzare da un’applicazione Windows Phone sono tre: i servizi
WCF, i servizi di tipo REST (grazie ai quali abbiamo imparato a interagire con il formato
JSON) e i servizi basati sul protocollo OData.
Infine, abbiamo visto come, grazie alle Proximity API, siamo in grado di interagire con
altri dispositivi anche senza utilizzare una connessione a Internet, grazie alle tecnologie
Bluetooth e NFC. Due sono gli scenari che abbiamo esaminato: lo scambio di messaggi e
la comunicazione tra due applicazioni.
Interagire con il telefono

Al giorno d’oggi gli smartphone sono dotati di una configurazione


hardware sempre più ricca: sensori di movimento e GPS sono solo un
esempio degli strumenti che abbiamo a disposizione per poter interagire
con l’ambiente esterno. Questi strumenti sono uno dei motivi per cui è
molto diverso sviluppare applicazioni mobile rispetto a realizzare siti web
o applicazioni per desktop: la possibilità di interagire con il mondo reale
apre, infatti, le porte a nuovi scenari e nuove idee.
In questo capitolo vedremo in dettaglio come interagire nelle nostre applicazioni con gli
strumenti messi a disposizione dai device Windows Phone: sensori di movimento, servizi
di geolocalizzazione e touch screen.
In più, impareremo come scoprire le caratteristiche del device su cui sta girando la nostra
applicazione, come il tipo di processore o, molto più importante, la memoria a
disposizione.
I servizi di geolocalizzazione
Una delle funzionalità più utilizzate dalle applicazioni moderne è la geolocalizzazione,
ovvero la possibilità di recuperare la posizione dell’utente e di sfruttarla, ad esempio, per
tenere traccia di un percorso, per calcolare la velocità a cui ci stiamo spostando, o, più
semplicemente, per localizzare una foto o un tweet.
I servizi di geolocalizzazione integrati in Windows Phone hanno a disposizione tre
strumenti per recuperare la posizione dell’utente:
• GPS: è il sistema sicuramente più preciso, ma risente di due problemi, e cioè un
elevato consumo di batteria e la necessità di trovarsi in uno spazio aperto affinché la
posizione sia precisa;
• Rete 3G: questo sistema si affida alla triangolazione delle celle della rete cellulare a
cui è correntemente agganciato il nostro telefono. Viene consumata poca batteria, ma
è molto meno preciso del GPS;
• Wi-Fi: quest’ultimo sistema è una via di mezzo tra quelli precedenti e si affida alla
localizzazione dei router Wi-Fi a cui è collegato il dispositivo; ha un consumo medio
di batteria e offre una posizione abbastanza precisa.
Il sistema operativo è in grado in automatico di passare da un sistema all’altro a seconda
delle condizioni, anche se, come sviluppatori, avremo la possibilità di specificare il grado
di precisione che vogliamo (se stiamo sviluppando un’applicazione di tracking
probabilmente saremo interessati alla massima precisione possibile; se si tratta di un client
Twitter è preferibile consumare meno batteria e dare una posizione più approssimativa).
Utilizzare i servizi di geolocalizzazione: la classe GeoLocator
Il Windows Runtime for Windows Phone ha introdotto delle nuove API, allineate con
quelle disponibili in Windows 8: la classe di riferimento si chiama Geolocator e fa parte del
namespace Windows.Devices.Geolocation.
Se siete utenti Windows Phone saprete probabilmente che, dalle impostazioni del telefono,
è possibile disabilitare i servizi di geolocalizzazione, impedendo così a tutte le
applicazioni installate sul nostro device di utilizzarli.

L’utilizzo dei servizi di geolocalizzazione richiede che nel file di manifest sia
NOTA
stata dichiarata la capability <Capability Name=”ID_CAP_LQCATIQN”>.

Ecco, perciò, che la classe Geolocator offre la proprietà LocationStatus, che determina lo stato
corrente del segnale GPS: tale proprietà può assumere uno dei valori dell’enumeratore
PositionStatus, che vedremo in dettaglio a breve. Uno dei valori possibili è PositionStatus.Disabled:
è sempre bene verificare che lo stato del GPS non abbia tale valore prima di eseguire
qualsiasi operazione con la classe Geolocator, altrimenti si scatenerà un’eccezione appena
cercheremo di rilevare la posizione dell’utente.
Geolocator geolocator = new Geolocator();
if (geolocator.LocationStatus == PositionStatus.Disabled)
{
MessageBox.Show("I servizi di geolocalizzazione sono disabilitati!");
}
else
{
//inizio la rilevazione
}

Rispetto alle API che erano presenti in Windows Phone 7.5, la classe Geolocator offre
un’opportunità in più: la possibilità di ottenere una rilevazione puntuale, senza essere
costretti a rimanere in ascolto di tutti i cambiamenti di posizione.
private async void GetPosition(object sender, RoutedEventArgs e)
{
Geolocator geolocator = new Geolocator();
Geoposition geoposition = await geolocator.GetGeopositionAsync();
Coordinate.Text = string.Format("{⊘} - {1}", geoposition.Coordinate.Latitude,
geoposition.Coordinate.Longitude);
}

Una volta creata una nuova istanza della classe Geolocator, utilizziamo il metodo
GetGeopositionAsync(), che è asincrono. Tale metodo ci restituisce un oggetto di tipo Geoposition,
che contiene tutte le informazioni relative alla rilevazione di posizione. Nello specifico, è
disponibile la proprietà Coordinate che contiene i dati relativi a latitudine, longitudine,
altezza, velocità e così via.
Questo metodo è molto utile quando non avete la necessità di tracciare continuamente la
posizione dell’utente, ma semplicemente di fare una rilevazione puntuale, ad esempio per
geolocalizzare un tweet o per mostrare i luoghi di interesse vicini.
È comunque possibile utilizzare la classe Geolocator anche per tracciare la posizione in
modo continuativo: per questo esistono due eventi che si possono sottoscrivere, ovvero
StatusChanged (che viene scatenato nel momento in cui lo stato del segnale cambia) e
PositionChanged (che viene scatenato nel momento in cui la posizione dell’utente cambia). La
frequenza con cui viene invocato l’evento PositionChanged dipende da due proprietà della
classe Geolocator:
• MovementThreshold:
è la distanza in metri che deve essere percorsa rispetto alla
rilevazione precedente;
• ReportInterval: è il tempo minimo, espresso in millisecondi, che deve trascorrere tra due
rilevazioni.
Vediamo ora nel dettaglio quali informazioni ci vengono restituite in questi eventi.
StatusChanged
Questo evento viene scatenato dalla classe Geolocator nel momento in cui lo stato del
segnale cambia. Quello che viene restituito tra i parametri dell’evento è un oggetto che
contiene una proprietà chiamata Status che è di tipo PositionStatus. Si tratta dello stesso
enumeratore che è disponibile tramite la proprietà LocationStatus vista in precedenza e che
può assumere i seguenti stati:
• Disabled:
i servizi di geolocalizzazione sono stati disabilitati dall’utente nelle
impostazioni generali del telefono;
• initializing: i servizi di geolocalizzazione sono in fase di inizializzazione; non sono,
perciò, ancora pronti a restituire dati;
• NotAvailable: i servizi di geolocalizzazione non sono disponibili sul device. Questo
valore, in realtà, è un retaggio del fatto che le API sono in comune con Windows 8: i
device Windows Phone, infatti, sono sempre dotati di GPS in base alle specifiche
minime, perciò la proprietà non potrà mai assumere tale valore;
• NoData: i servizi di geolocalizzazione sono attivi e pronti, ma non è possibile al
momento recuperare le informazioni sulla posizione (ad esempio, perché non c’è
sufficiente copertura);
• NotInitialized: i servizi di geolocalizzazione sono attivi, ma le API non hanno ancora
iniziato a richiedere dati (nello specifico, non è ancora stato chiamato il metodo
GetGeopositionAsync() o non ci si è ancora sottoscritti all’evento PositionChanged);

• Ready: i servizi di geolocalizzazione sono attivi e stanno correttamente ricevendo dati.


Questa informazione può esserci utile per:
• dare un feedback all’utente sullo stato del segnale;
• adottare strategie alternative nel caso in cui non ci sia segnale.
PositionChanged
Questo evento viene scatenato ogni qualvolta una nuova posizione viene rilevata dai
servizi di geolocalizzazione, in base al valore che abbiamo impostato per la proprietà
MovementThreshold e ReportInterval. L’oggetto restituito contiene la proprietà Position con le
informazioni geografiche sulla posizione, memorizzate nella proprietà Coordinate.
È disponibile anche una proprietà chiamata CivicAddress, che dovrebbe contenere le
informazioni sulla posizione in termini cartografici (indirizzo, città ecc.): purtroppo, però,
tale classe non è implementata, perciò si otterranno sempre valori non corretti. Vedremo
più avanti un metodo alternativo per convertire le coordinate geografiche in un indirizzo
vero e proprio.
Location
è, a sua volta, un oggetto complesso, che contiene diverse informazioni
geografiche, come la latitudine, la longitudine e l’altitudine. Questi sono i dati che ci
permettono di geolocalizzare il nostro utente e che possiamo utilizzare all’interno
dell’applicazione, ad esempio, per identificare la sua posizione in una mappa. Vedremo
come realizzare questo obiettivo sfruttando il controllo Nokia Maps.
Ecco un esempio di utilizzo della classe Geolocator per rilevare in maniera continuativa la
posizione dell’utente e mostrarla sullo schermo:
private async void GetPosition(object sender, RoutedEventArgs e)
{
Geolocator geolocator = new Geolocator();

geolocator.MovementThreshold =1⊘⊘;
geolocator.ReportInterval = 1⊘⊘⊘;
geolocator.DesiredAccuracy = PositionAccuracy.High;

geolocator.PositionChanged += (obj, args) =>


{
Dispatcher.BeginInvoke(() => Coordinate.Text = string.Format("{⊘} - {1}",
args.Position.Coordinate.Latitude,
args.Position.Coordinate.Longitude));
};
}

In questo caso l’evento PositionChanged viene scatenato ogni qualvolta lo spostamento è


superiore ai 100 metri e con una frequenza minima di 1 secondo. Possiamo notare l’uso
del dispatcher per mostrare sullo schermo le coordinate: questo perché l’evento
PositionChanged viene eseguito in un thread separato da quello della UI.

Attenzione! Se testate l’applicazione con l’emulatore è necessario impostare la


NOTA DesiredAccuracy su High, altrimenti i cambiamenti di posizione non saranno
rilevati.
Il controllo Nokia Maps di Windows Phone 8
Una delle principali novità di Windows Phone 8 è stata l’inclusione dei servizi cartografici
di Nokia all’interno del sistema operativo stesso: se, fino a Windows Phone 7.5, tali
servizi erano offerti da Bing Maps, nella nuova versione sono stati sostituiti da quelli di
Nokia Maps.
Questo si traduce, in primis, in un’applicazione preinstallata su tutti i device, che permette
di ricercare una posizione o di calcolare percorsi sfruttando il servizio di cartografia
offerto da Nokia. Come vedremo nel Capitolo 7, dedicato all’interazione con le
applicazioni native, una delle possibilità che il sistema ci offre è di sfruttare questa
applicazione per mostrare un luogo sulla mappa, semplicemente fornendone le coordinate.
Esiste, però, un sistema che permette un grado di interazione e di personalizzazione
maggiore: l’utilizzo del controllo Nokia Maps, il quale permette di includere una mappa
all’interno della nostra applicazione, che possiamo personalizzare a piacimento. Possiamo,
infatti, selezionare il tipo di vista (cartografia stradale o immagine satellitare), il livello di
zoom, nonché inserire dei segnaposto per mostrare, ad esempio, dei punti di interesse.
Il controllo Bing Maps è ancora disponibile nell’SDK di Windows Phone 8, ma è stato
mantenuto solamente per motivi di retrocompatibilità: non ci sono, infatti, motivi per
continuare a utilizzarlo in un nuovo progetto, dato che il controllo di Nokia offre
performance nettamente superiori e maggiori possibilità di personalizzazione.
Figura 6.1 - Il controllo Nokia Maps.

Il controllo Nokia Maps non è disponibile tra quelli predefiniti: occorre aggiungere,
pertanto, una reference al namespace Microsoft.Phone.Controls.Maps e poi dichiararlo
nello XAML all’interno della pagina in cui vogliamo utilizzare il controllo.
xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"

Il modo più semplice per fare tutto questo in automatico è usare la Toolbox di Visual
Studio: trascinando il controllo Map all’interno della nostra pagina, la reference e il
namespace saranno aggiunti in automatico. Quello che troveremo nello XAML sarà una
dichiarazione di questo tipo:
<maps:Map x:Key="Map" />

Ovviamente, il controllo così com’è serve a poco o a niente: dobbiamo interagire con le
proprietà a disposizione per poterlo personalizzare. Come impostazione predefinita, viene
utilizzata la vista con la cartografia stradale: per cambiare questa impostazione possiamo
agire sulla proprietà CartographicMode, che può assumere valore Aerial (vista satellitare), Road
(cartografia stradale), Terrain (vista del terreno) o Hybrid (ibrida).
Un’altra proprietà importante che possiamo valorizzare direttamente da XAML è
ZoomLevel, che permette di impostare la scala con cui vogliamo visualizzare la mappa. È
accettato un valore intero da 1 (zoom minimo) a 19 (zoom massimo): abbiamo,
ovviamente, la possibilità di agire sul livello di zoom anche da codice, demandando perciò
l’operazione a dei pulsanti realizzati ad hocda noi. Questo è possibile semplicemente
incrementando o decrementando il valore della proprietà ZoomLevel. In questo esempio
abbiamo demandato l’operazione a due pulsanti legati ai metodi OnZoomInClicked() e
OnZoomOutClicked():

private void OnZoomInClicked(object sender, RoutedEventArgs e)


{
Map.ZoomLevel++;
}

private void OnZoomOutClicked(object sender, RoutedEventArgs e)


{
Map.ZoomLevel--;
}

Per fissare una posizione sulla mappa abbiamo, invece, a disposizione la proprietà Center,
che accetta un oggetto di tipo GeoCoordinate, che rappresenta un insieme di coordinate
geografiche composte da una serie di parametri, tra cui i più importanti sono latitudine e
longitudine.
Possiamo raggiungere questo scopo sia nello XAML, come nell’esempio:
<maps:Map
x:Name="myMap"
Center="37.792878,-122.39641"
/>

sia da codice, come nell’esempio seguente:


private void OnCenterButtonClicked(object sender, RoutedEventArgs e)
{
GeoCoordinate center = new GeoCoordinate(37.792878, -122.39641);
myMap.Center = center;
}

Esistono, poi, altre importanti proprietà che possiamo utilizzare per definire l’aspetto di
una mappa, molte delle quali sono una novità rispetto alle possibilità offerte in Windows
Phone 7.5:
• Heading: rappresenta la rotazione della mappa rispetto al centro;
• Pitch: rappresenta l’altezza della mappa rispetto all’orizzonte;
• ColorMode: è possibile impostare un tema chiaro (Light) o scuro (Dark): il primo è
ottimizzato per essere usato in condizioni di luminosità (tipicamente di giorno); il
secondo, invece, quando la luminosità è scarsa;
• LandmarskEnabled: una delle caratteristiche più interessanti di Nokia Maps è la capacità
di mostrare la rappresentazione tridimensionale di alcuni edifici importanti, come
monumenti, chiese, costruzioni storiche ecc. Impostando questa proprietà a true,
avremo la possibilità di mostrare questi elementi in 3D anche nel controllo;
• PedestrianFeaturesEnabled: quando questa proprietà è a true, vengono messi in evidenza
alcuni elementi specifici per l’utilizzo della mappa da parte di un pedone, come scale
o passaggi sotterranei.
Utilizziamo la mappa con i servizi di geolocalizzazione
Mettiamo ora insieme quanto abbiamo appreso riguardo ai servizi di geolocalizzazione e
al controllo Nokia Maps per realizzare una semplice applicazione dove la posizione
dell’utente viene visualizzata su una mappa.
Riutilizziamo il codice descritto in precedenza per il controllo Nokia Maps, ma, questa
volta, sfrutteremo l’evento PositionChanged per centrare la posizione della mappa con quella
rilevata dai servizi di geolocalizzazione:
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
Geocoordinate coordinate = args.Position.
Coordinate;
myMap.Center = coordinate.ToGeoCoordinate();
});
}

In questo esempio di codice possiamo notare due cose importanti: la prima è che l’evento
PositionChanged viene eseguito in un thread secondario; di conseguenza, dobbiamo utilizzare
il dispatcher per interagire con i controlli della UI (nel nostro caso, la mappa). La seconda
è che, purtroppo, i servizi di geolocalizzazione di Windows Phone e il controllo Nokia
Maps utilizzano due classi differenti per gestire le coordinate geografiche: la classe
Geolocator utilizza il tipo Geocoordinate, che fa parte del namespace Windows.
Devices.Geolocation, mentre il controllo Nokia Maps utilizza il tipo GeoCoordinate (con la C
maiuscola), che fa parte del namespace System.Device.Location. Per risolvere questa
incongruenza ci viene in aiuto il Phone Toolkit che abbiamo imparato a utilizzare nel
Capitolo 3: oltre a offrire una serie di controlli supplementari, esso contiene infatti una
serie di utility che ci semplificheranno l’utilizzo delle mappe.
Una volta installatolo tramite NuGet all’interno del nostro progetto (vedi maggiori dettagli
nel Capitolo 3), avremo a disposizione un extension method in grado di convertire le
coordinate da un formato all’altro.
Gli extension method sono porzioni di codice in grado di estendere le funzionalità di una
classe: sostanzialmente si tratta di un metodo, le cui operazioni vanno a interagire con
l’oggetto stesso.
Nell’esempio di codice sopra riportato possiamo vedere tale metodo in azione:
sull’oggetto di tipo Geocoordinate, fornito dalla classe Geolocator, applichiamo il metodo
ToGeoCoordinate(). In questo modo, possiamo passare il valore risultante alla proprietà Center
della mappa, facendo sì che questa si sposti per visualizzare la posizione rilevata dai
servizi di geolocalizzazione. Abbiamo anche un secondo metodo per ottenere lo stesso
risultato, che è in grado di rendere l’esperienza d’uso più piacevole: il metodo SetView().
Esso è esposto dal controllo e, al contrario della proprietà Center, è utilizzabile solo da
codice. Tale metodo consente di impostare diverse proprietà relative alle coordinate che si
vogliono visualizzare (non solo latitudine e longitudine, ma anche zoom, pitch e heading);
in questo modo è possibile, in una volta sola, cambiare le caratteristiche della mappa che
sta visualizzando l’utente, applicando anche un’animazione (tramite una proprietà di tipo
MapAnimationKind) che rende l’effetto più piacevole. Ecco come cambia il codice visto in
precedenza utilizzando questa nuova modalità:
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
Geocoordinate coordinate = args.Position.
Coordinate;
myMap.SetView(coordinate.
ToGeoCoordinate(), 15, MapAnimationKind.Parabolic);
});
}

In questo esempio, a ogni nuova rilevazione della posizione, la visuale della mappa sarà
centrata sulle nuove coordinate con un livello di zoom pari a 15 e con un’animazione di
tipo parabolico (ovvero, la visuale si sposta verso l’alto per poi scendere a volo d’uccello
sulla nuova posizione).
Ora possiamo testare l’applicazione con l’emulatore, grazie al tool Location introdotto
con la versione 7.1 dell’SDK: lanciamo il debug da Visual Studio con il pulsante F5 e
premiamo il bottone Start, così da avviare la rilevazione della posizione. A questo punto
apriamo gli Additional Tools dell’emulatore, selezioniamo il tab Location e
posizioniamo un segnaposto in un punto qualsiasi: la mappa verrà automaticamente
centrata e zoomata sulla stessa posizione che abbiamo selezionato nel tool Location.
Gestire i livelli
Una delle possibilità offerte dalle mappe è quella di gestire più livelli, che possono essere
associati a delle coordinate geografiche e mostrare elementi sovrapposti alle mappe stesse.
Possiamo utilizzare questa funzionalità, ad esempio, per creare un layer con tutti i punti di
interesse della zona e sovrapporlo alla mappa su richiesta dell’utente.
Un layer è rappresentato dalla classe MapLayer, che a sua volta contiene una collezione di
oggetti di tipo MapOverlay: ogni overlay rappresenta un elemento, associato a quel layer, che
sarà mostrato in sovrapposizione alla mappa. Se, ad esempio, volessimo mostrare dei
ristoranti sulla mappa, potremmo creare un oggetto MapLayer dedicato: ogni ristorante
sarebbe rappresentato da un oggetto MapOverlay.
Nel prossimo esempio andremo a creare un semplice layer contenente un cerchio (creato
utilizzando il controllo Ellipse), che sarà impiegato per indicare la posizione corrente
dell’utente.
private void OnAddShapeClicked(object sender, RoutedEventArgs e)
{
MapOverlay overlay = new MapOverlay
{
GeoCoordinate = myMap.Center,
Content = new Ellipse
{
Fill = new
SolidColorBrush(Colors.Blue),
Width = 4⊘,
Height = 4⊘
}
};
MapLayer layer = new MapLayer();
layer.Add(overlay);

myMap.Layers.Add(layer);
}

Innanzitutto definiamo un nuovo oggetto di tipo MapOverlay, del quale valorizziamo due
proprietà: la prima è GeoCoordinate, ovvero le coordinate in cui posizionare l’elemento (nel
nostro caso, esse coincidono con quelle correnti della mappa); la seconda è Content ed è un
generico object. Possiamo passargli, perciò, qualsiasi oggetto o controllo da visualizzare in
quella posizione: nell’esempio, come anticipato poco fa, mostriamo un cerchio di colore
blu, utilizzando il controllo XAML Ellipse.
A questo punto dobbiamo creare un nuovo oggetto di tipo MapLayer e includere al suo
interno l’overlay appena creato. Infine, aggiungiamo il layer alla mappa, tramite la
collezione Layers.
Ovviamente questo è solamente un esempio: un MapLayer reale potrebbe contenere più
oggetti di tipo MapOverlay.
Figura 6.2 - Un layer sovrapposto alla mappa.
Mostrare dei segnaposto sulla mappa
Come detto poco fa, il meccanismo dei layer consente di creare dei livelli che vengono
sovrapposti alla mappa, utili per mostrare la posizione dell’utente o dei punti di interesse.
Il controllo MapOverlay è molto potente: dato che la proprietà Content consente di utilizzare
qualsiasi tipo di oggetto, le possibilità di personalizzazione sono infinite. Allo stesso
tempo, però, questo approccio richiede parecchio lavoro da parte dello sviluppatore anche
solo per definire dei semplici segnaposto, con i quali far interagire l’utente.
Per questo scopo il Phone Toolkit già citato in precedenza include una serie di layer già
pronti per soddisfare due scenari di uso comune: identificare la posizione dell’utente e
aggiungere dei segnaposto. Vediamo come fare.

Utilizzare un segnaposto generico


Il Phone Toolkit include un generico segnaposto, da utilizzare per evidenziare alcuni
luoghi sulla mappa: il suo aspetto (che potete vedere in Figura 6.3) è lo stesso del
segnaposto standard che era disponibile in Bing Maps.
Tale segnaposto è identificato dalla classe Pushpin, facente parte del namespace
Microsoft.Phone.Maps.Toolkit, ed è possibile inserirlo direttamente nello XAML, previa
dichiarazione del namespace corretto nell’intestazione della pagina, ovvero:
xmlns:toolkit="clr-namespace:Microsoft.Phone.Maps.Toolkit;assembly=Microsoft.Phone.
Controls.Toolkit"

A questo punto dobbiamo utilizzare una delle proprietà incluse nel tookit, chiamata
MapExtensions. Tale proprietà funge da contenitore per tutti quei controlli che il toolkit ha
incluso per semplificare la vita agli sviluppatori: tra queste figura anche il controllo Pushpin.
<maps:Map x:Name="myMap">
<toolkit:MapExtensions.Children>
<toolkit:Pushpin x:Name="MyPushpin" Content="My Position"></
toolkit:Pushpin>
</toolkit:MapExtensions.Children>
</maps:Map>

L’oggetto Pushpin espone una proprietà GeoCoordinate che ne definisce la posizione


sulla mappa: ci è sufficiente, perciò, modificare l’evento PositionChanged affinché tale
proprietà assuma lo stesso valore restituito tra i parametri di ritorno.
La proprietà Content, analogamente a quanto abbiamo visto per la classe MapOverlay,
ne definisce il contenuto: nel nostro caso, My Position sarà l’etichetta che verrà mostrata
nel segnaposto.
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
Geocoordinate coordinate = args.Position.
Coordinate;
myMap.SetView(coordinate.
ToGeoCoordinate(), 15, MapAnimationKind.Parabolic);
Pushpin pushpin = (Pushpin)this.
FindName("MyPushpin");
pushpin.GeoCoordinate = coordinate.
ToGeoCoordinate();
});
}

Quello che facciamo è, tramite il metodo FindName messo a disposizione dalla pagina,
andare a recuperare il controllo identificato dal nome MyPushpin (questo perché, essendo
contenuto all’interno della collezione Children delle MapExtensions, non siamo in grado
di recuperare la reference solamente dal nome), dopodiché andiamo a cambiare la
proprietà GeoCoordinate con il valore restituito dai servizi di geolocalizzazione (anche in
questo caso, previa conversione tramite l’apposito metodo ToGeoCoordinate).

Figura 6.3 - Il segnaposto indica la posizione esatta rilevata dai servizi di geolocalizzazione.
Interagire con il segnaposto
Per interagire con il segnaposto è sufficiente intercettare l’evento Tap, che viene scatenato
nel momento in cui l’utente lo seleziona. In questo esempio, dato che abbiamo un unico
segnaposto, andiamo a definire l’evento direttamente nello XAML:
<maps:Map x:Name="myMap">
<my:Pushpin x:Name="MyPosition" Tap="OnPushpinTapped" />
</maps:Map>

Dopodiché da codice andiamo a gestire l’evento mostrando a video una MessageBox con
l’etichetta del segnaposto.
private void OnPushpinTapped(object sender, GestureEventArgs e)
{
Pushpin pushpin = sender as Pushpin;
MessageBox.Show(pushpin.Content.ToString());
}
Aggiungere segnaposto da codice
Nell’esempio precedente abbiamo inserito un solo segnaposto, che rappresentava la
posizione dell’utente sulla mappa. A volte, però, si ha la necessità di avere più segnaposto
oppure non è possibile conoscerne a priori il numero (ad esempio, vogliamo mostrare dei
punti di interesse intorno all’utente, il cui numero è variabile in base alla sua posizione).
Vediamo ora come ottenere lo stesso risultato (un segnaposto nella posizione dell’utente)
da codice invece che da XAML.
Per raggiungere questo risultato dobbiamo introdurre un nuovo attore, ovvero il controllo
MapItemsControl, tramite il quale siamo in grado di definire un template del layer che
vogliamo sovrapporre alla mappa, che può essere utilizzato anche per mostrare dei
segnaposto.
Ipotizziamo di voler utilizzare questo controllo per mostrare una serie di ristoranti: di
conseguenza, nel nostro progetto abbiamo definito una classe Restaurant, che rappresenta il
singolo ristorante.
public class Restaurant
{
public GeoCoordinate Coordinate { get; set; }
public string Address { get; set; }
}

Il primo passo è, perciò, quello di modificare lo XAML della nostra mappa nel seguente
modo:
<maps:Map x:Name="myMap">
<toolkit:MapExtensions.Children>
<toolkit:MapItemsControl Name="RestaurantItems">
<toolkit:MapItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:Pushpin GeoCoordinate="{Binding
Coordinate}" Content="{Binding Address}" Tap="OnPushpinTapped"/>
</DataTemplate>
</toolkit:MapItemsControl.ItemTemplate>
</toolkit:MapItemsControl>
</toolkit:MapExtensions.Children>
</maps:Map>

Come potete vedere, abbiamo definito un MapItemsControl e il relativo ItemTemplate: un


controllo Pushpin, le cui coordinate e il cui contenuto sono messi in binding con le due
proprietà della classe Restaurant, Coordinate e Address. Per ognuno degli elementi che andremo a
inserire nel controllo RestaurantItems sarà generato un nuovo segnaposto. A questo
punto, nel momento in cui il caricamento della pagina è stato completato, andiamo a
creare una collezione fittizia di ristoranti:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<Restaurant> restaurants = new ObservableCollection<
Restaurant>()
{
new Restaurant { Coordinate = new GeoCoordinate(47.6⊘5⊘338745117,
-122.334243774414), Address = "Ristorante 1" },
new Restaurant() { Coordinate = new GeoCoordinate(47.6⊘45697927475,
-122.3298856616⊘2), Address = "Ristorante 2" },
new Restaurant() { Coordinate = new GeoCoordinate(47.6⊘571289⊘625,
-122.33⊘268859863), Address = "Ristorante 3" },
new Restaurant() { Coordinate = new GeoCoordinate(47.6⊘15319824219,
-122.335113525391), Address = "Ristorante 4" },
new Restaurant() { Coordinate = new GeoCoordinate(47.6⊘56594848633,
-122.334243774414), Address = "Ristorante 5" }
};

ObservableCollection<DependencyObject> children = MapExtensions.GetChildren(myMap);


var obj =
children.FirstOrDefault(x => x.GetType() == typeof(MapItemsControl))
as MapItemsControl;

obj.ItemsSource = restaurants;
}

Anche in questo caso abbiamo lo stesso problema visto in precedenza: non abbiamo modo
di riferirci all’oggetto di tipo MapItemsControl direttamente. Ci viene in aiuto, perciò, la classe
MapExtensions (facente parte del toolkit) che ci permette di recuperare tutti gli elementi
che sono stati definiti al suo interno, tramite il metodo GetChildren(): nel nostro caso,
andiamo a cercare con una query LINQ l’unico di tipo MapItemsControl valorizzandone la
proprietà ItemsSource con la collezione fittizia appena definita.
Se ora lanciamo nuovamente l’applicazione, potremo notare che i cinque segnaposto sono
stati posizionati correttamente, secondo le coordinate che abbiamo definito.
Figura 6.4 - I segnaposto vengono posizionati sulla mappa.

Un tipo particolare di segnaposto: UserLocationMarker


In molte applicazioni che hanno a che fare con le mappe uno degli scenari più frequenti è
quello che consiste nel tracciare la posizione dell’utente e nel segnalargli, visivamente,
dove si trova: per questo scopo il Phone Toolkit include un segnaposto ad hoc, che utilizza
lo stesso stile visivo dell’applicazione nativa Nokia Maps.
Il suo comportamento e il suo utilizzo sono esattamente gli stessi che abbiamo visto per i
pushpin, ma il controllo di riferimento si chiama UserLocationMarker.
Ecco un esempio di dichiarazione nello XAML:
<maps:Map x:Name="myMap">
<toolkit:MapExtensions.Children>
<toolkit:UserLocationMarker x:Name="UserLocationMarker" />
</toolkit:MapExtensions.Children>
</maps:Map>

A questo punto, esattamente come facevamo con i segnaposto, da codice possiamo


recuperarne il riferimento e agire sulla proprietà GeoCoordinate, per definirne la posizione:
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
Geocoordinate coordinate = args.Position.
Coordinate;
UserLocationMarker marker =
(UserLocationMarker)this.FindName("UserLocationMarker");
marker.GeoCoordinate = coordinate.
ToGeoCoordinate();
});
}
Figura 6.5 - Un segnaposto di tipo UserLocationMarker.
Calcolare un percorso
Una delle novità più interessanti introdotte grazie al controllo Nokia Maps in Windows
Phone 8 è la grande semplicità con cui è possibile calcolare i percorsi per andare da un
punto all’altro e mostrarli sulla mappa. Abbiamo anche la possibilità di impostare diversi
parametri per il calcolo, esattamente come faremmo con un navigatore satellitare.
Vediamo un esempio di codice:
private void OnCalculateRouteClicked(object sender, RoutedEventArgs e)
{
RouteQuery query = new RouteQuery
{
TravelMode = TravelMode.Driving,
RouteOptimization = RouteOptimization.MinimizeTime,
};
List<GeoCoordinate> coordinates = new List<GeoCoordinate>();
coordinates.Add(new GeoCoordinate(47.6⊘45697927475, -122.3298856616⊘2));
coordinates.Add(new GeoCoordinate(47.6⊘571289⊘625, -122.33⊘268859863));
query.Waypoints = coordinates;

query.QueryCompleted += query_QueryCompleted;
query.QueryAsync();
}

Il fulcro è la classe RouteQuery, che permette di interrogare i servizi di Nokia Maps per
cercare il percorso migliore per andare da una posizione all’altra della mappa: nel
momento in cui creiamo una nuova istanza di questo oggetto, possiamo definire diverse
proprietà che rappresentano il tipo di calcolo che vogliamo effettuare. Nell’esempio
possiamo notare due di questi parametri: TravelMode, che ci permette di specificare se
vogliamo calcolare un percorso a piedi o in auto, e RouteOptimization, se vogliamo cercare di
ottimizzare il calcolo in base al tempo o alla distanza.
Dopodiché l’oggetto di tipo RouteQuery espone una proprietà chiamata Waypoints, una
collezione di coordinate di tipo GeoCoordinate, la quale rappresenta tutti i punti della mappa
che devono essere attraversati dal percorso che vogliamo calcolare, nell’ordine in cui sono
stati inseriti. Nell’esempio, ci limitiamo a calcolare il percorso da un punto A a un punto
B: aggiungiamo a tale collezione, perciò, solamente due punti.
Dato che il calcolo del percorso è un’operazione che può richiedere del tempo per essere
completata, le API ci mettono a disposizione un metodo asincrono, tramite il meccanismo
delle callback. Occorre, perciò, sottoscrivere l’evento QueryCompleted, che viene scatenato
nel momento in cui il calcolo è completato e i servizi di Nokia ci hanno restituito il
percorso. A questo punto possiamo lanciare il calcolo vero e proprio chiamando il metodo
QueryAsync() dell’oggetto RouteQuery.

Come comportarsi nel momento in cui il calcolo è terminato? Ecco un esempio di gestione
dell’evento QueryCompleted:
void query_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
MapRoute route = new MapRoute(e.Result);
myMap.AddRoute(route);
}

Il parametro di ritorno di tipo QueryCompletedEventArgs<Route> contiene una proprietà Result, che


è di tipo Route. Tale valore può essere passato al costruttore di una classe esposta dalle API
chiamata MapRoute, che rappresenta un percorso disegnato sulla mappa. Una volta creato un
oggetto di tipo MapRoute, non dobbiamo fare altro che mostrarlo sulla mappa chiamando il
metodo AddRoute() del controllo Nokia Maps e il gioco è fatto: sullo schermo comparirà un
percorso che toccherà tutti i punti che abbiamo definito in precedenza.

Figura 6.6 - Il percorso visualizzato sulla mappa.


Risolvere le coordinate geografiche in un indirizzo e viceversa
Fino a questo momento abbiamo lavorato con coordinate geografiche, ma, nella maggior
parte dei casi, gli utenti hanno la necessità di visualizzare informazioni in un formato a
loro comprensibile, come un indirizzo.
Per questo motivo le API di Windows Phone 8 offrono la possibilità, sempre grazie ai
servizi di Nokia, di trasformare le coordinate geografiche in un indirizzo vero e proprio e
viceversa.
Sono due le classi che abbiamo a disposizione: GeocodeQuery e ReverseGeocodeQuery. Entrambe
utilizzano il medesimo approccio, anche se hanno due scopi diversi: la prima serve per
trasformare un indirizzo in coordinate geografiche; la seconda, invece, svolge la funzione
opposta.
Ecco un esempio di utilizzo:
private void OnResolveCoordinatesClicked(object sender, RoutedEventArgs e)
{
GeocodeQuery query = new GeocodeQuery
{
GeoCoordinate = new GeoCoordinate(⊘, ⊘),
SearchTerm = "Milan,Italy"
};
query.QueryCompleted += query_QueryCompleted;
query.QueryAsync();
}

void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLo


cation>> e)
{
var item = e.Result.FirstOrDefault();
MessageBox.Show(string.Format("{⊘} / {1}", item.GeoCoordinate.Latitude,
item.GeoCoordinate.Longitude));
}

Il primo passaggio è la creazione di un nuovo oggetto di tipo GeocodeQuery, che espone due
proprietà: GeoCoordinate e SearchTerm. Dato che, quando si usa questa classe, non siamo a
conoscenza delle coordinate geografiche del luogo (anzi, è proprio l’informazione che
vogliamo scoprire), quello che ci interessa è valorizzare la proprietà SearchTerm con un
indirizzo. È importante non inserire spazi all’interno della parola chiave, altrimenti i
risultati non saranno attendibili.
Nell’esempio potete notare che specifichiamo comunque il parametro GeoCoordinate, anche
se con delle coordinate fittizie: si tratta di un passaggio necessario, altrimenti otterremo
un’eccezione nel momento in cui avvieremo la ricerca.
Dopodiché ci sottoscriviamo all’evento QueryCompleted, il quale viene invocato nel momento
in cui la ricerca è terminata e le API sono pronte per restituirci i risultati. Infine, avviamo
la ricerca vera e propria chiamando il metodo QueryAsync() dell’oggetto di tipo GeocodeQuery.
Cosa possiamo fare nell’evento QueryCompleted? Tra i parametri di ritorno ci viene restituito
un oggetto che contiene una proprietà Result, che è di tipo IList<MapLocation>. Si tratta di una
collezione di tutti i risultati che sono stati trovati: la ricerca, infatti, essendo basata su una
parola chiave, potrebbe restituire più indirizzi.
Ognuno degli elementi della collezione, di tipo MapLocation, contiene tutte le informazioni
che ci servono: una proprietà di tipo GeoCoordinate, con tutte le informazioni complete sulla
posizione geografica, e una proprietà di tipo Information, che racchiude invece l’indirizzo
dettagliato.
Nell’esempio di codice mostriamo semplicemente a schermo i valori di latitudine e
longitudine del primo risultato contenuto nella collezione.
void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLo
cation>> e)
{
var item = e.Result.FirstOrDefault();
MessageBox.Show(string.Format("{⊘} - {1}", item.GeoCoordinate.Latitude,
item.GeoCoordinate.Longitude));
}

Esiste un’altra informazione restituita dall’oggetto MapLocation che può esserci utile:
BoundingBox, di tipo LocationRectangle. Questa classe rappresenta una porzione di mappa e
racchiude tutte le coordinate geografiche per localizzarla: si tratta di un’informazione
utile, perché il metodo SetView() del controllo mappa che abbiamo visto in precedenza
accetta in ingresso anche una proprietà di questo tipo. Ecco, perciò, che, molto
semplicemente, siamo in grado di centrare la mappa sulle coordinate restituite dalla
ricerca:
void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLo
cation>> e)
{
var item = e.Result.FirstOrDefault();
myMap.SetView(item.BoundingBox, MapAnimationKind.Parabolic);
}

Per soddisfare lo scenario inverso (recuperare un indirizzo date le coordinate geografiche)


dobbiamo, invece, utilizzare la classe ReverseGeocodeQuery che, a parte il nome differente,
funziona allo stesso modo della classe GeocodeQuery che abbiamo appena visto.
private void OnResolveAddressClicked(object sender, RoutedEventArgs e)
{
ReverseGeocodeQuery reverseQuery = new ReverseGeocodeQuery
{
GeoCoordinate = new
GeoCoordinate(45.3967, ⊘⊘9.3163)
};
reverseQuery.QueryCompleted += reverseQuery_QueryCompleted;
reverseQuery.QueryAsync();
}

void reverseQuery_QueryCompleted(object sender, QueryCompletedEventArgs<IList


<MapLocation>> e)
{
var item = e.Result.FirstOrDefault();
MessageBox.Show(string.Format("{⊘},{1}", item.Information.Address.
Street, item.Information.Address.City));
}

Anche in questo caso definiamo un nuovo oggetto di tipo ReverseGeocodeQuery, con la


differenza che ci limitiamo a valorizzare solo la proprietà GeoCoordinate (dato che, in questo
caso, vogliamo recuperare l’indirizzo associato).
Ci sottoscriviamo, come in precedenza, all’evento QueryCompleted e avviamo la ricerca con il
metodo QueryAsync(). Anche il formato dei dati che viene restituito è lo stesso: nell’evento
QueryCompleted abbiamo sempre una collezione di tipo IList<MapLocation>, che possiamo
sfruttare utilizzando gli stessi esempi visti poco fa.
L’unica differenza è che, in questo esempio, andiamo a mostrare come verifica la via e la
città dell’indirizzo che è stato trovato dai servizi di Nokia.
Location tracking in background
Una delle novità introdotte in Windows Phone 8 è il supporto alla funzionalità di location
tracking in background: in questo modo, le applicazioni che fanno uso dei servizi di
geolocalizzazione sono in grado di tracciare in maniera continuativa la posizione
dell’utente, anche quando questa non è in primo piano.
Ci sono, però, delle condizioni, di seguito esposte, che devono essere rispettate, affinché
l’applicazione venga mantenuta in esecuzione in background. Se anche solo una di queste
condizioni non viene rispettata, l’applicazione sarà terminata:
• l’applicazione smette di tracciare la posizione dell’utente, deregistrandosi dagli eventi
PositionChanged e StatusChanged;

• l’applicazione è in esecuzione in background da quattro ore e non è mai stata riaperta;


• è attiva la modalità Battery Saver per il risparmio della batteria;
• la memoria libera del dispositivo si sta esaurendo;
• l’utente ha disabilitato i servizi di geolocalizzazione tra le impostazioni del telefono.
In più, solo un’applicazione alla volta ha diritto a essere eseguita in background: se
l’utente apre un’altra applicazione in grado di sfruttare tale funzionalità, questa prende il
posto di quella già in esecuzione.
Per abilitare questa funzionalità, occorre effettuare una modifica al file di manifest, che
però non è possibile gestire dall’editor visuale: dovete cliccare con il tasto destro sul file
WMAppManifest.xml presente nella cartella Properties e scegliere l’opzione View
code.
Troverete il nodo DefaultTask che indica il comportamento predefinito che viene messo
in atto quando l’applicazione viene aperta. Ecco la modifica che occorre applicare:
<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml">
<BackgroundExecution>
<ExecutionType Name="LocationTracking"/>
</BackgroundExecution>
</DefaultTask>
</Tasks>

L’applicazione, a questo punto, è pronta per continuare a funzionare in background.


Possiamo verificarlo con gli esempi visti in precedenza parlando di mappe e servizi di
geolocalizzazione: apriamo l’applicazione e, tramite il tool apposito dell’emulatore,
posizioniamo un segnaposto. Il controllo mappa incluso nell’applicazione si aggiornerà
per mostrare la posizione scelta. Ora sospendiamo l’applicazione (premendo il pulsante
Start), posizioniamo un altro segnaposto sulla mappa, aspettiamo qualche secondo e
riapriamo l’applicazione premendo il pulsante Back: il controllo mappa sarà posizionato
sull’ultimo segnaposto aggiunto, dato che l’operazione di tracking è stata eseguita in
background.
Come sviluppatori, abbiamo anche la possibilità di determinare quando l’applicazione è in
esecuzione in background o meno. Uno scenario tipico di utilizzo di questa informazione è
l’ottimizzazione: per ridurre al minimo le possibilità che l’esecuzione in background
venga sospesa, è buona regola sospendere tutte le attività non strettamente legate
all’utilizzo dei servizi di geolocalizzazione.
Possiamo gestire questa informazione tramite un nuovo evento esposto dalla classe
PhoneApplicationService dichiarato nel file App.xaml. Dal Capitolo 4, saprete che, tramite tale
classe, nel file App.xaml.cs sono dichiarati quattro eventi che permettono di interagire
con il ciclo di vita dell’applicazione: in Windows Phone 8 è disponibile un quinto evento
(che non viene registrato in automatico come gli altri dal template standard), chiamato
RunningInBackground, che potete registrare nel modo seguente:

<Application.ApplicationLifetimeObjects>
<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"
RunningInBackground="Application_RunningInBackground"
/>
</Application.ApplicationLifetimeObjects>

A questo punto, facendo generare l’event handler direttamente da Visual Studio, avrete a
disposizione nel file App.xaml.cs un metodo che sarà invocato nel momento in cui
l’applicazione sarà eseguita in background:
private void Application_RunningInBackground(object sender,
RunningInBackgroundEventArgs e)
{

Un esempio di ottimizzazione dell’applicazione potrebbe consistere nel fermare


l’aggiornamento della mappa con la posizione corrente (dato che questa non è più visibile)
e utilizzare un metodo alternativo per comunicare l’informazione all’utente, come
l’aggiornamento della tile principale.
Vediamo come implementare questo scenario: il primo passo è quello di memorizzare in
maniera globale lo stato di esecuzione dell’applicazione, così che anche le altre pagine
dell’applicazione possano accedere a questa informazione. Ecco, quindi, il codice da
includere nel file App.xaml.cs:
public static bool IsRunningInBackground { get; set; }

private void Application_RunningInBackground(object sender,


RunningInBackgroundEventArgs e)
{
IsRunningInBackground = true;
}

private void Application_Activated(object sender, ActivatedEventArgs e)


{
IsRunningInBackground = false;
}

Il valore di tale variabile viene impostato a true nel momento in cui viene scatenato
l’evento legato all’esecuzione in background; viene messo a false, invece, nel momento in
cui l’applicazione viene riportata in foreground.
A questo punto possiamo includere nella pagina che contiene il controllo mappa e che si
occupa di rilevare la posizione dell’utente il seguente codice:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Geolocator locator = new Geolocator
{
DesiredAccuracy = PositionAccuracy.High,
MovementThreshold = 5⊘
};
locator.PositionChanged += locator_PositionChanged;
}

void locator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)


{
if (!App.IsRunningInBackground)
{
Dispatcher.BeginInvoke(() =>
{
myMap.SetView(args.Position.
Coordinate.ToGeoCoordinate(), 17);
});
}
else
{
ShellTileData data = new FlipTileData()
{
Title = DateTime.Now.ToString(),
BackContent = string.Format("{⊘} - {1}",
args.Position.Coordinate.Latitude, args.Position.Coordinate.Longitude)
};
ShellTile.ActiveTiles.FirstOrDefault().Update(data);
}
}

Tramite la variabile globale che abbiamo definito in precedenza, discriminiamo tra le due
casistiche: se l’applicazione è eseguita in foreground ci comportiamo normalmente,
ovvero centriamo la posizione della mappa in base a quella rilevata dai servizi di
geolocalizzazione.
Se, invece, ci troviamo in una situazione di esecuzione in background, aggiorniamo la tile
principale dell’applicazione mostrando, sul lato principale, data e ora dell’ultimo
rilevamento, mentre sul retro le nuove coordinate geografiche. Non mi dilungherò in
questa sede a spiegare il funzionamento delle API utilizzate per aggiornare la tile: le
vedremo in dettaglio nel Capitolo 11.
Lanciando l’applicazione nell’emulatore, potremo verificarne il corretto comportamento:
fintanto che l’applicazione è in primo piano, la mappa si sposterà per seguire la posizione
dell’utente, simulata tramite l’apposito tool. Se, invece, la sospendiamo e facciamo il pin
della tile nella home screen, la vedremo aggiornarsi a ogni cambiamento di posizione.
È importante sottolineare come le applicazioni che fanno uso della funzionalità di tracking
della posizione siano automaticamente abilitate anche al Fast App Resume: è necessario,
perciò, gestire in maniera corretta il ciclo di vita, come spiegato nel Capitolo 4.
Occhio alla privacy
La privacy è sicuramente uno degli argomenti più delicati al giorno d’oggi, soprattutto
quando si parla di Internet e tecnologia. Dispositivi come gli smartphone permettono,
infatti, grazie ai servizi di localizzazione, di conoscere sempre l’esatta posizione
dell’utente: in passato (ma anche recentemente) è capitato che i produttori di smartphone e
sistemi operativi siano stati messi sotto accusa per raccolta e uso illecito dei dati di
posizione dell’utente.
È bene, quindi, nelle nostre applicazioni, evitare problemi di questo tipo, che possono
portare anche a noie legali non indifferenti.
Sono due gli accorgimenti da adottare se avete intenzione di usare i servizi di
geolocalizzazione nelle vostre applicazioni:
1. prevedete una schermata di opzioni in cui dare la possibilità all’utente, in qualsiasi
momento, di abilitare e disabilitare i servizi di geolocalizzazione;
2. inserite sempre, possibilmente nella stessa schermata di opzioni, un testo di
disclaimer che specifichi il motivo per cui utilizzate i servizi di geolocalizzazione e
in cui assicurate che i dati non vengono raccolti e memorizzati, ma sono utilizzati
solamente per gli scopi previsti dall’applicazione.
Queste indicazioni sono previste dalle guidelines stesse di Windows Phone: se la vostra
applicazione fa uso dei servizi di geolocalizzazione e non avete seguito questi due
suggerimenti, molto probabilmente essa verrà rifiutata durante il processo di
certificazione.
Pubblicare un’applicazione che fa uso del controllo mappe
In fase di testing, il controllo mappe può essere utilizzato senza alcuna limitazione; nel
momento in cui la pubblicate, però, avete la necessità di richiedere delle credenziali di
accesso, che vi autorizzano a utilizzare il controllo anche in produzione.
La richiesta avviene durante il processo di pubblicazione: nel momento in cui avviate sul
portale Windows Phone Dev Center la procedura di pubblicazione (che sarà illustrata in
dettaglio nel Capitolo 12), troverete l’opzione Map services. In questa sezione troverete il
pulsante Get token, che vi restituirà due codici: ApplicationID e AuthenticationToken.
Una volta in possesso di tali codici, dovete assegnarli alle proprietà ApplicationId e
AuthenticationToken esposte dalla classe MapsSettings, come nell’esempio:

Microsoft.Phone.Maps.MapsSettings.ApplicationContext.ApplicationId =
"ApplicationID";
Microsoft.Phone.Maps.MapsSettings.ApplicationContext.AuthenticationToken
= "AuthenticationToken";
I sensori di movimento
I sensori di movimento sono ormai una feature imprescindibile per gli smartphone: è
praticamente impossibile trovare un device mobile che non includa dei sensori in grado di
rilevare il movimento del telefono nello spazio.
Questi sensori vengono utilizzati per due scopi principali:
• per determinare l’orientamento del device: le applicazioni tradizionali spesso sono in
grado di adattare il proprio layout in base all’orientamento (portrait o landscape).
Tuttavia, tale aspetto non verrà qui trattato, dato che Windows Phone espone degli
eventi che ci permettono di gestire in maniera automatica l’orientamento, evitando
allo sviluppatore di dover andare a leggere le coordinate ricevute dai sensori per
determinare la posizione;
• nei giochi: l’esistenza dei sensori di movimento è una delle caratteristiche che hanno
permesso, negli ultimi anni, di realizzare giochi innovativi e originali; spesso gli
sviluppatori hanno utilizzato (e continuano a utilizzare) i sensori come metodo di
controllo all’interno dei loro giochi, per offrire un grado di interazione diverso dal
touch screen.
Prima di fare una panoramica specifica dei sensori di movimento presenti nei device
Windows Phone, è bene sottolineare come tutte le API presentino la medesima struttura.
Ogni sensore è rappresentato da una delle classi disponibili all’interno del namespace
Windows.Devices.Sensors: ognuna di esse espone il metodo GetDefault(), che restituisce il
riferimento al sensore. Se tale riferimento è nullo, allora vuol dire che quello specifico
sensore non è presente sul dispositivo corrente. È sempre bene, pertanto, per evitare
sorprese, controllare l’esistenza di un sensore prima di utilizzarlo effettivamente. Sono due
le modalità con cui possiamo interagire con i sensori:
• tramite il metodo GetCurrentReading(), che restituisce una singola rilevazione dei dati del
sensore;
• tramite l’evento ReadingChanged(), a cui possiamo sottoscriverci e che viene scatenato
ogni qualvolta la posizione è cambiata e quindi i dati rilevati dal sensore sono
differenti.
Un’altra caratteristica in comune tra i sensori è la disponibilità di due proprietà per
interagire con la frequenza di aggiornamento dei dati rilevati: MinimumReportInterval e
ReportInterval. La prima è una proprietà di sola lettura e restituisce la frequenza di
aggiornamento minima, sotto la quale non è possibile scendere; la seconda, invece,
consente di cambiare la frequenza di aggiornamento predefinita.

L’utilizzo dei sensori di movimento richiede che nel file di manifest sia stata
NOTA
dichiarata la capability <Capability Name=”ID_CAP_SENSORS”>.
L’accelerometro
L’unico sensore di movimento imposto dalle specifiche hardware minime fornite da
Microsoft è l’accelerometro, il cui scopo è quello di restituire informazioni sulle forze
che vengono applicate sul device, in modo da determinare la direzione in cui esso viene
spostato nello spazio.
La classe di riferimento si chiama Accelerometer e i dati vengono restituiti dal sensore tramite
la classe AccelerometerReading; essa contiene le proprietà AccelerationX, AccelerationY e AccelerationZ
che rappresentano le coordinate X, Y e Z dell’accelerazione, le quali permettono di
determinare la posizione del device. Come riferimento, ecco i valori delle coordinate per
gli orientamenti standard:
• Portrait standing (telefono in verticale in piedi):
– X: 0
– Y: -1
– Z: 0
• Landscape standing (telefono in orizzontale in piedi):
– X: -1
– Y: 0
– z: 0
• Portrait flat (telefono in verticale appoggiato su una superficie)
– X: 0
– Y: 0
– z: -1
• Landscape flat (telefono in orizzontale appoggiato su una superficie)
– X: 0
– Y: 0
– z: -1
Ecco un esempio di utilizzo della classe Accelerometer, in cui i valori restituiti
dall’accelerometro vengono visualizzati sullo schermo:
public MainPage()
{
InitializeComponent();
Accelerometer accelerometer = Accelerometer.GetDefault();
accelerometer.ReadingChanged += accelerometer_ReadingChanged;
}

void accelerometer_ReadingChanged(Accelerometer sender,


AccelerometerReadingChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
txtX.Text = args.Reading.AccelerationX.ToString();
txtY.Text = args.Reading.AccelerationY.ToString();
txtZ.Text = args.Reading.AccelerationY.ToString();
});
}

Notiamo l’uso del Dispatcher, di cui abbiamo parlato nel Capitolo 3: l’evento ReadingChanged,
infatti, viene eseguito su un thread diverso da quello della UI e quindi dobbiamo utilizzare
il Dispatcher per raggiungere i tre controlli TextBlock presenti nello XAML.
Questo tipo di sensore è l’unico che può essere testato anche utilizzando l’emulatore,
grazie al tool Accelerometer che permette di simulare la rotazione del device nello spazio.
Una volta effettuato il deploy della nostra applicazione, apriamo gli Additional Tools
dell’emulatore, dove troveremo un render in 3D di un ipotetico device Windows Phone.
Noteremo una sfera colorata al centro: cliccando e tenendo premuto il tasto sinistro del
mouse, potremo trascinare la sfera, causando la rotazione del device. Nella parte bassa
dello schermo verranno mostrate in tempo reale le coordinate X, Y e Z, che ritroveremo
anche nella nostra applicazione di esempio.
Il giroscopio
Il giroscopio rientra tra i sensori che sono stati inseriti come opzionali a partire da
Windows Phone 7.5: la classe di riferimento si chiama Gyrometer e restituisce i dati rilevati
tramite la classe GyrometerReading, che restituisce la posizione sugli assi X, Y e Z tramite le
proprietà AngularVelocityX, AngularVelocityY e AngularVelocityZ.
Il giroscopio è un sensore in grado di aumentare la precisione dei dati relativi allo
spostamento del device nello spazio e permette di determinarne l’orientamento. Il
funzionamento è analogo a quello visto con l’accelerometro:
public MainPage()
{
InitializeComponent();
Gyrometer gyrometer = Gyrometer.GetDefault();
gyrometer.ReadingChanged += gyrometer_ReadingChanged;
}

void gyrometer_ReadingChanged(Gyrometer sender,


GyrometerReadingChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
txtX.Text = args.Reading.AngularVelocityX.
ToString();
txtY.Text = args.Reading.AngularVelocityY.
ToString();
txtZ.Text = args.Reading.AngularVelocityZ.
ToString();
});
}

Anche in questo caso l’evento ReadingChanged viene eseguito su un thread differente da


quello della UI: è necessario, perciò, l’utilizzo del Dispatcher.
La bussola
Anche la bussola rientra tra i sensori classificati come opzionali, anche se, al contrario del
giroscopio, è difficile trovare un dispositivo (anche tra quelli della prima generazione) che
non ne sia dotato. La classe di riferimento si chiama Compass e i dati vengono manipolati
tramite la classe CompassReading, che contiene due informazioni: Heading-MagneticNorth, ovvero
l’orientamento rispetto al Nord magnetico, e HeadingTrueNorth, ovvero quello rispetto al Nord
reale.
public MainPage()
{
InitializeComponent();

Compass compass = Compass.GetDefault();


compass.ReadingChanged += compass_ReadingChanged;
}

void compass_ReadingChanged(Compass sender, CompassReadingChangedEventArgs args)


{
Dispatcher.BeginInvoke(() =>
{
txtMagneticNorth.Text = args.Reading.
HeadingMagneticNorth.ToString();
txtTrueNorth.Text = args.Reading.
HeadingTrueNorth.ToString();
});
}
Orientation Sensor
L’Orientation Sensor nasce per offrire agli sviluppatori un modo più semplice di utilizzare
e interpretare i dati rilevati dai vari sensori presenti nel dispositivo.
I dati restituiti dai singoli sensori, infatti, possono essere viziati da fattori esterni, che
rendono necessario elaborare i dati grezzi per ottenere la vera posizione del device nello
spazio. Inoltre, l’utilizzo simultaneo di diversi sensori migliora la qualità della rilevazione,
garantendo dati più precisi.
L’Orientation Sensor si occupa di fare questo per noi, restituendo dati già filtrati dai valori
fuori scala e combinando le informazioni rilevate dagli altri sensori: per questo motivo, è
consigliabile utilizzare questa API per gestire i sensori di movimento, demandando
l’utilizzo delle API relative ai singoli sensori solo nei casi eccezionali in cui sono
necessarie elaborazioni particolari.
L’Orientation Sensor, rappresentato dalla classe OrientationSensor, può essere testato
solamente con un device reale: l’emulatore, infatti, non lo supporta e vi restituirà null nel
momento in cui cercherete di ottenere un’istanza con il metodo GetDefault().
Questa classe restituisce un’informazione più complessa rispetto a quanto visto in
precedenza: l’oggetto di tipo OrientationSensorReading, infatti, contiene la posizione del device
rispetto a nove assi, tramite le proprietà Quaternion e RotationMatrix.
Nell’esempio seguente vediamo come recuperare le coordinate degli assi X, Y e Z
utilizzando questo nuovo sensore:
public MainPage()
{
InitializeComponent();

OrientationSensor orientationSensor = OrientationSensor.GetDefault();


orientationSensor.ReadingChanged += orientationSensor_ReadingChanged;
}

void orientationSensor_ReadingChanged(OrientationSensor sender,


OrientationSensorReadingChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
txtX.Text = args.Reading.Quaternion.X.ToString();
txtY.Text = args.Reading.Quaternion.Y.ToString();
txtZ.Text = args.Reading.Quaternion.Z.ToString();
});
}
Determinare lo shake del device
Anche se non stiamo sviluppando un gioco, a volte può essere utile determinare lo shake
del device, ovvero uno “scuotimento” di una certa intensità. Alcune applicazioni sfruttano
questo sistema, ad esempio, per forzare il refresh dei dati visualizzati in una pagina, come
alternativa al classico pulsante.
Determinare lo shake sfruttando l’accelerometro non è così banale, dato che è necessario
applicare una serie di funzioni matematiche per identificare il tipo di movimento. In nostro
aiuto viene una libreria sviluppata direttamente dal team di Windows Phone chiamata
Shake Gestures Library e scaricabile all’indirizzo
http://s.qmatteoq.com/ShakeGesturesLibrary.
Dopo aver scaricato il pacchetto e aperto il progetto incluso (chiamato
ShakeGestures.csproj), compiliamolo: nella cartella bin del progetto troveremo una DLL
chiamata ShakeGestures.dll. Questa è la DLL a cui dovremo aggiungere una reference
nel nostro progetto.
Una volta fatta questa operazione e aggiunto il namespace ShakeGestures nella nostra
classe, potremo utilizzare la classe ShakeGesturesHelper, che è un singleton. Questo significa
che non dovremo creare ogni volta una nuova istanza della classe, ma utilizzeremo sempre
la stessa istanza statica tramite la proprietà Instance. La prima cosa da fare è sottoscrivere
l’evento ShakeGesturesHelper.Instance.ShakeGesture, che viene invocato nel momento in cui viene
rilevato uno shake del device.
Dopodiché possiamo avviare la rilevazione impostando a true la proprietà
ShakeGesturesHelper.Instance.Active.

A questo punto sta a noi implementare il comportamento desiderato nell’event handler


dell’evento ShakeGesture: l’informazione che ci viene restituita è la direzione verso cui è
stato effettuato lo shake, sotto forma di enumeratore ShakeType, che può assumere i valori X,
Y e Z.

public MainPage()
{
InitializeComponent();

ShakeGesturesHelper.Instance.ShakeGesture += new EventHandler<ShakeGestu


reEventArgs>(Instance_ShakeGesture);
ShakeGesturesHelper.Instance.Active = true;
}

void Instance_ShakeGesture(object sender, ShakeGestureEventArgs e)


{
Dispatcher.BeginInvoke(() => MessageBox.Show("Shake detected!"));
}

Notiamo l’uso del Dispatcher, in quanto l’evento ShakeGesture viene eseguito su un thread
differente da quello della UI.
Lo ShakeGesturesHelper espone, inoltre, una serie di proprietà che permettono di
personalizzare le condizioni che devono essere soddisfatte affinché l’evento ShakeGesture
venga scatenato (ad esempio, tramite la proprietà MinimumRequiredMovesForShake è possibile
specificare quante “scosse” sia necessario dare affinché lo shake venga ritenuto valido).
Tramite il tool dell’emulatore Accelerometer possiamo testare lo shake anche senza avere
un device: nell’angolo inferiore destro, infatti, c’è una sezione chiamata Recorded Data,
con un menu a tendina con un unico elemento, Shake, per l’appunto. Premendo il pulsante
Play, verrà simulato lo shake del device e, di conseguenza, vedremo le coordinate X, Y e
Z variare continuamente.

Figura 6.7 - L’opzione per simulare lo shake del device tramite l’emulatore.
La vibrazione
È un dato di fatto che tutti i device Windows Phone siano dotati di un dispositivo per la
vibrazione, che viene utilizzato quando, ad esempio, riceviamo una telefonata o un sms.
Possiamo usare questo dispositivo anche all’interno delle nostre applicazioni, in seguito,
ad esempio, a un evento oppure alla pressione di un pulsante. Spesso questo meccanismo
viene utilizzato nei giochi, per dare un feedback tattile oltre che visivo e /o sonoro
all’utente.
L’utilizzo della vibrazione è molto semplice:
VibrateController.Default.Start(TimeSpan.FromSeconds(2));

Come potete vedere, è sufficiente chiamare il metodo Start() dell’istanza statica Default della
classe VibrateController, passando come parametro la durata della vibrazione (che
nell’esempio è di due secondi).
È disponibile anche il metodo Stop(), nel caso in cui volessimo fermare la vibrazione prima
che sia trascorso il tempo che abbiamo definito.
Le gesture
L’avvento dei touch screen capacitivi e dotati di più punti di contatto ha introdotto nuovi
modi di interagire con i device: il tap (ovvero il singolo tocco) è sicuramente la modalità
di interazione più diffusa, ma ci sono anche molti altri modi per interagire con lo schermo;
pinch, hold, double tap, swipe sono tutte gesture che possiamo implementare e gestire
all’interno delle nostre applicazioni.
Le gesture base
Windows Phone è in grado di gestire in maniera nativa tre tipologie di gesture:
• Tap: il singolo tocco;
• Double tap: doppio tocco;
• Hold: pressione prolungata con il dito;
Queste tre gesture sono rese disponibili tramite tre eventi implementati a livello di
UIElement, la classe base da cui ereditano tutti i controlli XAML. Questo significa che tutti i
controlli come TextBlock, Button o TextBox implementano questi tre eventi:
• Tap;
• DoubleTap;
• Hold.
È sufficiente, perciò, sottoscrivere l’evento e gestirlo poi a nostro piacimento.
<Button Content="Gestures" DoubleTap="Button_DoubleTap" Hold="Button_Hold" />

private void Button_DoubleTap(object sender, GestureEventArgs e)


{
MessageBox.Show("Double tap");
}

private void Button_Hold(object sender, GestureEventArgs e)


{
MessageBox.Show(«Hold»);
}

In questo esempio vengono gestiti gli eventi DoubleTap e Hold di un pulsante: al verificarsi
dell’evento, viene mostrato un messaggio sullo schermo con il nome dello stesso.

Questi eventi sono stati aggiunti con l’avvento di Windows Phone 7.5. La
pressione singola di un pulsante nella versione 7.0 veniva gestita con l’evento
NOTA
Click che, per motivi di retro-compatibilità, è rimasto anche nella versione 7.5.
Nella realtà, però, gli eventi Tap e Click si equivalgono.
I manipulation event
Ogni controllo espone una serie di eventi che ci permettono di gestire l’interazione
dell’utente con lo stesso: questi eventi sono chiamati Manipulation event in quanto
vengono utilizzati tipicamente per “manipolare” il controllo da parte dell’utente.
Questi eventi sono tre:
• ManipulationStarted viene scatenato nel momento in cui l’utente appoggia uno o più dita
sullo schermo;
• ManipulationDelta
viene scatenato continuamente nel momento in cui l’utente sta
muovendo le dita sullo schermo;
• ManipulationCompleted viene scatenato nel momento in cui l’utente ha tolto le dita dallo
schermo.
Un classico esempio di gestione dell’interazione con questo tipo di eventi è il drag and
drop: nel prossimo esempio vedremo come dare la possibilità all’utente di trascinare per lo
schermo un rettangolo.
Innanzitutto, dobbiamo definire nello XAML la figura geometrica e definire una
TranslateTransform, di cui abbiamo parlato nel Capitolo 2. Le trasformazioni ci danno la
possibilità di cambiare le modalità con cui il controllo viene rappresentato a video senza
intervenire direttamente sulle sue proprietà: nello specifico, grazie a una TranslateTransform,
saremo in grado di cambiare le coordinate X e Y a partire dalle quali viene disegnato il
rettangolo, senza intervenire direttamente sulle proprietà Margin del controllo.
In più, inseririamo una TextBlock in cui mostreremo in tempo reale lo stato della
manipolazione del rettangolo.
<Rectangle Width="2⊘⊘" Height="2⊘⊘" Fill="Red" x:Name="Rectangle"
ManipulationDelta="Rectangle_ManipulationDelta" ManipulationStarted="Rectangle_
ManipulationStarted" ManipulationCompleted="Rectangle_ManipulationCompleted">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="RectangleTransform" />
</Rectangle.RenderTransform>
</Rectangle>
<TextBlock Text=»Manipulation status» x:Name=»ManipulationStatus» />

Come potete vedere, abbiamo già sottoscritto i tre eventi relativi alla manipolazione:
useremo ManipulationStarted e ManipulationCompleted solamente per aggiornare la TextBlock;
l’evento che ci servirà per gestire lo spostamento del rettangolo è ManipulationDelta.
private void Rectangle_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
RectangleTransform.X = e.CumulativeManipulation.Translation.X;
RectangleTransform.Y = e.CumulativeManipulation.Translation.Y;
ManipulationStatus.Text = "Manipulation in progress";
}

private void Rectangle_ManipulationStarted(object sender, ManipulationStartedEventArgs e)


{
ManipulationStatus.Text = "Manipulation started";
}

private void Rectangle_ManipulationCompleted(object sender,


ManipulationCompletedEventArgs e)
{
ManipulationStatus.Text = «Manipulation completed»;
}

Le informazioni di ritorno dell’evento ManipulationDelta ci restituiscono un oggetto di tipo


ManipulationDeltaEventArgs, il quale contiene, all’interno della proprietà CumulativeManipulation,
una proprietà chiamata Translation che fa al caso nostro: contiene, infatti, le coordinate X e Y
relative allo spostamento corrente. Assegnando queste coordinate a quelle della
TranslateTransform (che nel nostro esempio prende il nome di RectangleTransform), otterremo il
risultato desiderato, che possiamo testare anche con l’emulatore.
Nel momento in cui cliccheremo sul rettangolo, lo status si aggiornerà con il messaggio
Manipulation started. Dopodiché, nel momento in cui inizieremo a muovere il mouse (o
il dito), lo status si aggiornerà a Manipulation in progress e, come previsto, il rettangolo
inizierà a seguire il nostro movimento. Infine, nel momento in cui rilasceremo il mouse (o
toglieremo il dito dal touch screen), l’evento ManipulationCompleted verrà invocato,
provocando un nuovo aggiornamento dello status.
Le caratteristiche hardware del device
L’SDK ci mette a disposizione una serie di API che ci permettono di recuperare diverse
informazioni sul device su cui è in esecuzione l’applicazione: tra i dati più importanti c’è
sicuramente la quantità di memoria disponibile nel telefono, che in alcuni scenari è
indispensabile per abilitare o meno determinate funzioni.
Per recuperare questo tipo di informazioni si utilizza la classe DeviceStatus, che espone
una serie di proprietà che ora analizzeremo.
Ottenere informazioni sulla memoria
Alcune delle proprietà esposte da questa classe permettono di ottenere informazioni sulla
memoria utilizzata dall’applicazione e si contraddistinguono per il prefisso Application.
Possiamo, perciò, scoprire quanta memoria l’applicazione sta consumando in quel
momento (ApplicationCurrentMemoryUsage), quanta ne ha consumata come valore massimo
durante l’utilizzo (ApplicationPeakMemoryUsage) e quanta ne può utilizzare al massimo
(ApplicationMemoryUsageLimit).
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval=new TimeSpan(⊘, ⊘, 1);
timer.Start();
}

void timer_Tick(object sender, EventArgs e)


{
txtMemoryInUse.Text = DeviceStatus.ApplicationCurrentMemoryUsage.ToString();
txtMaximumMemory.Text = DeviceStatus.ApplicationPeakMemoryUsage.ToString();
}
}

In questo esempio sfruttiamo un timer (rappresentato dalla classe DispatcherTimer) per


aggiornare ogni secondo due caselle di testo con la memoria correntemente in uso e la
quantità massima utilizzata nel corso dell’utilizzo dell’applicazione.
Ottenere informazioni sul device
La classe DeviceStatus espone una serie di proprietà (contraddistinte dal prefisso Device) che
permettono di recuperare diverse informazioni sul device, come il nome (DeviceName), il
produttore (DeviceManufacturer) o la memoria disponibile (DeviceTotalMemory).
string deviceFirmwareVersion = DeviceStatus.DeviceFirmwareVersion;
string deviceHardwareVersion = DeviceStatus.DeviceHardwareVersion;
string deviceManufacturer = DeviceStatus.DeviceManufacturer;
string deviceName = DeviceStatus.DeviceName;
long deviceTotalMemory = DeviceStatus.DeviceTotalMemory;
Interagire con la tastiera hardware
Tra le specifiche minime di un device Windows Phone è prevista la compatibilità con le
tastiere hardware, che possono essere integrate nel telefono e costituire, così, un secondo
metodo di input oltre alla tastiera virtuale.
In realtà, i produttori che hanno optato per questa strada sono pochissimi: i device dotati di
una tastiera hardware sono, infatti, piuttosto rari e tutti appartenenti alla prima generazione
(ad esempio, il Dell Venue Pro).
L’SDK offre comunque supporto a questi tipi di dispositivi nelle applicazioni, esponendo
due proprietà della classe DeviceStatus che ci permettono di capire se è presente una tastiera
hardware (IsKeyboardPresent) e se è stata attivata (IsKeyboardDeployed). Questo perché, dato che
tutti i device Windows Phone sono dotati di uno schermo ampio, la tastiera hardware è
sempre di tipo a scomparsa e può essere attivata solo quando serve.
In più, è disponibile l’evento KeyboardDeployedChanged che viene scatenato ogni qualvolta la
tastiera hardware viene attivata o disattivata.
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
DeviceStatus.KeyboardDeployedChanged += new EventHandler(DeviceStatus_
KeyboardDeployedChanged);
}

void DeviceStatus_KeyboardDeployedChanged(object sender, EventArgs e)


{
string message = DeviceStatus.IsKeyboardDeployed ? "Tastiera attiva" :
"Tastiera non attiva";
MessageBox.Show(message);
}
}

Nell’esempio ci siamo sottoscritti a questo evento per visualizzare un messaggio a video


con lo stato corrente della tastiera.
L’alimentazione
Un’ultima informazione che ci permette di scoprire questa classe è l’alimentazione:
tramite la proprietà PowerSource possiamo scoprire se il telefono è alimentato dalla batteria o
si trova in ricarica.
Possiamo anche sottoscrivere l’evento PowerStatusChanged per essere notificati in tempo reale
sul cambiamento del tipo di alimentazione: questo può essere utile se, ad esempio, la
nostra applicazione prevede delle operazioni che vogliamo eseguire solamente quando il
telefono è in ricarica (ad esempio, perché possono essere molto lunghe e consumare molta
batteria).
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
DeviceStatus.PowerSourceChanged += new EventHandler(DeviceStatus_
PowerSourceChanged);
}

void DeviceStatus_PowerSourceChanged(object sender, EventArgs e)


{
string message = DeviceStatus.PowerSource == PowerSource.Battery ?
«Batteria» : «Alimentazione di rete»;
MessageBox.Show(message);
}
}

In questo esempio mostriamo un messaggio a video ogni qualvolta il tipo di alimentazione


del device cambia.
Windows Phone 8 ha introdotto, inoltre, delle nuove API per conoscere lo stato di carica
della batteria: la classe di riferimento si chiama Battery e fa parte del namespace
Windows.Phone.Devices.Power.
Ecco un esempio di utilizzo:
int remainingCharge = Battery.GetDefaullt().RemainingChargePercent;
TimeSpan remainingTime = Battery.GetDefaullt().RemainingDischargeTime();

Una volta ottenuto un riferimento alla batteria tramite il metodo GetDefault(), siamo in grado
di recuperare due informazioni: la percentuale di carica rimanente, tramite la proprietà
RemainingChargePercent il cui tipo è intero, e il tempo di ricarica rimanente, tramite la proprietà
RemainingDischargeTime che, invece, è di tipo TimeSpan.

È a disposizione anche un evento che è possibile sottoscrivere e che viene invocato ogni
qualvolta la percentuale di caricamento cambia: si chiama RemainingChargePercentChanged.
La classe DeviceExtendedProperties
L’utilizzo della classe DeviceExtendedProperties era l’unico meccanismo disponibile in
Windows Phone 7.0 per recuperare le informazioni che abbiamo appena visto. L’utilizzo
di questa classe è diventato deprecato grazie all’avvento della classe DeviceStatus in
Windows Phone 7.5, molto più semplice da utilizzare.
La classe DeviceExtendedProperties non espone, infatti, una serie di proprietà come la classe
DeviceStatus, ma richiede di sfruttare il metodo GetValue() passando una chiave ben precisa per
ottenere il tipo di informazione richiesta. Si tratta sicuramente di un metodo molto meno
intuitivo, dato che richiede allo sviluppatore di ricordarsi a memoria le varie chiavi.
Esistono, però, due informazioni importanti che non sono state riportate sulla classe
DeviceStatus, e che sono perciò recuperabili solamente con la classe DeviceExtendedProperties.

L’id univoco del device


A ogni device viene assegnata una stringa che lo identifica univocamente: teoricamente
non possono esistere due dispositivi Windows Phone sul mercato con lo stesso id. Questo
id può essere utile in alcune situazioni in cui dobbiamo salvare dei dati e associarli a un
identificativo univoco: occorre, tuttavia, tenere presente che questo id è legato al device e
non all’utente. Se, quindi, ipoteticamente, decidiamo di utilizzare questo id al posto di
un’eventuale registrazione per tracciare l’utente, nel momento in cui questo cambierà
telefono, perderemo tali informazioni. Per questi scenari è consigliato utilizzare
l’anonymous id, che tratteremo nel paragrafo successivo.
La chiave per recuperare questa informazione è DeviceUniqueId, che restituisce un array di
byte. Ecco, perciò, come possiamo trasformare questo valore in una stringa
“comprensibile”:
byte[] uniqueId = (byte[]) DeviceExtendedProperties.GetValue("DeviceUniqueId");
txtDeviceUniqueId.Text = BitConverter.ToUInt32(uniqueId, ⊘).ToString();

La memoria massima utilizzabile da un’applicazione


A partire da Windows Phone 7.5 Refresh è stata introdotta una nuova informazione,
identificata dalla chiave ApplicationWorkingSetLimit, che restituisce la massima quantità di
memoria utilizzabile da un’applicazione.
Per ottenere questa informazione è consigliato l’utilizzo del metodo TryGetValue() della
classe DeviceExtendedProperties. Questo metodo vi permette di verificare preventivamente
l’esistenza di una proprietà con una determinata chiave: in caso affermativo, viene
restituito il valore, altrimenti otterrete null.
object memory;
DeviceExtendedProperties.TryGetValue("ApplicationWorkingSetLimit", out memory);
if (memory == null || (long)memory > 94371840)
{
MessageBox.Show(«Device con 512 MB di RAM»);
}
else
{
MessageBox.Show(«Device con 256 MB di RAM»);
}

In questo esempio creiamo una proprietà di nome memory, che viene passata al metodo
TryGetValue() con il prefisso out: questo significa che, se il metodo TryGetValue() va a buon
fine, il risultato sarà memorizzato in tale variabile.
La classe UserExtendedProperties
Anche se non è strettamente legata all’interazione con il telefono, credo che questa sia la
sede più appropriata per parlare della classe UserExtendedProperties, dato che funziona in
maniera analoga alla classe DeviceExtendedProperties che abbiamo visto. Il funzionamento è lo
stesso: possiamo utilizzare il metodo GetValue() (o la sua variante TryGetValue()) specificando
una data chiave per ottenere l’informazione richiesta.
In realtà, la classe UserExtendedProperties supporta solamente una chiave, che è però molto
importante: l’Anonymous Id, che rappresenta un id che permette di identificare
univocamente l’account Microsoft con cui è registrato l’utente. Si chiama anonymous
perché non viola la privacy dell’utente, dato che non fornisce allo sviluppatore alcuna
informazione personale, come l’indirizzo mail associato all’account o la password: è
semplicemente una stringa composta da numeri e lettere, che è univoca per quello
specifico account.
Si tratta di un’informazione molto utile perché ci permette di identificare in maniera
univoca un utente all’interno della nostra applicazione, anche nel caso in cui questi cambi
device (è plausibile, infatti, che il nuovo telefono venga registrato con lo stesso account
Microsoft).
Gli scenari d’uso sono molteplici: classifiche online, notifiche push, gestione di
abbonamenti ecc.
È importante sottolineare come ci sia stato un cambiamento tra Windows Phone 7 e
Windows Phone 8: se nella versione precedente del sistema operativo tale informazione
era recuperabile tramite la proprietà ANID, in Windows Phone 8 essa è stata sostituita da
ANID2.
Di conseguenza, è necessario tenere conto di questo cambiamento se state portando la
vostra applicazione da Windows Phone 7 a Windows Phone 8 e avete la necessità di
continuare a tracciare l’utente in forma anonima.
string anonymousId= UserExtendedProperties.GetValue("ANID2");
Gestire il blocco automatico del dispositivo
Windows Phone, così come tutti gli altri sistemi operativi mobile, prevede la possibilità di
bloccare in automatico il dispositivo dopo un certo intervallo di tempo di inutilizzo.
Questa funzionalità viene implementata per incrementare la durata della batteria di un
telefono: dato che lo schermo è una delle componenti che consumano maggiormente la
batteria, in questo modo si garantisce che esso non rimanga attivo anche quando non ce
n’è bisogno.
In alcuni casi, però, questo comportamento può creare problemi con le nostre applicazioni:
pensiamo, ad esempio, a un player video o a un gioco che fa uso solamente dei sensori di
movimento. In casi come questi, il fatto che non stiamo interagendo con il touch screen
non significa che non stiamo utilizzando l’applicazione e che il telefono sia quindi
inattivo.
Windows Phone offre allo sviluppatore due possibilità di gestire il blocco del dispositivo:
disabilitandolo o facendo sì che l’applicazione sia in grado di continuare a lavorare anche
durante il blocco.

Disabilitare il blocco automatico


Disabilitare il blocco automatico del telefono è molto semplice, basta sfruttare la proprietà
UserIdleDetectionMode esposta dalla classe PhoneApplicationService che abbiamo già visto in
precedenza parlando di tombstoning.
Questa proprietà può assumere due valori:
• IdleDetectionMode.Disabled per disabilitare il blocco automatico;
• IdleDetectionMode.Enabled per abilitarlo nuovamente.
Ecco un esempio di codice:
PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;

È importante usare questa opzione con cautela e solo se strettamente necessario: in questa
modalità, infatti, il telefono consuma molta batteria.

Disabilitare la sospensione dell’applicazione con il blocco del device


Rispetto alla prima versione del sistema operativo, l’utilizzo di questa feature è stato
ridimensionato, grazie all’introduzione di una nuova serie di API per il multitasking. In
Windows Phone 7.0, infatti, questo era l’unico meccanismo per simulare una sorta di
background e far sì che l’applicazione fosse in grado di continuare a eseguire operazioni
anche quando non era in uso.
In realtà, si trattava di una simulazione: l’applicazione doveva comunque rimanere aperta,
anche se il telefono poteva essere bloccato.
Allo stato attuale, perciò, l’utilizzo di questa API è consentito solamente quando alcune
delle funzioni principali dell’applicazione devono continuare a funzionare anche a
telefono bloccato, altrimenti l’esperienza d’uso dell’utente risulterebbe compromessa (ad
esempio, un timer sportivo).
Il meccanismo per abilitare o disabilitare la sospensione è analogo a quello per disabilitare
il blocco automatico, solo che la proprietà su cui agire si chiama ApplicationIdleDetectionMode.
Anche in questo caso può assumere i valori IdleDetectionMode.Disabled per disabilitare la
sospensione o IdleDetectionMode.Enabled per riabilitarla.
C’è, però, una differenza importantissima: non è possibile disabilitare e poi riabilitare la
sospensione durante la stessa sessione di utilizzo dell’applicazione, pena il sollevamento
di un’eccezione. In questi casi occorre, perciò, intercettare l’eccezione e mostrare un
messaggio all’utente con il quale venga notificato che deve riavviare l’applicazione prima
di procedere. Ecco un esempio di codice per la disattivazione:
PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;

Questo invece è relativo alla riattivazione:


private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
PhoneApplicationService.Current.ApplicationIdleDetectionMode =
IdleDetectionMode.Enabled;
}
catch
{
MessageBox.Show("Devi riavviare l'applicazione per riabilitare la
sospensione");
}
}

Una volta che la sospensione è stata disabilitata, abbiamo accesso a due eventi che
vengono scatenati nel momento in cui il telefono viene bloccato o sbloccato. Questi eventi
servono per gestire la disattivazione e la riattivazione di tutte le risorse che non sono
necessarie e che non possono essere utilizzate quando lo schermo è bloccato, come i timer,
i sensori di movimento o la lettura/scrittura di dati nell’Isolated Storage. Entrambi gli
eventi vengono resi disponibili dalla classe PhoneApplicationFrame, che, come abbiamo
imparato parlando di navigazione, è unica per ogni applicazione. Per accedere all’istanza
corrente dobbiamo passare dalla classe App, che si occupa di inizializzare il frame
all’avvio:
PhoneApplicationFrame appFrame = (Application.Current as App).RootFrame;

A questo punto possiamo sottoscrivere gli eventi Obscured (quando il telefono viene
bloccato) e Unobscured (quando il telefono viene sbloccato).
public MainPage()
{
InitializeComponent();
PhoneApplicationFrame appFrame = (Application.Current as App).RootFrame;
appFrame.Obscured += new EventHandler<ObscuredEventArgs>(MainPage_Obscured);
appFrame.Unobscured += new EventHandler(MainPage_Unobscured);
}

void MainPage_Unobscured(object sender, EventArgs e)


{
//ricollega le risorse disattivate in precedenza
}

void MainPage_Obscured(object sender, ObscuredEventArgs e)


{
//scollega le risorse non necessarie
}

Al centro l’utente!
Ricordatevi che il motto di Windows Phone è “al centro l’utente”: entrambe queste
modalità devono poter essere gestite direttamente dall’utente, in modo che sia lui a
scegliere se vuole disabilitare il blocco automatico o la sospensione dell’applicazione. Tali
opzioni possono essere inserite in una pagina di gestione delle impostazioni, oppure, se si
tratta di una scelta determinante per offrire la miglior esperienza possibile
dell’applicazione, chiedere all’utente una conferma al primo avvio.
Come regola generale, entrambe le modalità UserIdleDetectionMode e ApplicationIdleDetectionMode
non devono mai essere disabilitate senza previo consenso dell’utente.

Un esempio di utilizzo della modalità ApplicationIdleDetectionMode: un


timer
Vediamo ora un esempio concreto di come utilizzare la disattivazione della sospensione al
blocco del telefono, realizzando un timer in grado di contare anche quando il telefono è in
blocco. Per prima cosa introduciamo il DispatcherTimer, una classe che il runtime ci mette a
disposizione per gestire un timer all’interno di un’applicazione. Il suo utilizzo è molto
semplice: una volta creata una nuova istanza, dobbiamo agire su tre fronti:
1. valorizzare la proprietà Interval, di tipo TimeSpan, che permette di specificare l’intervallo
di tempo trascorso il quale il timer scatta;
2. sottoscrivere l’evento Tick, che viene scatenato nel momento in cui è trascorso
l’intervallo di tempo impostato;
3. chiamare il metodo Start, che avvia il timer.
Ecco un esempio di timer impostato per invocare l’evento Tick ogni secondo:
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = new TimeSpan(⊘, ⊘, 1);
timer.Start();

Ora definiamo una variabile a livello di classe che utilizzeremo come contatore, che
inizializziamo assieme al timer nel costruttore della pagina:
private int cont;

// Constructor
public MainPage()
{
InitializeComponent();
cont = ⊘;
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = new TimeSpan(⊘, ⊘, 1);
timer.Start();
}
Ora andiamo a scrivere il codice che verrà invocato nell’evento Tick: quello che facciamo è
solamente incrementare il valore della variabile cont e visualizzare il valore a video in una
TextBlock.
void timer_Tick(object sender, EventArgs e)
{
cont++;
txtTimer.Text = cont.ToString();
}

Infine, inseriamo due pulsanti nello XAML, uno per disabilitare la sospensione
dell’applicazione e uno per riabilitarla.
private void btnIdle_Click(object sender, RoutedEventArgs e)
{
PhoneApplicationService.Current.ApplicationIdleDetectionMode =
IdleDetectionMode.Disabled;
}

private void Button_Click(object sender, RoutedEventArgs e)


{
try
{
PhoneApplicationService.Current.ApplicationIdleDetectionMode =
IdleDetectionMode.Enabled;
}
catch
{
MessageBox.Show(«Devi riavviare l'applicazione per riabilitare la
sospensione»);
}
}

Per testare l’applicazione e simulare il blocco del telefono possiamo utilizzare la


Simulation Dashboard di Visual Studio, che abbiamo trattato nel Capitolo 1.
Una volta che l’abbiamo lanciata, noteremo il timer avviarsi: ora blocchiamo il telefono
senza fare nulla. Aspettiamo qualche secondo e sblocchiamolo: il timer riprenderà a
contare, ma partirà dallo stesso valore che abbiamo lasciato quando lo abbiamo bloccato.
Ora facciamo un secondo test, premendo questa volta il pulsante con il quale abbiamo
impostato la ApplicationIdleDetectionMode a IdleDetectionMode.Disabled. Blocchiamo nuovamente il
dispositivo e, dopo qualche secondo, sblocchiamolo: questa volta il contatore mostrerà un
valore maggiore di quello che abbiamo lasciato, dato che l’applicazione non è stata
sospesa e il DispatcherTimer è stato in grado di proseguire la sua azione.
In conclusione
In questo capitolo abbiamo imparato come interagire nella nostra applicazione con
l’hardware che ci mette a disposizione il device: abbiamo visto come utilizzare il sensore
GPS per sviluppare applicazioni che fanno uso dei servizi di geolocalizzazione e come
usare il controllo Nokia Maps per mostrare la posizione dell’utente. Abbiamo
approfondito le novità introdotte per quanto riguarda la cartografia grazie ai servizi Nokia:
conversione di coordinate geografiche, calcolo del percorso ecc.
Abbiamo proseguito l’approfondimento con i sensori di movimento, che ci permettono di
rilevare la posizione del device nello spazio. Abbiamo visto come utilizzare
accelerometro, bussola e giroscopio, come utilizzare l’Orientation Sensor (in grado di
offrire un’interpolazione dei dati ricevuti dai singoli sensori) e come determinare lo
“shake” del device grazie a una libreria messa a disposizione dal team di Windows Phone.
Abbiamo trattato brevemente anche il sensore di vibrazione, che possiamo utilizzare nelle
nostre applicazioni per dare un feedback “tattile” all’utente.
L’argomento successivo è stato il touch screen e il modo in cui è possibile interagire con
esso all’interno delle nostre applicazioni, sia con le gesture base (come il tap o il double
tap), sia con delle gesture più complesse, che ci permettono di manipolare i controlli sullo
schermo.
Infine, abbiamo concluso il capitolo imparando a recuperare alcune importanti
informazioni sul dispositivo, come la quantità di memoria disponibile o il modello e il
produttore, e su come gestire il blocco automatico.
Interagire con il sistema operativo

Le applicazioni Windows Phone girano in una specie di sandbox, cioè un


ambiente protetto che impedisce loro di danneggiare (accidentalmente o
volutamente) il sistema operativo o le altre applicazioni. L’Isolated
Storage (che sarà trattato nel Capitolo 8) ne è un esempio: ogni
applicazione ha uno spazio di memoria riservato, dove è possibile salvare i
propri dati, e non ha modo di accedere allo spazio di un’altra
applicazione.
Può presentarsi, tuttavia, la necessità di interagire con le funzionalità del sistema operativo
o con i dati dell’utente: contatti della rubrica, immagini della galleria fotografica,
appuntamenti del calendario sono solo alcuni esempi a questo riguardo.
In alcuni casi, invece, si vogliono eseguire operazioni che, tipicamente, sono a carico di
un’applicazione nativa, come inviare una e-mail o un sms.
In questo capitolo vedremo come interagire con il sistema operativo, per sfruttarne le
funzioni, per utilizzare i dati dell’utente, ma anche per integrarsi con gli hub, per
riprodurre musica e video, per navigare su Internet o per usare le funzionalità vocali.
I launcher e i chooser
I launcher e i chooser rappresentano una serie di classi che abbiamo a disposizione per
interagire con il sistema operativo nelle nostre applicazioni. Tutti i launcher e i chooser
condividono la medesima struttura e il medesimo meccanismo di funzionamento e fanno
parte del namespace Microsoft.Phone.Tasks. Vediamo in dettaglio, perciò, quali sono le
differenze.
I launcher sono utilizzati per demandare un’operazione al sistema operativo (come inviare
un sms o una e-mail) e non si aspettano alcuna informazione di ritorno. Questo significa
che non restituiscono alcun dato da riutilizzare all’interno della nostra applicazione.
I chooser sono utilizzati per importare dei dati all’interno della nostra applicazione (come
contatti dalla rubrica o una foto dalla libreria) e, per questo motivo, restituiscono il dato
richiesto.
Ogni launcher e chooser è rappresentato da una classe specifica, ognuna delle quali è
strutturata nello stesso modo. Utilizzare un launcher significa:
• creare una nuova istanza della classe;
• definire alcune proprietà che rappresentano le informazioni in ingresso necessarie al
launcher per eseguire il suo compito (ad esempio, il numero di telefono e il testo
dell’sms da inviare);
• chiamare il metodo Show() per eseguirlo.
Utilizzare un chooser significa, invece:
• creare una nuova istanza della classe;
• se necessario, definire alcune proprietà che rappresentano le informazioni necessarie
al chooser per eseguire il suo compito;
• sottoscrivere l’evento Completed, che viene scatenato nel momento in cui il chooser ha
terminato il suo compito e il controllo viene restituito alla nostra applicazione.
Questo evento restituisce, tra i parametri di ritorno, un oggetto con i dati recuperati
dall’applicazione nativa;
• chiamare il metodo Show() per eseguirlo.
È importante sottolineare come i launcher e i chooser demandino effettivamente il compito
all’applicazione nativa: questo significa che, quando un launcher o un chooser è in uso,
non vi trovate più all’interno della vostra applicazione, ma questa viene sospesa,
esattamente come se l’utente avesse premuto il pulsante Start del device o avesse fatto tap
su una notifica push.
In Windows Phone 7.0 questa situazione poteva causare qualche problema, in quanto, se
ricordate quanto spiegato nel Capitolo 4, le applicazioni venivano sempre mandate in
tombstone. Era perciò importante gestire il salvataggio e il recupero dei dati a ogni utilizzo
di un launcher e di un chooser.
A partire da Windows Phone 7.5 in poi questa casistica è molto più rara, grazie alla
presenza dello stato dormant, in cui l’applicazione è sospesa ma i dati vengono preservati.
Dato che la durata di un launcher o di un chooser è tipicamente molto breve, è molto
difficile che la vostra applicazione passi dallo stato dormant allo stato tombstoned durante
questo intervallo di tempo.
La caratteristica principale dei launcher e dei chooser è che non sono in grado di agire
senza l’intervento diretto dell’utente: non possiamo eseguire operazioni o importare dati
senza il suo esplicito consenso. Questo significa che, ad esempio, il launcher per inviare e-
mail si limita ad aprire il client di posta con una e-mail già preimpostata: sarà l’utente a
dover confermare l’invio; oppure il chooser per importare foto richiederà all’utente
esplicitamente quali sono le immagini che vuole importare.
Vediamo ora una panoramica di tutti i launcher e chooser disponibili in Windows Phone,
con qualche esempio su come utilizzarli.
I launcher
Maps Direction Task
Questo launcher permette di mostrare su una mappa dell’applicazione nativa Mappe un
percorso, con le relative indicazioni stradali per raggiungere il punto di arrivo.

Le proprietà da valorizzare
Il launcher supporta le proprietà Start e End, entrambe di tipo LabeledMapLocation. Tali oggetti,
quando vengono istanziati, accettano due parametri in ingresso: un indirizzo (sotto forma
di stringa, ad esempio “Piazza Duomo, Milano”) e delle coordinate, sotto forma di oggetto
di tipo GeoCoordinate. Il secondo parametro è opzionale: passando valore null, sarà fatta una
ricerca in base all’indirizzo.
Volendo, è possibile non valorizzare la proprietà Start: in questo caso, come punto di
partenza sarà utilizzata la posizione corrente dell’utente.

Esempio di utilizzo
MapsDirectionsTask task = new MapsDirectionsTask();
task.End = new LabeledMapLocation(“Piazza Duomo, Milano”, null);
task.Show();

Esiste un altro launcher, chiamato BingMapsDirectionTask, che fa parte di quelli


introdotti in Windows Phone 7.5 e che è rimasto tra le API di Windows Phone
NOTA
8 solo per retrocompatibilità. In realtà, ha lo stesso scopo del launcher
MapsDirectionTask: aprire l’applicazione Mappe per mostrare un percorso.

Maps Task
Questo launcher permette di mostrare una posizione specifica su una mappa
dell’applicazione nativa.

Le proprietà da valorizzare
Il launcher supporta due modalità di funzionamento:
1. se conosciamo le coordinate geografiche della posizione, possiamo assegnarle alla
proprietà Center, di tipo GeoCoordinate;
2. se conosciamo l’indirizzo del luogo, possiamo assegnarlo alla proprietà SearchTerm, di
tipo stringa.
Possiamo, poi, selezionare un livello di zoom predefinito, assegnando un valore compreso
tra 1 e 19 alla proprietà ZoomLevel.

Esempio di utilizzo
MapsTask task = new MapsTask();
task.SearchTerm = “Piazza Duomo, Milano”;
task.ZoomLevel = 10;
task.Show();

Anche in questo caso esiste un launcher equivalente mantenuto solamente per


NOTA
retrocompatibilità, chiamato BingMapsTask.

Map Downloader Task


Questo launcher è stato introdotto in Windows Phone 8 e consente di accedere
velocemente alla sezione dedicata al download delle mappe offline.
Può risultare utile nelle applicazioni che fanno uso del controllo mappe.

Le proprietà da valorizzare
Non è disponibile alcuna proprietà per personalizzare il launcher.

Esempio di utilizzo
MapDownloaderTask task = new MapDownloaderTask();
task.Show();

Map Updater Task


Anche questo launcher fa parte delle novità di Windows Phone 8 e consente all’utente di
controllare la disponibilità di aggiornamenti per le mappe offline da lui scaricate.

Le proprietà da valorizzare
Non è disponibile alcuna proprietà per personalizzare il launcher.

Esempio di utilizzo
MapUpdaterTask task = new MapUpdaterTask();
task.Show();

Connection Settings Task


Questo launcher permette all’utente di accedere direttamente alla sezione Impostazioni, in
una delle varie schermate che permettono di abilitare o disabilitare le varie connessioni di
cui è dotato il telefono.
Questo task viene utilizzato da applicazioni come Connection Tiles per dare la possibilità
all’utente di aggiungere delle tile in home che fungono da scorciatoia per le connessioni
usate più frequentemente, come il WiFi o il Bluetooth.

Le proprietà da valorizzare
L’unica proprietà disponibile è ConnectionSettingsType, che accetta un valore dell’enumeratore
dal medesimo nome e specifica quale tipo di connessione vogliamo far configurare
all’utente.
I valori supportati sono:
• ConnectionSettingsType.WiFi
per accedere alla schermata di configurazione della
connessione alle reti wireless;
• ConnectionSettingsType.Cellular per accedere alla schermata di configurazione della
connessione della rete dati;
• ConnectionSettingsType.AirplaneMode per accedere alla schermata di configurazione della
modalità aereo, che permette di disattivare in un colpo solo tutte le connessioni del
telefono (utile, appunto, quando si viaggia in aereo, dove l’utilizzo di connessioni
radio non è consentito);
• ConnectionSettingsType.Bluetooth per accedere alla schermata di configurazione della
connessione Bluetooth.

Esempio di utilizzo
ConnectionSettingsTask task = new ConnectionSettingsTask();
task.ConnectionSettingsType = ConnectionSettingsType.WiFi;
task.Show();

Email Compose Task


Questo launcher permette di inviare e-mail dalla nostra applicazione. Un utilizzo tipico è
nella pagina di contatto, che offre un modello già pronto di e-mail per consentire all’utente
di contattare lo sviluppatore.

Le proprietà da valorizzare
Le proprietà disponibili servono a specificare le tipiche informazioni necessarie per
inviare una e-mail, ovvero:
• To per specificare l’indirizzo del destinatario;
• Cc per specificare un eventuale indirizzo che riceverà la e-mail in copia;
• Subject per specificare l’oggetto della e-mail;
• Body per specificare il testo della e-mail.

Esempio di utilizzo
EmailComposeTask mailTask = new EmailComposeTask();
mailTask.To = “mail@domain.com”;
mailTask.Cc = “mail2@domain.com”;
mailTask.Subject = “Subject”;
mailTask.Body = “Body”;
mailTask.Show();

Marketplace Detail Task


Questo launcher viene utilizzato per aprire lo Store sulla pagina di dettaglio di una
specifica applicazione (o dell’applicazione stessa). Può essere utile, ad esempio, nella
versione trial di un’applicazione per rimandare l’utente alla pagina di acquisto della
versione completa.

Le proprietà da valorizzare
Questo launcher supporta due proprietà, benché in realtà possa funzionare tranquillamente
anche senza specificare alcuna opzione:
• ContentIdentifier è la proprietà che specifica l’id univoco dell’applicazione di cui
vogliamo mostrare la pagina di dettaglio. Se questa proprietà non viene valorizzata,
verrà mostrata la pagina di dettaglio dell’applicazione stessa;
• ContentType è la proprietà che specifica la categoria del Marketplace all’interno della
quale vogliamo fare la ricerca. In realtà questa proprietà è inutilizzata, in quanto
l’unico valore accettato è MarketplaceContentType.Applications.

Esempio di utilizzo
MarketplaceDetailTask task = new MarketplaceDetailTask();
task.ContentIdentifier = “c14e93aa-27d7-df11-a844-⊘237de2db9e”;
task.Show();

Marketplace Hub Task


Questo launcher permette di aprire il Marketplace, eventualmente su una sezione specifica
(ad esempio, la sezione Musica).

Le proprietà da valorizzare
L’unica proprietà disponibile è ContentType, che accetta uno dei due valori dell’enumeratore
MarketplaceContentType:

• MarketplaceContentType.Applications per accedere alla sezione delle applicazioni;


• MarketplaceContentType.Music per accedere alla sezione musicale.

Esempio di utilizzo
MarketplaceHubTask task = new MarketplaceHubTask();
task.ContentType = MarketplaceContentType.Applications;
task.Show();

Marketplace Review Task


Questo launcher permette di aprire la pagina del Marketplace in cui l’utente può votare e
recensire l’applicazione. Tipicamente questo launcher viene utilizzato da parte degli
sviluppatori per invitare gli utenti a lasciare un feedback sulla qualità dell’applicazione.

Le proprietà da valorizzare
Questo launcher non richiede alcuna informazione, in quanto, al contrario del Marketplace
Detail Task, può essere utilizzato solamente per votare e recensire l’applicazione stessa.
Esempio di utilizzo
MarketplaceReviewTask task = new MarketplaceReviewTask();
task.Show();

Marketplace Search Task


Questo launcher permette di lanciare una ricerca per una specifica parola chiave sul
Marketplace.

Le proprietà da valorizzare
Questo launcher supporta due proprietà per determinare i parametri della ricerca:
• ContentType accetta uno dei due valori dell’enumeratore MarketplaceContentType e permette
di specificare se la ricerca deve essere effettuata tra le applicazioni
(MarketplaceContentType.Applications) o nel catalogo musicale (MarketplaceContentType.Music). La
ricerca predefinita è tra le applicazioni: in tal caso, è possibile anche non specificare
il valore della proprietà ContentType;
• SearchTerms è la parola chiave per cui si vuole effettuare la ricerca.

Esempio di utilizzo
MarketplaceSearchTask task = new MarketplaceSearchTask();
task.ContentType = MarketplaceContentType.Music;
task.SearchTerms = “Coldplay”;
searchTask.Show();

Media Player Launcher


Questo launcher permette di riprodurre un elemento multimediale (audio o video).
L’elemento non sarà riprodotto all’interno dell’applicazione, ma verrà utilizzato un player
esterno, che può essere personalizzato.

Le proprietà da valorizzare
Sono diverse le proprietà su cui possiamo intervenire, sia per definire l’elemento da
riprodurre, sia per configurare il player e il suo comportamento:
• Media rappresenta, sotto forma di Uri, il percorso dell’elemento multimediale da
riprodurre;
• Location può assumere uno dei due valori dell’enumeratore MediaTypeLocation:
MediaTypeLocation.Data significa che il valore della proprietà Media si riferisce a un file
memorizzato nell’Isolated Storage dell’applicazione; MediaTypeLocation. Install significa,
invece, che il file fa parte del progetto di Visual Studio;
• Controls rappresenta i controlli che saranno abilitati nel player e accetta uno o più valori
dell’enumeratore MediaPlaybackControls. Si può scegliere, semplicemente, di mostrarli
tutti (MediaPlayerbackControls.All) oppure di concatenare uno o più dei valori disponibili
mediante l’operatore logico | (ad esempio, MediaPlaybackControls.Pause |
MediaPlayBackControls. Stop);

• Orientation indica se il player deve essere utilizzato in modalità portrait


(MediaPlayerOrientation.Portrait) o landscape (MediaPlayerOrientation.Landscape).

Esempio di utilizzo
MediaPlayerLauncher task = new MediaPlayerLauncher();
task.Media = new Uri(“Media/Taal_Violin.mp3”, UriKind.Relative);
task.Location = MediaLocationType.Install;
task.Controls = MediaPlaybackControls.Pause | MediaPlaybackControls.Stop;
task.Orientation= MediaPlayerOrientation.Landscape;
task.Show();

Phone Call Task


Questo launcher permette di effettuare una telefonata dalla nostra applicazione. Una volta
chiamato il metodo Show(), prima di effettuare la chiamata, verrà mostrato un messaggio
all’utente nel quale sarà richiesta una conferma.

Le proprietà da valorizzare
Sono due i parametri che ci permettono di personalizzare questo launcher:
• DisplayName rappresenta il nome della persona da contattare;
• PhoneNumber rappresenta il numero di telefono da chiamare.
Entrambe queste informazioni vengono mostrate nel messaggio di conferma che verrà
visualizzato all’utente.

Esempio di utilizzo
PhoneCallTask task = new PhoneCallTask();
task.DisplayName = “Mario Rossi”;
task.PhoneNumber = “02123456789”;
task.Show();

Search Task
Questo launcher permette di lanciare una ricerca sul web per una determinata parola
chiave tramite Bing.

Le proprietà da valorizzare
L’unica proprietà disponibile è SearchQuery, che rappresenta la parola chiave da ricercare sul
web.

Esempio di utilizzo
SearchTask task = new SearchTask();
task.SearchQuery = “Windows Phone”;
task.Show();
Share Link Task
Una delle novità più gradite di Windows Phone 7.5 è stata l’espansione dei social network
supportati in maniera nativa dalla piattaforma. In concomitanza con questa espansione, il
team ha deciso di dare la possibilità agli sviluppatori di appoggiarsi all’integrazione nativa
per condividere contenuti dall’applicazione.
Quale differenza c’è tra utilizzare i servizi integrati e implementare invece in modo nativo
il supporto ai principali social network tramite le API da essi esposte?
Dal punto di vista dell’utente finale, nessuna: in entrambi i casi l’utente sarà in grado di
condividere contenuti su Facebook, Twitter ecc.
Dal punto di vista dello sviluppatore, la differenza più evidente è la ”firma” con cui
vengono pubblicati i messaggi: nel caso di utilizzo delle API native, è possibile
personalizzare la firma (ad esempio, ”Questo è il mio primo tweet” via La mia
applicazione per Windows Phone). Utilizzando l’integrazione nativa, invece, i post
avranno la firma via Windows Phone, come se fossero stati pubblicati direttamente
dall’utente stesso e non dall’applicazione.
In più, l’utilizzo delle API native vi dà un maggiore controllo (ad esempio, potete revocare
i permessi in qualunque momento e ”bloccare” la condivisione dall’applicazione a tutti gli
utenti) e accesso a statistiche dettagliate.
Se quello di cui avete bisogno, però, è semplicemente condividere informazioni e non
siete interessati ai ”contro” sopra riportati, l’utilizzo di questo launcher è sicuramente la
soluzione più efficiente e veloce da implementare.
Questo è il primo dei tre launcher dedicati alla condivisione: lo Share Link Task serve per
condividere un URL sui social network configurati sul telefono.

Le proprietà da valorizzare
Sono tre le proprietà che permettono di personalizzare la condivisione:
• Title è il titolo o il nome del sito;
• LinkUri, di tipo Uri, è l’indirizzo del sito da condividere;
• Message è un messaggio opzionale che può accompagnare il post.
Nella schermata del launcher l’utente avrà la possibilità di cambiare il messaggio (o di
scriverlo; non è, infatti, obbligatorio valorizzare la proprietà Message) e di selezionare, tra i
social network configurati sul suo telefono, quelli su cui condividere l’URL.

Esempio di utilizzo
ShareLinkTask task = new ShareLinkTask();
task.Title = “Il blog di Matteo Pagani”;
task.Message = “Tutorial su Windows Phone”;
task.LinkUri = new Uri(“http://www.qmatteoq.com”);
task.Show();
Share Status Task
Questo launcher permette di condividere uno status su un social network ed equivale a
pubblicare un post su Twitter o Facebook tramite l’integrazione nativa disponibile in
Windows Phone.

Le proprietà da valorizzare
L’unica proprietà disponibile è Status, che contiene il messaggio che si vuole condividere
sui social network.
Nella schermata del launcher l’utente avrà comunque la possibilità di modificare il
messaggio, oltre che di scegliere su quale dei social network configurati nel telefono
pubblicare il post.

Esempio di utilizzo
ShareStatusTask share = new ShareStatusTask();
share.Status = “This is cool!”;
share.Show();

Share Media Task


Questo launcher è stato introdotto in Windows Phone 8 e consente di condividere una foto
utilizzando una delle applicazioni che si è registrata nel sistema operativo per gestire i file
multimediali (approfondiremo l’argomento nel corso del capitolo).

Le proprietà da valorizzare
L’unica proprietà disponibile è FilePath, che è il percorso dell’immagine da condividere. Se
vogliamo utilizzare una delle immagini presenti nella libreria multimediale dell’utente, ci
viene in aiuto un nuovo metodo che è stato introdotto in Windows Phone 8, chiamato
GetPath(), che consente di ottenere il percorso partendo da un oggetto Picture della classe
MediaLibrary. Ogni oggetto di tipo Picture (contenuto all’interno della collezione Pictures)
rappresenta una foto presente nella libreria dell’utente.

Esempio di utilizzo
MediaLibrary library = newMediaLibrary();
Picture picture = library.Pictures.FirstOrDefault();

ShareMediaTask share = newShareMediaTask();


share.FilePath = picture.GetPath();
share.Show();

SMS Compose Task


Questo launcher permette di preparare un sms dall’interno dell’applicazione, che poi
l’utente potrà inviare sfruttando l’applicazione nativa.

Le proprietà da valorizzare
Il launcher supporta le proprietà To, con il numero del destinatario, e Body, con il testo del
messaggio.

Esempio di utilizzo
SmsComposeTask task = new SmsComposeTask();
task.To = “1234567890”;
task.Body = “Message from Windows Phone”;
task.Show();

Web Browser Task


Questo launcher permette di aprire Internet Explorer su un URL specifico, così da
consentire all’utente di navigare su un sito di nostra scelta.

Le proprietà da valorizzare
L’unica proprietà disponibile è Uri, di tipo Uri, che rappresenta l’indirizzo del sito da
visualizzare.

Esempio di utilizzo
WebBrowserTask task = new WebBrowserTask();
task.Uri = new Uri(“http://www.qmatteoq.com”);
task.Show();

Save Appointment Task


Questo launcher permette di creare un nuovo appuntamento nel calendario. Apre la
schermata di creazione di un nuovo appuntamento con i campi impostati dall’applicazione
già precompilati.

Le proprietà da valorizzare
Il launcher espone una serie di proprietà per definire le caratteristiche dell’appuntamento:
• StartTime per impostare la data e l’ora di inizio;
• EndTime per impostare la data e l’ora di fine;
• Subject per impostare il titolo;
• Location per impostare il luogo;
• Details per impostare le eventuali note;
• IsAllDayEvent per specificare se si tratta di un evento che dura tutta la giornata;
• Reminder per impostare, tramite uno dei valori dell’enumeratore Reminder, quanto
tempo prima dell’appuntamento mostrare un promemoria;
• AppointmentStatus
per impostare, tramite uno dei valori dell’enumeratore
AppointmentStatus, lo stato dell’appuntamento.
Esempio di utilizzo
SaveAppointmentTask saveAppointmentTask = new SaveAppointmentTask();
saveAppointmentTask.StartTime = DateTime.Now.AddHours(2);
saveAppointmentTask.EndTime = DateTime.Now.AddHours(3);
saveAppointmentTask.Subject = “Titolo”;
saveAppointmentTask.Location = “Luogo”;
saveAppointmentTask.Details = “Note”;
saveAppointmentTask.IsAllDayEvent = false;
saveAppointmentTask.Reminder = Reminder.FifteenMinutes;
saveAppointmentTask.AppointmentStatus = Microsoft.Phone.UserData.AppointmentStatus.Busy;
saveAppointmentTask.Show();
I chooser
Un aspetto importante da evidenziare prima di analizzare i chooser disponibili è la
struttura dell’evento di callback che viene invocato nel momento in cui il chooser ha
terminato il suo compito e il controllo è stato restituito all’applicazione: l’oggetto che
viene restituito contiene sempre una proprietà TaskResult, che ci comunica se l’operazione è
andata a buon fine o meno. Sono molteplici i casi per cui qualcosa può andare storto: il più
semplice è che l’utente annulli l’operazione, tornando perciò all’applicazione senza aver
selezionato alcun dato.
È buona prassi, perciò, verificare sempre che la proprietà TaskResult abbia valore
TaskResult.OK prima di procedere con l’elaborazione dei dati restituiti.

Address Chooser Task


Questo chooser permette di accedere alla rubrica del device per importare l’indirizzo di un
contatto all’interno dell’applicazione. All’utente sarà mostrata la propria rubrica, dalla
quale potrà andare a selezionare il contatto e l’indirizzo che vuole importare.

Le proprietà da valorizzare
Non è necessario valorizzare alcuna proprietà per utilizzare questo chooser.

Cosa viene restituito


Viene restituito un oggetto di tipo AddressResult, che contiene le proprietà Address (l’indirizzo
selezionato) e DisplayName (il nome completo del contatto).

Esempio di utilizzo
public partial class AddressChooser : PhoneApplicationPage
{
private AddressChooserTask task;
public AddressChooser()
{
InitializeComponent();
task = new AddressChooserTask();
task.Completed += new EventHandler<AddressResult>(task_Completed);
}
private void Button_Click(object sender, RoutedEventArgs e)
{ task.Show();
}
void task_Completed(object sender, AddressResult e)
{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show(“Hai selezionato il contatto ” + e.DisplayName + “, il cui indirizzo è ” + e.Address);
}
}
}

Camera Capture Task


Questo chooser permette di accedere alla fotocamera per scattare una foto, da importare
all’interno dell’applicazione. Al contrario dell’integrazione nativa che vedremo nel
Capitolo 8, in questo caso l’operazione di cattura della foto viene completamente
demandata all’applicazione nativa.

Le proprietà da valorizzare
Non è necessario valorizzare alcuna proprietà per utilizzare questo chooser.

Cosa viene restituito


Il chooser restituisce un oggetto di tipo PhotoResult, che espone due proprietà:
• OriginalFileName contiene il nome del file che è stato selezionato;
• ChosenPhoto contiene l’immagine selezionata sotto forma di Stream.
Esempio di utilizzo
public partial class CameraCapture : PhoneApplicationPage
{
private CameraCaptureTask captureTask;
public CameraCapture()
{
InitializeComponent();
captureTask=new CameraCaptureTask();
captureTask.Completed += new EventHandler<PhotoResult>(captureTask_Completed);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
captureTask.Show();
}
void captureTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
BitmapImage image = new BitmapImage();
image.SetSource(e.ChosenPhoto);
imgPicture.Source = image;
}
}
}

Email Address Chooser Task


Questo chooser è simile all’Address Chooser Task, con la differenza che in questo caso
permette di recuperare un indirizzo e-mail dalla rubrica.
Anche utilizzando questo chooser, all’utente verrà mostrata la rubrica, dalla quale potrà
selezionare un contatto e successivamente l’indirizzo e-mail che vuole importare (nel caso
in cui ne esista più di uno).

Le proprietà da valorizzare
Non è necessario valorizzare alcuna proprietà per utilizzare questo chooser.

Cosa viene restituito


Il chooser restituisce un oggetto di tipo EmailResult, che contiene le proprietà Email
(l’indirizzo e-mail selezionato) e DisplayName (il nome completo del contatto a cui
appartiene l’indirizzo).

Esempio di utilizzo
public partial class EmailAddressChooser : PhoneApplicationPage
{
private EmailAddressChooserTask mailTask;
public EmailAddressChooser()
{
InitializeComponent();
mailTask=new EmailAddressChooserTask();
mailTask.Completed += new EventHandler<EmailResult>(mailTask_Completed);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
mailTask.Show();
}
void mailTask_Completed(object sender, EmailResult e)
{
if (e.TaskResult == TaskResult.OK)
{
txtEmail.Text = e.Email;
}
}
}

Game Invite Task


Questo chooser nasce per i giochi e permette di invitare un utente a partecipare a una
sessione in multiplayer. In realtà, questo chooser è stato inserito in previsione di
implementazioni future: al momento, infatti, non può essere utilizzato principalmente
perché è disponibile solo per i giochi che fanno parte del marchio Xbox Live. Questi
giochi sono certificati direttamente da Microsoft e, al momento, gli sviluppatori
indipendenti non hanno modo di ottenere tale certificazione.
Per completezza, però, ho deciso di includerlo comunque.

Le proprietà da valorizzare
L’unica proprietà disponibile è SessionId, che rappresenta l’id della sessione di gioco a cui
vogliamo invitare l’utente.

Cosa viene restituito


Il chooser non restituisce alcun dato particolare se non l’esito dell’operazione, sotto forma
di TaskResult.

Esempio di utilizzo
public partial class GameInvitation : PhoneApplicationPage
{
private GameInviteTask gameInviteTask;
public GameInvitation()
{
InitializeComponent();
gameInviteTask = new GameInviteTask();
gameInviteTask.SessionId = “SessionId”;
gameInviteTask.Completed += new EventHandler<TaskEventArgs>(gameInviteTask_Completed);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
gameInviteTask.Show();
}
void gameInviteTask_Completed(object sender, TaskEventArgs e)
{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show(“Invite sent succesfully”);
}
}
}

Phone Number Chooser Task


Ecco un altro chooser che permette di interagire con i contatti del telefono: nello specifico,
consente di recuperare un numero di telefono e importarlo nella nostra applicazione.
A partire da Windows Phone 7.5 è possibile accedere alla rubrica anche con una nuova
API, che permette di recuperare le informazioni sui contatti senza un intervento esplicito
dell’utente. Ne parleremo più approfonditamente nel corso di questo capitolo.

Le proprietà da valorizzare
Non è necessario valorizzare alcuna proprietà per utilizzare questo chooser.

Cosa viene restituito


Il chooser restituisce un oggetto di tipo PhoneNumberResult che espone le proprietà PhoneNumber
(che contiene il numero di telefono selezionato) e DisplayName (che contiene il nome
completo del contatto selezionato).

Esempio di utilizzo
public partial class PhoneNumberChooser : PhoneApplicationPage
{
private PhoneNumberChooserTask numberTask;

public PhoneNumberChooser()
{
InitializeComponent();
numberTask=new PhoneNumberChooserTask();
numberTask.Completed += new EventHandler<PhoneNumberResult>(numberTask_Completed);
}

private void Button_Click(object sender, RoutedEventArgs e)


{
numberTask.Show();
}

void numberTask_Completed(object sender, PhoneNumberResult e)


{
if (e.TaskResult == TaskResult.OK)
{
txtPhoneNumber.Text = e.PhoneNumber;
}
}
}

Photo Chooser Task


Questo chooser permette all’utente di selezionare una foto dalla sua libreria e di importarla
all’interno dell’applicazione.

Le proprietà da valorizzare
Nel momento in cui l’utente ha selezionato la foto da importare, il chooser dà la possibilità
di ritagliarla affinché rispetti i nostri requisiti di altezza e larghezza: per definire questi
requisiti abbiamo a disposizione le proprietà PixelWidth e PixelHeight, che rappresentano
rispettivamente la larghezza e l’altezza massima che dovrà avere la foto importata (l’unità
di misura è il pixel).
In più abbiamo a disposizione la proprietà ShowCamera di tipo booleano che, se impostata a
true, abilita nel chooser un pulsante per accedere allo stream della fotocamera e scattare la
foto da importare al momento.

Cosa viene restituito


Il chooser restituisce un oggetto di tipo PhotoResult, che contiene la proprietà ChosenPhoto, di
tipo Stream, con l’immagine selezionata.
Nell’esempio sottostante tale stream viene utilizzato per creare un oggetto di tipo
BitmapImage, che viene poi mostrato a video grazie a un controllo di tipo Image.

Esempio di utilizzo
public partial class PhotoChooser : PhoneApplicationPage
{
private PhotoChooserTask photoTask;
public PhotoChooser()
{
InitializeComponent();
photoTask=new PhotoChooserTask();
photoTask.Completed += new EventHandler<PhotoResult>(photoTask_Completed);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
photoTask.PixelWidth = 300;
photoTask.PixelHeight = 300;
photoTask.ShowCamera = true;
photoTask.Show();
}
void photoTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
BitmapImage image=new BitmapImage();
image.SetSource(e.ChosenPhoto);
imgPicture.Source = image;
txtFilename.Text = e.OriginalFileName;
}
}
}

Save Contact Task


Questo chooser, al contrario degli altri trattati finora, non serve per recuperare dati da
importare all’interno dell’applicazione, ma per salvarli all’interno delle applicazioni
native. Nello specifico, questo chooser permette di salvare un contatto all’interno dell’hub
People, le cui informazioni vengono valorizzate all’interno dell’applicazione.
Le proprietà da valorizzare
Non starò qui a elencare tutte le proprietà supportate, molto numerose in quanto
rappresentano tutte le possibili informazioni associabili a un contatto della rubrica, come
FirstName, LastName o MobilePhone.

Cosa viene restituito


Il chooser restituisce solamente l’esito dell’operazione, sotto forma di oggetto di tipo
TaskResult.

Esempio di utilizzo
public partial class SaveContact : PhoneApplicationPage
{
private SaveContactTask task;

public SaveContact()
{
InitializeComponent();
task.Completed += new EventHandler<SaveContactResult>(task_Completed);
}

private void Button_Click(object sender, RoutedEventArgs e)


{
task.FirstName = "Mario";
task.LastName = "Rossi";
task.MobilePhone = "1234567890";
task.Show();
}

void task_Completed(object sender, SaveContactResult e)


{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show(”Il contatto è stato salvato correttamente”);
}
}
}

Save Email Address Task


Questo chooser permette di salvare un indirizzo e-mail nella rubrica: l’indirizzo può essere
associato a un contatto già esistente oppure l’utente ha l’opportunità di creare un nuovo
contatto.

Le proprietà da valorizzare
L’unica proprietà disponibile è Email e deve essere valorizzata con l’indirizzo e-mail che si
vuole salvare in rubrica.

Cosa viene restituito


Questo chooser restituisce solamente l’esito dell’operazione, sotto forma di oggetto di tipo
TaskResult.

Esempio di utilizzo
public partial class SaveEmailAddress : PhoneApplicationPage
{
private SaveEmailAddressTask mailTask;
public SaveEmailAddress()
{
InitializeComponent();
mailTask=new SaveEmailAddressTask();
mailTask.Completed += new EventHandler<TaskEventArgs>(mailTask_Completed);
}

private void Button_Click(object sender, RoutedEventArgs e)


{
mailTask.Email = txtMailAddress.Text;
mailTask.Show();
}

void mailTask_Completed(object sender, TaskEventArgs e)


{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show(”L’indirizzo è stato salvato correttamente”);
}
}
}

Save Phone Number Task


Questo chooser permette di salvare un numero di telefono all’interno della rubrica. Anche
in questo caso, l’utente avrà la possibilità di associarlo a un contatto già esistente o di
crearne uno nuovo.

Le proprietà da valorizzare
L’unica proprietà disponibile è PhoneNumber, che rappresenta il numero di telefono da
salvare.

Cosa viene restituito


Questo chooser restituisce solamente l’esito dell’operazione, sotto forma di oggetti di tipo
TaskResult.

Esempio di utilizzo
public partial class SavePhoneNumber : PhoneApplicationPage
{
private SavePhoneNumberTask phoneTask;

public SavePhoneNumber()
{
InitializeComponent();
phoneTask = new SavePhoneNumberTask();
phoneTask.Completed += new EventHandler<TaskEventArgs>(phoneTask_Completed);
}

private void Button_Click(object sender, RoutedEventArgs e)


{
phoneTask.PhoneNumber = txtPhoneNumber.Text;
phoneTask.Show();
}

void phoneTask_Completed(object sender, TaskEventArgs e)


{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show(”Il numero di telefono è stato salvato correttamente”);
}
}
}

Save Ringtone Task


Una delle novità introdotte in Windows Phone 7.5 è il supporto alle suonerie
personalizzate: anche gli sviluppatori hanno la possibilità di esportare delle suonerie dalla
propria applicazione nella libreria dell’utente grazie a questo chooser. Prima di vedere
come utilizzarlo, è importante considerare quali sono i requisiti affinché un file audio
possa essere salvato come suoneria:
• il formato deve essere MP3 o WMA;
• la dimensione del file deve essere inferiore a 1 MB;
• la durata del file audio deve essere inferiore a 40 secondi;
• il file non deve essere protetto da DRM.
Il file può essere indifferentemente parte del progetto oppure memorizzato all’interno
dell’Isolated Storage.

Le proprietà da valorizzare
Le due principali proprietà da valorizzare sono Source, ovvero il percorso del file audio
sotto forma di Uri, e DisplayName, ovvero il nome della suoneria.
Il percorso del file audio può avere prefisso appdata: se il file è incluso nel progetto o
isostore: se invece è memorizzato nello storage dell’applicazione.
Nel momento in cui il chooser viene eseguito, l’utente ha la possibilità di modificare il
nome della suoneria e, tramite un checkbox, di impostarla come predefinita.

Cosa viene restituito


Come gli altri chooser dedicati al salvataggio, anche in questo caso viene restituito
semplicemente l’esito dell’operazione sotto forma di oggetto di tipo TaskResult.

Esempio di utilizzo
public partial class SaveRingtoneTaskView : PhoneApplicationPage
{
private SaveRingtoneTask task;

public SaveRingtoneTaskView()
{
InitializeComponent();
task.Completed += new EventHandler<TaskEventArgs>(task_Completed);
}

private void Button_Click(object sender, RoutedEventArgs e)


{
task.Source = new Uri("appdata:/Assets/Ringtones/Ringtone01.wma");
task.DisplayName = "Ringtone 1";
task.Show();
}

void task_Completed(object sender, TaskEventArgs e)


{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show("Suoneria salvata correttamente");
}
}
}
Le API per la rubrica e per il calendario
All’inizio di questo capitolo abbiamo visto come l’SDK metta a disposizione dei launcher
e dei chooser per interagire con la rubrica per importare o salvare dei dati: queste classi,
però, richiedono sempre un esplicito intervento dell’utente, rendendo impossibile, ad
esempio, eseguire delle operazioni di ricerca sulla rubrica.
Pensiamo, ad esempio, a un’applicazione per consultare la rubrica alternativa a quella
nativa, che presenti i contatti con una UI differente; oppure, ancora, un’applicazione
turistica dedicata a una città che sia in grado di mostrare, in una sezione dedicata, tutti i
contatti della propria rubrica che vivono in quella città.
A partire da Windows Phone 7.5 questi scenari possono finalmente essere realizzati, grazie
a delle nuove API che permettono di interagire direttamente (senza l’intervento
dell’utente) sia con la rubrica sia con il calendario.
Ricordiamoci sempre, però, che la filosofia di Windows Phone è ”al centro l’utente”: per
questo motivo tali API sono di sola lettura, così da evitare che un’applicazione possa
intaccare l’integrità dei dati dell’utente.
Le API permettono di accedere a tutti gli account configurati nel telefono dell’utente,
siano essi di Windows Live, di Outlook o di Facebook.
Ci sono, però, delle limitazioni nell’accesso ad alcune informazioni, principalmente legate
ai social network: per motivi di privacy e di accordi, alcuni dati, infatti, non vengono resi
disponibili. Ecco la tabella riassuntiva pubblicata direttamente da Microsoft nella
documentazione MSDN relativa alle possibilità di accesso:
Nome del Immagine del Altre informazioni Appuntamenti del
Provider
contatto contatto sul contatto calendario
Dispositivo Sì Sì Sì Sì
Windows
Sì Sì Sì Sì
Live
Exchange Sì Sì Sì Sì
SIM Sì Sì Sì No
Facebook Sì Sì No No
Altri social
No No No No
network
I dati restituiti sia dalle API della rubrica che da quelle del calendario contengono una
proprietà Accounts che comprende l’elenco di tutti gli account nei quali quel contatto è
memorizzato. Ogni account ha una proprietà Kind, che è di tipo StorageKind e che ci indica la
tipologia di account:
• Facebook;
• Other (per i contatti importati dalla SIM o memorizzati in un ContactStore privato, il
cui funzionamento vedremo a breve);
• Outlook;
• Phone;
• Windows Live.

Accedere alla rubrica


La classe che ci permette di accedere ai dati della rubrica si chiama Contacts e, come la
maggior parte delle classi in Windows Phone, lavora in maniera asincrona: il metodo per
lanciare una ricerca è, infatti, asincrono e si chiama SearchAsync(); abbiamo, poi, un evento
di callback da sottoscrivere, chiamato SearchCompleted, che viene scatenato nel momento in
cui la ricerca è stata completata e i dati sono pronti per essere elaborati. Il metodo
SearchAsync() accetta tre parametri in ingresso:

• la parola chiave con cui effettuare la ricerca;


• il filtro su cui deve essere applicata la parola chiave. Sono cinque i valori disponibili
dell’enumeratore FilterKind, che però sono raggruppabili in due categorie: FilterKind.None
e FilterKind.PinnedToStart ignorano la parola chiave che si è specificata; servono, infatti,
rispettivamente, per recuperare tutti i contatti della rubrica oppure solamente quelli
che l’utente ha aggiunto al menu Start; FilterKind.DisplayName, FilterKind.EmailAddress e
FilterKind.PhoneNumber utilizzano, invece, la parola chiave per filtrare i risultati in base,
rispettivamente, al nome e cognome dell’utente, all’indirizzo e-mail o al numero di
telefono;
• l’object state, ovvero un’informazione che ci permette di identificare la richiesta: può
essere utile nel caso in cui lanciamo più ricerche contemporaneamente e, nell’evento
di callback SearchCompleted, dobbiamo determinare a quale richiesta si riferisce il
risultato.
L’evento SearchCompleted, invece, restituisce un oggetto di tipo ContactsSearchEventArgs che, oltre
a contenere le informazioni che sono state impostate per la ricerca, espone la proprietà
Results, che è una collezione di oggetti di tipo Contact.

La classe Contact rappresenta il singolo contatto della rubrica e contiene una serie di
proprietà che vengono mappate con i vari campi, come DisplayName, Addresses ecc. Molte di
queste proprietà sono, a loro volta, delle collezioni: questo perché in rubrica un contatto
può avere più informazioni dello stesso tipo (ad esempio, più indirizzi email, più numeri
di telefono ecc.).

I filtri di ricerca della classe FilterKind sfruttano il database nativo di Windows


Phone della rubrica e, per tale motivo, sono ottimizzati per restituire i risultati
nel minor tempo possibile e avere un impatto minimo sulle performance.
Esiste, però, una seconda possibilità per ricercare informazioni, ovvero filtrare i
dati in un secondo momento: una volta recuperati tutti i contatti della rubrica,
ottiamo la la collezione Contacts, che è di tipo IEnumerable<T>, e, per questo
NOTA
motivo, possiamo sfruttare LINQ per effettuare ricerche e filtrare dati.
È importante, però, ricorrere a questa strategia solo nel caso in cui si vogliano
effettuare ricerche specifiche su campi che non sono già disponibili con i filtri
(ad esempio, per la città in cui vive l’utente). Nel caso si debba effettuare una
ricerca per nome, indirizzo e-mail o numero di telefono, è vivamente
consigliato utilizzare, invece, i filtri nativi, così da ottimizzare le performance.

Vediamo ora un esempio in cui vengono recuperati tutti i contatti dalla rubrica il cui nome
è Marco e mostrati a video sfruttando una ListBox. Ecco lo XAML, in cui viene creata la
ListBox e viene definito il template, che si limita a mostrare la proprietà DisplayName del
contatto.

<ListBox x:Name="Contacts">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=DisplayName}" Margin="1⊘, ⊘, ⊘, 2⊘" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Dopodiché da codice lanciamo la ricerca e, nell’evento SearchCompleted, andiamo a popolare


la ListBox.
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
Contacts contacts = new Contacts();
contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>
(contacts_SearchCompleted);
contacts.SearchAsync("Marco", FilterKind.None, null);
}
void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
{
Contacts.ItemsSource = e.Results;
}
}

L’accesso alla rubrica tramite questa API richiede la dichiarazione nel file di
NOTA
manifest della capability ID_CAP_CONTACTS.

Accedere al calendario
La classe di riferimento per accedere ai dati del calendario è Appointments, che,
analogamente all’API per accedere alla rubrica, è asincrona ed espone un metodo
SearchAsync() per avviare la ricerca, e una callback SearchCompleted per recuperare i risultati
una volta che l’operazione di ricerca è stata completata.
Trattandosi, però, di una ricerca sul calendario, il metodo SearchAsync() ovviamente accetta
parametri diversi in ingresso:
• data di inizio e di fine ricerca: questi due parametri sono gli unici obbligatori e
servono per specificare un range temporale all’interno del quale effettuare la ricerca.
Sono due parametri di tipo DateTime;
• il massimo numero di elementi da recuperare (opzionale);
• la tipologia di account dal quale recuperare le informazioni (opzionale; è possibile
recuperare tutti gli appuntamenti indipendentemente dall’account sul quale sono
memorizzati);
• analogamente a quanto avviene per la rubrica, anche in questo caso possiamo passare
un’informazione che ci può risultare utile per identificare la richiesta.
La callback SearchCompleted ritorna un oggetto di tipo AppointmentsSearchEventArgs che, oltre a
contenere una copia dei parametri di ricerca che abbiamo impostato, espone una proprietà
Results, che è una collezione di oggetti di tipo Appointment. Ognuno di questi oggetti
rappresenta un evento sul calendario e ne espone, tramite le proprietà, i vari dettagli:
oggetto (Subject), luogo (Location), dettagli (Details) e così via.
Contrariamente a quanto avviene con le API per la ricerca nella rubrica, in questo caso
non possiamo specificare dei filtri, ma solamente effettuare ricerche in base al range
temporale. Se abbiamo necessità di filtrare i dati in maniera più precisa (ad esempio, per
recuperare solo gli appuntamenti segnati come privati), dobbiamo ricorrere a LINQ ed
effettuare delle query di ricerca sui dati che vengono restituiti, come nell’esempio
seguente, in cui recuperiamo tutti gli appuntamenti marcati come “privati”:
e.Results.Where(x => x.IsPrivate);

Vediamo ora un esempio in cui recuperiamo tutti gli appuntamenti a partire dal mese
precedente rispetto alla data corrente e li mostriamo a video sempre tramite una ListBox, il
cui template ci permette semplicemente di visualizzare l’oggetto dell’appuntamento.
<ListBox x:Name="Calendar">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Subject}" Margin="10, 0, 0, 20" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Da codice andiamo a lanciare la ricerca specificando come parametro solamente le due


date di inizio e fine, dopodiché, nell’evento SearchCompleted, andiamo a popolare la ListBox
con i risultati.
public partial class MainPage : PhoneApplicationPage
{

public MainPage()
{
InitializeComponent();

Appointments calendar = new Appointments();


calendar.SearchCompleted += new EventHandler<AppointmentsSearchEventArgs>
(calendar_SearchCompleted);
DateTime start = DateTime.Now.AddMonths(-1);
DateTime end = DateTime.Now;
calendar.SearchAsync(start, end, null);
}
void calendar_SearchCompleted(object sender, AppointmentsSearchEventArgs e)
{
Calendar.ItemsSource = e.Results;
}
}

L’accesso alla rubrica tramite questa API richiede la dichiarazione nel file di manifest
della capability ID_CAP_APPOINTMENTS.
ContactStore: una rubrica “privata” dell’applicazione
Come abbiamo visto, le API introdotte in Windows Phone 7.5 ci consentono di recuperare
le informazioni dalla rubrica, ma non di scrivere dati: per aggiungere un nuovo contatto
dobbiamo sempre passare dal chooser di cui abbiamo parlato all’inizio del capitolo.
Windows Phone 8 ha introdotto una novità che espande le funzionalità messe a
disposizione degli sviluppatori per quanto riguarda l’integrazione con la rubrica: il
ContactStore. Tramite questa API è possibile creare una rubrica di contatti che rimane di
proprietà dell’applicazione stessa: se questa viene disinstallata, anche i contatti vengono
eliminati. Dato che la rubrica non fa parte di quelle di sistema di Windows Phone, ma è
specifica dell’applicazione, lo sviluppatore ha la possibilità non solo di leggere, ma anche
di scrivere dati.
Il vero vantaggio di questa nuova funzionalità è che è in grado di integrarsi con il People
hub: i contatti facenti parte della nostra rubrica privata saranno mostrati insieme agli altri
contatti e la nostra applicazione comparirà tra le sorgenti disponibili nel telefono, così
come adesso siamo in grado di visualizzare all’interno dell’hub i contatti provenienti da
Facebook, Twitter o dai nostri account di posta elettronica.
In questo modo, come sviluppatori, siamo in grado di interagire con la rubrica del sistema
operativo senza alcuna restrizione, rispettando allo stesso tempo il motto “al centro
l’utente” di Windows Phone: saremo in grado di aggiungere, cancellare e modificare
solamente i contatti che fanno parte della nostra rubrica, senza toccare quelli di sistema o
provenienti da altre applicazioni che fanno uso dell’API ContactStore.

Anche questa funzionalità richiede che sia dichiarata nel manifest la capability
NOTA
ID_CAP_CONTACTS.

Il primo passo per la creazione di una rubrica privata passa per la classe ContactStore,
contenuta all’interno del namespace Windows.Phone.PersonalInformation, che espone
il metodo asincrono CreateOrOpenAsync(). Abbiamo la possibilità di specificare le modalità
con cui il sistema operativo e le altre applicazioni potranno interagire con la nostra
rubrica, tramite due proprietà:
• la prima accetta un valore dell’enumeratore ContactStoreSystemAccessMode e serve per
specificare le modalità di accesso da parte del sistema operativo alla rubrica: può
assumere i valori ReadOnly, per specificare che solo la nostra applicazione sarà in
grado di modificare i contatti, o ReadWrite, se vogliamo invece che l’utente, tramite il
People hub, sia in grado di modificare le informazioni sui contatti senza dover aprire
la nostra applicazione; nel primo caso, se l’utente premerà il pulsante di modifica
presente nel People hub, sarà invitato a creare una copia del contatto all’interno di un
altro account; nel secondo caso, invece, si aprirà direttamente la schermata di
modifica, consentendogli di cambiare le informazioni che preferisce;
• la seconda accetta un valore dell’enumeratore ContactStoreApplicationAccessMode e
determina, invece, le modalità di accesso da parte di altre applicazioni di terze parti
tramite le API viste nel paragrafo precedente per interagire in sola lettura con la
rubrica: può assumere i valori LimitedReadOnly (in tal caso l’applicazione sarà in grado
di recuperare solamente il nome e l’immagine del contatto) o ReadOnly (in tal caso,
invece, sarà in grado di recuperare tutte le informazioni).
Ecco un esempio di inizializzazione della rubrica:
private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)
{
ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccess
Mode.ReadWrite,
ContactStoreApplicationAccessMode.ReadOnly);
}

È possibile chiamare il metodo CreateOrOpenAsync() anche senza specificare alcun parametro:


in tal caso verranno applicati i valori predefiniti, che sono
ContactStoreSystemAccessMode.ReadOnly per quanto riguarda l’accesso da parte del sistema
operativo e ContactStoreApplicationAccessMode.LimitedReadOnly per quanto riguarda quello da parte
delle applicazioni di terze parti.
Come dice il nome stesso del metodo, il suo scopo non è solamente quello di creare una
nuova rubrica privata: dato che ogni applicazione può avere solamente una rubrica, se
questa esiste già verrà semplicemente aperta, così da darci la possibilità, con le API che
vedremo a breve, di interagire con la stessa.
Bisogna fare attenzione, però, al fatto che non si possono cambiare i permessi di accesso
una volta che la rubrica è stata creata: se cercherete di accedere alla rubrica specificando
dei parametri del metodo CreateOrOpenAsync() differenti da quelli utilizzati la prima volta che
il metodo è stato usato per la creazione, otterrete un’eccezione.
Se lanciate questo esempio di codice sull’emulatore o sul telefono, sarete in grado di
verificare subito se l’operazione di creazione della rubrica è andata a buon fine. Se
accedete alle opzioni dell’hub People, infatti, ed entrate nella sezione filter my contact
list (che consente di specificare i contatti di quale account mostrare all’interno dell’hub),
troverete anche la vostra applicazione tra quelle elencate.
Figura 7.1 - La rubrica privata dell’applicazione all’interno degli account disponibili.

Creare un contatto
Un contatto all’interno della rubrica è rappresentato dalla classe StoredContact: al contrario,
però, di quanto abbiamo visto per le API di lettura della rubrica del sistema operativo, in
questo caso vengono esposte solamente le proprietà base GivenName e FamilyName, che
rappresentano il nome e cognome del contatto.
Per accedere a tutte le altre proprietà dobbiamo utilizzare il metodo GetPropertiesAsync(), che
ci mette a disposizione una collezione di tipo Dictionary<string, object> contenente tutte le
proprietà che possiamo valorizzare del contatto.
private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)
{
ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMo
de.ReadWrite,
ContactStoreApplicationAccessMode.ReadOnly);

StoredContact contact = new StoredContact(store);


contact.GivenName = "Matteo";
contact.FamilyName = "Pagani";
IDictionary<string, object> properties = await contact.GetPropertiesAsync();
properties.Add(KnownContactProperties.Email, "info@qmatteoq.com");

await contact.SaveAsync();
}

In questo esempio vediamo la procedura di creazione: innanzitutto occorre creare un


oggetto di tipo StoredContact, passando come parametro il ContactStore all’interno del quale
vogliamo salvare il contatto.
Dopo aver valorizzato le proprietà GivenName e FamilyName, recuperiamo le proprietà a nostra
disposizione tramite il metodo asincrono GetPropertiesAsync(): tutte le informazioni che
andremo a inserire all’interno della collezione saranno associate al contatto. Ogni oggetto
della collezione è rappresentato da due elementi: la chiave, di tipo string, rappresenta
l’identificatore della proprietà che vogliamo valorizzare e l’informazione vera e propria,
che è di tipo object (dato che, in alcuni casi, questa potrebbe essere memorizzata tramite un
oggetto complesso).
Per accedere a tutte le proprietà disponibili le API ci mettono a disposizione un
enumeratore chiamato KnownContactProperties, che contiene un valore per ogni possibile
proprietà di un contatto: alcuni esempi sono Email, BirthDate, Telephone, JobTitle ecc.
È sufficiente chiamare il metodo Add(), passando uno dei valori di tale enumeratore e il
valore che vogliamo dare a quella proprietà, per aggiungere l’informazione al contatto:
nell’esempio valorizziamo l’indirizzo e-mail dell’utente.
La chiave dell’elemento (quindi uno dei valori dell’enumeratore KnownContactProperties) deve
essere univoca all’interno della collezione: non possiamo, ad esempio, avere due
informazioni identificate dalla chiave Email o dalla chiave Telephone. Se esiste già, significa
che quella proprietà è già stata valorizzata in precedenza.
Infine, occorre chiamare il metodo asincrono SaveAsync() sull’oggetto di tipo ContactStore,
affinché questo venga effettivamente salvato all’interno della rubrica.
Figura 7.2 - Un contatto visualizzato all’interno dell’hub People appartenente a un’applicazione di terze parti.

Attenzione! Non tutte le proprietà accettano una semplice stringa come valore (come il
nome o l’indirizzo e-mail): alcune di queste richiedono un oggetto complesso, come una
data o un indirizzo. In linea di massima, fate riferimento alle proprietà della classe Contact,
che abbiamo trattato parlando delle API per accedere in sola lettura alla rubrica di sistema.
Ad esempio, tale classe gestisce gli indirizzi tramite un oggetto di tipo ContactAddress, che
contiene al suo interno diverse proprietà testuali che rappresentano l’indirizzo, come
Country e StreetAddress.

Il tipo ContactAddress è esattamente quello che le API della classe ContactStore si aspettano
nel momento in cui cercate di aggiungere un indirizzo a un contatto, come nell’esempio:
private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)
{
ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccess
Mode.ReadWrite,
ContactStoreApplicationAccessMode.ReadOnly);

StoredContact contact = new StoredContact(store);


contact.GivenName = "Matteo";
contact.FamilyName = "Pagani";
IDictionary<string, object> properties = await contact.GetPropertiesAsync();
ContactAddress address = newContactAddress
{
Country = "Italy",
Locality = "Milan",
StreetAddress = "Piazza del Duomo 1"
};
properties.Add(KnownContactProperties.Address, address);

await contact.SaveAsync();
}

Fanno eccezione a questa regola tutte le proprietà che rappresentano delle date, come
KnownContactProperties.BirthDate e KnownContactProperties.Anniversary. In tal caso, è richiesto un
oggetto di tipo DateTimeOffset e non semplicemente DateTime, come ci si potrebbe aspettare.
Ecco un esempio di codice in cui valorizziamo la proprietà BirthDate (la data di nascita):
private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)
{
ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccess
Mode.ReadWrite,
ContactStoreApplicationAccessMode.ReadOnly);

StoredContact contact = new StoredContact(store);


contact.GivenName = "Matteo";
contact.FamilyName = "Pagani";

IDictionary<string, object> properties = await contact.GetPropertiesAsync();


properties.Add(KnownContactProperties.Birthdate, newDateTimeOffset(newDateTime(1983, 9, 3)));

await contact.SaveAsync();
}

In questi esempi abbiamo visto come agire con le proprietà standard di un contatto, come
l’indirizzo e-mail, la data di nascita o il numero di telefono. Abbiamo la possibilità, però,
di creare proprietà personalizzate: il procedimento è analogo a quello che abbiamo appena
visto, solo che in questo caso si utilizza il metodo GetExtendedPropertiesAsync() della classe
StoredContact.

private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)


{
ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccess
Mode.ReadWrite,
ContactStoreApplicationAccessMode.ReadOnly);
StoredContact contact = new StoredContact(store);
contact.GivenName = "Matteo";
contact.FamilyName = "Pagani";
IDictionary<string, object> extendedProperties = await contact.GetExtendedPropertiesAsync();
extendedProperties.Add("MVP Category", "Windows Phone Development");

await contact.SaveAsync();
}

In questo esempio associamo al contatto una proprietà di nome MVP Category, che ha
come valore Windows Phone Development. Ovviamente queste proprietà possono essere
gestite solamente dalla vostra applicazione: la rubrica è in grado di comprendere
solamente le proprietà standard, perciò non le mostrerà nella pagina di dettaglio all’interno
del People hub.
Il concetto di Extended Properties è stato esteso anche alla classe ContactStore: anche la
rubrica vera e propria può avere delle proprietà aggiuntive, che possiamo memorizzare per
i nostri scopi. Nel prossimo paragrafo vedremo un esempio di utilizzo.

Gestire la sincronizzazione con un servizio remoto


In molti casi le API del ContactStore possono essere utilizzate per gestire scenari di
sincronizzazione con un servizio esterno: pensiamo all’applicazione di Skype per
Windows Phone, che fa uso di questa funzionalità per mostrare i propri contatti di Skype
all’interno della rubrica. In tal caso, non stiamo parlando semplicemente di contatti locali,
ma di informazioni che sono memorizzate su un server remoto, per le quali può sorgere la
necessità di mantenere l’associazione tra i due ”mondi” tramite un id univoco.
Per questo motivo la classe StoredContact espone la proprietà RemoteId, di tipo string, che nasce
specificamente per soddisfare questa esigenza. È fondamentale tenere conto, però, che si
tratta di una proprietà che deve essere univoca, non solo a livello di applicazione ma anche
di sistema operativo: non è possibile avere due applicazioni dotate di rubrica privata con
all’interno un contatto con lo stesso RemoteId. Nel caso si dovesse verificare questa
situazione, l’operazione di salvataggio del contatto fallirà.
Come essere sicuri al 100% che l’id utilizzato dal nostro servizio sia univoco e che altre
applicazioni non tenteranno di utilizzarlo? Se, ad esempio, nel nostro servizio utilizziamo
semplicemente un numero incrementale per identificare univocamente un utente, le
probabilità che un’altra applicazione utilizzi un valore già assegnato sono molto alte. Sono
due le strade che possiamo percorrere. La prima ci viene suggerita direttamente dalla
documentazione Microsoft e consiste nell’utilizzo di un Guid che, combinato con il nostro
identificativo, ci dà la garanzia che nessun’altra applicazione possa usare lo stesso valore.
Tale Guid viene memorizzato all’interno delle Extended Properties dello store in modo
che, in qualsiasi momento, sia possibile ricostruire il vero identificativo remoto.
La stessa documentazione ci suggerisce l’implementazione di una classe chiamata
RemoteIdHelper, che contiene una serie di metodi per effettuare queste operazioni:

public class RemoteIdHelper


{
private const string ContactStoreLocalInstanceIdKey = "LocalInstanceId";

public async Task SetRemoteIdGuid(ContactStore store)


{
IDictionary< string, object> properties;
properties = await store.LoadExtendedPropertiesAsync().AsTask< IDictionary< string,
object> > ();
if (!properties.ContainsKey(ContactStoreLocalInstanceIdKey))
{
// the given store does not have a local instance id so set one against
store extended properties
Guid guid = Guid.NewGuid();
properties.Add(ContactStoreLocalInstanceIdKey, guid.ToString());
System.Collections.ObjectModel.ReadOnlyDictionary< string, object> readonlyProperties
= new System.Collections.ObjectModel.ReadOnlyDictionary< string, object> (properties);
await store.SaveExtendedPropertiesAsync(readonlyProperties).AsTask();
}
}

public async Task< string> GetTaggedRemoteId(ContactStore store, string remoteId)


{
string taggedRemoteId = string.Empty;

System.Collections.Generic.IDictionary< string, object> properties;


properties = await store.LoadExtendedPropertiesAsync().AsTask< System.
Collections.Generic.IDictionary< string, object> > ();
if (properties.ContainsKey(ContactStoreLocalInstanceIdKey))
{
taggedRemoteId = string.Format("{0}_{1}", properties[ContactStoreLocalInstance
IdKey], remoteId);
}
else
{
// handle error condition
}

return taggedRemoteId;
}

public async Task< string> GetUntaggedRemoteId(ContactStore store, string taggedRemoteId)


{
string remoteId = string.Empty;

System.Collections.Generic.IDictionary< string, object> properties;


properties = await store.LoadExtendedPropertiesAsync().AsTask< System.
Collections.Generic.IDictionary< string, object> > ();
if (properties.ContainsKey(ContactStoreLocalInstanceIdKey))
{
string localInstanceId = properties[ContactStoreLocalInstanceIdKey] asstring;
if (taggedRemoteId.Length > localInstanceId.Length + 1)
{

remoteId = taggedRemoteId.Substring(localInstanceId.Length + 1);


}
}
else
{
// handle error condition
}

return remoteId;
}
}

Questa classe fornisce tre metodi:


• SetRemoteIdGuid() deve essere utilizzato la prima volta che lo store viene creato e serve a
generare un id univoco (di tipo Guid), che sarà memorizzato tra le extended
properties della classe ContactStore e che sarà riutilizzato a ogni operazione. In questo
modo, ci assicuriamo che per tutti i contatti della nostra rubrica sarà utilizzato lo
stesso Guid. Il metodo richiede in ingresso l’oggetto di tipo ContactStore che
rappresenta la nostra rubrica;
• GetTaggedRemoteId() serve per convertire l’identificativo del contatto utilizzato nel nostro
servizio in quello locale da assegnare alla proprietà RemoteId. Il metodo richiede in
ingresso l’oggetto di tipo ContactStore che rappresenta la nostra rubrica e l’identificativo
del nostro servizio;
• GetUntaggedRemoteId() serve per fare il procedimento inverso: possiamo usarlo ogni
qualvolta si abbia la necessità, dato il valore della proprietà RemoteId, di ottenere l’id
originale del nostro servizio. Il metodo richiede in ingresso l’oggetto di tipo
ContactStore che rappresenta la nostra rubrica e l’identificativo locale del contatto.

Ecco come cambia il metodo di salvataggio di un contatto sfruttando la classe


RemoteIdHelper:

private async void OnCreateStoreClicked(object sender, RoutedEventArgs e)


{
ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccess
Mode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);
RemoteIdHelper remoteHelper = new RemoteIdHelper();
await remoteHelper.SetRemoteIdGuid(store);

string myRemoteId = "2918";

StoredContact contact = new StoredContact(store);


contact.GivenName = "Matteo";
contact.FamilyName = "Pagani";
contact.RemoteId = await remoteHelper.GetTaggedRemoteId(store, myRemoteId);
await contact.SaveAsync();
}

In questo esempio utilizziamo un identificativo remoto fittizio: in uno scenario reale


questa informazione sarebbe recuperata dal nostro servizio.
Alternativamente, esiste un’altra soluzione per evitare la collisione di identificativi remoti
uguali: non affidarsi alla proprietà RemoteId, ma utilizzare una proprietà personalizzata
definita nelle extended properties del contatto. In tal caso, dato che si tratta di una
proprietà creata dalla nostra applicazione, non viene applicato alcun controllo dal sistema
operativo sull’univocità del suo valore.
Dal punto di vista pratico, entrambe le soluzioni sono valide: il vantaggio di affidarsi alla
proprietà RemoteId è che, come vedremo a breve, le API consentono la ricerca di un contatto
in maniera diretta sfruttando questo identificativo.

Associare una foto a un contatto


Per associare una foto personalizzata a un contatto la classe StoredContact espone il metodo
asincrono SetDisplayPictureAsync(), che accetta in ingresso lo stream contenente l’immagine,
che deve essere di tipo IInputStream, uno dei nuovi formati introdotti nel Windows Runtime.
Per gestire questo scenario ci viene in aiuto uno degli extension method introdotti in
Windows Phone 8, ovvero AsInputStream(), che richiede la dichiarazione del namespace
System.IO all’interno della vostra classe: tale metodo è in grado di convertire un generico
oggetto Stream in un InputStream. Ecco come utilizzare queste API per scattare una foto con il
chooser CameraCaptureTask e assegnarla al nostro contatto:
private async void OnSavePictureClicked(object sender, RoutedEventArgs e)
{
ContactStore store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccess
Mode.ReadWrite, ContactStoreApplicationAccessMode.ReadOnly);

StoredContact contact = new StoredContact(store);


contact.GivenName = "Matteo";
contact.FamilyName = "Pagani";

CameraCaptureTask task = newCameraCaptureTask();


task.Completed += async (obj, args) =>
{
await contact.SetDisplayPictureAsync(args.ChosenPhoto.AsInputStream());
await contact.SaveAsync();
};
task.Show();
}

Viceversa, se abbiamo la necessità di recuperare la foto del nostro contatto dalla nostra
applicazione, la classe StoredContact ci mette a disposizione il metodo asincrono
GetDisplayPictureAsync().
Effettuare operazioni di ricerca di un contatto
Il modo più semplice per cercare un contatto è tramite il suo identificativo remoto. La
classe ContactStore espone, infatti, il metodo FindContactByRemoteIdAsync(). Ecco un esempio di
utilizzo, sfruttando la classe RemoteIdHelper vista poco fa:
private async void OnSearchContactClicked(object sender, RoutedEventArgs e)
{
ContactStore store = await ContactStore.CreateOrOpenAsync();

string myRemoteId = "2918";

RemoteIdHelper remoteHelper = new RemoteIdHelper();


string taggedRemoteId = await remoteHelper.GetTaggedRemoteId(store, myRemoteId);
StoredContact contact = await store.FindContactByRemoteIdAsync(taggedRemoteId);
}

Quello che viene restituito dal metodo è l’oggetto di tipo StoredContact con lo specifico
RemoteId.

Non in tutti i casi, però, abbiamo a disposizione la proprietà RemoteId: potrebbe darsi, ad
esempio, che la rubrica privata che stiamo utilizzando non sia sincronizzata con alcun
servizio remoto.
In questo caso ci viene in aiuto la classe ContactQueryResult, che consente di effettuare
operazioni sul database dei contatti della nostra rubrica privata. In realtà, non sono molte
le possibilità a nostra disposizione: possiamo recuperare tutti i contatti (con il metodo
GetContactsAsync(), oppure recuperarne semplicemente il numero totale (con il metodo
GetContactCountAsync()).

private async void OnSearchContactClicked(object sender, RoutedEventArgs e)


{
ContactStore store = await ContactStore.CreateOrOpenAsync();

ContactQueryResult result = store.CreateContactQuery();


IReadOnlyList<StoredContact> contacts = await result.GetContactsAsync();

uint total = await result.GetContactCountAsync();


}

Per ottenere un’istanza della classe ContactQueryResult in grado di interrogare la rubrica


privata della nostra applicazione dobbiamo utilizzare il metodo CreateContactQuery()
sull’oggetto di tipo ContactStore che la rappresenta.
Se invece volessimo cercare in maniera specifica un contatto in base al valore di una delle
sue proprietà saremmo costretti a passarli tutti in rassegna, alla ricerca dell’informazione
desiderata. L’unico meccanismo previsto dalle API che viene in aiuto è la classe
ContactQueryOptions, che permette di specificare:

• se vogliamo effettuare una ricerca per un campo specifico (tramite la proprietà


DesiredFields);

• se vogliamo che i risultati vengano ordinati in un certo modo (tramite la proprietà


OrderBy).

In realtà, l’utilizzo della proprietà DesiredFields non ci esenta dal dover richiamare i metodi
GetProperties() o GetExtendedProperties() per accedere all’informazione vera e propria:
semplicemente ”preparano” la rubrica al tipo di interrogazione che andremo a fare, così da
ottimizzare le query di ricerca successive. Vediamo un esempio:
private async void OnSearchContactClicked(object sender, RoutedEventArgs e)
{
ContactStore store = await ContactStore.CreateOrOpenAsync();

ContactQueryOptions options = new ContactQueryOptions();


options.DesiredFields.Add(KnownContactProperties.Email);
options.OrderBy = ContactQueryResultOrdering.FamilyNameGivenName;

ContactQueryResult result = store.CreateContactQuery(options);


IReadOnlyList< StoredContact> contacts = await result.GetContactsAsync();

StoredContact searchedContact = null;

foreach (var contact in contacts)


{
IDictionary< string, object> properties = await contact.GetPropertiesAsync();
if (properties.ContainsKey(KnownContactProperties.Email) && properties[KnownContact
Properties.Email].ToString() == "info@qmatteoq.com")
{
searchedContact = contact;
}
}

MessageBox.Show(searchedContact.GivenName);
}

Dopo aver creato un oggetto di tipo ContactQueryOptions, aggiungiamo alla collezione


DesiredFields le informazioni del contatto che vorremo utilizzare per la ricerca, tramite
l’enumeratore KnowContactProperties che abbiamo già conosciuto.
Per l’ordinamento, invece, sfruttiamo uno dei valori dell’enumeratore
ContactQueryResultOrdering: nell’esempio, i risultati saranno ordinati in base al cognome e al
nome.
L’operazione per recuperare l’elenco di tutti i contatti è identica a quella precedente:
l’unica differenza è che, in questo caso, passiamo l’oggetto di tipo ContactQueryOptions
appena creato al metodo CreateContactQuery().
Una volta in possesso dell’elenco di tutti i contatti, non possiamo fare altro che esaminarli
uno per uno all’interno di un ciclo foreach: recuperiamo le proprietà base di ogni contatto
con il metodo GetPropertiesAsync() e verifichiamo che ne sia già presente una per l’indirizzo e-
mail e, nello specifico, che il suo valore sia info@qmatteoq.com. In tal caso, si tratta del
contatto che cercavamo: ne mostriamo il nome a schermo con una MessageBox.

Aggiornare le informazioni di un contatto


Alla luce di quanto abbiamo visto finora è semplice capire come aggiornare le
informazioni di un contatto: una volta recuperato l’oggetto di tipo StoredContact
corrispondente (utilizzando i meccanismi di ricerca trattati nel paragrafo precedente), è
sufficiente andare a modificare le proprietà già esistenti e chiamare nuovamente il metodo
SaveAsync(). Ecco un esempio:

private async void OnSearchContactClicked(object sender, RoutedEventArgs e)


{
ContactStore store = await ContactStore.CreateOrOpenAsync();

string myRemoteId = "2918";


RemoteIdHelper remoteHelper = newRemoteIdHelper();

string taggedRemoteId = await remoteHelper.GetTaggedRemoteId(store, myRemoteId);


StoredContact contact = await store.FindContactByRemoteIdAsync(taggedRemoteId);
IDictionary<string, object> properties = await contact.GetPropertiesAsync();
properties[KnownContactProperties.Email] = "test@qmatteoq.com";

await contact.SaveAsync();
}

In questo caso, dopo aver recuperato il contatto desiderato, andiamo a cambiarne


l’indirizzo e-mail.

Eliminare un contatto
Per eliminare un contatto, la classe ContactStore ci mette a disposizione il metodo asincrono
DeleteContactAsync(), che accetta come parametro l’identificativo dello stesso. Anche se non
utilizziamo la proprietà RemoteId, infatti, ogni contatto è comunque identificato da un id
univoco, locale e generato dal sistema operativo. Non abbiamo la possibilità, infatti, di
cambiarne il valore, ma solo di leggerlo.
Quello che dobbiamo fare, perciò, è recuperare tramite i metodi di ricerca visti in
precedenza il contatto che vogliamo eliminare, dopodiché chiamare il metodo
DeleteContactAsync() sfruttando il valore della proprietà Id di tale contatto.

private async void OnDeleteButtonClicked(object sender, RoutedEventArgs e)


{
ContactStore store = await ContactStore.CreateOrOpenAsync();
string myRemoteId = "2918";

RemoteIdHelper remoteHelper = new RemoteIdHelper();


string taggedRemoteId = await remoteHelper.GetTaggedRemoteId(store, myRemoteId);
StoredContact contact = await store.FindContactByRemoteIdAsync(taggedRemoteId);

await store.DeleteContactAsync(contact.Id);
}

Nell’esempio, dopo aver recuperato il contatto che vogliamo eliminare tramite il suo
RemoteId, lo eliminiamo grazie alla proprietà Id dello stesso.
Integrare Internet Explorer 10
Uno dei principali limiti della prima versione di Windows Phone era proprio il browser:
era infatti installata una versione intermedia tra Internet Explorer 7 e 8, che non
permetteva di sfruttare tutte le ultime tecnologie web, come HTML5 e CSS3.
Questa lacuna è stata colmata, prima, da Windows Phone 7.5, che ha introdotto Internet
Explorer 9, e poi ulteriormente migliorata con Windows Phone 8, che ha portato con sé la
versione 10: non si tratta, però, di una versione ”ridotta”, ma offre le stesse feature della
versione desktop, quindi pieno supporto ad HTML5, CSS3 e accelerazione hardware.
Oltre al launcher di cui abbiamo parlato in questo capitolo, abbiamo la possibilità di
includere una vista web direttamente nella nostra applicazione, sfruttando lo stesso engine
di Internet Explorer utilizzato dall’applicazione nativa.
In più, grazie a una serie di eventi e proprietà esposti dal controllo saremo in grado di
interagire con la pagina web in entrambi i sensi grazie a Javascript.
Il controllo WebBrowser fa parte del namespace Microsoft.Phone.Controls, che dobbiamo
aggiungere tramite reference nello XAML della nostra pagina per poterlo inserire:
<phone:WebBrowser x:Name="webBrowser" Width="48⊘" Height="4⊘⊘" />

Visualizzare una pagina web


Sono due le modalità con cui possiamo visualizzare una pagina web all’interno
dell’applicazione: sfruttare la proprietà Source o il metodo Navigate().
Nel primo caso, si tratta semplicemente di una proprietà del controllo che possiamo
valorizzare anche da XAML, nel caso in cui si voglia che, al caricamento della pagina che
contiene il controllo, venga caricato direttamente il sito web.
<phone:WebBrowser x:Name="WebBrowser" Width="48⊘" Height="4⊘⊘" Source="http://www.qmatteoq.com" />

Da codice, invece, abbiamo a disposizione anche il metodo Navigate() che, a differenza della
proprietà Source, ci permette una maggiore flessibilità, in quanto possiamo aggiungere dei
dati in post e degli header alla richiesta HTP.
private void Button_Click(object sender, RoutedEventArgs e)
{
WebBrowser.Navigate(new Uri("http://www.qmatteoq.com", UriKind.Absolute));
}

Infine, abbiamo una terza alternativa: sfruttare il controllo WebBrowser per visualizzare del
codice HTML, che può essere disponibile in locale o ottenuto tramite una chiamata a un
servizio web. In questo caso, dobbiamo utilizzare il metodo NavigateToString(), che accetta
come parametro una stringa contenente l’HTML da visualizzare.
private void Button_Click(object sender, RoutedEventArgs e)
{
string html = "<div>This is HTML</div>";
WebBrowser.NavigateToString(html);
}

Interagire con il controllo WebBrowser


Il primo passo per interagire con la pagina web dalla nostra applicazione consiste
nell’abilitare la proprietà IsScriptEnabled del controllo, impostandola a true.
<;phone:WebBrowser x:Name="WebBrowser" Width="48⊘" Height="4⊘⊘" IsScriptEnabled="True" />

In questo modo, daremo la possibilità al codice Javascript incluso nella pagina web di
interagire con il controllo e viceversa.
Il primo esempio è relativo al modo in cui effettuare, da una pagina web, una chiamata
Javascript che possa essere intercettata dalla nostra applicazione: in questo modo,
possiamo realizzare applicazioni web in grado di interagire con funzioni native del sistema
operativo (è il meccanismo utilizzato da framework come PhoneGap, che, tramite l’uso di
HTML e Javascript, permettono di realizzare applicazioni mobile cross platform in grado
di interagire con funzioni del sistema operativo che normalmente non sarebbero accessibili
da un’applicazione web).
La funzione Javascript in questione si chiama window.external.notify e accetta un parametro in
ingresso, il quale sarà poi passato all’applicazione Windows Phone.
Ecco un esempio:
<!DOCTYPE>
<html>
<head>
<title></title>
</head>
<body>
<input type="button" value="Click here" onclick="SendMessage()"/>
<script type="text/javascript">
function SendMessage() {
window.external.notify("This is a message");
}
</script>
</body>
</html>

La pagina web contiene un pulsante che, al clic, richiama la funzione Javascript


SendMessage, che non fa altro che eseguire la funzione window.external.notify passando una
stringa di testo.
E per quanto riguarda l’applicazione Windows Phone? Dobbiamo sottoscrivere l’evento
ScriptNotify del controllo WebBrowser, che viene invocato ogni qualvolta viene intercettata una
chiamata da una funzione Javascript.
<phone:WebBrowser x:Name="WebBrowser" Width="48⊘" Height="4⊘⊘"
IsScriptEnabled="True" ScriptNotify="WebBrowser_ScriptNotify" />

private void WebBrowser_ScriptNotify(object sender, NotifyEventArgs e)


{
MessageBox.Show(e.Value);
}

Come possiamo vedere nell’esempio, l’evento ScriptNotify ci restituisce un oggetto di tipo


NotifyEventArgs, che contiene la proprietà Value con il valore passato dalla funzione Javascript.
In questo caso, verrà mostrato a video il testo ”This is a message”.
Vediamo ora l’esempio contrario: come passare dei valori dall’applicazione alla pagina
web e richiamare una funzione Javascript.
Lato web quello che dobbiamo fare è semplicemente definire una funzione Javascript
come se dovessimo utilizzarla all’interno della pagina web.
<!DOCTYPE>
<html>
<head>
<title></title>
</head>
<body>
<div id="name"></div>
<script type="text/javascript">
function ShowName(name, surname) {
document.getElementById("name").innerHTML = name + surname;
}
</script>
</body>
</html>

In questo esempio è stata definita una funzione chiamata ShowName che accetta in ingresso
due parametri name e surname: il suo scopo è quello di andare a recuperare l’elemento HTML
con id name (nel nostro caso, un div) e usarlo per visualizzare i due parametri name e surname
ricevuti. Nell’applicazione Windows Phone dobbiamo invece andare a utilizzare il metodo
InvokeScript esposto dal controllo WebBrowser, che accetta come parametri in ingresso il nome
della funzione Javascript da richiamare e gli eventuali parametri che verranno passati alla
funzione.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
WebBrowser.InvokeScript("ShowName", "Matteo", "Pagani");
}

In questo esempio, alla pressione di un pulsante, andiamo a chiamare la funzione


ShowName, passando come parametri name e surname i valori Matteo e Pagani.

Quello che succederà è che, alla pressione del pulsante nell’applicazione Windows Phone,
verrà invocata la funzione Javascript ShowName e, nella pagina web mostrata all’interno del
controllo WebBrowser, comparirà il testo Matteo Pagani dove è posizionato il div con id
name.
Kid’s Corner: come interagire con l’angolo dei bambini
Kid’s Corner (l’Angolo dei Bambini, nella versione italiana del sistema operativo) è il
nome di una delle funzionalità più originali introdotte in Windows Phone 8 ed è un valido
supporto per le persone che, saltuariamente, si trovano a dover prestare il loro telefono a
un bambino, magari per giocare a un videogame o per guardare un video.
In queste situazioni si verifica spesso che il bambino, senza volerlo, utilizzi il telefono in
maniera non consona: facendo partire un gioco o un video non adatto alla sua età, oppure
collegandosi a Internet e iniziando a visitare siti non adatti o a pubblicare, erroneamente,
contenuti sui social network.
Grazie alla funzione Kid’s Corner è possibile creare una sorta di sandbox, alla quale si può
accedere tramite uno swipe laterale dalla lock screen del telefono. All’interno di questa
sezione abbiamo la possibilità di scegliere quali giochi, applicazioni e contenuti
multimediali (musica e video) rendere disponibili: una volta che il telefono entra in
modalità Kid’s Corner, l’utilizzatore non ha alcun modo di accedere ad applicazioni e
contenuti al di fuori di quelli scelti da noi.
La funzionalità interessante, per noi sviluppatori, è data dal fatto che, tramite una API,
abbiamo la possibilità di determinare se l’applicazione è in esecuzione normalmente o
all’interno del Kid’s Corner: in questo modo possiamo abilitare o disabilitare determinate
funzionalità in base all’ambiente di esecuzione. Sono molti gli scenari possibili: ad
esempio, un gioco potrebbe disabilitare la funzionalità di condivisione sui social network
del punteggio mentre è in esecuzione all’interno del Kid’s Corner; un’applicazione
potrebbe scegliere di non mostrare alcuni contenuti non adatti ai bambini, e così via.
Figura 7.3 - La funzionalità Kid’s Corner.

Determinare la modalità di esecuzione è molto semplice: all’interno del namespace


Windows.Phone.ApplicationModel è disponibile la classe ApplicationProfile, la cui proprietà
Modes può assumere due valori:
• ApplicationProfileModes.Default quando l’applicazione è in esecuzione normalmente;
• ApplicationProfileModes.Alternate quando, invece, è in esecuzione all’interno del Kid’s
Corner:
if (ApplicationProfile.Modes == ApplicationProfileModes.Default)
{
MessageBox.Show("L'applicazione è in esecuzione normalmente");
}
else
{
MessageBox.Show("L'applicazione è in esecuzione nel Kid's Corner");
}
Parliamo con la nostra applicazione: le Speech API
Una delle caratteristiche introdotte a partire da Windows Phone 7.5 è il supporto ai
comandi vocali: tenendo premuto il pulsante Start per qualche secondo, il telefono inizierà
a riconoscere quello che stiamo dicendo e a cercare di eseguire un comando in base alla
frase che abbiamo utilizzato.
Sono molti gli scenari supportati: utilizzo delle funzioni base (come effettuare una
chiamata o inviare un sms a qualcuno), apertura di applicazioni di sistema (come il
calendario o OneNote) ecc.
In più, Windows Phone è dotato di un sintetizzatore vocale integrato, in grado di
trasformare la voce in testo: tale funzionalità è supportata, ad esempio, dall’applicazione
Messaggi e ci consente di dettare un sms invece di scriverlo utilizzando la tastiera.
A partire da Windows Phone 8 queste funzionalità sono state estese alle applicazioni di
terze parti: grazie alle nuove API del Windows Runtime possiamo sfruttare anche noi i
comandi vocali per interagire con la nostra applicazione o utilizzare il sintetizzatore
vocale di sistema per dettare dei testi, da memorizzare poi all’interno dell’applicazione.
Figura 7.4 - La schermata di configurazione dei servizi di riconoscimento vocale.

La configurazione delle impostazioni vocali parte dalla sezione Impostazioni del telefono,
nello specifico la sezione Speech. Al suo interno l’utente ha la possibilità di scegliere in
che lingua utilizzare le funzionalità vocali: affinché la nostra applicazione possa sfruttare i
comandi vocali, occorre che la lingua impostata dall’utente in questa sezione coincida con
una delle lingue per cui andremo a gestire i comandi vocali.

Per utilizzare i servizi vocali è necessario che nel file di manifest sia dichiarata
NOTA
la capability ID_CAP_SPEECH_RECOGNITION.
Il riconoscimento vocale: attivare l’applicazione
Il riconoscimento vocale è una delle funzionalità più interessanti che abbiamo a
disposizione grazie alle speech API: possiamo, infatti, creare un vero e proprio dialogo
con l’applicazione, affinché riconosca i nostri comandi ed esegua delle operazioni senza la
necessità di interagire con lo schermo.
Inoltre, i comandi vocali possono essere utilizzati anche quando la nostra applicazione non
è in esecuzione: in qualsiasi momento, infatti, l’utente può tenere premuto il pulsante Start
del telefono e iniziare a pronunciare un comando e, di conseguenza, lanciare la nostra
applicazione: in tal caso, all’interno dell’URL di navigazione, avremo la possibilità di
scoprire quale comando è stato utilizzato.
L’utente, per utilizzare questa funzionalità con le applicazioni di terze parti, dovrà prima
pronunciarne il nome, seguito dal comando vero e proprio. Vedremo a breve come gestire
al meglio i vari comandi vocali da utilizzare.
È importante sottolineare come le funzionalità di riconoscimento vocale siano disponibili
solamente in presenza di connettività: l’interpretazione delle frasi e la relativa traduzione
in comandi vocali, infatti, è a carico dei servizi web di Microsoft.
Il riconoscimento dei comandi nelle applicazioni di terze parti passa per i file VCD,
ovvero dei file XML che contengono la definizione dei comandi vocali con i quali
possiamo interagire. Visual Studio include già un template per questo tipo di file: se
facciamo clic con il tasto destro sul progetto e selezioniamo Add new item troveremo tra
le tipologie disponibili il template VoiceCommandDefinition. Sarà creato un file XML,
con al suo interno alcuni dati fittizi.
Ipotizzando di voler aggiungere il supporto ai comandi vocali in un’applicazione per
gestire le proprie note, ecco come potrebbe apparire la definizione di un file VCD:
<?xmlversion="1.0"encoding="utf-8"?>

<VoiceCommandsxmlns="http://schemas.microsoft.com/voicecommands/1.0">
<CommandSetxml:lang="it"Name="NotesCommandSet">
<CommandPrefix>Le mie note</CommandPrefix>
<Example> Aggiungi una nuova nota </Example>

<CommandName="AddNote">
<Example>add a new note </Example>
<ListenFor> [e] aggiungi [una] nuova nota </ListenFor>
<ListenFor> [e] crea [una] nuova nota </ListenFor>
<Feedback> Sto creando una nuova nota… </Feedback>

<Navigate />
</Command>

</CommandSet>
</VoiceCommands>

Ogni set di comandi è identificato dal nodo CommandSet, il cui attributo xml:lang specifica
la lingua alla quale si riferisce: tipicamente, in un file VCD avremo un CommandSet
differente per ognuna delle lingue supportate dalla nostra applicazione, al cui interno
troveremo gli stessi comandi, ma tradotti in maniera differente. Ricordate il discorso fatto
poco fa riguardo alla lingua che l’utente ha scelto come predefinita tra le opzioni dei
comandi vocali? Affinché l’utente sia in grado di utilizzare i nostri comandi vocali, il file
VCD deve contenere un CommandSet per quella lingua. Opzionalmente possiamo anche
specificare un attributo Name, per dare un nome univoco al nostro set di comandi: ci tornerà
utile nei prossimi esempi, quando dovremo interagire da codice con il file.
Un CommandSet è caratterizzato da un elemento CommandPrefix, che rappresenta il nome della
nostra applicazione che l’utente dovrà pronunciare prima del comando vero e proprio: si
tratta di un elemento opzionale e occorre specificarlo solo nel caso in cui vogliamo che il
nome da pronunciare per attivare il supporto ai comandi vocali sia differente da quello
dell’applicazione. Può essere una valida soluzione per applicazioni dal nome troppo lungo
o di difficile pronuncia, oppure nel caso in cui vogliamo localizzare il nome
dell’applicazione in base alla lingua.
Nel nostro esempio, l’applicazione viene attivata, in lingua italiana, tramite il comando Le
mie note; avrebbe senso, nella definizione di un CommandSet per la lingua inglese, utilizzare
un altro comando, ad esempio la frase My notes.
E se l’utente cercasse di installare due applicazioni che utilizzano lo stesso
CommandPrefix? Purtroppo non abbiamo modo di sapere se un determinato prefisso sia
già stato utilizzato o meno da un’altra applicazione installata: in tal caso, quella installata
più recentemente avrà la priorità.
La sezione Example, invece, permette di specificare un esempio di interazione con
l’applicazione tramite comandi vocali: questa informazione viene mostrata all’interno
della schermata di supporto delle funzionalità vocali, che mostra all’utente quali sono le
applicazioni installate nel sistema in grado di supportare i comandi vocali e quali di questi
possiamo usare.
Per visualizzare tale guida, occorre tenere premuto il pulsante Start per qualche secondo,
dopodiché fare tap sul simbolo del punto interrogativo che compare nella schermata di
riconoscimento: nella sezione Apps troveremo l’elenco delle applicazioni di terze parti
installate in grado di supportare i comandi vocali; sia nell’elenco che all’interno della
guida specifica (che compare facendo tap sull’applicazione) troveremo il testo che
abbiamo incluso nel nodo Example.
Figura 7.5 - La guida mostra i comandi vocali disponibili per l’applicazione.

All’interno di un CommandSet sono presenti uno o più elementi Command (ce ne possono
essere fino a 100): ognuno di questi rappresenta un singolo comando che l’utente potrà
pronunciare per interagire con l’applicazione.
Ogni comando è caratterizzato da:
• un nome, contenuto all’interno dell’attributo Name: è il nome del comando, che
riceveremo nell’applicazione e che ci consentirà di capire il tipo di interazione
richiesto dall’utente;
• un esempio di come deve essere invocato il comando, all’interno della sezione
Example: questa informazione viene mostrata sempre nella sezione di supporto dei
comandi vocali, nel momento in cui l’utente fa tap sul nome dell’applicazione. In tal
caso, la guida mostrerà tutti i comandi vocali supportati utilizzando il valore di
questo elemento;
• il testo vero e proprio da pronunciare per attivare il comando: è contenuto all’interno
della sezione ListenFor ed è possibile aggiungerne fino a 10, così da poter supportare
più variazioni dello stesso testo aventi, tuttavia, lo stesso significato. Possiamo
vedere un esempio nel VCD sopra riportato: il comando AddNote può essere
invocato sia pronunciando la frase ”aggiungi una nuova nota” sia con la frase ”crea
una nuova nota”. Potete notare, inoltre, come il comando contenga alcune parole
racchiuse tra parentesi quadre; significa che si tratta di parole opzionali, ovvero il
comando viene attivato sia pronunciando la frase ”aggiungi una nuova nota”, sia
dicendo, invece, semplicemente, ”aggiungi nuova nota”;
• il testo che viene mostrato a video e letto dalla voce sintetizzata nel momento in cui il
comando è stato riconosciuto: è contenuto all’interno della sezione Feedback, dato che
il suo scopo è proprio quello di dare un feedback all’utente del fatto che il comando
da lui pronunciato sia stato riconosciuto correttamente;
• ogni volta che un comando viene eseguito, la nostra applicazione viene aperta: ecco il
significato della sezione Navigate. Se non specifichiamo alcun attributo, significa che
l’utente sarà portato alla pagina principale dell’applicazione; viceversa, se vogliamo
demandare a un’altra pagina dell’applicazione l’elaborazione dello specifico
comando, possiamo impostare l’attributo Target con il nome della pagina che viene
aperta, come nell’esempio:
<CommandName="AddNote">
<Example> aggiungi una nuova nota </Example>
<ListenFor> [e] aggiungi [una] nuova nota </ListenFor>
<ListenFor> [e] crea [una] nuova nota </ListenFor>
<Feedback> Sto creando una nuova nota… </Feedback>
<NavigateTarget="/AddNote.xaml" />
</Command>

Ora che abbiamo definito un file VCD, come facciamo a inizializzarlo e a far sì che
Windows Phone inizi a riconoscere i nostri comandi?
È sufficiente utilizzare le API all’interno del namespace Windows.Phone.Speech.
VoiceCommands, nello specifico la classe VoiceCommandService, che espone il metodo
asincrono InstallCommandSetsFromFileAsync(): il parametro richiesto è il nome del file VCD
contenuto all’interno del nostro progetto.
private async void OnInitVoiceClicked(object sender, RoutedEventArgs e)
{
await VoiceCommandService.InstallCommandSetsFromFileAsync(new Uri("ms-appx:///
VoiceCommands.xml"));
}

Il protocollo dell’URL da utilizzare per recuperare il file VCD del nostro progetto è
msappx:///, che corrisponde alla root. Nell’esempio, viene caricato il file
VoiceCommands.xml presente nella radice del progetto di Visual Studio.
Una volta eseguito questo codice, il sistema operativo sarà pronto ad accettare i nostri
comandi: possiamo vederne una dimostrazione provando a pronunciare uno dei comandi
nel file VCD. Se il comando viene riconosciuto correttamente, l’applicazione sarà aperta:
dietro le quinte l’URL di navigazione della pagina, però, conterrà le informazioni sul
comando che è stato pronunciato. Vedremo a breve come gestire questi parametri.
Gestire le liste di elementi
Nell’esempio di file VCD abbiamo visto che, nella definizione di un comando, è possibile
utilizzare le parentesi quadre per specificare dei testi opzionali. Abbiamo un’altra
possibilità: popolare una parte di un comando vocale con dei valori presi da una lista.
Ipotizziamo, nell’applicazione di gestione delle note che stiamo scrivendo, di voler
consentire all’utente di aprire una delle note salvate direttamente con un comando vocale
del tipo ”Apri la nota uno”. In tal caso, ci troviamo a gestire uno scenario nuovo: il
termine ”uno” non è fisso e può variare a seconda della nota che vuole aprire l’utente.
Racchiuderlo tra parentesi quadre non è la soluzione: il comando verrebbe effettivamente
riconosciuto indipendentemente dal numero di nota, ma a noi mancherebbe l’informazione
sulla nota effettivamente richiesta dall’utente.
Per questo scopo i file VCD supportano l’elemento PhraseList, che consente di specificare
una serie di valori che l’utente potrà pronunciare insieme al comando. Per definire la
posizione, all’interno del comando, del valore lo si racchiude tra parentesi graffe. Vediamo
un esempio:
<VoiceCommandsxmlns="http://schemas.microsoft.com/voicecommands/1.0">
<CommandSetxml:lang="it">
<CommandPrefix>Le mie note</CommandPrefix>
<Example> Usa note e aggiungi una nuova nota </Example>

<CommandName="OpenNote">
<Example> apri la nota </Example>
<ListenFor> apri la nota {number} </ListenFor>
<Feedback> Sto aprendo la nota… </Feedback>
<Navigate />
</Command>

<PhraseListLabel="number">
<Item>1</Item>
<Item>2</Item>
<Item>3</Item>
</PhraseList>

</CommandSet>
</VoiceCommands>

In questo caso abbiamo creato una PhraseList identificata dall’etichetta number (il valore
dell’attributo Label): al suo interno abbiamo incluso un elemento Item per ognuno dei valori
che vogliamo supportare (ce ne possono essere fino a 2000).
Abbiamo poi definito un nuovo comando tramite il nodo Command, che useremo per aprire
la nota. Potete notare che il valore dell’elemento ListenFor è ”Apri la nota {number}”.
Questo significa che l’utente potrà pronunciare la frase ”Apri la nota” seguita da uno
qualsiasi dei valori contenuti all’interno della PhraseList la cui Label è number.
In questo modo, nel momento in cui l’applicazione sarà aperta, non saranno più solamente
due le informazioni che riceveremo tramite l’URL di navigazione (ovvero il nome del
comando e il testo pronunciato dall’utente), ma, in un parametro a parte, anche l’item
selezionato dall’elenco all’interno dell’elemento PhraseList.
Come potete notare dall’esempio, siamo in grado di utilizzare, all’interno di una PhraseList,
non solo lettere, ma anche numeri: in questo modo ci penserà il sistema operativo a
determinare il valore corretto in base alla lingua (e quindi riconoscerà, ad esempio, ”apri
la nota due” se l’utente è italiano o ”open the note two” se l’utente è inglese).
Determinare il comando utilizzato
Come anticipato più volte nel corso del paragrafo, il meccanismo utilizzato dai servizi di
riconoscimento vocale per passare alla nostra applicazione il comando pronunciato
dall’utente è quello dei parametri in query string, inclusi all’interno dell’URL di
navigazione.
Nel momento in cui l’applicazione viene aperta in seguito a un comando vocale, l’utente
sarà automaticamente portato alla pagina specificata all’interno dell’elemento Navigate del
file VCD: nell’evento OnNavigatedTo, dichiarato nel code behind di quella pagina, saremo in
grado, tramite la classe NavigationContext, di recuperare le informazioni. Ma quali sono questi
parametri? Ecco un esempio di URL di navigazione in seguito al comando per creare una
nuova nota che abbiamo visto nel file VCD di esempio:
/MainPage.xaml?voiceCommandName=AddNote&reco=Le%20mie%2⊘note%2⊘e%2⊘crea%2⊘una%2⊘nuova%2⊘nota

• il parametro voiceCommandName contiene il nome del comando, in base al valore


che abbiamo definito nell’attributo Name dell’elemento Command;
• il parametro reco contiene il testo che è stato riconosciuto dal sistema operativo.
Nel caso in cui, invece, il comando impiegato preveda l’utilizzo di un parametro gestito
tramite una PhraseList, avremo un parametro in più, come possiamo vedere nell’esempio:
/MainPage.xaml?voiceCommandName=OpenNote&reco=Le%20mie%20note%20apri%20la%20nota%20tre&number=3

Il nome del parametro è variabile, perché coincide con il valore dell’attributo Label della
PhraseList: il valore del parametro, invece, corrisponde all’elemento che è stato scelto. In
questo esempio, il nome del parametro è number (come il nome della PhraseList
contenuta nel VCD di esempio), mentre il valore scelto dall’utente è 3.
Ecco un esempio di come estrarre e gestire queste informazioni all’interno della pagina:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("voiceCommandName"))
{
string commandName = NavigationContext.QueryString["voiceCommandName"];
int selectedNote;

switch (commandName)
{

case "AddNote":
//gestisci la creazione di una nuova nota
break;
case "OpenNote":
if (NavigationContext.QueryString.ContainsKey("number"))
{
selectedNote = int.Parse(NavigationContext.QueryString["number"]);
//carico la nota selezionata
}
break;
}
}
}

Come prima cosa, verifico che l’applicazione sia stata aperta in seguito a un comando,
controllando la presenza in query string di un parametro dal nome voiceCommand-
Name. In caso affermativo, all’interno di un blocco switch vado a gestire i vari casi
possibili, in base ai comandi che ho definito nel file VCD.
Nel caso in cui l’utente abbia scelto di aprire una nota, verifico anche la presenza del
parametro number, che contiene il riferimento alla nota scelta: se è presente, lo converto
in un numero intero, dopodiché lo utilizzo per andare a recuperare tale nota in base alla
logica della mia applicazione (ad esempio, potrei andare a cercarla all’interno di un
database locale).
Aggiornare una lista di frasi
L’esempio che abbiamo visto finora delle PhraseList non è molto realistico: nella
maggior parte dei casi, l’elenco di frasi tra cui l’utente potrebbe scegliere è dinamico,
ovvero cambia nel tempo, non è possibile fissare a priori i valori che saranno disponibili.
Nel nostro caso, l’utente può creare un numero virtualmente infinito di note all’interno
dell’applicazione, ognuna delle quali avrà un suo identificativo: tramite il VCD di esempio
che abbiamo definito, l’utente sarebbe in grado solamente di aprire le prime tre note, dato
che i numeri successivi al 4 non sono contenuti all’interno della Phrase-List e non
sarebbero, perciò, riconosciuti.
Per questo motivo le API dei servizi di riconoscimento vocale consentono di aggiornare
una PhraseList direttamente da codice, andando a sovrascrivere i valori dichiarati nel file
VCD: nel nostro esempio, ogni volta che l’utente inserisce una nuova nota dovremo
andare ad aggiornare la PhraseList per includere l’identificativo della stessa.
Nell’esempio, possiamo vedere come fare:
private async void OnUpdatePhraseListClicked(object sender, RoutedEventArgs e)
{
VoiceCommandSet commandSet = VoiceCommandService.InstalledCommandSets["NotesCommandSet"];
await commandSet.UpdatePhraseListAsync("number", newstring[] {"1", "2", "3", "4", "5"});
}

Tramite la proprietà InstalledCommandSets della classe VoiceCommandService siamo in grado di


recuperare il set di comandi desiderato dal nostro file VCD. Per farlo utilizziamo il nome
del set, specificato all’interno dell’attributo Name dell’elemento CommandSet.
Dopodiché è sufficiente richiamare il metodo UpdatePhraseListAsync() sull’oggetto di tipo
VoiceCommandSet restituito, passando l’identificativo della PhraseList da aggiornare (il
valore dell’attributo Label nel file VCD) e l’elenco di valori da includere nella lista, sotto
forma di collezione di stringhe.
Nell’esempio, vengono passati dei valori fissi: in un caso reale, avremmo passato gli
identificativi di tutte le note presenti nel database della nostra applicazione. È importante
ricordare, infatti, che questo metodo, nonostante contenga la parola Update al suo interno,
in realtà non aggiorna la lista, ma la sovrascrive: di conseguenza, ogni volta che l’utente
inserirà una nuova nota dovremo passare come secondo parametro del metodo l’elenco
degli identificativi di tutte le note presenti nel database e non solamente quello della nuova
nota che è stata appena inserita.
Dialogare con l’applicazione: leggere un testo utilizzando la funzione TTS
(Text-To-Speech)
Fino a questo momento abbiamo visto come interpretare i comandi che permettono
all’utente di aprire l’applicazione indipendentemente dalla schermata in cui si trova,
tramite la pressione prolungata del pulsante Start. Una volta, però, che l’applicazione è
aperta, si aprono diversi nuovi scenari: una delle possibilità che ci mettono a disposizione
le Speech API è quella di far leggere un testo al sintetizzatore vocale integrato, tramite un
meccanismo chiamato TTS (Text-To-Speech). In questo modo potremo dialogare e
interagire con l’utente senza costringerlo a guardare lo schermo.
L’utilizzo base è molto semplice, come possiamo vedere da questo esempio:
private async void OnSpeakClicked(object sender, RoutedEventArgs e)
{
SpeechSynthesizer synth = newSpeechSynthesizer();
await synth.SpeakTextAsync("Questo è un testo");
}

La classe SpeechSyntesizer espone il metodo SpeakTextAsync(), che accetta semplicemente come


parametro il testo da leggere.
Esiste, però, un metodo più evoluto per leggere un testo: sfruttando, cioè, lo Speech
Synthesis Markup Language (SSML). Si tratta di uno standard promosso da W3C per
l’interazione vocale ed è basato su XML: tramite una serie di tag e attributi è possibile
intervenire sul modo in cui una frase viene pronunciata, cambiandone, ad esempio,
l’intonazione, la velocità o aggiungendo delle pause.
Ecco un esempio di definizione di un file SSML:
<?xmlversion="1.0"?>
<speak xmlns="http://www.w3.org/2001/10/synthesis"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xml:lang="it"
version="1.0">

Questo è un testo letto a velocità normale.


<break/>
<prosodyrate="x-slow"> Questo, invece, è un testo letto molto lentamente</prosody>

</speak>

Il nodo principale di un file SSML è speak: gli attributi che vedete nell’esempio sono tutti
fissi, a parte xml:lang, che rappresenta la lingua a cui si riferisce il file.
Dopodiché, all’interno del nodo speak, si inserisce il testo che deve essere letto: esattamente
come si fa con una pagina HTML, nella quale, all’interno del testo, si includono dei tag
per formattare in maniera diversa alcune sezioni (ad esempio, una parola in grassetto), in
un file SSML possiamo racchiudere alcune sezioni all’interno di un tag, che definisce il
modo in cui la frase sarà pronunciata.
Nell’esempio vediamo due tag facenti parte dello standard SSML: break, che serve per
inserire una pausa nella lettura, e prosody, che permette di modificare la pronuncia del testo.
Nello specifico, tramite l’attributo rate possiamo impostare la velocità di lettura: con il
valore x-slow il testo sarà letto molto lentamente.
Ci sono due modi per sfruttare lo standard SSML all’interno di un’applicazione Windows
Phone: utilizzando un file esterno oppure caricando la stringa direttamente da codice.
Il primo caso può essere utilizzato nel momento in cui abbiamo dei testi fissi, che non
subiscono variazioni durante l’utilizzo dell’applicazione. Per sfruttare questo sistema
occorre innanzitutto creare un file XML all’interno del progetto, facendo clic con il tasto
destro sullo stesso in Visual Studio e scegliendo, dalla finestra Add new item, la tipologia
XML file.
Dopo aver definito il file SSML, possiamo caricarlo da codice in questo modo:
private async void OnSpeakClicked(object sender, RoutedEventArgs e)
{
SpeechSynthesizer synth = newSpeechSynthesizer();
await synth.SpeakSsmlFromUriAsync(new Uri("ms-appx:///SSML.xml"));
}

Analogamente a ciò che abbiamo visto per quanto riguarda il file VCD, anche in questo
usiamo un URL il cui protocollo è ms-appx:// per specificare un percorso all’interno del
nostro progetto, che passiamo al metodo SpeakSsmlFromUriAsync().
La seconda strategia è quella di caricare il testo da leggere a runtime, direttamente nel
codice: è la più indicata per scenari in cui il testo è dinamico (ad esempio, viene inserito
dall’utente o cambia in base al contesto).
In tal caso, il metodo da utilizzare si chiama SpeakSsmlAsync() e accetta in ingresso il file
SSML sotto forma di stringa, come nell’esempio:
private async void OnSpeakClicked(object sender, RoutedEventArgs e)
{
SpeechSynthesizer synth = new SpeechSynthesizer();

string textToRead = "<speak version=\"1.0\"";


textToRead += " xmlns=\"http://www.w3.org/2001/10/synthesis\"";
textToRead += " xml:lang=\"it\">";
textToRead += " <prosody rate=\"x-slow\"> Questo, invece, è un testo letto molto
lentamente</prosody>";
textToRead += "</speak>";

await synth.SpeakSsmlAsync(textToRead);
}

In entrambi i casi il risultato sarà lo stesso.


Negli esempi abbiamo visto l’utilizzo dei tag prosody e break. Quali sono gli altri tag utili a
nostra disposizione?
• Emphasis permette di specificare, tramite l’attributo level, l’enfasi da dare al testo
che viene letto;
• Voice permette di personalizzare alcune proprietà della voce, tramite gli attributi:
– Age, per modificare l’età;
– Gender, per scegliere il sesso;
– Xml:lang per utilizzare una lingua differente da quella scelta dall’utente;
• Say-as consente di specificare, utilizzando l’attributo interpret-as, come deve essere
interpretato il testo racchiuso all’interno di questo tag: alcuni esempi sono date per le
date; ordinal e cardinal per i numeri ordinali e cardinali; characters per fare lo
spelling; telephone per un numero di telefono.
Nella creazione di un file SSML vi verrà in aiuto Visual Studio: grazie alle dichiarazioni
dei namespace all’interno del nodo speak, si attiverà l’Intellisense, che vi suggerirà di
volta in volta gli attributi che potrete usare e i possibili valori.
Dialogare con l’applicazione: riconoscere la voce dell’utente
Nei paragrafi precedenti abbiamo visto le modalità con cui l’utente può aprire
un’applicazione, indipendentemente da quello che sta facendo, tramite i comandi vocali,
semplicemente tenendo premuto il pulsante Start per attivare il riconoscimento.
Il dialogo con l’utente, però, potrebbe continuare anche una volta che l’applicazione è
stata aperta: ritornando all’esempio dell’applicazione di gestione delle note, potremmo
invitare l’utente a dettare il testo della nota invece che a scriverlo, oppure farlo interagire
con l’applicazione stessa tramite comandi vocali.
Le Speech API ci mettono a disposizione due strade per implementare questi scenari.
Vediamole in dettaglio.
Fornire un supporto visuale all’utente: la classe SpeechRecognizerUI
La classe SpeechRecognizerUI consente allo sviluppatore di far riconoscere il testo dettato
dall’utente offrendo un supporto visuale analogo a quello nativo del sistema operativo: una
schermata terrà informato l’utente su quello che sta succedendo, sul fatto che la
registrazione è in corso e, opzionalmente, gli darà la possibilità di riascoltare il testo
dettato e di chiedere conferma che sia stato riconosciuto correttamente.
Al centro c’è la classe SpeechRecognizerUI, che rappresenta la schermata di dialogo con
l’utente. Le impostazioni più importanti, che ci permettono di personalizzare l’esperienza
d’uso, sono contenute all’interno della proprietà Settings della classe; vediamole di seguito:
• ListenText: rappresenta il testo, evidenziato con un carattere più grande, che indica
all’utente cosa l’applicazione si aspetta. Nell’immagine, è il testo ”Dettami la nota”;
• Example è un testo aggiuntivo, che può essere mostrato sotto il ListenText e che aiuta
l’utente a capire cosa deve fare. Nell’immagine, è il testo ”pronuncia il testo”;
• ReadoutEnabled: quando questa proprietà è impostata a true, l’applicazione leggerà per
conferma all’utente il testo che è stato interpretato al termine del riconoscimento;
• ShowConfirmation:
quando questa proprietà è impostata a true, al termine del
riconoscimento l’utente ha la possibilità di annullare l’operazione.
Figura 7.6 - La schermata di dialogo con l’utente durante il riconoscimento vocale.

Ecco un esempio di utilizzo:


private async void OnStartRecordingClicked(object sender, RoutedEventArgs e)
{
SpeechRecognizerUI sr = newSpeechRecognizerUI();
sr.Settings.ListenText = "Dettami la nota";
sr.Settings.ExampleText = "pronuncia il testo";
sr.Settings.ReadoutEnabled = false;
sr.Settings.ShowConfirmation = true;

SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();


if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
{
RecordedText.Text = result.RecognitionResult.Text;
}
}

Nel momento in cui abbiamo configurato l’oggetto di tipo SpeechRecognizerUI possiamo


avviare il riconoscimento tramite il metodo asincrono RecognizeWithUIAsync(). Quando
l’utente ha terminato di dettare il testo, otteniamo in ritorno un oggetto di tipo
SpeechRecognitionUIResult, la cui proprietà ResultStatus ci comunica l’esito dell’operazione:
• SpeechRecognitionUIStatus.Succeeded significa che l’operazione è andata a buon fine e il testo
è stato riconosciuto correttamente;
• SpeechRecognitionUIStatus.Busy significa che il telefono era occupato in quel momento (ad
esempio, per una telefonata o perché era in corso un altro riconoscimento vocale) e
non è stato in grado di elaborare la richiesta;
• SpeechRecognitionUIStatus.Cancelled
significa che l’utente ha annullato l’operazione,
premendo il pulsante Cancel presente nella schermata durante l’ascolto;
• SpeechRecognitionUIStatus.Preempted significa che l’operazione è iniziata correttamente, ma
poi è stata interrotta da un evento, come la ricezione di una telefonata;
• SpeechRecognitionUIStatus.PrivacyPolicyDeclined: l’utilizzo dei servizi vocali è subordinato
all’accettazione di una policy per la privacy, dato che il riconoscimento viene
elaborato da un servizio online di Microsoft. Questa policy viene proposta all’utente
la primissima volta che tenta di utilizzare i servizi vocali: nel caso l’avesse rifiutata,
questo è il tipo di errore che riceviamo. Se l’operazione è andata a buon fine,
all’interno della proprietà RecognitionResult.Text del risultato troviamo il testo dettato
dall’utente: nell’esempio, lo mostriamo semplicemente sullo schermo.
Riconoscere semplicemente un testo: la classe SpeechRecognizer
La classe SpeechRecognizer permette di riconoscere un testo analogamente a quanto visto con
la classe SpeechRecognizerUI, ma senza un ausilio visuale in supporto all’utente:
l’applicazione rimarrà semplicemente in ascolto del testo e, nel momento in cui capisce
che l’utente ha finito di parlare, elaborerà il risultato e lo restituirà pronto per essere
utilizzato.
Di conseguenza, l’utilizzo è ancora più semplice di quanto visto in precedenza, dato che
non c’è la necessità di personalizzare e controllare l’interfaccia visuale:
private async void OnStartRecordingWithoutUIClicked(object sender, RoutedEventArgs e)
{
SpeechRecognizer recognizer = newSpeechRecognizer();
SpeechRecognitionResult result = awaitrecognizer.RecognizeAsync();
RecordedText.Text = result.Text;
}

Dopo aver creato una nuova istanza della classe SpeechRecognizer, chiamiamo il metodo
asincrono RecognizeAsync() che ci restituisce direttamente un oggetto di tipo
SpeechRecognitionResult, la cui proprietà Text contiene il testo che è stato riconosciuto.
Come configurare il riconoscimento vocale
Con entrambe le classi per il riconoscimento vocale abbiamo la possibilità di configurare
alcuni parametri che ci permettono di personalizzarne il funzionamento.
Tali impostazioni sono contenute all’interno della proprietà Settings della classe
SpeechRecognizer (nel caso della classe SpeechRecognizerUI, sono contenute all’interno
dell’oggetto Recognizer) e sono:
• InitialSilenceTimeout è il tempo massimo di silenzio che viene atteso prima di annullare
l’operazione di riconoscimento, espresso con un TimeSpan. Il valore predefinito è 5
secondi: questo significa che, se viene avviata l’operazione e il sistema non riconosce
alcuna voce per 5 secondi, il riconoscimento sarà considerato fallito;
• BabbleTimeout è anch’esso un valore espresso con un TimeSpan e rappresenta il tempo
massimo che il riconoscimento attende prima di terminare l’operazione nel momento
in cui non riesce più a distinguere chiaramente una voce, ma solo rumore di fondo. Il
valore predefinito è 0: nel momento in cui il rumore prende il posto della voce
dell’utente, il riconoscimento viene automaticamente terminato;
• EndSilenceTimeout è sempre un valore di tipo TimeSpan e rappresenta la durata del silenzio
che il riconoscimento attende prima di considerare conclusa l’operazione. Il valore
predefinito è 150 millisecondi: una volta che l’utente ha terminato di parlare,
l’applicazione attenderà 150 millisecondi di silenzio prima di completare
l’operazione.
Ecco un esempio di personalizzazione di queste impostazioni:
private async void OnStartRecordingClicked(object sender, RoutedEventArgs e)
{
SpeechRecognizerUI sr = newSpeechRecognizerUI();
sr.Settings.ListenText = "Dettami la nota";
sr.Settings.ExampleText = "pronuncia il testo";
sr.Settings.ReadoutEnabled = false;
sr.Settings.ShowConfirmation = true;

sr.Recognizer.Settings.InitialSilenceTimeout = TimeSpan.FromSeconds(6.0);
sr.Recognizer.Settings.BabbleTimeout = TimeSpan.FromSeconds(4.0);
sr.Recognizer.Settings.EndSilenceTimeout = TimeSpan.FromSeconds(1.2);

SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();


if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
{
RecordedText.Text = result.RecognitionResult.Text;
}
}
Gestire grammatiche personalizzate
Come impostazione predefinita il riconoscimento vocale di Windows Phone utilizza una
grammatica piuttosto vasta, in grado di riconoscere la maggior parte delle parole di uso
comune. Esiste un’altra grammatica disponibile tra quelle predefinite, specifica per le
ricerche sul web, che contiene, infatti, molti termini tecnici di uso comune utilizzati
quando l’utente sta effettuando una ricerca su Internet.
Per utilizzare questa grammatica bisogna agire sulla proprietà Grammars della classe
SpeechRecognizer (la classe SpeechRecognizerUI è contenuta all’interno dell’oggetto Recognizer, così
come abbiamo visto nel paragrafo precedente per quanto riguarda le impostazioni del
riconoscimento).
Tale oggetto espone il metodo AddGrammarFromPredefinedType(), che permette di caricare uno
dei due database predefiniti:
private async void OnStartRecordingWithoutUIClicked(object sender, RoutedEventArgs e)
{
SpeechRecognizer recognizer = new SpeechRecognizer();
recognizer.Grammars.AddGrammarFromPredefinedType("WebSearch", SpeechPredefinedGrammar.
WebSearch);
SpeechRecognitionResult result = await recognizer.RecognizeAsync();
RecordedText.Text = result.Text;
}

Il metodo AddGrammarFromPredefinedType richiede due parametri: il primo è il nome


identificativo della grammatica (è una stringa a piacimento), mentre il secondo
rappresenta il database che vogliamo caricare, tramite un valore dell’enumeratore
SearchPredefinedGrammar. I valori possibili sono WebSearch e Dictation: il primo rappresenta il set
di parole specifico per il web; il secondo, invece, il set predefinito (e che, quindi, non è
necessario impostare esplicitamente).
Queste due tipologie di grammatica sono perfette per quando l’utente deve dettare veri e
propri testi, come nell’esempio dell’applicazione di gestione delle note: l’utente ha la
possibilità di dettare il testo della nota, perciò la grammatica da utilizzare deve essere la
più vasta possibile. Ci sono, però, due svantaggi: è necessario che il telefono abbia
connettività (dato che il database viene caricato in tempo reale da Internet) e i tempi di
caricamento sono più lunghi, in virtù del fatto che il set di parole è molto corposo.
In alcuni casi, invece, si ha la necessità di far riconoscere all’utente un set limitato di
parole: se riprendiamo l’esempio dell’applicazione di gestione delle note, potremmo voler
chiedere all’utente, terminata la dettatura, se vuole salvarla oppure scartarla. In tal caso,
mettere a disposizione l’intera grammatica italiana sarebbe inutile: è sufficiente proporre
le sole parole ”salva” e ”annulla”. In questo modo, inoltre, ridurremo drasticamente la
possibilità che l’utente sbagli, dato che il riconoscimento vocale accetterà solamente
questi due termini.
Per raggiungere questo scopo l’oggetto Grammars mette a disposizione il metodo
AddGrammarForList(), che accetta in ingresso la lista dei comandi supportati. Vediamone un
esempio:
private async void OnStartRecordingWithCustomGrammar(object sender, RoutedEventArgs e)
{
SpeechRecognizer recognizer = new SpeechRecognizer();
string[] commands = new[] { "salva", "annulla" };
recognizer.Grammars.AddGrammarFromList("customCommands", commands);
SpeechRecognitionResult result = await recognizer.RecognizeAsync();
if (result.Text == "salva")
{
//salvo la nota
}
else if (result.Text == "annulla")
{
//scarto la nota
}
Else
{
MessageBox.Show("Comando non riconosciuto");
}
}

Nell’esempio andiamo a definire una collezione contenente i nostri comandi, utilizzando


un array di stringhe che rappresenta la nostra grammatica: in questo caso, contiene solo le
parole salva e annulla.
Dopodiché utilizziamo il metodo AddGrammarFromList() dell’oggetto SpeechRecognizer, passando
una stringa che identifica la nostra grammatica e la collezione di parole.
A questo punto possiamo avviare il riconoscimento in maniera tradizionale: la differenza,
in questo caso, è che saranno riconosciute solamente le due parole salva e annulla.
Qualsiasi altra parola sarà scartata e il risultato, all’interno della proprietà Text del risultato
dell’operazione, sarà una stringa vuota.
Esiste un terzo modo per gestire le proprie grammatiche: tramite un file esterno, sfruttando
un altro standard definito da W3C chiamato Speech Recognition Grammar
Specification, abbreviato in SRGS.
Lavorare con i file SRGS
Visual Studio supporta questa tipologia di file in modo nativo: facendo clic con il tasto
destro sul nostro progetto e scegliendo l’opzione Add new item, avremo la possibilità di
includere un file di tipo SRGS Grammar: sarà creato un file XML con, all’interno, un
esempio di definizione.
Vediamo ora alcuni degli scenari più comuni supportati da questo standard.
<grammarversion="1.0"xml:lang="it-IT"root="rootRule"tag-format="semantics/1.0"
xmlns="http://www.w3.org/2001/06/grammar"
xmlns:sapi="http://schemas.microsoft.com/Speech/2002/06/SRGSExtensions">

<ruleid="rootRule">
<item>Apri</item>

<item>Carica</item>
</rule>

</grammar>

Un file SRGS ha, come nodo principale, il tag grammar: sono due gli attributi importanti
che, come sviluppatori, dovremo modificare. Il primo è xml:lang, che rappresenta la lingua
di riferimento di questo file; il secondo è root, che rappresenta quale degli elementi di tipo
rule contenuti all’interno del file sarà attivo. Tutti gli altri non saranno presi in
considerazione, a meno che non li includeremo utilizzando un riferimento (vedremo a
breve come fare).
All’interno del nodo grammar possiamo trovare uno o più elementi rule, che rappresentano
un set di parole o frasi supportate dalla nostra grammatica. Nell’esempio vediamo la
definizione più semplice in assoluto: l’inclusione di una serie di parole che il sistema sarà
in grado di riconoscere, identificate dal tag item. Con questa definizione siamo in grado di
raggiungere lo stesso risultato che, in precedenza, abbiamo ottenuto caricando le parole
supportate a runtime tramite il metodo AddGrammarFromList().
Con i file SRGS possiamo, però, fare di più e creare delle frasi di senso compiuto, in base
alle interazioni che prevediamo di utilizzare all’interno della nostra applicazione. Ad
esempio, è possibile utilizzare il tag one-of per specificare che le parole definite al suo
interno, nella frase, sono intercambiabili: è possibile utilizzare l’una o l’altra,
indifferentemente.
<grammarversion="1.0"xml:lang="it-IT"root="rootRule"tag-format="semantics/1.0"
xmlns="http://www.w3.org/2001/06/grammar"
xmlns:sapi="http://schemas.microsoft.com/Speech/2002/06/SRGSExtensions">

<ruleid="openAction">
<one-of>
<item>Apri</item>
<item>Carica</item>
</one-of>
</rule>

</grammar>

Per creare delle frasi è possibile aggiungere altri elementi di tipo rule, da includere poi
all’interno della rule principale utilizzando il tag ruleref, come nell’esempio:
<grammarversion="1.0"xml:lang="it-IT"root="rootRule"tag-format="semantics/1.0"
xmlns="http://www.w3.org/2001/06/grammar"
xmlns:sapi="http://schemas.microsoft.com/Speech/2002/06/SRGSExtensions">

<ruleid="openAction">
<one-of>
<item>Apri</item>
<item>Carica</item>

</one-of>
</rule>

<ruleid="fileWords">
<one-of>
<item> nota </item>
<item> promemoria </item>
</one-of>
</rule>

<ruleid="rootRule">
<rulerefuri="#openAction" />
<one-of>
<item>la</item>
<item>il</item>
</one-of>
<rulerefuri="#fileWords" />
</rule>

</grammar>

Prendiamo in esame la rule definita dall’id rootRule: l’ordine in cui sono inseriti gli elementi
al suo interno coincide con l’ordine con cui l’utente dovrà pronunciare le parole per
rispettare la nostra grammatica.
Utilizzando il tag ruleref possiamo includere un’altra regola semplicemente assegnando al
valore dell’attributo uri il nome della stessa preceduto da un cancelletto. Nell’esempio, la
grammatica si aspetta, come prima parola pronunciata dall’utente, una qualsiasi delle
parole definite all’interno della rule con id openAction.
Con la grammatica che vediamo nell’esempio, frasi come ”Apri la nota” o ”Carica il
promemoria” o ”Carica la nota” saranno accettate. Qualsiasi parola al di fuori di quelle
incluse o pronunciate in un ordine non corretto (ad esempio, ”La nota apri”) non sarà
riconosciuta e porterà, come risultato, un valore vuoto.
Una volta che abbiamo definito il nostro file SRGS, lo possiamo caricare tramite il metodo
AddGrammarFromUri() della classe Grammars, con la stessa sintassi che abbiamo usato in
precedenza per caricare il file VCD:
private async void OnStartRecordingWithCustomFile(object sender, RoutedEventArgs e)
{
SpeechRecognizer recognizer = new SpeechRecognizer();
recognizer.Grammars.AddGrammarFromUri("CustomGrammar", new Uri("ms-appx:///CustomGrammar.xml"));
SpeechRecognitionResult result = await recognizer.RecognizeAsync();
if (result.Text != string.Empty)
{
RecordedText.Text = result.Text;
}
else
{
MessageBox.Show("Testo non riconosciuto");
}
}
Per concludere: dialogare con l’applicazione
Come avete potuto vedere, le possibilità offerte dalle Speech API sono davvero numerose.
Le funzionalità che abbiamo visto sono state trattate singolarmente, ma in realtà possono
essere combinate tra di loro, così da creare un vero e proprio dialogo con l’utente,
dandogli la possibilità di interagire con l’applicazione senza dover mai guardare lo
schermo e utilizzare il touch screen.
Riprendendo quanto visto nel corso del capitolo, ecco un esempio di come potrebbe
apparire la funzionalità di creazione di una nuova nota all’interno dell’applicazione:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("voiceCommandName") &&
NavigationContext.QueryString["voiceCommandName"] == "AddNote")
{
SpeechRecognizerUI sr = newSpeechRecognizerUI();
sr.Settings.ListenText = "Dettami la nota";
sr.Settings.ExampleText = "Detta il testo";
sr.Settings.ReadoutEnabled = true;
sr.Settings.ShowConfirmation = true;

SpeechRecognitionUIResult result = await sr.RecognizeWithUIAsync();


if (result.ResultStatus == SpeechRecognitionUIStatus.Succeeded)
{
Note.Text = result.RecognitionResult.Text;

SpeechSynthesizer synth = new SpeechSynthesizer();

await synth.SpeakTextAsync("puoi pronunciare salva o annulla");


SpeechRecognizer recognizerCommand = new SpeechRecognizer();

var customCommands = new[] { "salva", "annulla" };


recognizerCommand.Grammars.AddGrammarFromList ("customCommands", customCommands);
SpeechRecognitionResult commandresult = await recognizerCommand.RecognizeAsync();
if (commandresult.Text.ToLower() == "salva")
{
await synth.SpeakTextAsync("La nota è stata salvata");
//salvo la nota nel database
}
else if (commandresult.Text.ToLower() == "annulla")
{
await synth.SpeakTextAsync("Operazione annullata");
Note.Text = string.Empty;
}
}
}
}

Nel momento in cui l’utente atterra nella pagina di creazione di una nuova nota,
verifichiamo se questa è stata raggiunta in seguito a una navigazione manuale (l’utente ha
fatto tap sul pulsante per creare una nuova nota dalla pagina principale) o se, invece, è
stata attivata in seguito a un comando vocale.
In tal caso, proseguiamo l’interazione con l’utente utilizzando la voce, chiedendogli,
tramite la classe SpeechRecognizerUI, di dettare il testo della nota; se il testo è stato
riconosciuto con successo, lo mostriamo sullo schermo e chiediamo all’utente se vuole
salvare o annullare l’operazione, utilizzando, per ricevere la risposta, la classe
SpeechRecognizer con una grammatica personalizzata, in grado di supportare solamente le
parole salva e annulla.
In entrambi i casi, diamo un feedback di conferma all’utente sempre utilizzando la voce,
grazie alla classe SpeechSynthesizer.
Wallet: un portafoglio digitale
Windows Phone 8 include una nuova applicazione di sistema, preinstallata su tutti i
device: Wallet. Come si evince dal nome stesso, tale applicazione nasce per consentire agli
utenti di gestire una versione digitale del loro portafoglio, per memorizzare coupon, carte
di pagamento ecc.
Sono tre le tipologie di informazioni che un’applicazione può creare all’interno
dell’applicazione wallet:
• i deal, ovvero coupon che un negozio può regalare agli utenti per avere dei buoni
sconto o per riscattare un premio;
• le membership card, ovvero le carte fedeltà, che i negozi tipicamente distribuiscono
per raccogliere punti a ogni acquisto;
• le carte di pagamento, ovvero carte di credito oppure carte prepagate.
L’aspetto interessante dell’applicazione Wallet è il suo forte legame con la tecnologia
NFC; in Italia non è ancora possibile sfruttare questa funzionalità al meglio, dato che
nessun negozio consente di utilizzarla per effettuare pagamenti.
Ci sono, però, alcuni scenari, come i deal o le carte fedeltà, che invece possono essere
utilizzati anche in Italia, grazie alla possibilità, ad esempio, di associare un codice a barre
alla carta.
Anche lo store di Windows Phone è in grado di sfruttare l’applicazione Wallet: potete
utilizzare una delle carte di pagamento disponibili sia per effettuare acquisti dallo store, sia
direttamente da un’applicazione tramite l’In App Purchase. Potete notare questa
funzionalità semplicemente avviando l’applicazione Wallet: troverete già inclusa la carta
di credito legata al vostro Microsoft account, che utilizzate abitualmente per effettuare
acquisti sullo store.
Vediamo ore le modalità a nostra disposizione per integrarci con il Wallet.
Il file di manifest: le opzioni disponibili
Il file di manifest di Windows Phone mette a disposizione ben tre capability dedicate
all’applicazione Wallet, che abbiamo trattato brevemente nei primi capitoli. Rivediamole
in dettaglio, per evidenziare quali utilizzare in base al nostro scenario:
• ID_CAP_WALLET è la capability base, che deve essere abilitata per qualsiasi
utilizzo delle API per l’interazione con il Wallet;
• ID_CAP_WALLET_PAYMENTINSTRUMENTS è la capability che deve essere
abilitata se la nostra applicazione consente l’utilizzo e l’aggiunta di carte di
pagamento;
• ID_CAP_WALLET_SECUREELEMENT è la capability legata ai pagamenti
tramite NFC. In realtà, almeno in Italia, non avrete la possibilità di utilizzarla, dato
che deve essere legata a una Secure SIM, ovvero una SIM (disponibile al momento
solo negli Stati Uniti) che viene associata in maniera sicura a una carta di credito,
consentendo così all’utente di effettuare pagamenti nei negozi impiegando
direttamente il telefono. Inoltre, l’utilizzo di tale capability necessita di un accordo
con gli operatori mobili che offrono l’opportunità di utilizzare una Secure SIM.
Un’opportunità di marketing: registrare l’applicazione come Wallet
extension
Come sviluppatori abbiamo la possibilità di registrare un’applicazione che fa uso delle
funzionalità Wallet all’interno del file di manifest, nella sezione Extensions che abbiamo
imparato a conoscere nel corso del libro.
Perché parlo di un’opportunità di marketing? Perché, al contrario di altri scenari che
abbiamo visto e che vedremo nel corso del libro, in questo caso la registrazione all’interno
del manifest non è obbligatoria: è possibile utilizzare comunque i servizi dedicati
all’integrazione con il Wallet senza aggiungerla. Il vantaggio è solamente di visibilità:
nell’elenco di opzioni che compare nel momento in cui l’utente preme il pulsante add per
aggiungere una nuova carta troverete la voce other, nella quale vengono elencate tutte le
applicazioni presenti sullo Store in grado di integrarsi con il Wallet. Aggiungere la
dichiarazione nel file di manifest consente alla vostra applicazione di essere presente
all’interno di questo elenco.
La dichiarazione deve essere inclusa all’interno del nodo Extensions: se non è presente,
dovete crearlo manualmente dopo la sezione Tokens. Vi ricordo, inoltre, che la gestione
delle estensioni non è supportata dall’editor visuale di Visual Studio: dovrete fare clic con
il tasto destro sul file WMAppManifest.xml e scegliere l’opzione View code per
modificare a mano l’XML.
Ecco un esempio di dichiarazione:
<Extension ExtensionName="Wallet_app_other" ConsumerID="{3860f72b-b8fc-4503-b922-
c25f315da9c3}"TaskID="_default"/>

L’attributo ExtensionName permette di specificare che tipo di integrazione con il Wallet


vogliamo utilizzare:
• Wallet_app_other per applicazioni generiche, in grado di gestire più tipologie di servizi;
• Wallet_app_loyalty per la gestione delle carte fedeltà;
• Wallet_app_membership per la gestione di membership card;
• Wallet_app_transit per la gestione di carte legate a servizi di trasporto (ad esempio,
abbonamenti per i mezzi pubblici);
• Wallet_app_payment per la gestione di carte per il pagamento.
L’attributo ConsumerID, invece, contiene l’application id della vostra applicazione: potete
recuperarlo sia dall’editor visuale del file di manifest (all’interno della sezione
Packaging) sia direttamente dall’XML stesso. È infatti contenuto nell’attributo ProductID
del nodo App.
Il valore dell’attributo TaskID, invece, è fisso e deve sempre essere valorizzato con il valore
_default, come nell’esempio.

Attenzione! Non è possibile testare questa feature senza pubblicare


l’applicazione: dato che la sezione other mostra solamente applicazioni
NOTA presenti sullo store e non installate sul telefono, anche dopo aver aggiunto la
dichiarazione nel file di manifest e fatto il deploy sull’emulatore o sul telefono
non troveremo la nostra applicazione nell’elenco.
Gestire i deal
I deal sono rappresentati dalla classe Deal, che espone tantissime proprietà per impostare
le varie informazioni: se volete un’idea della varietà di opzioni a disposizione, è
sufficiente che apriate l’applicazione Wallet e proviate a inserirne uno nuovo.
Ecco un esempio di definizione e salvataggio di un deal:
private async void OnCreateDealClicked(object sender, RoutedEventArgs e)
{
Deal deal = new Deal("MyDeal")
{
DisplayName = "Buono sconto",
MerchantName = "Il mio supermercato",
Description = "Con questo buono hai diritto ad uno sconto del 5% sulla tua spesa",
ExpirationDate = newDateTime(2⊘13, 6, 1),
};

await deal.SaveAsync();
}

Le uniche proprietà obbligatorie sono DisplayName (ovvero il nome del deal) e MerchantName
(ovvero il nome del negozio o della società che ha emesso il coupon), oltre
all’identificativo del deal stesso, che viene passato nel costruttore della classe. Ce ne sono,
però, tantissime altre, che permettono di descrivere il coupon: la validità (tramite le
proprietà StartDate e EndDate), le condizioni d’uso (TermsAndConditions) o il sito web dell’offerta
(OfferWebsite) o del venditore (IssuerWebsite).
Una proprietà importante è IsUsed, che permette di determinare se il deal sia stato utilizzato
o meno.
È possibile personalizzare anche l’aspetto visivo del deal, assegnandogli un logo che lo
renda più facile da identificare: in tal caso, sono tre i formati richiesti, da assegnare tramite
le proprietà Logo99x99, Logo159x159 e Logo336x336 (il suffisso della proprietà rappresenta la
risoluzione richiesta).
Esiste un’altra proprietà di tipo immagine molto importante: BarcodeImage, che rappresenta il
codice a barre del coupon. È infatti una valida alternativa a NFC: tramite questa immagine
il venditore sarà in grado di elaborare il vostro coupon semplicemente passando il lettore a
barre sullo schermo del vostro telefono.
Tutte le immagini richieste dalla classe Deal sono di tipo BitmapImage. Tipicamente, le
immagini non fanno parte del progetto locale, ma sono memorizzate in un server remoto:
in uno scenario reale, infatti, la vostra applicazione avrà un backend di servizi in grado di
gestire i coupon. Ecco un esempio di come, sfruttando la classe WebClient e l’Async
Targeting Pack (di cui abbiamo parlato nel Capitolo 2), siamo in grado di scaricare
un’immagine da un server remoto e trasformarla in un oggetto BitmapImage, che possa poi
essere assegnato alla proprietà BarcodeImage.
public async Task<BitmapImage> GetImage(string uri)
{
WebClient client = newWebClient();
Stream stream = await client.OpenReadTaskAsync(uri);
BitmapImage image = new BitmapImage();
image.SetSource(stream);
return image;
}
A questo punto è sufficiente valorizzare la proprietà BarcodeImage in questo modo:
deal.BarcodeImage = await GetImage("http://www.qmatteo.com/fake-barcode.png");

Figura 7.7 - Un deal creato da un’applicazione di terze parti.

L’ultimo aspetto importante di cui parlare è il modo in cui il deal può interagire con
l’applicazione che l’ha creato: se osservate con attenzione l’immagine sopra riportata,
noterete il pulsante open app e, poco sotto, il nome dell’applicazione che ha generato il
deal. Premendo quel pulsante, come comportamento predefinito, la pagina principale
dell’applicazione sarà semplicemente aperta.
Come sviluppatori avete la possibilità di controllare il comportamento di questa
interazione, specificando un diverso URL di navigazione tramite la proprietà NavigationUri
della classe Deal, tramite la quale potete impostare sia una pagina di destinazione differente
(ad esempio, nel caso in cui abbiate una pagina specifica dedicata ai deal) sia dei
parametri in query string che, ad esempio, vi aiutino a identificare il deal che è stato
selezionato. Ecco un esempio:
private async void OnCreateDealClicked(object sender, RoutedEventArgs e)
{
Deal deal = new Deal("MyDeal")
{
DisplayName = "Buono sconto",
MerchantName = "Il mio supermercato",
Description = "Con questo buono hai diritto ad uno sconto del 5% sulla tua spesa",
ExpirationDate = new DateTime(2013, 6, 1),
NavigationUri = new Uri("/DealDetail.xaml?id=12", UriKind.Relative)
};

await deal.SaveAsync();
}

In questo modo, ogni qualvolta l’utente premerà il pulsante open app, l’applicazione si
aprirà direttamente sulla pagina DealDetail.xaml, nella quale, tramite la classe
NavigationContext che abbiamo utilizzato più volte nel corso del libro, potrete recuperare il
valore del parametro id all’interno dell’evento OnNavigatedTo:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("id"))
{
string id = NavigationContext.QueryString["id"];
//recupero il deal con l'id ricevuto
}
}
Gestire le carte fedeltà
La gestione delle carte fedeltà (o membership card) è molto simile a quella vista in
precedenza per i deal, con una differenza: non possiamo salvare direttamente una carta
fedeltà nella nostra applicazione, ma dobbiamo utilizzare un apposito chooser, per motivi
di sicurezza. Con questo meccanismo, infatti, l’utente sarà preventivamente informato che
un’applicazione sta cercando di salvare una nuova carta nel Wallet e avrà la possibilità di
modificarne i dettagli prima di confermare.
La classe di riferimento si chiama WalletTransactionItem e, analogamente alla classe Deal,
richiede in fase di definizione un identificatore univoco ed espone diverse proprietà per
personalizzare la carta fedeltà.
private async void OnAddMembershipCardClicked(object sender, RoutedEventArgs e)
{
WalletTransactionItem item = new WalletTransactionItem("MyMembershipCard")
{
DisplayName = "Carta fedeltà",
IssuerName = "Il mio supermercato",
ExpirationDate = newDateTime(2013, 6, 1),
NavigationUri = new Uri("/DealDetail.xaml?id=12", UriKind.Relative),
Logo99x99 = await GetImage("http://www.mywebsite.com/99x99.png"),
Logo159x159 = await GetImage("http://www.mywebsite.com/159x159.png"),
Logo336x336 = await GetImage("http://www.mywebsite.com/336x366.png")

};
AddWalletItemTask task = new AddWalletItemTask();
task.Item = item;
task.Completed += task_Completed;
task.Show();
}

È importante sottolineare come, in questo caso, siamo obbligati a specificare anche il logo
della carta fedeltà nelle tre risoluzioni richieste (99x99, 159x159 e 336x336), valorizzando
le tre proprietà Logo99x99, Logo159x159 e Logo336x336: in caso contrario, si scatenerà
un’eccezione in fase di salvataggio.
Anche nel caso delle carte fedeltà è presente, all’interno dell’applicazione Wallet, un
pulsante open app per aprire l’applicazione che l’ha generata: esattamente come per i
deal, tramite la proprietà NavigationUri possiamo decidere quale pagina della nostra
applicazione deve essere aperta e includere eventuali parametri.
Figura 7.8 - Il messaggio che viene proposto all’utente al tentativo di salvataggio di una carta fedeltà.

Come potete notare dal codice di esempio, non possiamo salvare la carta fedeltà
direttamente come facevamo con i deal, ma dobbiamo utilizzare uno dei nuovi chooser
messi a disposizione da Windows Phone 8: AddWalletItemTask.
Questo chooser richiede come parametro, tramite la proprietà Item, l’oggetto di tipo
WalletTransactionItem che rappresenta la carta fedeltà da inserire. Richiamando il metodo
Show(), il chooser viene attivato e all’utente viene proposto di salvare la carta all’interno del
Wallet. Il chooser espone anche un evento Completed, al quale possiamo sottoscriverci per
sapere l’esito dell’operazione (l’utente, infatti, potrebbe aver scelto di annullare il
salvataggio).
void task_Completed(object sender, AddWalletItemResult e)
{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show("Salvataggio completato");
}
else
{
MessageBox.Show("Salvataggio fallito");
}
}

L’utente in questo caso ha la possibilità di premere il pulsante Save, per confermare


l’operazione; Review, per vedere i dettagli della carta fedeltà ed eventualmente
modificarli prima di salvarla; Back per annullare l’operazione.

Attenzione! Se esplorate i metodi esposti dalla classe WalletTransactionItem,


troverete anche il metodo SaveAsync(). In realtà, si tratta di una conseguenza del
fatto che tutti gli oggetti che rappresentano elementi che è possibile salvare nel
NOTA
wallet ereditano dalla classe WalletBase: il metodo, però, non è implementato
per la classe WalletTransactionItem e, se tenterete di utilizzarlo, si scatenerà
un’eccezione.
Gestire le carte di pagamento
La gestione delle carte di pagamento è molto simile a quella che abbiamo visto per le carte
fedeltà: anche in questo caso, infatti, non potremo salvarle direttamente, ma dovremo
passare tramite il chooser AddWalletItemTask.
Le carte di pagamento sono rappresentate dalla classe PaymentInstrument, che segue le stesse
regole viste in precedenza: è richiesto un identificatore nel costruttore.
private async void OnAddPaymentCardClicked(object sender, RoutedEventArgs e)
{
PaymentInstrument item = new PaymentInstrument("MyPaymentCard")
{
DisplayName = "Carta di pagamento",
IssuerName = "Il mio supermercato",
ExpirationDate = new DateTime(2013, 6, 1),
NavigationUri = new Uri("/DealDetail.xaml?id=12", UriKind.Relative),
Logo99x99 = await GetImage("http://research.microsoft.com/en-us/projects/
touchdevelop/TouchDevelop99x99.png"),
Logo159x159 = await GetImage("http://research.microsoft.com/en-us/projects/
touchdevelop/TouchDevelop159x159.png"),
Logo336x336 = await GetImage("http://research.microsoft.com/en-us/projects/
touchdevelop/TouchDevelop336x336.png"),
PaymentInstrumentKinds = PaymentInstrumentKinds.Credit
};

AddWalletItemTask task = newAddWalletItemTask();


task.Item = item;
task.Completed+=task_Completed;
task.Show();
}

Anche in questo caso, il logo, espresso nelle tre differenti risoluzioni, è obbligatorio; in
più, è presente un altro campo obbligatorio, ovvero PaymentInstrumentKinds. Questa proprietà
accetta uno dei valori dell’enumeratore PaymentInstrumentKinds e serve per specificare il tipo
di carta di pagamento: di credito (Credit), di debito (Debit) o entrambe (CreditAndDebit).
Una delle caratteristiche che contraddistinguono le carte di pagamento rispetto alle altre è
la possibilità di utilizzarle per effettuare pagamenti; di conseguenza, la classe
PaymentInstrument mette a disposizione una serie di proprietà per gestirne il bilancio:
DisplayAvailableBalance e DisplayAvailableCredit (per gestire il fondo disponibile), DisplayBalance (per
gestire l’ammontare speso) e DisplayCreditLimit (per gestire l’eventuale limite di spesa).
Inoltre, le carte di pagamento introducono il concetto di transazione: l’applicazione può
caricare dei pagamenti sulla carta, in modo che compaiano nella sezione transaction del
Wallet. Non si tratta di transazioni economiche vere e proprie: il meccanismo permette
semplicemente di aggiungere le informazioni sulla transazione allo storico, ma non viene
messo in atto alcun meccanismo per effettuare il pagamento. Inoltre, le proprietà che
abbiamo visto poco fa per gestire il bilancio della carta non vengono aggiornate in
automatico: dovrete farlo voi manualmente, se avete questo requisito. Vediamo come fare:
private async void OnPayClicked(object sender, RoutedEventArgs e)
{
PaymentInstrument myCard = Wallet.FindItem("MyPaymentCard") asPaymentInstrument;
if (myCard != null)
{
WalletTransaction transaction = new WalletTransaction();
transaction.DisplayAmount = "5⊘ €";
transaction.Description = "Spesa";
transaction.TransactionDate = DateTime.Now;
myCard.TransactionHistory.Add(string.Format("Spesa effettuata il {⊘}", DateTime.
Now.ToShortDateString()), transaction);

await myCard.SaveAsync();
}
}

La prima operazione da fare è recuperare la carta di pagamento alla quale vogliamo


aggiungere la transazione: per questo scopo la classe Wallet ci mette a disposizione il
metodo FindItem, che ci permette di cercare una qualsiasi delle carte che abbiamo aggiunto
(non solo di pagamento, ma anche deal e carte fedeltà) in base al nome. Come potete
vedere, è necessario un cast: questo perché Wallet è la classe base che permette di accedere
a qualsiasi tipologia di carta aggiunta alla nostra applicazione e perciò il metodo FindItem
restituisce un generico WalletItem. Sta a noi convertirlo nel tipo che dobbiamo gestire, in
questo caso PaymentInstrument.

Figura 7.9 - Le transazioni di una carta di pagamento all’interno del Wallet.

Un altro aspetto molto importante consiste nel verificare che il metodo FindItem abbia
effettivamente restituito qualcosa: l’utente, infatti, potrebbe aver eliminato la carta
direttamente dall’applicazione Wallet e, di conseguenza, questa non sarebbe più
disponibile. Se volessimo utilizzare un altro approccio per recuperare gli elementi del
Wallet creati dalla nostra applicazione, potremmo usare un altro metodo della classe Wallet,
ovvero GetItemsAsync(): è un metodo asincrono che restituisce una collezione di tutti i
WalletItem disponibili.

Torniamo a parlare della transazione, che è rappresentata dalla classe WalletTransaction: le


proprietà principali (che rappresentano le informazioni da mostrare nel Wallet) sono
DisplayAmount (l’ammontare della transazione), Description (la descrizione della spesa) e
TransactionDate (la data in cui è avvenuto il pagamento). Come potete notare, DisplayAmount è
una stringa: è un ulteriore segno del fatto che non c’è alcuna gestione automatica dei
pagamenti.
La transazione così viene definita viene poi aggiunta alla collezione TransactionHistory della
classe PaymentInstrument, che contiene il suo storico delle transazioni: infine, salviamo le
modifiche tramite il metodo SaveAsync() della classe, come abbiamo fatto in precedenza,
quando abbiamo creato i vari elementi del Wallet.
Mandare delle notifiche all’utente tramite il Wallet
Una delle caratteristiche più interessanti per le applicazioni di terze parti che interagiscono
con il Wallet è la possibilità di mandare delle notifiche, che vengono mostrate nella pagina
di dettaglio dell’elemento.
Sono tanti gli scenari in cui questa funzionalità può tornare utile: ad esempio, potremmo
notificare agli utenti in possesso di una carta fedeltà che c’è in corso una promozione
particolare riservata a loro.
Per questo scopo tutti gli oggetti che rappresentano elementi che possono essere aggiunti
al Wallet espongono la proprietà Message: quando questa viene valorizzata, il testo del
messaggio viene mostrato di fianco al logo.
È possibile anche gestire l’interazione con l’utente, che potrebbe voler fare tap sul
messaggio per saperne di più: in tal caso abbiamo a disposizione la classe
MessageNavigationUri, che rappresenta l’URL (con eventuali parametri) della pagina a cui
vogliamo portare l’utente.
Ecco un esempio:
private async void OnAddMessageClicked(object sender, RoutedEventArgs e)
{
WalletTransactionItem myMembershipCard = Wallet.FindItem("MyMembershipCard")
asWalletTransactionItem;
myMembershipCard.Message = "Vieni in negozio a scoprire la nuova promozione!";
myMembershipCard.MessageNavigationUri = new Uri("/Message.xaml?Id=5", UriKind.Relative);
await myMembershipCard.SaveAsync();
}

Nell’esempio recuperiamo la carta fedeltà inserita in precedenza (potevamo utilizzare


anche un deal o una carta di pagamento) e valorizziamo le proprietà Message e
MessageNavigationUri. Infine, salviamo nuovamente l’oggetto all’interno del Wallet.
Figura 7.10 - Il messaggio mostrato nel dettaglio di una membership card.

Esiste anche un tipo di notifica più invasiva, da utilizzare nel caso in cui dobbiate
comunicare qualcosa di urgente all’utente (ad esempio, che sta raggiungendo il limite
della sua carta di credito):
myMembershipCard.SetUserAttentionRequiredNotification(true);

Utilizzando il metodo SetUserAttentionRequiredNotification (e passando il valore true per mostrarla


o false per annullarla), verrà mostrato un messaggio di avviso sotto l’elemento,
direttamente nella pagina principale del Wallet. In più, nel momento in cui l’utente entrerà
nel dettaglio, comparirà un messaggio che lo avviserà che l’applicazione collegata alla
carta ha riportato un problema e che occorre aprirla per avere maggiori informazioni.
Figura 7.11 - Una membership card con una notifica.
Aggiornare gli elementi del Wallet in background
Windows Phone 7.5 ha introdotto i background agent, ovvero un meccanismo per
consentire alle applicazioni di terze parti di effettuare operazioni in background, ovvero
quando non sono in esecuzione.
Tra le nuove tipologie di background agent introdotte in Windows Phone 8 ce n’è uno
legato all’applicazione Wallet, che consente di utilizzare le API che abbiamo visto in
questo paragrafo in background: in questo modo, periodicamente, l’agent è in grado di
controllare, ad esempio, la presenza di un nuovo deal o di un messaggio per l’utente e di
aggiornare i relativi elementi del Wallet in maniera silente, senza che l’utente sia costretto
ad aprire l’applicazione per forzare l’aggiornamento.
Questo scenario verrà trattato in maniera approfondita nel Capitolo 10, dedicato ai
background agent.
In conclusione
Windows Phone è un sistema operativo ricco di applicazioni native, che prendono il nome
di hub in quanto fungono da punto di aggregazione di dati provenienti da diverse fonti.
Pensiamo all’hub People, in grado non solamente di mostrare i contatti della rubrica, ma
anche di interagire con i principali social network.
Come sviluppatori ci viene data la possibilità di interagire con queste applicazioni e con i
dati dell’utente, come i contatti o la libreria fotografica.
In questo capitolo abbiamo visto tutte le possibilità a nostra disposizione: abbiamo iniziato
con una panoramica dei launcher e dei chooser, una serie di classi che ci permettono,
rispettivamente, di demandare un’operazione al sistema operativo e di accedere ai dati
esposti dalle applicazioni.
Abbiamo visto poi due novità introdotte specificatamente in Windows Phone 7.5: le API
per accedere direttamente alla rubrica e al calendario, senza passare per i chooser, che
richiedono un’interazione diretta dell’utente per importare i dati. Su questo argomento c’è
stato spazio anche per una novità di Windows Phone 8: la classe ContactStore, che
consente di creare delle rubriche private all’interno dell’applicazione.
Un altro argomento importante trattato nel capitolo è il riconoscimento vocale: grazie alle
Speech API, siamo in grado di far dialogare l’utente con l’applicazione, senza costringerlo
a usare il touch screen o a guardare lo schermo per interagire.
Abbiamo parlato anche di web: l’interazione tra il mondo mobile e quello web è sempre
più forte al giorno d’oggi; ecco, perciò, che nelle nostre applicazioni abbiamo la
possibilità di includere una vista web sfruttando l’engine di Internet Explorer 10 e quindi
con pieno supporto ad HTML 5, CSS 3 e accelerazione hardware.
Il capitolo si conclude con la trattazione di una delle nuove applicazioni native di
Windows Phone 8, Wallet, che consente la gestione di un portafoglio digitale: non si tratta
di una semplice applicazione, dato che, come abbiamo potuto vedere, sono numerose le
possibilità di integrazione con le applicazioni per gli sviluppatori.
Sviluppare applicazioni multimediali

Windows Phone è un device dalle spiccate doti multimediali, pertanto


scenari di utilizzo di audio e video all’interno delle applicazioni sono
molto comuni.
Nel Capitolo 7 abbiamo visto il modo più semplice per riprodurre file multimediali,
ovvero utilizzando un launcher che, dato in input un file audio o video, si fa carico per noi
di gestirne la riproduzione.
A volte, però, si ha la necessità di avere maggiore controllo sulla riproduzione e di
personalizzare l’esperienza d’uso: in tal caso l’SDK ci mette a disposizione alcune classi
per integrare la riproduzione audio e video direttamente all’interno dell’applicazione,
senza appoggiarci al player nativo del sistema operativo.
In questo capitolo vedremo in dettaglio come utilizzare queste API. In più, vedremo in
maniera approfondita altri aspetti del sistema operativo strettamente legati all’ambito
multimediale: l’utilizzo della fotocamera e lo sviluppo di applicazioni in grado di
integrarsi con gli hub Photos e Music di Windows Phone.
Riprodurre audio e video
Il sistema operativo mette a disposizione due meccanismi per raggiungere questo scopo:
utilizzare il controllo MediaElement oppure ricorrere alla classe SoundEffect di XNA.

Il controllo MediaElement
Il controllo MediaElement può essere incluso nello XAML e permette la riproduzione di file
audio e video sia locali che remoti. Si tratta di una specie di placeholder invisibile: il
controllo non viene, infatti, visualizzato sullo schermo e non mostra alcun pulsante per la
gestione della riproduzione.
Il controllo MediaElement nasce, infatti, se si ha la necessità di includere audio e video nella
propria applicazione e di avere pieno controllo sulla riproduzione.
La prima cosa da fare è includere il controllo nello XAML, come nell’esempio:
<MediaElement x:Name="Media" Source="Audio.mp3" />

La proprietà Source specifica qual è il file audio o video da riprodurre: nell’esempio, il file è
incluso all’interno del progetto di Visual Studio (attenzione: la proprietà Build Action
deve essere impostata a Content, altrimenti il file non sarà riconosciuto).
È possibile far puntare la proprietà Source anche a una risorsa web: in questo caso, il
controllo MediaElement si occuperà in automatico di gestire il download e il buffering.
Ecco un esempio:
<MediaElement Source="http://www.example.com/audio.mp3" x:Name="Media" />

Infine, è possibile valorizzare la proprietà Source anche con un file memorizzato


nell’Isolated Storage (ad esempio, scaricato preventivamente da Internet). In tal caso,
però, non possiamo farlo dallo XAML ma dobbiamo agire da codice, per recuperare il file
dallo storage (sotto forma di Stream) e passarlo al controllo sfruttando il metodo SetSource.
Ecco un esempio:
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("audio.mp3");
IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
Media.SetSource(stream.AsStreamForRead());
}

Gestire la riproduzione
Il controllo MediaElement espone dei metodi per gestire i vari stati della riproduzione: Play() ,
Pause() e Stop().

Esiste, poi, una proprietà chiamata CurrentState che ci restituisce, tramite uno dei valori
dell’enumeratore MediaElementState, lo stato corrente della riproduzione, che possiamo usare
per aggiornare l’interfaccia grafica della nostra applicazione di conseguenza. Ad esempio,
possiamo avere un unico pulsante per gestire la riproduzione e la pausa, che, in base al
valore della proprietà CurrentState, chiami il metodo corretto e aggiorni l’etichetta del
pulsante di conseguenza.
private void btnPlay_Click(object sender, RoutedEventArgs e)
{
if (Media.CurrentState == MediaElementState.Stopped ||
Media.CurrentState == MediaElementState.Paused)
{
Media.Play();
btnPlay.Content = "Pause";
}
else if (Media.CurrentState == MediaElementState.Playing)
{
Media.Pause();
btnPlay.Content = "Play";
}
}

Abbiamo anche la possibilità di gestire in modo automatico il cambio dello stato della
riproduzione, nel caso in non cui si verifichi in seguito all’intervento dell’utente (ad
esempio, il file multimediale è terminato).
Per farlo, abbiamo a disposizione l’evento CurrentStateChanged, che possiamo sottoscrivere e
che viene scatenato ogni volta che lo stato della riproduzione cambia:
private void Media_CurrentStateChanged(object sender, RoutedEventArgs e)
{
if (Media.CurrentState == MediaElementState.Stopped ||
Media.CurrentState == MediaElementState.Paused)
{
btnPlay.Content = "Play";
}
}

E se si tratta di una risorsa scaricata direttamente dal web? Nulla cambia, il modo di
gestire la riproduzione è sempre lo stesso; la differenza è che abbiamo a disposizione delle
proprietà e dei metodi che ci permettono di conoscere lo stato del download della risorsa
ed, eventualmente, notificarlo all’utente, ad esempio, tramite una progress bar. L’evento da
sottoscrivere si chiama DownloadProgressChanged, nel quale si può andare a leggere il valore
della proprietà DownloadProgress esposta dal controllo MediaElement.
private void Media_DownloadProgressChanged(object sender, RoutedEventArgs e)
{
double value = Media.DownloadProgress;
Progress.Value = value * 1⊘⊘;
}

Il controllo MediaElement è in grado di riprodurre numerosi formati audio e video: WAV,


MP3, WMA, 3GP, MP4, AAC, WMV, M4V, nonché audio e video protetto da DRM.
Tale controllo, utilizzato all’interno di un’applicazione Windows Phone, nasce soprattutto
per la riproduzione video o per la riproduzione di audio di breve durata: se lo scopo
principale della vostra applicazione è la riproduzione di audio di lunga durata, come
canzoni o streaming (ad esempio, un’applicazione legata a una radio online), è meglio
utilizzare le API per la riproduzione audio in background, che tratteremo nel Capitolo 10,
dedicato al multitasking. Il controllo MediaElement, infatti, non supporta la riproduzione in
background: nel momento in cui l’utente chiuderà o sospenderà l’applicazione, la
riproduzione terminerà.
Una caratteristica importante del controllo MediaElement consiste nell’essere in grado di
riprodurre un solo file alla volta, indipendentemente da quanti controlli MediaElement sono
presenti nella pagina.

La classe SoundEffect
La classe SoundEffect fa parte delle librerie di XNA, nello specifico del namespace
Microsoft.XNA.Framework.Audio, e permette di riprodurre un sample audio.
Fa parte del framework di XNA poiché nasce per la riproduzione di effetti audio
all’interno di videogiochi: esplosioni, rumori, jingle sono solo alcuni esempi di suoni che
possono essere riprodotti con questa classe.
Può essere usata, però, anche all’interno di applicazioni Windows Phone per riprodurre
effetti audio in seguito a un evento o a un’azione dell’utente.
C’è, però, una differenza molto importante da tenere a mente: il Windows Runtime è un
ambiente basato sugli eventi; l’applicazione tipicamente rimane “silente” fino al momento
in cui qualche evento scatena l’esecuzione di un’operazione; gli eventi possono essere
indifferentemente scatenati dall’utente (la pressione di un pulsante, ad esempio) oppure
legati al ciclo di vita dell’applicazione (ad esempio, il caricamento della pagina). Il mondo
XNA si comporta, invece, in maniera differente: esiste, infatti, un ciclo che viene ripetuto
a una frequenza molto elevata (e che coincide con i frame per secondo), durante il quale il
gioco viene aggiornato; la grafica viene ridisegnata, i suoni vengono riprodotti, gli eventi
vengono gestiti e così via.
Per questo motivo anche la classe SoundEffect deve rispettare questo meccanismo, anche se
viene utilizzata all’interno di un’applicazione Windows Phone. Per questo scopo esiste
una classe chiamata FrameworkDispatcher che serve per elaborare i messaggi che vengono
inseriti all’interno di un’apposita coda e che espone il metodo Update(). All’interno di un
gioco XNA, tale metodo viene invocato automaticamente al termine di ogni ciclo;
all’interno di un’applicazione Windows Phone occorre ricreare manualmente questo ciclo,
sfruttando, ad esempio, un timer implementato con la classe DispatcherTimer.
In realtà, la classe SoundEffect ci consente una “scorciatoia”: dato che non verrà utilizzata
per riprodurre effetti audio continui, ma legati a un singolo evento (ad esempio, la
pressione di un pulsante), non abbiamo la necessità di implementare un timer, ma sarà
sufficiente chiamare il metodo FrameworkDispatcher.Update() prima di riprodurre il sample.
Fatta questa doverosa premessa, vediamo un esempio di utilizzo della classe SoundEffect:
public partial class Sound : PhoneApplicationPage
{
private SoundEffect sound;
public Sound()
{
InitializeComponent();
Stream stream = Application.GetResourceStream(new
Uri("audio.wav", UriKind.Relative)).Stream;
sound = SoundEffect.FromStream(stream);
}

private void Button_Click(object sender, RoutedEventArgs e)


{
FrameworkDispatcher.Update();
sound.Play();
}
}

In questo caso, viene creato un oggetto di tipo SoundEffect partendo dallo stream di un file
audio chiamato audio.wav incluso all’interno del progetto di Visual Studio.
Alla pressione del pulsante viene semplicemente invocato il metodo Play() della classe
SoundEffect.

E se volessimo intervenire sul sample audio, per applicare, ad esempio, degli effetti? In
questo caso dobbiamo creare una nuova istanza del sample audio, sfruttando la classe
SoundEffectInstance. Questa classe crea una copia del sample audio e ci permette di
manipolare alcune proprietà come Pan, Pitch e Volume.
Per ottenere un’istanza dell’oggetto SoundEffectInstance ci basta utilizzare il metodo
CreateInstance() esposto dalla classe SoundEffect. Una volta manipolato il suono, possiamo poi
riprodurlo con il metodo Play, esattamente come prima.
Ecco lo stesso esempio di prima, ma modificato per utilizzare un’istanza della classe
SoundEffectInstance a cui è stato applicato un effetto di pitch:

public partial class Sound : PhoneApplicationPage


{
private SoundEffect sound;

public Sound()
{
InitializeComponent();
Stream stream = Application.GetResourceStream(new
Uri("audio.wav", UriKind.Relative)).Stream;
sound = SoundEffect.FromStream(stream);
}

private void Button_Click(object sender, RoutedEventArgs e)


{
SoundEffectInstance instance = sound.CreateInstance();
instance.Pitch = .5⊘f;
FrameworkDispatcher.Update();
instance.Play();
}
}
Utilizzare il microfono
Il microfono di cui sono dotati i telefoni può essere utilizzato, oltre che, ovviamente, per
parlare durante le telefonate, anche per registrare: l’SDK mette a disposizione alcune API
per registrare l’audio e salvarlo all’interno dell’applicazione.
Queste API, però, fanno parte di XNA e richiedono, perciò, gli stessi accorgimenti che
abbiamo visto nel paragrafo precedente, dedicato alla riproduzione audio e video: la
differenza, in questo caso, è data dal fatto che il “trucchetto” che ci ha permesso di usare
la classe SoundEffect senza utilizzare un timer non funzionerà.
La prima cosa da fare, perciò, è creare un DispatcherTimer che, ciclicamente, chiami il metodo
FrameworkDispatcher.Update() fintanto che la registrazione è in corso.

La classe per accedere al microfono si chiama Microphone e fa parte del namespace


Microsoft.Xna.Framework.Audio: tale classe espone un’istanza statica, chiamata Default,
per interagire con il microfono standard incorporato nei device.
Tale classe espone un evento chiamato BufferReady, che viene scatenato ogni qualvolta il
buffer di registrazione è pronto e contiene dei dati che possiamo salvare. Per questo scopo,
utilizzeremo un oggetto di tipo MemoryStream, che rappresenta uno stream in memoria in cui
andremo a salvare la registrazione.
private MemoryStream recording;
private DispatcherTimer timer;

// Constructor
public MainPage()
{
InitializeComponent();
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(50);
timer.Tick += new EventHandler(timer_Tick);
Microphone.Default.BufferReady += new
EventHandler<EventArgs>(mic_BufferReady);
}

void timer_Tick(object sender, EventArgs e)


{
FrameworkDispatcher.Update();
}

A questo punto inseriamo nell’interfaccia grafica un pulsante per avviare la registrazione,


uno per fermarla e uno per riprodurre quanto registrato.
Avviare la registrazione significa avviare sia il timer, sia il microfono invocando il metodo
Start. Viceversa, fermare la registrazione significa fermare sia il timer sia il microfono
tramite il metodo Stop.
private void OnRecordButtonClicked(object sender, RoutedEventArgs e)
{
recording = new MemoryStream();
timer.Start();

Microphone.Default.Start();
}

private void OnStopButtonClicked(object sender, RoutedEventArgs e)


{
timer.Stop();
Microphone.Default.Stop();
}

Nell’evento BufferReady, invece, andiamo a scrivere nell’oggetto di tipo MemoryStream i dati


registrati dal microfono:
private void mic_BufferReady(object sender, EventArgs e)
{
Microphone microphone = Microphone.Default;
//calcolo la dimensione del buffer
int size = microphone.GetSampleSizeInBytes(microphone.BufferDuration);

//creo il buffer
byte[] buffer = new byte[size];

//recupero i dati registrati dal microfono


int data = Microphone.Default.GetData(buffer);

//salvo i dati in memoria


recording.Write(buffer, 0, data);
}

Una volta che i dati sono disponibili nell’oggetto di tipo MemoryStream, possiamo procedere
a salvarli nello storage o nel cloud, oppure semplicemente riprodurre la registrazione. Il
risultato è, infatti, un file in formato wav; nell’esempio seguente riproduciamo l’audio
registrato utilizzando la classe SoundEffect vista in precedenza:
private void OnPlayButtonClicked(object sender, RoutedEventArgs e)
{
if (Microphone.Default.State == MicrophoneState.Stopped
&& recording != null)
{
SoundEffect effect = new SoundEffect(recording.
ToArray(), Microphone.Default.SampleRate,
AudioChannels.Stereo);
FrameworkDispatcher.Update();
effect.Play();
}
}

Vi faccio notare la proprietà State esposta dall’istanza di default del microfono, che ci
informa se il microfono sta ancora registrando o meno: la riproduzione audio viene avviata
solo nel momento in cui il microfono non sta più registrando dati.
La fotocamera
Uno degli accessori hardware richiesti dalle specifiche minime per i device Windows
Phone è la fotocamera, che offre la possibilità di usare il telefono come una macchina
fotografica. La maggior parte dei device in commercio, inoltre, è dotata anche di
fotocamera frontale.
L’accesso alla fotocamera da parte delle applicazioni di terze parti è una delle novità
introdotte in Windows Phone 7.5: nella prima versione del sistema operativo era possibile
accedervi solo usando l’apposito chooser, che consentiva unicamente di scattare foto da
importare nell’applicazione. Non era possibile l’accesso ai dati “grezzi” catturati dalla
fotocamera, il che rendeva di fatto impossibile lo sviluppo di applicazioni fotografiche
complesse oppure di realtà aumentata.
In questo capitolo vedremo come utilizzare la nuova classe PhotoCaptureDevice introdotta in
Windows Phone 8 per interagire sia con la fotocamera principale, sia con quella frontale.
Questa API è differente da quella che era disponibile in Windows Phone 7.5, ma offre
migliori performance e maggiori possibilità di personalizzazione: è possibile controllare il
focus, l’esposizione, il flash e tanto altro ancora. Le API che andremo a utilizzare si
trovano nel namespace Windows.Phone.Media.Capture.

L’utilizzo della fotocamera richiede che nel file di manifest sia stata abilitata la
capability ID_CAP_ISV_CAMERA e che, tra i Requirements, siano abilitate
NOTA le tipologie di fotocamere che vogliamo utilizzare
(ID_REQ_FRONTCAMERA per quella frontale e
ID_REQ_REARCAMERA per quella sul retro).

La prima cosa è definire nello XAML un controllo che conterrà il viewfinder, ovvero
l’immagine in tempo reale ripresa dalla fotocamera. Per farlo, useremo i Brush di cui
abbiamo parlato nel Capitolo 2, dedicato alle basi di XAML.
Nello specifico, andiamo a definire un controllo Canvas e impostiamo la proprietà Background
utilizzando un VideoBrush, ovvero un brush in grado di mostrare immagini in movimento.
Nel dettaglio, andremo a impostare come sorgente del VideoBrush l’immagine ripresa dalla
fotocamera.
<Canvas Height="4⊘⊘" Width="4⊘⊘">
<Canvas.Background>
<VideoBrush x:Name="video" Stretch="UniformToFill">
<VideoBrush.RelativeTransform>
<CompositeTransform x:Name="previewTransform" CenterX=".5" CenterY=".5" />
</VideoBrush.RelativeTransform>
</VideoBrush>
</Canvas.Background>
</Canvas>>

Al VideoBrush applichiamo anche una trasformazione: tale passaggio è necessario per poter
orientare correttamente l’immagine ripresa che, altrimenti, sarebbe indipendente dalla
posizione del telefono nello spazio (portrait o landscape).
A questo punto possiamo, nel codice, recuperare l’istanza della fotocamera:
public partial class MainPage : PhoneApplicationPage
{
private PhotoCaptureDevice camera;

public MainPage()
{
InitializeComponent();
}

protected override async void OnNavigatedTo(NavigationEventArgs e)


{
Size resolution = PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensor
Location.Back).First();
camera = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back, resolution);
video.SetSource(camera);
previewTransform.Rotation = camera.SensorRotationInDegrees;
}
}

La prima operazione da fare è decidere a quale risoluzione vogliamo scattare le foto: per
questo motivo possiamo utilizzare il metodo PhotoCaptureDevice.GetAvailableCaptureResolutions,
specificando quale delle due fotocamere utilizzare tramite l’enumeratore CameraSensorLocation
(Back per quella sul retro, Front per quella frontale). Questa API ci restituisce una collezione
di oggetti di tipo Size, contenenti le proprietà Width e Height, che rappresentano le varie
risoluzioni supportate dalla fotocamera del telefono. Nell’esempio utilizziamo la
fotocamera alla massima risoluzione possibile: dato che la collezione contiene l’elenco
delle risoluzioni dalla più alta alla più bassa, ci basta prendere la prima. Dopodiché
andiamo a visualizzare nel VideoBrush lo stream ripreso dalla fotocamera (tramite il metodo
SetSource) e impostiamo la proprietà Rotation della trasformazione che abbiamo applicato con
il valore della rotazione rilevato dal sensore, tramite la proprietà SensorRotation. In questo
modo, l’immagine mostrata a schermo seguirà la rotazione del telefono.

Importante! Affinché il codice di esempio funzioni, dovrete aggiungere il


namespace Microsoft.Devices nella vostra classe: in questo modo avrete a
disposizione una variante del metodo SetSource del controllo VideoBrush che accetta
NOTA
come parametro un oggetto di tipo ICaptureCameraDevice (che è l’interfaccia base
da cui derivano tutte le API di Windows Phone 8 per interagire con la
fotocamera, tra cui PhotoCaptureDevice).

Nell’esempio abbiamo potuto accedere direttamente alla fotocamera senza fare controlli
preventivi: questo perché la fotocamera sul retro è uno dei dispositivi imposti da
Microsoft, per cui non esistono device sul mercato che non ne siano dotati. Lo stesso non
si può dire per la fotocamera frontale: in tal caso è buona prassi, prima di effettuare
qualsiasi operazione, verificare che questa sia presente, grazie alla proprietà
AvailableSensorLocation della classe PhotoCaptureDevice. Si tratta di una collezione che contiene
l’elenco di tutti i sensori disponibili.
protected override asyn cvoid OnNavigatedTo(NavigationEventArgs e)
{
if (PhotoCaptureDevice.AvailableSensorLocations.Contains(CameraSensorLocation.Front))
{
Size resolution = PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensor
Location.Front).First();
camera = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Front, resolution);
video.SetSource(camera);
previewTransform.Rotation = camera.SensorRotationInDegrees;
}
}

Nell’esempio, prima di inizializzare la fotocamera frontale e mostrarne lo stream a


schermo, ne verifichiamo la presenza.
Come gestire le opzioni della fotocamera
La classe PhotoCaptureDevice consente di impostare diverse opzioni della fotocamera, in
maniera molto più completa rispetto a quanto era possibile fare in Windows Phone 7.5.
Ciò è reso possibile dal metodo SetProperty(), che accetta due parametri: l’opzione che
vogliamo modificare e il valore che vogliamo assegnare.
Per quanto riguarda la fotocamera, esistono due classi che ci permettono di accedere alla
lista delle opzioni: KnownCameraGeneralProperties e KnownCameraPhotoProperties. La prima permette
di accedere alle impostazioni generiche, mentre la seconda a quelle specifiche per
l’acquisizione di foto; come vedremo più avanti, esiste una terza classe, specifica per
l’acquisizione di audio e video.
Ecco alcune delle proprietà più importanti:
• KnownCameraGeneralProperties.AutoFocusRange permette di specificare il range dell’autofocus,
accetta uno dei valori dell’enumeratore AutoFocusRange;
• KnownCameraGeneralProperties.PlayShutterSoundOnCapture abilita o disabilita la riproduzione di
un effetto sonoro nel momento in cui viene scattata una foto;
• KnownCameraPhotoProperties.ExposureCompensation
e KnownCameraPhotoProperties.ExposureTime per
personalizzare compensazione e tempo dell’esposizione;
• KnownCameraPhotoProperties.FlashMode per impostare la tipologia di flash che si vuole
utilizzare, tramite uno dei valori possibili dell’enumeratore FlashMode. È possibile
attivare (On) o disattivare (Off) manualmente il flash, oppure abilitare la riduzione
dell’effetto occhi rossi (RedEyeReduction) o, ancora, utilizzare la gestione automatica
(Auto);
• KnownCameraPhotoProperties.SceneMode permette di utilizzare uno dei filtri predefiniti della
fotocamera, tramite i valori dell’enumeratore CameraSceneMode. Alcuni esempi sono
Night (per foto notturne), Portrait e Landscape, e Sport (per foto in movimento).
Ecco un esempio di impostazione di alcune proprietà:
camera.SetProperty(KnownCameraPhotoProperties.SceneMode, CameraSceneMode.Night);
camera.SetProperty(KnownCameraPhotoProperties.FlashMode, FlashMode.RedEyeReduction);
camera.SetProperty(KnownCameraGeneralProperties.PlayShutterSoundOnCapture, true);

Alcune delle opzioni disponibili sono in sola lettura: non sono modificabili, ma
permettono solamente di conoscerne lo stato. Ad esempio, la proprietà
KnownCameraGeneralProperties.IsShutterSoundRequiredForRegion ci permette di sapere se il telefono è
in uso in una delle nazioni che, per legge, hanno imposto l’obbligo di riproduzione di un
effetto sonoro nel momento in cui viene scattata una foto.
In questo caso, dobbiamo utilizzare il metodo GetProperty per recuperarne il valore:
bool isShutterSoundRequiredForRegion = (bool)videoDevice.GetProperty(KnownCameraGeneral
Properties.IsShutterSoundRequiredForRegion);

Bisogna sempre ricordarsi di fare un cast, perché il metodo GetProperty()


restituisce un generico object.
Scattare una foto
Per acquisire una foto è necessario effettuare una serie di passaggi che comportano
l’inizializzazione di una sequenza contenente uno o più frame, ognuno dei quali
rappresenta una foto. In realtà, per un limite delle API, non è possibile al momento
scattare più foto in sequenza, perciò, come vedremo, saremo costretti a utilizzare sequenze
composte da un unico frame.
private async void OnTakePhotoClicked(object sender, RoutedEventArgs e)
{
CameraCaptureSequence cameraCaptureSequence = camera.CreateCaptureSequence(1);
StorageFile photo = await ApplicationData.Current.LocalFolder.
CreateFileAsync("picture.jpg",
CreationCollisionOption.ReplaceExisting);
var stream = await photo.OpenAsync(FileAccessMode.ReadWrite);
cameraCaptureSequence.Frames[⊘].CaptureStream = stream.GetOutputStreamAt(0);
await camera.PrepareCaptureSequenceAsync(cameraCaptureSequence);
await cameraCaptureSequence.StartCaptureAsync();

await stream.FlushAsync();
}

Il primo passo è inizializzare la sequenza, tramite il metodo CreateCaptureSequence() esposto


dalla classe PhotoCaptureDevice: come anticipato poco fa, siamo costretti a passare 1 come
parametro, dato che al momento è possibile creare solamente sequenze composte da un
solo frame.
Nell’esempio salviamo l’immagine scattata nello storage dell’applicazione: di
conseguenza, utilizzando le API di accesso allo storage (che approfondiremo nel Capitolo
9), creiamo un nuovo file di nome picture.jpg e ne recuperiamo lo stream.
Dopodiché impostiamo l’output dello stream (tramite l’extension method
GetOutputStreamAt(⊘)), che restituisce lo stream in scrittura partendo dall’inizio come
CaptureStream del primo (e unico) frame disponibile nella sequenza. In questo modo, nello
stream di output (ovvero il file nello storage) sarà scritto il flusso di dati catturato dalla
fotocamera. Infine inizializziamo la sequenza, chiamando il metodo
PrepareCaptureSequenceAsync() e passando come parametro la sequenza stessa, dopodiché
scattiamo la foto vera e propria, tramite il metodo asincrono StartCaptureAsync().
Entrambe le operazioni sono asincrone, di conseguenza sono precedute dalla parola chiave
await: in questo modo ci assicuriamo che l’operazione sia terminata prima di procedere
oltre.
Infine effettuiamo il “flush” del file, tramite il metodo asincrono FlushAsync(): in questo
modo ci assicuriamo che lo stream venga chiuso correttamente e il file venga memorizzato
nello storage.
Se proviamo l’applicazione e, con il tool Windows Phone Power Tools (di cui parleremo
nel Capitolo 9), andiamo a vedere i file memorizzati nello storage dell’applicazione,
troveremo il file picture.jpg, contenente la foto che abbiamo scattato.
Questo codice è testabile anche con l’emulatore: ovviamente, in tal caso, la foto scattata
sarà fittizia, dato che il flusso di dati ricevuto dalla classe PhotoCaptureDevice non è reale. Un
aspetto interessante di queste API è che il flusso di dati restituito dalla fotocamera include
anche una thumbnail della foto che è stata scattata, che ci può essere utile per
visualizzarla, ad esempio, all’interno dell’applicazione, dato le dimensioni notevolmente
inferiori rispetto all’originale (ad esempio, un’immagine scattata ad alta qualità delle
dimensioni di 3 MB genera una thumbnail di circa 150 KB).
Lo stream della thumbnail è contenuto all’interno della proprietà ThumbnailStream del frame:
di conseguenza, il codice per salvare nello storage anche la thumbnail è identico a quello
visto in precedenza; l’unica differenza è lo stream che andiamo a salvare.
private async void OnTakePhotoClicked(object sender, RoutedEventArgs e)
{
CameraCaptureSequence cameraCaptureSequence = camera.CreateCaptureSequence(1);

StorageFile thumb = await ApplicationData.Current.LocalFolder.


CreateFileAsync("thumb.jpg", CreationCollisionOption.ReplaceExisting);
var thumbStream = await thumb.OpenAsync(FileAccessMode.ReadWrite);

cameraCaptureSequence.Frames[⊘].ThumbnailStream = thumbStream.GetOutputStreamAt(0);

await camera.PrepareCaptureSequenceAsync(cameraCaptureSequence);
await cameraCaptureSequence.StartCaptureAsync();

await thumbStream.FlushAsync();
}

Questo codice può essere unito a quello precedente e, utilizzando una sola sequenza di
tipo CameraCaptureSequence, è possibile salvare su disco sia l’immagine alla massima
risoluzione che la thumbnail.

Salvare una foto nella libreria dell’utente


Una caratteristica tipica delle applicazioni che si sostituiscono a quella della fotocamera
nativa di Windows Phone è la necessità di salvare le foto scattate direttamente nella
libreria fotografica del telefono: per questo motivo esiste la classe MediaLibrary, che
consente di interagire con foto e audio memorizzati sul telefono. Nello specifico, la classe
MediaLibrary espone due metodi che fanno al caso nostro:

• SavePictureToCameraRoll() salva l’immagine nell’album chiamato Rullino (Camera roll


nella versione inglese), che è quello predefinito in cui vengono salvate le fotografie
scattate con l’applicazione nativa;
• SavePicture() salva, invece, l’immagine nell’album chiamato Foto salvate (Saved
pictures nella versione inglese), che è quello predefinito in cui vengono salvate le
immagini provenienti da fonti differenti dalla fotocamera (ad esempio, se salvate
un’immagine da un sito o presente come allegato in una e-mail).
Il codice per salvare una foto scattata con le API viste in precedenza direttamente nella
libreria dell’utente è piuttosto simile:
private async void OnTakePhotoClicked(object sender, RoutedEventArgs e)
{
CameraCaptureSequence cameraCaptureSequence = camera.CreateCaptureSequence(1);

MemoryStream stream = newMemoryStream();


cameraCaptureSequence.Frames[⊘].CaptureStream = stream.AsOutputStream();

await camera.PrepareCaptureSequenceAsync(cameraCaptureSequence);
await cameraCaptureSequence.StartCaptureAsync();
stream.Seek(⊘, SeekOrigin.Begin);

MediaLibrary library = newMediaLibrary();


library.SavePictureToCameraRoll("picture1", stream);
}

La differenza principale è che, in questo caso, non avendo la possibilità di salvare lo scatto
direttamente nello stream di un file sullo storage, dobbiamo salvarlo in memoria: per
questo motivo utilizziamo la classe MemoryStream.
Dopodiché è sufficiente creare un’istanza della classe MediaLibrary e richiamare uno dei due
metodi SavePictureToCameraRoll() o SavePicture() a seconda della necessità: in entrambi i casi i
parametri richiesti saranno il nome dell’immagine e lo stream di dati che la contiene.
È importante sottolineare la necessità, prima di salvare l’immagine, di riportare il
puntatore dello stream alla posizione originale, tramite il metodo Seek: in caso contrario,
dato che il puntatore è posizionato al termine dello stream e quindi non ci sarebbero
ulteriori dati al suo interno da scrivere, si scatenerà un’eccezione.
Sfruttare il tasto dedicato di Windows Phone
Tutti i device Windows Phone sono dotati di un tasto dedicato alla fotocamera, che viene
utilizzato per la messa a fuoco e per scattare foto o avviare la registrazione video
all’interno dell’applicazione nativa.
In più, questo tasto può essere utilizzato anche quando il device è bloccato per avviare
immediatamente l’utilizzo della fotocamera.
Possiamo sfruttare questo tasto anche all’interno delle nostre applicazioni, invece di
definire un pulsante personalizzato come abbiamo fatto nell’esempio precedente. In più, il
vantaggio di utilizzare il tasto hardware dedicato è che questo può assumere tre stati
possibili, che possiamo gestire e intercettare: tasto premuto, tasto rilasciato e tasto semi-
premuto (nell’applicazione nativa questo stato viene utilizzato per gestire la messa a fuoco
automatica).
Per utilizzare il tasto nelle nostre applicazioni è sufficiente sottoscrivere uno degli eventi
esposti dalla classe CameraButtons:
• ShutterKeyPressed: il pulsante è stato premuto;
• ShutterKeyReleased: il pulsante è stato rilasciato;
• ShutterKeyHalfPressed: il pulsante viene mantenuto premuto, ma con una pressione ridotta.
Nell’esempio sottostante, ci siamo sottoscritti all’evento ShutterKeyReleased (in cui andiamo a
inserire il codice visto in precedenza per acquisire la foto) e all’evento ShutterKeyHalfPressed
(in cui chiamiamo il metodo FocusAsync() , che avvia la procedura di messa a fuoco
automatica).
public partial classMainPage : PhoneApplicationPage
{
private PhotoCaptureDevice camera;

public MainPage()
{
InitializeComponent();
CameraButtons.ShutterKeyHalfPressed += CameraButtons_ShutterKeyHalfPressed;
CameraButtons.ShutterKeyReleased += CameraButtons_ShutterKeyReleased;
}

void CameraButtons_ShutterKeyReleased(object sender, EventArgs e)


{
//inizio l'acquisizione della foto, come visto nell'esempio precedente
}

async void CameraButtons_ShutterKeyHalfPressed(object sender, EventArgs e)


{
await camera.FocusAsync();
}
}
Registrare un video
In Windows Phone 7.5 era complicato gestire la registrazione video: per fortuna questo
scenario è stato notevolmente semplificato in Windows Phone 8, grazie all’introduzione di
una nuova serie API, che utilizza lo stesso approccio che abbiamo appena visto per
acquisire foto.

L’utilizzo della fotocamera per la registrazione video richiede che nel file di
NOTA manifest sia abilitata anche la capability ID_CAP_MICROPHONE, oltre ai
requisiti che sono già stati messi in evidenza per l’acquisizione di foto.

La classe di riferimento si chiama AudioVideoCaptureDevice e, derivando anch’essa


dall’interfaccia ICaptureCameraDevice, viene inizializzata nel modo visto in precedenza per la
classe PhotoCaptureDevice.
public partial class MainPage : PhoneApplicationPage
{
private AudioVideoCaptureDevice videoDevice;

public MainPage()
{
InitializeComponent();
}

protected override async void OnNavigatedTo(NavigationEventArgs e)


{
Size resolution = AudioVideoCaptureDevice.GetAvailableCaptureResolutions(Camera
SensorLocation.Back).First();
videoDevice = awaitAudioVideoCaptureDevice.OpenAsync(CameraSensorLocation.
Back, resolution);
video.SetSource(videoDevice);
previewTransform.Rotation = videoDevice.SensorRotationInDegrees;
}
}

Anche in questo caso, abbiamo a disposizione il metodo GetAvailableCaptureResolutions() per


ottenere le risoluzioni supportate, passando come parametro il tipo di fotocamera da
utilizzare, tramite l’enumeratore CameraSensorLocation. Dopodiché inizializziamo il flusso di
dati con il metodo OpenAsync e lo mostriamo nel VideoBrush inserito nello XAML.
Anche l’acquisizione video consente di modificare diverse impostazioni: oltre a quelle
generiche rappresentate dalla classe KnownCameraGeneralProperties, che abbiamo visto in
precedenza, ne troviamo anche di specifiche nella classe KnownCameraAudioVideoProperties,
come il frame rate e i parametri dell’encoding H264.
Quello che cambia, rispetto a quanto visto in precedenza per acquisire foto, è la procedura
di registrazione video.
public partial class MainPage : PhoneApplicationPage
{
private AudioVideoCaptureDevice videoDevice;
private IRandomAccessStream stream;
private StorageFile file;

public MainPage()
{
InitializeComponent();
}


private async void OnRecordVideoClicked(object sender, RoutedEventArgs e)
{
file = await ApplicationData.Current.LocalFolder.CreateFileAsync("video.wmv",
CreationCollisionOption.ReplaceExisting);
stream = await file.OpenAsync(FileAccessMode.ReadWrite);

videoDevice.StartRecordingToStreamAsync(stream);
}
}

Rispetto a quanto visto in precedenza, l’operazione è ancora più semplice: una volta creato
un file nello storage locale, ci limitiamo a chiamare il metodo StartRecordingToStreamAsync()
dell’oggetto di tipo AudioVideoCaptureDevice, passando come parametro lo stream dove salvare
il flusso video (nel nostro caso, il file creato nello storage).
Rispetto all’acquisizione di una foto, in questo caso abbiamo la necessità di gestire non
solo l’avvio, ma anche lo stop della registrazione: possiamo demandare questa
funzionalità, ad esempio, a un altro pulsante, tramite il metodo StopRecordingAsync().
private async void OnStopRecordingClicked(object sender, RoutedEventArgs e)
{
await videoDevice.StopRecordingAsync();
await stream.FlushAsync();
}

In questo caso ci preoccupiamo anche di gestire correttamente la chiusura dello stream del
file, tramite il metodo FlushAsync().
A questo punto la registrazione è stata memorizzata nello storage dell’applicazione, pronta
per essere utilizzata per i nostri scopi: se vogliamo, semplicemente, verificare che la
registrazione sia andata a buon fine, possiamo utilizzare uno dei launcher che ci mette a
disposizione il sistema (l’argomento è approfondito nel Capitolo 7).
private void OnPlayVideoClicked(object sender, RoutedEventArgs e)
{
MediaPlayerLauncher launcher = new MediaPlayerLauncher
{
Media = new Uri(file.Path, UriKind.Relative)
};
launcher.Show();
}
Le lens app
Una delle novità introdotte in Windows Phone 8 è la possibilità di trasformare le
applicazioni fotografiche in “lens app”: così come con una macchina fotografica
professionale siamo in grado di sostituire la lente per applicare diversi filtri ed effetti, lo
stesso possiamo fare con la fotocamera di Windows Phone 8. In questo caso, le lenti sono
rappresentate dalle applicazioni, che possono essere aperte direttamente dall’applicazione
nativa di Windows Phone: il tutto a vantaggio dell’esperienza d’uso, dato che l’utente può
continuare a utilizzare le funzionalità offerte dal sistema operativo, come la possibilità di
attivare la fotocamera anche a device bloccato con una pressione prolungata del tasto
dedicato.
L’attivazione di una lens app passa dall’applicazione nativa della fotocamera: il primo
pulsante dell’application bar consente di aprire in overlay una griglia con l’elenco delle
applicazioni installate nel telefono in grado di supportare questa funzionalità.

Figura 8.1 - Le applicazioni di tipo Lens App disponibili.

L’integrazione della nostra applicazione tra le lens app è gestita dal file di manifest: è
necessario, infatti, aggiungere una dichiarazione nella sezione Extensions. Tale modifica,
però, non è supportata dall’editor visuale: dovete, perciò, fare clic con il tasto destro sul
file di manifest (il file WMAppManifest.xml contenuto all’interno della cartella
Properties) e scegliere l’opzione View code. Tale modifica va inclusa all’interno della
sezione Extensions, che è posizionata sotto Tokens: se non è presente, dovete crearla.
<Extensions>
<Extension ExtensionName="Camera_Capture_App"ConsumerID="{5B04B775-356B-4AA⊘
-AAF8-6491FFEA5631}"TaskID="_default" />
</Extensions>
Il secondo passaggio consiste nel creare un’icona dedicata: tale immagine sarà visualizzata
nel momento in cui l’utente, dalla fotocamera, accederà all’elenco delle lens app
disponibili. Dato che Windows Phone supporta tre risoluzioni differenti, devono essere
incluse tre immagini differenti nella cartella Assets del progetto. Ecco le caratteristiche
che devono avere:

Risoluzione del telefono Dimensione dell’icona Nome del file


Lens.Screen-
WVGA (480x800) 173 x 173
WVGA.png
HD720p (720x1280) 259x259 Lens.Screen-720p.png
Lens.Screen-
WXGA (768x1280) 277 x 277
WXGA.png

Il terzo e ultimo passaggio consiste nell’intercettare l’apertura dell’applicazione dalla


fotocamera: in questo caso l’applicazione sarà aperta alla pagina principale, ma con un
parametro in query string nell’URL.
/MainPage.xaml?Action=ViewfinderLaunch

Per gestisce questo scenario dobbiamo utilizzare un UriMapper personalizzato, che


abbiamo imparato a conoscere nel Capitolo 4. Grazie a questa classe siamo in grado di
intercettare tutti gli URL di navigazione di un’applicazione e comportarci di conseguenza.
Nel nostro caso, il compito sarà quello di portare l’utente alla pagina specifica
dell’applicazione dedicata all’interazione con la fotocamera: vogliamo, infatti, che
l’utente, quando apre l’applicazione come lens app, non venga portato alla pagina
principale, ma direttamente a quella che si occupa di manipolare il flusso video.
Ecco un esempio di implementazione di UriMapper:
public class MyUriMapper : UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = uri.ToString();
if (tempUri.Contains("ViewfinderLaunch"))
{
return new Uri("/Lens.xaml", UriKind.Relative);
}
Else
{
return uri;
}
}
}

Nel caso in cui l’URL di navigazione contenga la parola chiave ViewfinderLaunch,


portiamo l’utente alla pagina dell’applicazione dedicata alla gestione del flusso video, che,
nell’esempio, è chiamata Lens.xaml. In tutti gli altri casi, invece, restituiamo l’URL
originale, così che la navigazione possa proseguire normalmente.
Ricordiamoci, come sempre, di registrare la classe MyAppUriMapper anche nell’App. xaml.
cs, all’interno del metodo InitializePhoneApplication():
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;

// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.UriMapper = new MyUriMapper();
RootFrame.Navigated += CompleteInitializePhoneApplication;

// Handle navigation failures


RootFrame.NavigationFailed += RootFrame_NavigationFailed;

// Handle reset requests for clearing the backstack


RootFrame.Navigated += CheckForResetNavigation;

// Ensure we don't initialize again


phoneApplicationInitialized = true;
}
Integrarsi con l’hub Photos
L’hub Photos è, assieme all’hub Music, il centro multimediale di Windows Phone,
all’interno del quale l’utente può visualizzare le foto scattate con la fotocamera o i propri
album pubblicati su Facebook o Skydrive.
Le applicazioni di tipo fotografico sono tra le più diffuse sullo Store, tanto da avere una
propria categoria dedicata.
Tra le più popolari troviamo sicuramente le applicazioni di fotoritocco, in grado di
elaborare e aggiungere effetti alle nostre foto. Tipicamente, l’utente apre una di queste
applicazioni, seleziona una delle proprie foto grazie al chooser che abbiamo visto in
questo capitolo e inizia poi a elaborarla.
Può anche capitare, però, che l’utente stia sfogliando la propria galleria e decida di
elaborare una delle sue foto: in questo caso vi viene in aiuto la possibilità di integrare le
applicazioni di terze parti con l’hub Photos, affinché l’utente possa accedere
all’applicazione direttamente dalla foto che sta visualizzando.
Sono diverse le possibilità per effettuare l’integrazione: la più semplice è quella di
aggiungerla all’elenco delle applicazioni in grado di elaborare fotografie; all’interno
dell’hub Photos è presente una sezione chiamata Apps, che mostra l’elenco delle
applicazioni installate in grado di lavorare con le immagini. Questa sezione viene
visualizzata solo nel momento in cui almeno un’applicazione di questo tipo è installata.
Esistono, poi, diverse possibilità (alcune delle quali sono una novità di Windows Phone 8)
a cui l’utente può accedere nel momento in cui sta guardando una foto:
• integrarla all’interno dello share picker, ovvero l’opzione (disponibile all’interno
della application bar) che consente di condividere la foto corrente, ad esempio, su un
social network;
• integrarla nel menu edit: nel momento in cui l’utente sta visualizzando una foto, è in
grado di accedere dall’application bar a un menu chiamato edit, che contiene un
elenco di applicazioni che supportano l’elaborazione di immagini. In questo modo
l’utente è in grado di aprire l’applicazione con già importata la fotografia selezionata;
• aprirla con l’applicazione originale: in Windows Phone 8 le immagini create o salvate
da un’applicazione di terze parti vengono “firmate”. Nella application bar è presente
il pulsante open YourAppName (dove YourAppName è il nome dell’applicazione),
che consente di aprire la foto direttamente all’interno dell’applicazione che l’ha
creata.
Vediamo ora in dettaglio come supportare le due tipologie di integrazione.

Integrarsi nella sezione Apps dell’hub Photos


L’integrazione di un’applicazione all’interno della schermata principale dell’hub Photos
passa per il file di manifest: esiste, infatti, una sezione apposita del file
WMAppManifest.xml che si chiama Extensions e che serve proprio per definire i punti
di estensione della nostra applicazione.
Di default questa sezione non esiste: a meno che non abbiamo altre estensioni già
registrate, dobbiamo crearla all’interno del nodo principale App, dopo la definizione del
nodo Tokens. La sezione Extensions non è supportata dall’editor visuale: dovrete
modificare il file manualmente, facendo clic con il tasto destro sullo stesso e scegliendo
l’opzione View code.
Ecco la dichiarazione che dobbiamo aggiungere nel file di manifest:
<Extensions>
<Extension ExtensionName="Photos_Extra_Hub" ConsumerID="{5B04B775-356B-4AA0-AAF8-
6491FFEA5632}" TaskID="_default" />
</Extensions>

Per testarne il funzionamento è sufficiente installare l’applicazione sull’emulatore o su un


dispositivo, dopodiché uscire ed entrare nell’hub Photos: la troveremo all’interno della
sezione Apps.

Figura 8.2 - La sezione Apps dell’hub Photos con la nostra applicazione.


Integrarsi nello share picker
Anche in questo caso, l’integrazione nello share picker, presente nel dettaglio di una foto,
passa per il file di manifest, sempre all’interno della sezione Extensions. Ecco la
dichiarazione da includere:
<Extension ExtensionName="Photos_Extra_Share"ConsumerID="{5B04B775-356B-4AA0-AAF8-
6491FFEA5632}"TaskID="_default" />

La differenza è che, in questo caso, non possiamo limitarci a registrare l’applicazione nel
file di manifest: dobbiamo anche gestire il fatto che questa potrebbe essere aperta dalla
voce di menu share presente nella application bar.
Come in tanti altri casi che troveremo nel corso del libro, ci viene in aiuto la classe
UriMapper, che abbiamo imparato a conoscere nel Capitolo 3, dato che, nel momento in
cui un’applicazione viene aperta dal menu share, l’URL di navigazione sarà simile al
seguente:
/MainPage.xaml?Action=ShareContent&FileId=%7BA3D54E2D-7977-4E2B-B92D-3EB126E5D168%7D

L’utente viene portato, come impostazione predefinita, alla pagina principale


dell’applicazione (la Mainpage.xaml), alla quale vengono però aggiunti due parametri:
l’informazione sull’azione che è stata richiesta (il parametro Action, con valore
ShareContent) e un identificativo di tipo Guid che rappresenta in maniera univoca
l’immagine della libreria che l’utente ha scelto di condividere.
Ecco, perciò, che con queste due informazioni siamo in grado di implementare una classe
che erediti da UriMapperBase e che porti l’utente alla pagina della nostra applicazione
specifica per la funzione di share nel momento in cui vengono rilevati questi due
parametri:
public class MyUriMapper: UriMapperBase
{
public override Uri MapUri(Uri uri)
{
string tempUri = uri.ToString();
string mappedUri;

if ((tempUri.Contains("ShareContent")) && (tempUri.Contains("FileId")))


{
mappedUri = tempUri.Replace("MainPage", "Share");
return new Uri(mappedUri, UriKind.Relative);
}

return uri;
}
}

In questo esempio ipotizziamo che nella nostra applicazione sia presente la pagina
Share.xaml, alla quale vogliamo demandare il compito di gestire la condivisione
dell’immagine: quello che facciamo, perciò, è andare a sostituire nell’URL originale di
navigazione il nome della pagina principale (MainPage) con quella da noi creata (Share).
In questo modo, il nuovo URL di navigazione sarà:
/SharePage.xaml?Action=ShareContent&FileId=%7BA3D54E2D-7977-4E2B-B92D-3EB126E5D168%7D
Ricordatevi di registrare, all’interno del file App.xaml.cs della vostra applicazione,
l’UriMapper che avete appena definito: espandete la sezione chiamata Phone application
initialization e, all’interno del metodo InitializePhoneApplication(), dopo l’inizializzazione della
classe RootFrame, impostate la proprietà UriMapper assegnando la classe che avete appena
creato.
// Do not add any additional code to this method
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;

// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.UriMapper = new MyUriMapper();
RootFrame.Navigated += CompleteInitializePhoneApplication;

// Handle navigation failures


RootFrame.NavigationFailed += RootFrame_NavigationFailed;

// Handle reset requests for clearing the backstack


RootFrame.Navigated += CheckForResetNavigation;

// Ensure we don't initialize again


phoneApplicationInitialized = true;
}

All’interno della pagina SharePage.xaml dobbiamo ora andare a gestire l’operazione di


sharing. Ci viene in aiuto la classe MediaLibrary, facente parte del namespace
Microsoft.Xna.Framework.Media, che rappresenta la libreria multimediale dell’utente.
Uno dei metodi esposti dalla classe si chiama GetPictureFromToken(), il quale è in grado, dato
in ingresso l’identificativo di una immagine della libreria, di restituire l’oggetto Picture che
corrisponde all’immagine.
Tale oggetto espone diversi metodi e proprietà che consentono di elaborare l’immagine:
possiamo scoprire informazioni come la data di creazione o le dimensioni, ma, soprattutto,
siamo in grado di recuperare lo stream dell’immagine, per poterlo manipolare in base alle
nostre esigenze.
Grazie alla classe Picture siamo in grado di recuperare tre tipologie di stream:
• GetImage() restituisce lo stream dell’immagine alla risoluzione originale;
• GetThumbnail() restituisce lo stream dell’immagine a una risoluzione molto ridotta,
adatta, ad esempio, per essere mostrata all’interno di una galleria come miniatura;
• GetPreviewImage() è una via di mezzo tra i due stream precedenti: l’immagine non è alla
risoluzione originale, ma è comunque sufficientemente alta per essere mostrata a tutto
schermo come anteprima all’utente. Per utilizzare questo metodo è necessario
aggiungere il namespace Microsoft.Xna.Framework.Media.PhoneExtensions
all’interno della classe.
Vediamo ora un esempio in cui, all’interno dell’evento OnNavigatedTo che viene scatenato
quando navighiamo verso la pagina SharePage.xaml, verifichiamo la presenza del