Vous êtes sur la page 1sur 640

DANS LA MME COLLECTION

Propulsez votre site avec


WORDPRESS

Prenez en main BOOTSTRAP

Julien Chichignoud
ISBN : 979-10-90085-73-2

Maurice Chavelli
ISBN : 979-10-90085-62-6

Des applications ultra


rapides avec NODE.JS

Quallez-vous apprendre ?
Les bases dActionscript

Guillaume Chau
Guillaume Lapayre

Programmez en ACTIONSCRIPT 3
Vous avez toujours voulu raliser des animations Flash sans jamais vraiment savoir comment
faire ? Ce livre est fait pour vous ! Conu pour les dbutants, il vous apprendra pas pas
Actionscript 3, le langage de programmation de Flash.

PROGRAMMEZ EN

La programmation oriente objet


La cration de dessins vectoriels
La manipulation dimages
Les animations et interpolations
Linteraction avec lutilisateur

Guillaume Chau
Etudiant ingnieur, passionn par les nouvelles technologies et le Web, il a
remport le premier prix de InnovGame 2011, grce un jeu de stratgie
ralis laide de Actionscript 3.0 et du Flex SDK.

Mathieu Nebra
ISBN : 979-10-90085-59-6

Guillaume Lapayre
Diplm dune cole dingnieurs gnralistes et dun master en acoustique
et vibrations, Guillaume Lapayre est toujours en qute de connaissances
dans de nouveaux domaines. Cest ainsi quaprs avoir fait ses premiers pas
avec divers langages tels que le C, le C++, le Java ou lActionscript, il dcide
de partager ses connaissances sur OpenClassrooms.

Lesprit dOpenClassrooms

ACTIONSCRIPT 3

La cration de mini jeux

propos de lauteur

ACTIONSCRIPT 3
Guillaume Chau et Guillaume Lapayre

Guillaume Chau & Guillaume Lapayre


ISBN : 979-10-90085-64-0

Des cours clairs et ludiques, conus pour tous


Une communaut de professionnels et de passionns prts vous aider sur nos forums
Des ressources disponibles partout, tout le temps : sur le web, en PDF, en eBook

ISBN : 979-10-90085-55-8

32
9 791090 085558

Vous souhaitez structurer les donnes manipules ou changes par vos programmes ou vos
applications mobiles ? Ce livre est fait pour vous! Conu pour les dbutants, il vous apprendra
pas pas matriser le XML.

Quallez-vous apprendre ?

Ludovic Roland

Structurez vos donnes avec XML

Apprenez programmer en
ADA

Les lments de bases

STRUCTUREZ VOS DONNES AVEC

La Document Type Definition (DTD)

Les schmas XML

XML

LAPI DOM
LAPI XPath
Les espaces de noms
La mise en forme avec CSS

Ludovic Roland

propos de lauteur

Programmez en
ACTIONSCRIPT 3

Structurez vos donnes


avec XML

Vincent Jarc
ISBN : 979-10-90085-58-9

Apprenez votre rythme


grce loffrePremium
OpenClassrooms :
tlchargez des eBooks,
des vidos des cours et
faites-vous certifier.

DevenezPremium!

ISBN : 979-10-90085-56-5

27
9 791090 085565

XML

Ludovic Roland
Dveloppeur Android et Windows Phone, Ludovic voit le XML comme un
moyen efficace de structurer les donnes changer entre un serveur et les
nouveaux appareils que sont les smartphones et tablettes. Il dcide alors
de partager ses connaissances en rdigeant un cours sur OpenClassrooms.

Lesprit dOpenClassrooms
Des cours clairs et ludiques, conus pour tous
Une communaut de professionnels et de passionns prts vous aider sur nos forums
Des ressources disponibles partout, tout le temps : sur le web, en PDF, en eBook

Ludovic Roland
ISBN : 979-10-90085-56-5

Rejoignez la communaut
OpenClassrooms :
www.openclassrooms.com
www.facebook.com/openclassrooms
@OpenClassrooms

Devenez Premium
Tlchargez
les eBooks
Accdez
aux certifications
Tlchargez
les vidos en HD
www.openclassrooms.com/premium

CREZ DES APPLICATIONS EN C# POUR

WINDOWS PHONE 8

Nicolas Hilaire

Sauf mention contraire, le contenu de cet ouvrage est publi sous la licence : Creative Commons
BY-NC-SA 2.0 La copie de cet ouvrage est autorise sous rserve du respect des conditions de la licence
Texte complet de la licence disponible sur : http : //creativecommons.org/licenses/by-nc-sa/2.0/fr/
Mentions lgales
Conception couverture : Sophie Bai
Illustrations chapitres : Fan Jiyong, Alexandra Persil et Sophie Bai
OpenClassrooms 2014 - ISBN : 979-10-90085-63-3

Avant-propos
a rvolution de la mobilit est en marche. Nous connaissons tous liPhone qui
a su conqurir un grand nombre dutilisateurs, ainsi que les tlphones Android
dont le nombre ne cesse de crotre. . . Ces tlphones intelligents (ou smartphones)
deviennent omniprsents dans nos usages quotidiens. Microsoft se devait de monter dans
le TGV de la mobilit ! Sont donc apparus, peu aprs ses deux grands concurrents,
les tlphones Windows. Avec un peu plus de retard sur eux, Microsoft attaque ce
march avec plus de maturit quApple, qui a fonc en tant que pionnier, et nous
propose son systme dexploitation : Windows Phone. Cest une bonne nouvelle !
Cest aujourdhui un nouveau march avec plein dapplications potentielles raliser
grce vos talents de dveloppeur. Vous souhaitez raliser des applications mobiles
pour Windows Phone 8 avec le langage C# ? Ce livre est fait pour vous ! Conu pour
les dbutants, il vous apprendra pas pas tout ce quil faut pour vous lancer dans
le dveloppement XAML/C# pour Windows Phone 8. Sachez que vous pouvez suivre
beaucoup de chapitres de ce cours sans possder forcment de Windows Phone, ce
qui est idal lorsque lon souhaite simplement dcouvrir le sujet. Le seul prrequis
sera de matriser un tant soit peu le C#. Pour ceux qui auraient besoin dune piqre
de rappel, vous pouvez consulter mon cours C# sur le site OpenClassrooms - http:
//fr.openclassrooms.com/informatique/cours/apprenez-a-developper-en-c.

Quallez-vous apprendre en lisant ce livre ?


Il est possible de raliser deux grands types dapplication Windows Phone :
Des applications dites de gestion : cest a que vous allez apprendre dans ce
livre.
Des jeux graphiques avec DirectX ou XNA : a, on ne sen proccupera pas ici.
Il est aussi possible de dvelopper des applications pour Windows Phone en VB.NET
et en F#, ainsi quen C++. Je ne traiterai ici que de C#.
Dans ce cours, vous allez apprendre dvelopper des applications de gestion avec
Silverlight pour Windows Phone, qui est utilis dans les versions 7 de Windows Phone,
mais galement des applications XAML/C#, langage utilis pour dvelopper des
applications pour Windows Phone 8. Vous allez dcouvrir pas pas les points suivants :
1. La thorie et les bases :la premire partie va vous permettre de dcouvrir le
i

CHAPITRE 0. AVANT-PROPOS
XAML, lment indispensable pour pouvoir dvelopper pour Windows Phone. Il
sagit du langage permettant de raliser la partie graphique de nos applications.
2. Un mobile orient donnes : une application, cest avant tout de la mise en
forme de donnes. Nous allons voir comment manipuler ces donnes dans cette
partie et nous allons apprendre bien architecturer nos applications.
3. Une bibliothque de contrles :en plus du XAML, vous avez accs plein
de contrles ddis aux Windows Phone. De la carte du monde, au panorama,
en passant par le navigateur web, vous dcouvrirez des contrles optimiss pour
enrichir vos applications.
4. Un tlphone ouvert vers lextrieur : dans cette partie, vous verrez comment faire pour interagir avec votre smartphone grce ses diffrents capteurs
ou des gestes. Le GPS et lacclromtre nauront plus de secrets pour vous
mais vous verrez galement comment envoyer des notifications ou communiquer
avec Facebook.
Avant de commencer, je dois quand mme vous signaler que le dveloppement pour
Windows Phone peut rendre accroc ! Vous allez avoir envie de crer des applications
sans jamais vous arrter ! Si vous tes prts assumer cette probable dpendance, cest
que vous tes bons pour continuer. Alors, cest parti !

Comment lire ce livre ?


Suivez lordre des chapitres
Lisez ce livre comme on lit un roman : il a t conu comme tel. Contrairement
beaucoup de livres techniques o il est courant de lire en diagonale et de sauter certains
chapitres, ici il est trs fortement recommand de suivre lordre du cours, moins que
vous ne soyez dj un peu expriments (et trs sr de vous).

Pratiquez en mme temps


Pratiquez rgulirement. Nattendez pas davoir fini la lecture de ce livre pour faire
vos propres essais ; nhsitez pas modifier les codes donns en exemples, afin de
bien cerner le comportement de chaque instruction. Plus vous vous exercerez, et plus
lapprentissage sera rapide et efficace.

Remerciements
Je souhaite remercier un certain nombre de personnes qui, de prs ou de loin, ont
contribu la naissance de cet ouvrage :
ma femme Delphine, qui me soutient au quotidien et moffre chaque jour une
raison davancer dans la vie ses cts et mon fils Timo, mme sil ntait
encore quun rve lors de la rdaction de cet ouvrage,
ii

REMERCIEMENTS
Anna, Jonathan, Jessica, Marine, Mathieu, Pierre, et toute lquipe dOpenClassrooms,
tous les relecteurs et particulirement Rudy Huyn, qui ma donn dexcellents
conseils.

iii

CHAPITRE 0. AVANT-PROPOS

iv

Table des matires

Avant-propos

Quallez-vous apprendre en lisant ce livre ? . . . . . . . . . . . . . . . . . . . .

Comment lire ce livre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

ii

Suivez lordre des chapitres . . . . . . . . . . . . . . . . . . . . . . . . .

ii

Pratiquez en mme temps . . . . . . . . . . . . . . . . . . . . . . . . . .

ii

Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

ii

La thorie et les bases

1 Windows Phone, un nouveau priphrique

Historique, la mobilit chez Microsoft . . . . . . . . . . . . . . . . . . . . . .

Le renouveau : Windows Phone 7 . . . . . . . . . . . . . . . . . . . . . . . . .

Windows Phone 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Lesprit Modern UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Le Windows Phone Store . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2 Les outils de dveloppements

Prrequis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

Installer Visual Studio Express pour Windows Phone . . . . . . . . . . . . . .

11

Lmulateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

XAML et code behind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

Blend pour le design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25

TABLE DES MATIRES


3 Notre premire application
Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

Linterface en XAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34

Le code-behind en C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

37

Le contrle Grid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38

Le contrle StackPanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39

Le contrle TextBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

Le contrle TextBlock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

Les vnements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

Le bouton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

Et Silverlight dans tout a ? . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

4 Les contrles

47

Gnralits sur les contrles . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

Utiliser le designer pour ajouter un CheckBox . . . . . . . . . . . . . . . . . .

49

Utiliser Expression Blend pour ajouter un ToggleButton . . . . . . . . . . . .

52

5 Le clavier virtuel

55

Afficher le clavier virtuel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

56

Intercepter les touches du clavier virtuel . . . . . . . . . . . . . . . . . . . . .

56

Les diffrents types de clavier . . . . . . . . . . . . . . . . . . . . . . . . . . .

57

6 Les conteneurs et le placement

61

StackPanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

ScrollViewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

64

Grid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67

Canvas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

Alignement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

Marges et espacement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

7 Ajouter du style

vi

29

77

Afficher des images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78

Les ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

80

Les styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

84

Les thmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

TABLE DES MATIRES


Changer lapparence de son contrle . . . . . . . . . . . . . . . . . . . . . . .
8 TP1 : Cration du jeu du plus ou du moins

92
95

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . .

96

Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96

9 Dessiner avec le XAML

101

Dessin 2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Pinceaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Les transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10 Crer des animations

113

Principes gnraux des animations . . . . . . . . . . . . . . . . . . . . . . . . 114


Cration dune animation simple (XAML) . . . . . . . . . . . . . . . . . . . . 114
Cration dune animation complexe (Blend) . . . . . . . . . . . . . . . . . . . 119
Projections 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

II

Un mobile orient donnes

11 Une application plusieurs pages, la navigation

129
131

Naviguer entre les pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132


Grer le bouton de retour arrire . . . . . . . . . . . . . . . . . . . . . . . . . 139
Ajouter une image daccueil (splash screen) . . . . . . . . . . . . . . . . . . . 142
Le tombstonning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
12 TP 2 : Crer une animation de transition entre les pages

155

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 156


Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
13 Les proprits de dpendances et proprits attaches
Les proprits de dpendances

161

. . . . . . . . . . . . . . . . . . . . . . . . . . 162

Les proprits attaches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163


14 O est mon application ?

165

Le .XAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Affichage dimages en ressources . . . . . . . . . . . . . . . . . . . . . . . . . 167
vii

TABLE DES MATIRES


Accder au flux des ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
15 ListBox

169

Un contrle majeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170


Grer les modles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Slection dun lment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
16 La manipulation des donnes (DataBinding & Converters)

181

Principe du Databinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182


Le binding des donnes

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182

Binding et mode design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192


Utiliser lObservableCollection

. . . . . . . . . . . . . . . . . . . . . . . . . . 201

Les converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206


17 MVVM

211

Principe du patron de conception . . . . . . . . . . . . . . . . . . . . . . . . . 212


Premire mise en place de MVVM . . . . . . . . . . . . . . . . . . . . . . . . 214
Les commandes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Les frameworks la rescousse : MVVM-Light . . . . . . . . . . . . . . . . . . 225
Dautres frameworks MVVM . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Faut-il utiliser systmatiquement MVVM ? . . . . . . . . . . . . . . . . . . . 245
18 Gestion des tats visuels

247

Les tats dun contrle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248


Modifier un tat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Changer dtat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
Crer un nouvel tat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
19 Le traitement des donnes

263

HttpRequest & WebClient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264


Linq-To-Json . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
La bibliothque de Syndication . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Asynchronisme avanc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Le rpertoire local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
viii

TABLE DES MATIRES

III

Une bibliothque de contrles

20 Panorama et Pivot

289
291

Panorama . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Pivot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
21 Navigateur web

307

Naviguer sur une page web . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308


Evnements de navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
Navigation interne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
Communiquer entre XAML et HTML . . . . . . . . . . . . . . . . . . . . . . 311
22 TP : Cration dun lecteur de flux RSS simple

321

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 322


Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
23 Grer lorientation
Les diffrentes orientations

341
. . . . . . . . . . . . . . . . . . . . . . . . . . . . 342

Dtecter les changements dorientation . . . . . . . . . . . . . . . . . . . . . . 345


Stratgies de gestion dorientation . . . . . . . . . . . . . . . . . . . . . . . . 347
24 Grer les multiples rsolutions

361

Les diffrentes rsolutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362


Grer plusieurs rsolutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Les images

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364

Limage de lcran daccueil . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366


25 Lapplication Bar

367

Prsentation et utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368


Appliquer le Databinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
Mode plein cran . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
26 Le toolkit Windows Phone

381

Prsentation et installation du toolkit Windows Phone . . . . . . . . . . . . . 382


PerformanceProgressBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
ListPicker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
ix

TABLE DES MATIRES


WrapPanel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
LongListSelector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
Avantages & limites du toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . 401
Les autres toolkits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
27 Le contrle de cartes (Map)

405

Prsentation et utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406


Interactions avec le contrle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
Epingler des points dintrt . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Afficher un itinraire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
28 TP : Une application mto

429

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 430


Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432

IV

Un tlphone ouvert vers lextrieur

29 La gestuelle

447
449

Le simple toucher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450


Les diffrents touchers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
Gestuelle avance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
Le toolkit la rescousse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
30 Lacclromtre

455

Utiliser lacclromtre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456


Utiliser lacclromtre avec lmulateur . . . . . . . . . . . . . . . . . . . . . 457
Exploiter lacclromtre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
Les autres capteurs facultatifs . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
La motion API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
31 TP : Jeux de hasard (Grattage et secouage)

467

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 468


Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
32 La golocalisation
x

481

TABLE DES MATIRES


Dterminer sa position . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
Utiliser la golocalisation dans lmulateur . . . . . . . . . . . . . . . . . . . . 486
Utiliser la golocalisation avec le contrle Map . . . . . . . . . . . . . . . . . 488
33 Les Tasks du tlphone

489

Les choosers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490


Les launchers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
Etat de lapplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
34 Les tuiles

499

Que sont les tuiles ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500


Des tuiles pour tous les gots . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
Personnaliser les tuiles par dfaut . . . . . . . . . . . . . . . . . . . . . . . . . 504
Crer des tuiles secondaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
Modifier et supprimer une tuile . . . . . . . . . . . . . . . . . . . . . . . . . . 515
35 Les notifications

517

Le principe darchitecture des notifications . . . . . . . . . . . . . . . . . . . . 518


Le principe de cration du serveur de notification . . . . . . . . . . . . . . . . 519
Les diffrents messages de notifications . . . . . . . . . . . . . . . . . . . . . . 520
Cration du client Windows Phone recevant la notification . . . . . . . . . . . 522
36 TP : Amliorer lapplication mto avec golocalisation et tuiles

531

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 532


Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
37 Une application fluide = une application propre !

541

Un thread, cest quoi ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542


Le thread dinterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
Utiliser un thread darrire-plan . . . . . . . . . . . . . . . . . . . . . . . . . . 544
Utiliser le Dispatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
Utiliser un BackgroundWorker . . . . . . . . . . . . . . . . . . . . . . . . . . 546
Enchaner les threads dans un pool . . . . . . . . . . . . . . . . . . . . . . . . 550
Le DispatcherTimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
Thread de composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
xi

TABLE DES MATIRES


Les outils pour amliorer lapplication . . . . . . . . . . . . . . . . . . . . . . 555
38 Utiliser des tches Background Agent

557

Crer un Background Agent pour une tche priodique . . . . . . . . . . . . . 558


Crer une tche aux ressources intensives . . . . . . . . . . . . . . . . . . . . 564
Remarques gnrales sur les tches . . . . . . . . . . . . . . . . . . . . . . . . 564
Envoyer une notification avec un agent darrire-plan . . . . . . . . . . . . . . 565
39 Utiliser Facebook dans une application mobile

569

Crer une application Facebook . . . . . . . . . . . . . . . . . . . . . . . . . . 570


Simplifier les connexions Facebook avec lAPI . . . . . . . . . . . . . . . . . 570
Scuriser laccs Facebook avec OAuth . . . . . . . . . . . . . . . . . . . . . 572
Se connecter Facebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573
Exploiter le graphe social avec le SDK . . . . . . . . . . . . . . . . . . . . . . 580
Rcuprer des informations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580
Obtenir la liste de ses amis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
Publier un post sur son mur Facebook . . . . . . . . . . . . . . . . . . . . . . 585
Utiliser les tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
40 Publier son application

593

Crer un compte dveloppeur . . . . . . . . . . . . . . . . . . . . . . . . . . . 594


Inscrire un tlphone de dveloppeur . . . . . . . . . . . . . . . . . . . . . . . 600
Proposer une version dessai de votre application . . . . . . . . . . . . . . . . 605
Certifier son application avec le Store Test Kit . . . . . . . . . . . . . . . . . 606
Publier son application sur le Windows Phone Store . . . . . . . . . . . . . . 612

xii

Premire partie

La thorie et les bases

Chapitre

Windows Phone, un nouveau


priphrique
Difficult :
Windows Phone est la nouvelle plateforme pour tlphones mobiles de Microsoft. Elle
permet de raliser des applications pour les smartphones quips du systme dexploitation
Windows Phone. Apparues dans un premier temps sous sa version 7, Windows Phone en
est actuellement sa version 8.
Microsoft arrive en rupture avec son nouveau systme dexploitation pour smartphone afin
de concurrencer les deux gants que sont Apple et Google.

CHAPITRE 1. WINDOWS PHONE, UN NOUVEAU PRIPHRIQUE

Historique, la mobilit chez Microsoft


Cela fait longtemps que Microsoft dispose de matriels mobiles. On a eu dans un
premier temps les priphriques quips de Windows CE ; ce systme dexploitation
tait une variation de Windows destine aux systmes embarqus. Il a notamment t
beaucoup utilis dans les PC de poche (Pocket PC). Cette version de Windows tait
optimise pour les appareils possdant peu despace de stockage.
Est arrive ensuite la gamme de Windows Mobile. Ce systme dexploitation tait
utilis sur des smartphones, PDA ou PC de poche. Il est arriv pour concurrencer les
Blackberry et permettait de recevoir ses emails, dutiliser la suite bureautique, etc. Ces
mobiles ressemblaient beaucoup au Windows que lon connait, avec son fameux menu
dmarrer. On utilisait en gnral un stylet la place de la souris.
Avec les nouvelles interfaces tactiles, on a vu apparatre Apple et son fameux iPhone
venu pour rvolutionner la faon dont on se servait jusqu prsent des appareils mobiles. Ce qui a donn un srieux coup de vieux Windows Mobile, comme le montre
la figure 1.1. . .

Figure 1.1 Un Windows CE et un IPhone

Le renouveau : Windows Phone 7


Microsoft a, son tour, chang radicalement son systme dexploitation afin de prendre
ce virage de la mobilit en proposant Windows Phone, dont la premire version est la
version 7, sortie en octobre 2010. Il ne sagit pas dune volution de Windows Mobile,
part au niveau de la numrotation des versions (dans la mesure o Windows Mobile
sest arrt avec la version 6.5). Windows Phone 7 a t redvelopp de zro et arrive
avec un nouveau look, nomm dans un premier temps Mtro , plus pur, fortement
ractif et intuitif, et valorisant linformation structure. Microsoft se positionne ainsi en
rupture avec ses systmes dexploitation prcdents et propose des concepts diffrents
pour son nouvel OS, comme la navigation exclusive au doigt par exemple. Il se veut
plutt grand public quuniquement destin aux entreprises.
Pour tre autoris utiliser le systme dexploitation Windows Phone 7 (voir figure
1.2), un smartphone doit respecter un minimum de spcifications. Ces spcifications
garantissent quune application aura un minimum de puissance, vitant davoir des
4

WINDOWS PHONE 8
applications trop lentes. Lcran doit tre multipoint dau moins 3.5 pouces, cest-dire quil doit pouvoir ragir plusieurs pressions simultanes et permettant une
rsolution de 480x800. Les tlphones doivent galement tre munis obligatoirement de quelques quipements, comme le fait davoir un GPS, davoir une camra,
un acclromtre, etc . . . Chaque tlphone possdera galement trois boutons faisant
partie intgrante de son chssis. Le premier bouton permettra de revenir en arrire, le
second daccder au menu et le dernier de faire des recherches.

Figure 1.2 Windows Phone 7

Windows Phone 8
Cest tout dernirement, avec la sortie de Windows 8, que le systme dexploitation
Windows Phone a chang de version pour passer galement la version 8. Lobjectif
de Microsoft est dunifier au maximum le cur de Windows 8 et de Windows Phone 8,
permettant de faire facilement des passerelles entre eux. Windows 8 tant un systme
dexploitation cr avant tout pour des tablettes, il paraissait logique que Windows 8 et
Windows Phone 8 partagent beaucoup de fonctionnalits. Windows 8 sest largement
inspir de Windows Phone pour crer son style, Modern UI, et cest dsormais au tour
de Windows Phone de subir une volution majeure Windows Phone 8, prsent dans
la figure 1.3 afin de se rapprocher de son grand frre, Windows 8.
Beaucoup de choses sont partages entre les deux systmes, cest ce quon appelle le
Shared Windows Core . Ainsi, il deviendra trs facile de crer des applications pour
Windows Phone 8 qui ne ncessiteront que trs peu dadaptation pour fonctionner sur
Windows 8. Cest une des grandes forces de Windows Phone 8.
Notez que les applications Windows Phone 7 fonctionnent galement sur Windows Phone 8, cest une bonne chose si vous possdiez dj des applications
Windows Phone 7 et que celles-ci nont pas t portes pour WP8.
5

CHAPITRE 1. WINDOWS PHONE, UN NOUVEAU PRIPHRIQUE


Windows Phone 8 est galement plus performant grce au support du code natif. Il est
ainsi possible de dvelopper des jeux en C++, utilisant DirectX.
Windows Phone 8 apporte en plus des nouvelles rsolutions dcran : WVGA (800x480
pixels), WXVGA (1280x768), et True 720p (1280x720), avec une adaptation automatique de chacune.

Figure 1.3 Windows Phone 8

Lesprit Modern UI
Anciennement appel Mtro , Modern UI est le nom donn par Microsoft son
langage de design. Plutt que dadapter linterface des anciens Windows Mobile, pour
Windows Phone 7 il a t dcid de repartir sur un tout nouveau design.
Le nom Mtro a t inspir par les affichages qui se trouvent effectivement dans les
stations de mtro et qui guident efficacement les voyageurs jusqu leurs destinations.
Les affichages sont clairs, prcis, souvent minimalistes et sans fioritures, par exemple
une flche et un gros 5 pour indiquer que cest par l quon va trouver le mtro numro
5. . . Voil quoi doivent ressembler les interfaces pour Windows Phone. Elles doivent
valoriser linformation et la fluidit plutt que les interfaces lourdes et charges. Le but
est de faire en sorte que lutilisateur trouve le plus rapidement possible linformation
dont il a besoin et dviter les images ou animations superflues qui pourraient le ralentir.
Dans cette optique, les applications doivent tre fluides et rpondre rapidement aux
actions de lutilisateur, ou du moins lui indiquer que son action a t prise en compte.
Ras le bol des applications Windows ou autre o on ne sait mme plus si on a cliqu
sur un bouton car rien ne se passe !
Courant 2012, Mtro a chang de nom pour devenir Modern UI. Les applications Windows 8 et Windows Phone 8 doivent chacune suivre les normes
dictes par les principes de Modern UI .
6

LESPRIT MODERN UI

Modern UI se veut donc simple, pur et moderne. Les fonctionnalits sont spares en
Hubs qui sont des espaces regroupant des informations de plusieurs sources de donnes.
Ainsi, il existe plusieurs Hubs (voir figure 1.4), comme le Hub contacts o lon
retrouvera les contacts du tlphone mais aussi les contacts Facebook, Twitter, . . . Nous
avons aussi le Hub Photos , Musique et vidos , Jeux , Microsoft Office ,
Windows Phone Store ou encore le hub Entreprise qui permettra daccder aux
applications mtiers via un portail que les entreprises peuvent personnaliser.

Figure 1.4 Ecran daccueil de lmulateur o lon voit laccs aux Hubs et aux
applications
Une fois rentr dans un Hub, nous avons accs plusieurs informations disposes sous
la forme dun panorama. Nous verrons un peu plus loin ce quest le panorama mais
je peux dj vous dire quil permet dafficher des crans de manire cyclique avec un
dfilement latral. Ainsi, dans le Hub de contact, on arrive dans un premier temps sur
la liste de tous les contacts. Lcran de panorama que lon peut faire glisser avec le
doigt nous permet dobtenir sur lcran suivant la liste des dernires activits de nos
contacts, puis la liste des contacts rcents, etc. Et cest pareil pour les autres Hub, le
but est davoir un point dentre qui centralise toutes les informations relatives un
point dintrt.
Cest avec tous ces principes en tte que vous devrez dvelopper votre application.
Nhsitez pas observer comment sont faites les autres, on trouve souvent de bonnes
sources dinspirations permettant de voir ce qui fait la qualit du design dune appli7

CHAPITRE 1. WINDOWS PHONE, UN NOUVEAU PRIPHRIQUE


cation.

Le Windows Phone Store


Les applications que nous crons sont ensuite tlchargeables sur la place de march
Windows Phone, appele encore Windows Phone Store. Elles peuvent tre gratuites
ou payantes, permettant ainsi son crateur de gnrer des revenus. Sur le store, on
trouvera galement des musiques et des vidos.
Comme nous lavons dj dit en introduction, nous allons apprendre dvelopper pour
Windows Phone sans forcment possder un Windows Phone. Cest un point important,
mme sil sera trs utile den possder un, vous pouvez tout fait dbuter sans.
Par contre, pour publier une application sur le Windows Phone Store, il faudra possder un compte dveloppeur. Celui-ci est factur 19$ par an, ce qui
correspond 14.
Voil, vous savez tout sur Windows Phone, il est temps dapprendre raliser de
superbes applications !

En rsum
Windows Phone est le nouveau systme dexploitation de Microsoft pour Smartphone.
Il arrive avec une nouvelle philosophie de design des applications : Modern UI.
Les applications ralises sont tlchargeables via le magasin en ligne (store) de
Microsoft.

Chapitre

Les outils de dveloppements


Difficult :
a a lair super a, de pouvoir dvelopper des applications pour les tlphones ! Cest trs
la mode et ces mini-ordinateurs nous rservent plein de surprises. Voyons donc ce quil
nous faut pour nous lancer dans le monde merveilleux du dveloppement pour Windows
Phone.
Nous allons apprendre dvelopper pour Windows Phone quips de la version 8, qui
est la dernire version lheure o jcris ces lignes. Vous serez galement capables de
crer des applications pour Windows Phone 7.5 et 7.8. Mme si la version 8 semble trs
allchante, les versions 7 ne sont pas oublier trop rapidement. En effet, tous les utilisateurs
nont pas encore achet de Windows Phone 8 et seraient srement dus de manquer votre
application rvolutionnaire. De plus, Windows Phone 8 sera galement capable de faire
tourner des applications 7.X. Un march ne pas oublier. . .

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS

Prrequis
Avant toute chose, je vais vous donner les lments qui vont vous permettre de choisir
entre lenvironnement de dveloppement ddi au dveloppement dapplications pour
Windows Phone 7.5 et celui ddi au dveloppement dapplications pour Windows
Phone 7.5 et 8.
Pourquoi choisir entre un environnement qui fait tout et un environnement
qui ne fait que la moiti des choses ?
Bonne question. . . ! En effet, qui peut le plus peut bien sr le moins. Mais bien que
les environnements de dveloppement soient gratuits, vous nallez peut-tre pas avoir
envie de changer de machine de dveloppement pour autant.
Voici le matriel requis pour dvelopper pour Windows Phone 7.5 :
La premire chose est de possder Windows Vista SP2, ou bien Windows 7 ou encore Windows 8, qui sont les seules configurations supportes
permettant de dvelopper pour Windows Phone 7.5.
Il est galement grandement conseill de possder une carte graphique compatible avec DirectX 10 afin de pouvoir utiliser correctement lmulateur. Je
reviendrai plus tard sur ce quest lmulateur.
La plupart des PC est aujourdhui quipe de cette configuration. Ce qui est trs
pratique pour se lancer et dcouvrir le dveloppement pour Windows Phone. Par contre,
pour pouvoir dvelopper pour Windows Phone 8, cest un peu plus dlicat :
Il vous faut avant tout Windows 8 en version 64 bits, rien dautre. La version conseille est dailleurs la version PRO ou la version Entreprise qui vont
permettre dutiliser lmulateur.
Pour faire tourner lmulateur, il faut que votre processeur supporte la technologie SLAT (qui permet de faire de la virtualisation) et quelle soit active dans le bios ; ce qui est le cas gnralement des PC rcent ( partir de 2011). Il faut galement installer le systme de virtualisation de Microsoft, Hyper-V - http://fr.wikipedia.org/wiki/Hyper-V, qui est disponible avec les versions PRO ou Entreprise de Windows 8. Si vous ntes pas
certain que votre processeur supporte cette technologie, vous pouvez le vrifier avec cette procdure - http://msdn.microsoft.com/fr-fr/library/
windowsphone/develop/ff626524(v=vs.105).aspx (en anglais) ou alors,
tentez linstallation et il vous dira une fois que tout est fini sil y a un problme ou pas.
Il vous faut aussi une bonne une carte graphique compatible avec DirectX
10 ainsi que 4 Go de mmoire.
Ces contraintes matrielles peuvent rendre difficile daccs le dveloppement pour Windows Phone 8 quelquun qui souhaite simplement sinitier ou dcouvrir cette technologie. Si cest votre cas et que vous ne possdez pas ce matriel, alors je vous conseille
dinstaller lenvironnement de dveloppement pour Windows Phone 7.5, qui vous per10

INSTALLER VISUAL STUDIO EXPRESS POUR WINDOWS PHONE


mettra de suivre 95% du cours. Jindiquerai au cours de ce cours ce qui est rserv
exclusivement Windows Phone 8. Sinon, si votre matriel le permet, installez sans
hsiter ce quil faut pour dvelopper pour Windows Phone 8.
Si vous possdez un tlphone quip de Windows Phone 8, que vous disposez
dun abonnement de dveloppeur et que vous ne souhaitez pas vous servir de
lmulateur, il est possible de nutiliser que la version normale de Windows 8.
Ces lments expliqus, voici la suite des prrequis :
Bien sr, vous aurez intrt possder un smartphone quip de Windows
Phone : il est primordial de tester son application sur un tlphone avant de
songer la rendre disponible sur le Windows Phone Store.
Enfin le dernier prrequis est de savoir parler le C#.

Le C# est le langage de dveloppement phare de Microsoft et permet la


cration dapplications informatiques de toutes sortes. Il est indispensable de
connaitre un peu le langage de programmation C# afin de pouvoir dvelopper des applications pour smartphones quips de Windows Phone. Son
tude nest pas traite dans ce cours, mais vous pouvez retrouver un cours
C# complet sur le site OpenClassrooms - http://msdn.microsoft.com/
fr-fr/library/windowsphone/develop/ff626524(v=vs.105).aspx

Pour rsumer ce quest le C#, il sagit dun langage orient objet apparu en mme
temps que le framework .NET qui na cess dvoluer depuis 2001. Il permet dutiliser les briques du framework .NET pour raliser des applications de toutes sortes et
notamment des applications pour Windows Phone. Cest le ciment et les outils qui
permettent dassembler les briques de nos applications.

Installer Visual Studio Express pour Windows Phone


Puisquon est partis dans le btiment, il nous faut un chef de chantier qui va nous
permettre dorchestrer nos dveloppements. Cest ce quon appelle lIDE, pour Integrated Development Environment, ce qui signifie Environnement de dveloppement
intgr . Cet outil de dveloppement est un logiciel qui va nous permettre de crer des
applications et qui va nous fournir les outils pour orchestrer nos dveloppements. La
gamme de Microsoft est riche en outils professionnels de qualit pour le dveloppement,
notamment grce Visual Studio.
Pour apprendre et commencer dcouvrir lenvironnement de dveloppement, Microsoft propose gratuitement Visual Studio dans sa version express. Cest une version
allge de lenvironnement de dveloppement qui permet de faire plein de choses, mais
avec moins doutils que dans les versions payantes. Rassurez-vous, ces versions gratuites
sont trs fournies et permettent de faire tout ce dont on a besoin pour apprendre
dvelopper sur Windows Phone et suivre ce cours !
11

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS


Pour raliser des applications denvergure, il pourra cependant tre judicieux dinvestir dans loutil complet et ainsi bnficier de fonctionnalits complmentaires qui
permettent damliorer, de faciliter et dindustrialiser les dveloppements.
Pour dvelopper pour Windows Phone gratuitement, nous allons avoir besoin de Microsoft Visual Studio Express pour Windows Phone. Pour le tlcharger, rendez-vous
sur : http://dev.windowsphone.com. Il est important de noter que les images de ce
site changent rgulirement, donc ne vous tonnez pas si celles que vous trouvez en
ligne diffrent lgrement de celles que je vais maintenant vous prsenter !
Si vous possdez la version payante de Visual Studio, rendez-vous galement
sur : http://dev.windowsphone.com pour tlcharger les outils complmentaires et notamment le SDK permettant le dveloppement sur Windows
Phone.
Attention, le site est en anglais, mais ne vous inquitez pas, je vais vous guider. Cliquez
ensuite sur Get SDK qui va nous permettre de tlcharger les outils gratuits (voir la
figure 2.1).

Figure 2.1 Obtenir le SDK depuis la page daccueil du dev center


On arrive sur une nouvelle page o il est indiqu que lon doit tlcharger le Windows
Phone SDK . SDK signifie Software Development Kit que lon peut traduire par :
Kit de dveloppement logiciel. Ce sont tous les outils dont on va avoir besoin pour
dvelopper dans une technologie particulire, ici en loccurrence pour Windows Phone.
On nous propose de tlcharger soit la version 8.0 du SDK qui va nous permettre de
dvelopper pour Windows Phone 7.5 et 8.0, soit la version 7.1 du SDK qui nous permettra de dvelopper uniquement pour Windows Phone 7.5. La version 7.11 du SDK
est une mise jour de la version 7.1 permettant de dvelopper sous Windows 8. Tlchargez la version qui vous convient en cliquant sur le bouton Download correspondant
(voir la figure 2.2).
12

INSTALLER VISUAL STUDIO EXPRESS POUR WINDOWS PHONE


Notez ici que je tlcharge et installe la version 8.0 du SDK, les actions sont
sensiblement les mmes pour la version 7.1 du SDK.

Figure 2.2 Tlcharger le SDK


On arrive sur une nouvelle page o nous allons enfin pouvoir passer en franais (voir
la figure 2.3).
Une nouvelle page se charge et nous allons pouvoir tlcharger linstalleur qui va nous
installer tout ce quil nous faut. Comme sa taille le suggre, il ne sagit que dun petit
excutable qui aura besoin de se connecter internet pour tlcharger tout ce dont il
a besoin (voir la figure 2.4).
Donc, dmarrez le tlchargement et enchanez tout de suite sur linstallation, tant que
vous tes connects internet (voir la figure 2.5).
Linstallation est plutt classique et commence par lacceptation du contrat de licence
(voir les figures 2.6 et 2.7).
Pour le SDK 7.1, il y a un cran supplmentaire pour choisir dinstaller les outils
maintenant (voir la figure 2.8).
Linstallation est globalement plutt longue, surtout sur un PC fraichement install.
Jespre que vous russirez contenir votre impatience ! Enfin, nous arrivons la fin
de linstallation et vous pouvez dmarrer Visual Studio.
13

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS

Figure 2.3 Obtenir le SDK en franais

Figure 2.4 Tlcharger linstalleur


14

INSTALLER VISUAL STUDIO EXPRESS POUR WINDOWS PHONE

Figure 2.5 Logo du SDK pour Windows Phone 8

Figure 2.6 Lacceptation du contrat de licence pour le SDK 7.1

15

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS

Figure 2.7 Lacceptation du contrat de licence pour le SDK 8.0

16

INSTALLER VISUAL STUDIO EXPRESS POUR WINDOWS PHONE

Figure 2.8 Insaller les outils avec le SDK 7.1


Remarquez que si vous avez install le SDK 8.0, vous aurez la version 2012
de Visual Studio Express pour Windows Phone alors que si vous avez install
la version 7.1 du SDK, vous aurez la version 2010 de Visual Studio Express
pour Windows Phone.
Vous pouvez galement dmarrer Visual Studio Express pour Windows Phone partir
du Menu Dmarrer. Si vous possdez une version payante de Visual Studio, vous pouvez
prsent le lancer.
Pour la suite du cours, je me baserai sur la version gratuite de Visual Studio
2012 Express que nous venons dinstaller, mais tout ceci sera valable avec
la version 2010 ou avec les versions payantes de Visual Studio. Les captures
seront sans doute diffrentes, mais je suppose que vous vous y retrouverez !
son ouverture, vous pouvez constater que nous arrivons sur la page de dmarrage
(voir la figure 2.9).
Nous allons donc crer un nouveau projet en cliquant sur le lien (comme indiqu sur
la capture dcran), ou plus classiquement par le menu Fichier > Nouveau projet.
ce moment-l, Visual Studio Express 2012 pour Windows Phone (que jappellerai
dsormais Visual Studio pour conomiser mes doigts et les touches de mon clavier)
nous ouvre sa page de choix du modle du projet (voir la figure 2.10).
Nous allons choisir de crer une Application Windows Phone, qui est la version la plus
17

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS

Figure 2.9 Page de dmarrage de Visual Studio permettant de crer un nouveau


projet

Figure 2.10 Modle de projet pour crer une application Windows Phone

18

INSTALLER VISUAL STUDIO EXPRESS POUR WINDOWS PHONE


basique du projet permettant de raliser une application pour Windows Phone avec le
XAML. Remarquons que le choix du langage est possible entre Visual Basic, Visual
C++ et Visual C#. Nous choisissons videmment le C# car cest le langage que nous
matrisons. Jen profite pour nommer mon projet HelloWorld . . . (ici, personne ne
se doute quel type dapplication nous allons faire trs bientt).
Enfin, aprs avoir valid la cration du projet, il nous demande la version cibler (voir
la figure 2.11).

Figure 2.11 Choix de la version du SDK utiliser


Choisissez 8.0 pour dvelopper pour Windows Phone 8 ou 7.1 pour dvelopper
pour Windows Phone 7.5. Rappelez-vous quune application 7.5 sera excutable sur les
tlphones quips de Windows Phone 8 (cest lavantage) mais ne pourra pas utiliser
les nouveauts de Windows Phone 8 (cest linconvnient).
Visual Studio gnre son projet, les fichiers qui le composent et souvre sur la page
suivante (voir la figure 2.12).

Figure 2.12 Linterface de Visual Studio, ainsi que la liste droulante permettant de
choisir lmulateur
Nous allons revenir sur cet cran trs bientt. Ce quil est important de remarquer
cest que si nous dmarrons lapplication telle quelle, elle va se compiler et sexcuter
dans lmulateur Windows Phone. Vous le voyez dans le petit encadr en haut de Visual
Studio, cest la cible du dploiement. Il est possible de dployer soit sur lmulateur, soit
directement sur un tlphone reli au poste de travail. Il ne reste plus qu rellement
excuter notre application. . .
19

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS


Il ne sera pas possible de dployer notre application sur le tlphone sans
une manipulation pralable que nous dcouvrirons dans le dernier chapitre. Si
vous navez pas les prrequis matriels pour installer lmulateur et que vous
possdez un Windows Phone, vous pouvez dores et dj vous reporter ce
chapitre afin de pouvoir continuer suivre le cours.

Lmulateur
Attention, si vous avez install le SDK 8.0, vous allez avoir besoin galement dinstaller
le logiciel gestion de la virtualisation : Hyper-v. Celui-ci nest disponible quavec les
versions PRO ou Entreprise de Windows 8 et uniquement si votre processeur supporte
la technologie SLAT. Allez dans le panneau de configuration, programmes, et choisissez
dactiver des fonctionnalits Windows (voir la figure 2.13).

Figure 2.13 Panneau de configuration pour permettre linstallation dHyper-V


Puis, installez hyper-V en cochant la case correspondante (voir la figure 2.14).

Excutons donc notre application en appuyant sur F5 qui nous permet de dmarrer
lapplication en utilisant le dbogueur.
20

LMULATEUR

Figure 2.14 Cocher pour installer Hyper-V


Vous aurez peut-tre la fentre dlvation des privilges (voir figure 2.15)
qui permet de configurer lmulateur. En ce cas, cliquez sur Ressayer.

Figure 2.15 Dmarrage de lmulateur avec lvation de privilge


Nous constatons que lmulateur se lance, il ressemble un tlphone Windows Phone. . .
On les reconnait dun coup dil car ils ont les trois boutons en bas du tlphone, la
flche (ou retour arrire), le bouton daccs au menu et la loupe pour faire des recherches
(voir la figure 2.16).
Lmulateur possde galement des boutons en haut droite qui permettent (de haut
en bas) de :
Fermer lmulateur
Minimiser lmulateur
21

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS

Figure 2.16 Emulateur Windows Phone 8

Faire pivoter de 90 vers la gauche lmulateur


Faire pivoter de 90 vers la droite lmulateur
Adapter la rsolution
Zoomer/dzoomer sur lmulateur
Ouvrir les outils supplmentaires

Ces boutons sont prsents dans la figure 2.17.


Remarquons galement que des chiffres saffichent sur le ct droit de lmulateur. Ce
sont des informations sur les performances de lapplication, nous y reviendrons en fin
de cours. Enfin, vous pouvez fermer lapplication en arrtant le dbogueur en cliquant
sur le carr (voir la figure 2.18).

XAML et code behind


Revenons un peu sur cette page que nous a affich Visual Studio. Nous pouvons voir
que le milieu ressemble lmulateur et que le ct droit ressemble un fichier XML.
Vous ne connaissez pas les fichiers XML ? Si vous voulez en savoir plus, nhsitez pas
faire un petit tour sur internet, cest un format trs utilis dans linformatique ! Pour
faire court, le fichier XML est un langage de balise, un peu comme le HTML, o lon
dcrit de linformation. Les balises sont des valeurs entoures de < et > qui dcrivent
la smantique de la donne. Par exemple :
22

XAML ET CODE BEHIND

Figure 2.17 Boutons permettant de faire des actions sur lmulateur

Figure 2.18 Bouton pour arrter le dbogage dans Visual Studio

23

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS


1

< prenom > Nicolas </ prenom >

La balise <prenom> est ce quon appelle une balise ouvrante, cela signifie que ce qui se
trouve aprs (en loccurrence la chane Nicolas ) fait partie de cette balise jusqu
ce que lon rencontre la balise fermante </prenom> qui est comme la balise ouvrante
lexception du / prcdant le nom de la balise. Le XML est un fichier facile lire par
nous autres humains. On en dduit assez facilement que le fichier contient la chane
Nicolas et quil sagit smantiquement dun prnom. Une balise peut contenir des
attributs permettant de donner des informations sur la donne. Les attributs sont
entours de guillemets et et font partis de la balise. Par exemple :
1

< client nom = " Nicolas " age = " 30 " > </ client >

Ici, la balise client possde un attribut nom ayant la valeur Nicolas et un attribut
age ayant la valeur 30 . Encore une fois, cest trs facile lire pour un humain.
Il est possible que la balise nait pas de valeur, comme cest le cas dans lexemple cidessus. On peut dans ce cas-l remplacer la balise ouvrante et la balise fermante par
cet quivalent :
1

< client nom = " Nicolas " age = " 30 " / >

Enfin, et nous allons terminer notre aperu rapide du XML avec un dernier point. Il
est important de noter que le XML peut imbriquer ses balises et quil ne peut possder
quun seul lment racine, ce qui nous permet davoir une hirarchie de donnes. Par
exemple nous pourrons avoir :
1
2
3
4
5
6
7
8
9
10

< listesDesClient >


< client type = " Particulier " >
<nom > Nicolas </ nom >
<age > 30 </ age >
</ client >
< client type = " Professionel " >
<nom > J r mie </ nom >
<age > 40 </ age >
</ client >
</ listesDesClient >

On voit tout de suite que le fichier dcrit une liste de deux clients. Nous en avons un
qui est un particulier, qui sappelle Nicolas et qui a 30 ans alors que lautre est un
professionnel, prnomm Jrmie et qui a 40 ans.
quoi cela nous sert-il ? comprendre ce fameux fichier de droite. Cest le fichier
XAML (prononcez Zammel ). Le XAML est un langage qui permet de dcrire des
interfaces et en loccurrence ici le code XAML ( droite dans Visual Studio) dcrit
linterface que nous retrouvons au milieu. Cette zone est la prvisualisation du rendu
du code crit dans la partie droite. On appelle la zone du milieu, le concepteur (ou
plus souvent le designer en anglais). En fait, le fichier XAML contient des balises qui
dcrivent ce qui doit safficher sur lcran du tlphone. Nous allons y revenir.
Nous allons donc devoir apprendre un nouveau langage pour pouvoir crer des applications sur Windows Phone : le XAML. Ne vous inquitez pas, il est assez facile
24

BLEND POUR LE DESIGN


apprendre et des outils vont nous permettre de simplifier notre apprentissage.
Ok pour le XAML si tu dis que ce nest pas trop compliqu, mais le C# dans
tout a ?
Eh bien il arrive dans le fichier du mme nom que le fichier XAML et il est suffix par
.cs. Nous le retrouvons si nous cliquons sur le petit triangle ct du fichier XAML
qui permet de dplier les fichiers (voir la figure 2.19).

Figure 2.19 Le XAML et son code-behind


Il est juste en dessous, on lappelle le code behind. Le code behind est le code C#
qui est associ lcran qui va safficher partir du code XAML. Il permet de grer
toute la logique associe au XAML. Si vous ouvrez ce fichier C#, vous pouvez voir
quelques instructions dj cres en mme temps que le XAML. Nous allons galement
y revenir.
Dans la suite de ce cours, les extraits de code XAML seront indiqus par le
site OpenClassrooms comme du XML. Cest seulement pour les besoins de la
coloration syntaxique, mais rassurez-vous, il sagira bien de XAML.

Blend pour le design


Afin de faciliter la ralisation de jolis crans destination du tlphone, nous pouvons
modifier le XAML. Cest un point que nous verrons plus en dtail un peu plus loin.
Il est possible de le modifier directement la main lorsquon connat la syntaxe, mais
nous avons aussi notre disposition un outil ddi au design qui le fait tout seul :
Blend.
Microsoft Expression Blend est un outil professionnel de conception dinterfaces
25

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS


utilisateur de Microsoft. Une version gratuite pour Windows Phone a t installe
en mme temps que Visual Studio Express 2012 pour Windows Phone et permet de
travailler sur le design de nos crans XAML. Nous verrons comment il fonctionne mais
nous ne nous attarderons pas trop sur son fonctionnement car il mriterait un cours
entier.
Ce qui est intressant cest quil est possible de travailler en mme temps sur le mme
projet dans Visual Studio et dans Blend, les modifications se rpercutant de lun
lautre. Faisons un clic droit sur le fichier XAML et choisissons de louvrir dans Expression Blend (voir la figure 2.20).

Figure 2.20 Dmarrage de Blend depuis Visual Studio


Blend souvre alors et affiche nouveau le rendu de notre cran (voir la figure 2.21).

Figure 2.21 Interface de Blend


On peut voir galement une grosse bote outils qui va nous permettre de styler notre
application. Nous y reviendrons.
26

BLEND POUR LE DESIGN

En rsum
Pour dvelopper pour Windows Phone, nous avons besoin de Visual Studio et
du kit de dveloppement pour Windows Phone.
Il existe une version totalement gratuite de Visual Studio permettant de raliser
des applications sous Windows Phone.
Un mulateur accompagne Visual Studio pour tester ses applications en mode
dveloppement.
Blend est loutil de design permettant de styler son application, dont une version
gratuite est fournie avec le kit de dveloppement pour Windows Phone.

27

CHAPITRE 2. LES OUTILS DE DVELOPPEMENTS

28

Chapitre

Notre premire application


Difficult :
Nous avons dsormais tous les outils quil faut pour commencer apprendre raliser des
applications pour Windows Phone. Nous avons pu voir que ces outils fonctionnent et nous
avons pu constater un dbut de rsultat grce lmulateur. Mais pour bien comprendre et
assimiler toutes les notions, nous avons besoin de plus de concret, de manipuler des lments
et de voir quest-ce qui exactement agit sur quoi. Aussi, il est temps de voir comment raliser
une premire application avec le classique Hello World ! Cette premire application va
nous servir de base pour commencer dcouvrir ce quil faut pour raliser des applications
pour Windows Phone.

29

CHAPITRE 3. NOTRE PREMIRE APPLICATION

Hello World
Revenons donc sur notre cran o nous avons le designer et le XAML. Positionnonsnous sur le code XAML et ajoutons des lments sans trop comprendre ce que nous
allons faire afin de raliser notre Hello World . Nous reviendrons ensuite sur ce que
nous avons fait pour expliquer le tout en dtail.
Pour commencer, on va modifier la ligne suivante :
1

< TextBlock Text = " nom de la page " Margin = "9 , -7 ,0 , 0 " Style = " {
StaticResource PhoneTextTitle1Style } " / >

et crire ceci :
1

< TextBlock Text = " Hello World " Margin = "9 , -7 ,0 , 0 " Style = " {
StaticResource PhoneTextTitle1Style } " / >

Nous changeons donc la valeur de lattribut Text de la balise <TextBlock>.


Jemploie ici du vocabulaire XML, mais ne le retenez pas car ce nest pas
exactement de a quil sagit. Nous y reviendrons.
Ensuite, juste aprs :
1

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >

Donc, lintrieur de cette balise <Grid>, rajoutez :


1
2
3
4
5
6

< StackPanel >


< TextBlock Text = " Saisir votre nom " HorizontalAlignment = "
Center " / >
< TextBox x : Name = " Nom " / >
< Button Content = " Valider " Tap = " Button_Tap_1 " / >
< TextBlock x : Name = " Resultat " Foreground = " Red " / >
</ StackPanel >

Remarquez que lorsque vous avez saisi la ligne :


1

< Button Content = " Valider " Tap = " Button_Tap_1 " / >

au moment de taper : Tap="", Visual Studio Express vous a propos de gnrer un


nouveau gestionnaire dvnement (voir la figure 3.1).

Vous pouvez le gnrer en appuyant sur Entre , cela nous fera gagner du temps plus
tard. Sinon, ce nest pas grave. Vous pouvez constater que le designer sest mis jour
pour faire apparatre nos modifications (voir la figure 3.2).
Ouvrez maintenant le fichier de code behind et modifiez la mthode Button_Tap_1
pour avoir :
1

30

private void Button_Tap_1 ( object sender , System . Windows . Input .


GestureEventArgs e )

HELLO WORLD

Figure 3.1 Gnration du gestionnaire dvnement depuis le XAML

Figure 3.2 Le rendu du XAML dans la fentre du concepteur

31

CHAPITRE 3. NOTRE PREMIRE APPLICATION


2

3
4

Resultat . Text = " Bonjour " + Nom . Text ;


Nous pouvons ds prsent dmarrer notre application en appuyant sur F5 . Lmulateur se lance et nous voyons apparatre les nouvelles informations sur lcran (voir la
figure 3.3).

Figure 3.3 Rendu de lapplication dans lmulateur

La souris va ici permettre de simuler le toucher avec le doigt lorsque nous cliquons.
Cliquons donc dans la zone de texte et nous voyons apparatre un clavier virtuel
lintrieur de notre application (voir la figure 3.4).
Ce clavier virtuel sappelle le SIP (pour Soft Input Panel ) et apparait automatiquement quand on en a besoin, notamment dans les zones de saisie, nous y reviendrons.
Saisissons une valeur dans la zone de texte et cliquons sur le bouton Valider. Nous
voyons apparatre le rsultat en rouge avec un magnifique message construit (voir la
figure 3.5).
Et voil, notre Hello World est termin ! Chouette non ? Pour quitter lapplication, le
plus simple est darrter le dbogueur de Visual Studio en cliquant sur le carr Stop.
32

HELLO WORLD

Figure 3.4 Le clavier virtuel dans lmulateur

Figure 3.5 Affichage de lHello World dans lmulateur


33

CHAPITRE 3. NOTRE PREMIRE APPLICATION

Linterface en XAML
Alors, taper des choses sans rien comprendre, a va un moment. . . mais l, il est temps
de savoir ce que nous avons fait !
Nous avons dans un premier temps fait des choses dans le XAML. Pour rappel, le
XAML sert dcrire le contenu de ce que nous allons voir lcran. En fait, un fichier
XAML correspond une page. Une application peut tre dcoupe en plusieurs pages,
nous y reviendrons plus tard. Ce que nous avons vu sur lmulateur est laffichage de
la page MainPage.
Donc, nous avons utilis le XAML pour dcrire le contenu de la page. Il est globalement
assez explicite mais ce quil faut comprendre cest que nous avons ajout des contrles
du framework .NET dans la page. Un contrle est une classe C# complte qui sait
safficher, se positionner, traiter des vnements de lutilisateur (comme le clic sur le
bouton), etc. Ces contrles ont des proprits et peuvent tre ajouts dans le XAML.
Par exemple, prenons la ligne suivante :
1

< TextBlock Text = " Saisir votre nom " HorizontalAlignment = " Center "
/>

Nous demandons dajouter dans la page le contrle TextBlock - http://msdn.


microsoft.com/fr-fr/library/ms617591(v=vs.95).aspx. Le contrle TextBlock
correspond une zone de texte non modifiable. Nous positionnons sa proprit Text
la chane de caractres Saisir votre nom . Ce contrle sera align au centre grce
la proprit HorizontalAlignment positionne Center. En fait, jai dit que nous
lajoutons dans la page, mais pour tre plus prcis, nous lajoutons dans le contrle
StackPanel qui est lui-mme contenu dans le contrle Grid, qui est contenu dans la
page. Nous verrons plus loin ce que sont ces contrles.
Ce que nous avons appel balise plus tt est en fait un contrle, et ce que
nous avons appel attribut correspond une proprit de ce contrle.
Derrire, automatiquement, cette ligne de XAML entrane la dclaration et linstanciation dun objet de type TextBlock avec les affectations de proprits adquates. Puis
ce contrle est ajout dans le contrle StackPanel. Tout ceci nous est masqu. Grce
au XAML nous avons simplement dcrit linterface de la page et cest Visual Studio
qui sest occup de le transformer en C#. Parfait ! Moins on en fait et mieux on se
porte. . . et surtout il y a moins de risque derreurs.
Et cest pareil pour tous les autres contrles de la page, le TextBlock qui est une zone
de texte non modifiable, le TextBox qui est une zone de texte modifiable dclenchant
laffichage du clavier virtuel, le bouton, etc.
Vous laurez peut-tre devin, mais cest pareil pour la page. Elle est dclare tout en
haut du fichier XAML :
1
2

34

< phone : P h oneApplicationPage


x : Class = " HelloWorld . MainPage "

LINTERFACE EN XAML
xmlns = " http :// schemas . microsoft . com / winfx / 2006 / xaml /
presentation "
xmlns : x = " http :// schemas . microsoft . com / winfx / 2006 / xaml "
xmlns : phone = " clr - namespace : Microsoft . Phone . Controls ;
assembly = Microsoft . Phone "
xmlns : shell = " clr - namespace : Microsoft . Phone . Shell ; assembly =
Microsoft . Phone "
xmlns : d = " http :// schemas . microsoft . com / expression / blend / 2008
"
xmlns : mc = " http :// schemas . openxmlformats . org / markup compatibility / 2006 "
FontFamily = " { StaticResource Ph oneFo ntFami lyNor mal } "
FontSize = " { StaticResource PhoneFontSizeNormal } "
Foreground = " { StaticResource PhoneForegroundBrush } "
S u p p o r t e d Orient ation s = " Portrait " Orientation = " Portrait "
mc : Ignorable = " d " d : DesignHeight = " 768 " d : DesignWidth = " 480 "
shell : SystemTray . IsVisible = " True " >

3
4
5
6
7
8
9
10
11
12
13
14

Cest dailleurs le conteneur de base du fichier XAML, celui qui contient tous les autres
contrles. La page est en fait reprsente par la classe PhoneApplicationPage qui
est aussi un objet du framework .NET. Plus prcisment, notre page est une classe
gnre qui drive de lobjet PhoneApplicationPage. Il sagit de la class MainPage
situe dans lespace de nom HelloWorld, cest ce que lon voit dans la proprit :
1

x : Class = " HelloWorld . MainPage "

On peut sen rendre compte galement dans le code behind de la page o Visual Studio
a gnr une classe partielle du mme nom que le fichier XAML et qui drive de
PhoneApplicationPage :
1
2
3
4
5
6
7

public partial class MainPage : PhoneApplicationPage


{
// Constructeur
public MainPage ()
{
I ni t i alizeComponent () ;
}

8
9
10
11
12
13

private void Button_Tap_1 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
Resultat . Text = " Bonjour " + Nom . Text ;
}

Pourquoi partielle ? Parce quil existe un autre fichier dans votre projet. Ce fichier est
cach mais on peut lafficher en cliquant sur le bouton en haut de lexplorateur de
solution (voir la figure 3.6).
Et nous pouvons voir notamment un rpertoire obj contenant un rpertoire debug
contenant le fichier MainPage.g.i.cs. Si vous louvrez, vous pouvez trouver le code
35

CHAPITRE 3. NOTRE PREMIRE APPLICATION

Figure 3.6 Affichage des fichiers cachs dans lexplorateur de solutions


suivant :
1
2
3
4
5
6
7

public partial class MainPage : Microsoft . Phone . Controls .


P h o n e A pplicationPage
{
internal System . Windows . Controls . Grid LayoutRoot ;
internal System . Windows . Controls . StackPanel TitlePanel ;
internal System . Windows . Controls . Grid ContentPanel ;
internal System . Windows . Controls . TextBox Nom ;
internal System . Windows . Controls . TextBlock Resultat ;

8
9

private bool _contentLoaded ;

10
11
12
13
14
15
16
17
18
19
20
21
22

36

// / < summary >


// / I nitializeComponent
// / </ summary >
[ System . Diagnostics . D e b u g g e r N o n U s e r C o d e A t t r i b u t e () ]
public void InitializeComponent ()
{
if ( _contentLoaded )
{
return ;
}
_contentLoaded = true ;
System . Windows . Application . LoadComponent ( this , new
System . Uri ( " / HelloWorld ; component / MainPage . xaml " ,

LE CODE-BEHIND EN C#

23
24
25
26
27
28
29

System . UriKind . Relative ) ) ;


this . LayoutRoot = (( System . Windows . Controls . Grid ) ( this .
FindName ( " LayoutRoot " ) ) ) ;
this . TitlePanel = (( System . Windows . Controls . StackPanel )
( this . FindName ( " TitlePanel " ) ) ) ;
this . ContentPanel = (( System . Windows . Controls . Grid ) (
this . FindName ( " ContentPanel " ) ) ) ;
this . Nom = (( System . Windows . Controls . TextBox ) ( this .
FindName ( " Nom " ) ) ) ;
this . Resultat = (( System . Windows . Controls . TextBlock ) (
this . FindName ( " Resultat " ) ) ) ;

Il sagit dune classe qui est gnre lorsquon modifie le fichier XAML. Ne modifiez
pas ce fichier car il sera re-gnr tout le temps. On peut voir quil sagit dune classe
MainPage, du mme nom que la proprit x:Class de tout lheure, qui soccupe de
charger le fichier XAML et qui cre des variables partir des contrles quil trouvera
dedans. Nous voyons notamment quil a cr les deux variables suivantes :
1
2

internal System . Windows . Controls . TextBox Nom ;


internal System . Windows . Controls . TextBlock Resultat ;

Le nom de ces variables correspond aux proprits x:Name des deux contrles que nous
avons cr :
1
2

< TextBox x : Name = " Nom " / >


< TextBlock x : Name = " Resultat " Foreground = " Red " / >

Ces variables sont initialises aprs quil ait charg tout le XAML en faisant une recherche partir du nom du contrle. Cela veut dire que nous disposons dune variable
qui permet daccder au contrle de la page, par exemple la variable Nom du type
TextBox. Je vais y revenir. Nous avons donc :
Un fichier MainPage.xaml qui contient la description des contrles
Un fichier gnr qui contient une classe partielle qui drive de
PhoneApplicationPage et qui charge ce XAML et qui rend accessible nos
contrles via des variables
Un fichier de code behind qui contient la mme classe partielle o nous pourrons
crire la logique de notre code
Remarquez quil existe galement le fichier MainPage.g.cs qui correspond au fichier
gnr aprs la compilation. Nous ne nous occuperons plus de ces fichiers gnrs, ils ne
servent plus rien. Nous les avons regards pour comprendre comment cela fonctionne.

Le code-behind en C#
Revenons sur le code behind, donc sur le fichier MainPage.xaml.cs, nous avons :
1

public partial class MainPage : PhoneApplicationPage

37

CHAPITRE 3. NOTRE PREMIRE APPLICATION


2

3
4
5
6
7

// Constructeur
public MainPage ()
{
I nitializeComponent () ;
}

8
9
10
11
12
13

private void Button_Tap_1 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
Resultat . Text = " Bonjour " + Nom . Text ;
}

On retrouve bien notre classe partielle qui hrite des fonctionnalits de la classe
PhoneApplicationPage. Regardez lintrieur de la mthode Button_Tap_1, nous
utilisons les fameuses variables que nous navons pas dclar nous-mme mais qui ont
t gnres. . . Ce sont ces variables qui nous permettent de manipuler nos contrles
et en loccurrence ici, qui nous permettent de modifier la valeur de la zone de texte
non modifiable en concatnant la chane Bonjour la valeur de la zone de texte
modifiable, accessible via sa proprit Text.
Vous aurez compris ici que ce sont les proprits Text des TextBlock et TextBox qui
nous permettent daccder au contenu qui est affich sur la page. Il existe plein dautres
proprits pour ces contrles comme la proprit Foreground qui permet de modifier
la couleur du contrle, sauf quici nous lavions positionn grce au XAML :
1

< TextBlock x : Name = " Resultat " Foreground = " Red " / >

Chose que nous aurions galement pu faire depuis le code behind :


1
2
3
4
5

private void Button_Tap_1 ( object sender , System . Windows . Input .


GestureEventArgs e )
{
Resultat . Foreground = new SolidColorBrush ( Colors . Red ) ;
Resultat . Text = " Bonjour " + Nom . Text ;
}

Sachez quand mme que dune manire gnrale, on aura tendance essayer de mettre
le plus de chose possible dans le XAML plutt que dans le code behind. La proprit
Foreground ici a tout intrt tre dclare dans le XAML.

Le contrle Grid
Je vais y revenir plus loin un peu plus loin, mais pour que vous ne soyez pas compltement perdu dans notre Hello World, il faut savoir que la Grid est un conteneur.
38

LE CONTRLE STACKPANEL
<mode bilingue = on>Vous aurez compris que la Grid est en fait une
grille. . .<mode bilingue = off >
Aprs cet effort de traduction intense, nous pouvons dire que la grille sert contenir
et agencer dautres contrles. Dans notre cas, le code suivant :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

9
10

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock Text = " MON APPLICATION " Style = " {
StaticResource PhoneTextNormalStyle } " Margin = " 12 , 0 "
/>
< TextBlock Text = " Hello World " Margin = "9 , -7 ,0 , 0 " Style = "
{ StaticResource PhoneTextTitle1Style } " / >
</ StackPanel >

11
12
13
14
15
16
17
18
19
20

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< TextBlock Text = " Saisir votre nom "
HorizontalAlignment = " Center " / >
< TextBox x : Name = " Nom " / >
< Button Content = " Valider " Tap = " Button_Tap_1 " / >
< TextBlock x : Name = " Resultat " Foreground = " Red " / >
</ StackPanel >
</ Grid >
</ Grid >

Dfini une grille qui contient deux lignes. La premire contient un contrle StackPanel,
nous allons en parler juste aprs. La seconde ligne contient une nouvelle grille sans ligne
ni colonne, qui est galement compose dun StackPanel. Nous aurons loccasion den
parler plus longuement plus tard donc je marrte l pour linstant sur la grille.

Le contrle StackPanel
Ici cest pareil, le contrle StackPanel est galement un conteneur. Je vais y revenir un
peu plus loin galement mais il permet ici daligner les contrles les uns en dessous des
autres. Par exemple, celui que nous avons rajout contient un TextBlock, un TextBox,
un bouton et un autre TextBlock :
1
2

< StackPanel >


< TextBlock Text = " Saisir votre nom " HorizontalAlignment = "
Center " / >

39

CHAPITRE 3. NOTRE PREMIRE APPLICATION


3
4
5
6

< TextBox x : Name = " Nom " / >


< Button Content = " Valider " Tap = " Button_Tap_1 " / >
< TextBlock x : Name = " Resultat " Foreground = " Red " / >
</ StackPanel >

Nous pouvons voir sur le designer que les contrles sont bien les uns en dessous des
autres. Nous avons donc au final, la page qui contient une grille, qui contient un
StackPanel et une grille qui contiennent chacun des contrles.

Le contrle TextBox
Le contrle TextBox est une zone de texte modifiable. Nous lavons utilise pour saisir
le prnom de lutilisateur. On dclare ce contrle ainsi :
1

< TextBox x : Name = " Nom " / >

Lorsque nous cliquons dans la zone de texte, le clavier virtuel apparait et nous offre
la possibilit de saisir une valeur. Nous verrons un peu plus loin quil est possible de
changer le type du clavier virtuel. La valeur saisie est rcupre via la proprit Text
du contrle, par exemple ici je rcupre la valeur saisie que je concatne la chane
Bonjour et que je stocke dans la variable resultat :
1

string resultat = " Bonjour " + Nom . Text ;

Inversement, je peux pr-remplir la zone de texte avec une valeur en utilisant la proprit Text, par exemple depuis le XAML :
1
2
3
4
5

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< StackPanel >
< TextBox x : Name = " Nom " Text = " Nicolas " / >
</ StackPanel >
</ Grid >

Ce qui donne la figure 3.7.


La mme chose est faisable en code behind, il suffit dinitialiser la proprit de la
variable dans le constructeur de la page :
1
2
3
4
5
6
7
8

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;
Nom . Text = " Nicolas " ;
}
}

videmment, il sera toujours possible de modifier la valeur pr-remplie grce au clavier


virtuel.
40

LE CONTRLE TEXTBLOCK

Figure 3.7 La valeur du TextBox saffiche dans la fentre de rendu

Le contrle TextBlock
Le contrle TextBlock reprsente une zone de texte non modifiable. Nous lavons utilis
pour afficher le rsultat de notre Hello World. Il suffit dutiliser sa proprit Text pour
afficher un texte. Par exemple, le XAML suivant :
1

< TextBlock Text = " Je suis un texte non modifiable de couleur


rouge " Foreground = " Red " FontSize = " 25 " / >

affiche la fentre de prvisualisation prsente dans la figure 3.8.


Je peux modifier la couleur du texte grce la proprit Foreground. Cest la mme
chose pour la taille du texte, modifiable via la proprit FontSize. Nous pouvons
remarquer que le texte que jai saisi dpasse de lcran et que nous ne le voyons pas en
entier. Pour corriger a, jutilise la proprit TextWrapping que je positionne Wrap :
1

< TextBlock Text = " Je suis un texte non modifiable de couleur


rouge " Foreground = " Red " FontSize = " 25 " TextWrapping = " Wrap " / >

Comme nous lavons dj fait, il est possible de modifier la valeur dun TextBlock en
passant par le code-behind :
1

Resultat . Text = " Bonjour " + Nom . Text ;

41

CHAPITRE 3. NOTRE PREMIRE APPLICATION

Figure 3.8 Le texte saffiche en rouge dans le TextBlock

Les vnements
Il sagit des vnements sur les contrles. Chaque contrle est capable de lever une srie
dvnements lorsque cela est opportun. Cest le cas par exemple du contrle bouton qui
est capable de lever un vnement lorsque nous tapotons dessus (ou que nous cliquons
avec la souris). Nous lavons vu dans lexemple du Hello World, il suffit de dclarer une
mthode que lon associe lvnement, par exemple :
1

< Button Content = " Valider " Tap = " Button_Tap_1 " / >

qui permet de faire en sorte que la mthode Button_Tap_1 soit appele lors du clic sur
le bouton. Rappelez-vous, dans notre Hello World, nous avions la mthode suivante :
1
2
3
4

private void Button_Tap_1 ( object sender , System . Windows . Input .


GestureEventArgs e )
{
Resultat . Text = " Bonjour " + Nom . Text ;
}

Il est galement possible de sabonner un vnement via le code behind, il suffit


davoir une variable de type bouton, pour cela donnons un nom un bouton :
1

< Button x : Name = " UnBouton " Content = " Cliquez - moi " / >

Et dassocier une mthode lvnement de clic :


42

LES VNEMENTS
1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I ni t i alizeComponent () ;

6
7

UnBouton . Tap += UnBouton_Tap ;

9
10
11
12
13
14

void UnBouton_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
throw new N o tI mp le m en te dE x ce pt i on () ;
}

Il existe beaucoup dvnements de ce genre, par exemple la case cocher (CheckBox)


permet de sabonner lvnement qui est dclench lorsquon coche la case :
1

< CheckBox Content = " Cochez - moi " Checked = " CheckBox_Checked_1 " / >

Avec la mthode :
1
2

private void CheckBox_Checked_1 ( object sender , RoutedEventArgs


e)
{

3
4

Il existe normment dvnement sur les contrles, mais aussi sur la page, citons encore
par exemple lvnement qui permet dtre notifi lors de la fin du chargement de la
page :
1
2
3
4
5

public MainPage ()
{
I ni t i al i zeComponent () ;
Loaded += MainPage_Loaded ;
}

6
7
8
9
10

private void MainPage_Loaded ( object sender , RoutedEventArgs e )


{
throw new N o tI mp le m en te dE x ce pt i on () ;
}

Nous aurons loccasion de voir beaucoup dautres vnements tout au long de ce cours.
Remarquez que les vnements sont toujours construits de la mme faon. Le premier
paramtre est du type object et reprsente le contrle qui a dclench lvnement.
En loccurrence, dans lexemple suivant :
1

< Button Content = " Valider " Tap = " Button_Tap_1 " / >

Nous pouvons accder au contrle de cette faon :


43

CHAPITRE 3. NOTRE PREMIRE APPLICATION


1
2
3
4
5

private void Button_Tap_1 ( object sender , System . Windows . Input .


GestureEventArgs e )
{
Button bouton = ( Button ) sender ;
bouton . Content = "C ' est valid ! " ;
}

Le second paramtre est, quant lui, spcifique au type dvnement et peut fournir
des informations complmentaires.

Le bouton
Revenons prsent rapidement sur le bouton, nous lavons vu il nest pas trs compliqu
utiliser. On utilise la proprit Content pour mettre du texte et il est capable de lever
un vnement lorsquon clique dessus, grce lvnement Tap. Le bouton possde
galement un vnement Click qui fait la mme chose et qui existe encore pour des
raisons de compatibilit avec Silverlight.
Prfrez cependant lvnement Tap qui est plus performant.

Il est galement possible de passer des paramtres un bouton. Pour un bouton tout
seul, ce nest pas toujours utile, mais dans certaines situations cela peut tre primordial.
Dans lexemple qui suit, jutilise deux boutons qui ont la mme mthode pour traiter
lvnement de clic sur le bouton :
1
2
3
4
5

< StackPanel >


< Button Content = " Afficher " Tap = " Button_Tap "
CommandParameter = " Nicolas " / >
< Button Content = " Afficher " Tap = " Button_Tap "
CommandParameter = " J r mie " / >
< TextBlock x : Name = " Resultat " Foreground = " Red " / >
</ StackPanel >

Cest la proprit CommandParameter qui me permet de passer un paramtre. Je pourrais ensuite lutiliser dans mon code behind :
1
2
3
4
5
6

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
Button bouton = ( Button ) sender ;
bouton . IsEnabled = false ;
Resultat . Text = " Bonjour " + bouton . CommandParameter ;
}

Jutilise ainsi le paramtre CommandParameter pour rcuprer le prnom de la personne


qui dire bonjour (voir la figure 3.9).
44

ET SILVERLIGHT DANS TOUT A ?

Figure 3.9 Passage dun paramtre au bouton saffichant dans lmulateur

Remarquez au passage lutilisation de la proprit IsEnabled qui permet dindiquer si


un contrle est activ ou pas. Si un bouton est dsactiv, il ne pourra pas tre cliqu.

Et Silverlight dans tout a ?


Vous avez remarqu que jai parl de Silverlight et de XAML. Quelle diffrence ?
Pour bien comprendre, Silverlight tait utilis pour dvelopper avec les versions 7 de
Windows Phone. On utilise par contre le XAML/C# pour dvelopper pour la version
8. En fait, grosso modo cest la mme chose.
XAML est lvolution de Silverlight. Si vous avez des connaissances en Silverlight, vous
vous tes bien rendu compte que ce quon appelle aujourdhui XAML/C#, cest pareil.
Il sagit juste dun changement de vocabulaire afin dunifier les dveloppements utilisant
du code XAML pour dfinir linterface dune application, quelle soit Windows Phone
ou Windows . . .
Ce qui est valable avec Silverlight lest aussi avec XAML/C#, et inversement proportionnel.
45

CHAPITRE 3. NOTRE PREMIRE APPLICATION

En rsum
Le XAML permet de dcrire linterface de nos pages.
Le code behind permet dcrire le code C# de la logique de nos pages.
On utilise des contrles dans nos interfaces, comme le bouton ou la zone de
texte.
Les contrles sont des classes compltes qui savent safficher, se positionner ou
ragir des vnements utilisateurs, comme le clic sur un bouton.

46

Chapitre

Les contrles
Difficult :
Jusqu prsent nous avons vu peu de contrles, la zone de texte non modifiable, la zone de
saisie modifiable, le bouton. . . Il existe beaucoup de contrles disponibles dans les bibliothques de contrles XAML pour Windows Phone. Ceux-ci sont facilement visibles grce
la boite outils que lon retrouve gauche de lcran.
Voyons un peu ce quil y a autour de ces fameux contrles.

47

CHAPITRE 4. LES CONTRLES

Gnralits sur les contrles


Il y a plusieurs types de contrles, ceux-ci drivent tous dune classe de base
abstraite qui sappelle UIElement - http://msdn.microsoft.com/fr-fr/library/
system.windows.uielement(v=vs.95).aspx qui sert grer tout ce qui doit pouvoir safficher et qui est capable de ragir une interaction simple de lutilisateur. Mais la classe UIElement ne fait pas grand-chose sans la classe abstraite
drive FrameworkElement - http://msdn.microsoft.com/fr-fr/library/system.
windows.frameworkelement(v=vs.95).aspx qui fournit tous les composants de base
pour les objets qui doivent safficher sur une page. Cest cette classe galement qui
gre toute la liaison de donnes que nous dcouvrirons un peu plus tard. Cest donc
de cette classe que drivent deux grandes familles de contrles : les contrles proprement parler et les panneaux. Les panneaux drivent de la classe abstraite de base
Panel - http://msdn.microsoft.com/fr-fr/library/system.windows.controls.
panel(v=vs.95).aspx et servent comme conteneurs permettant de grer le placement
des contrles. Nous allons y revenir dans un prochain chapitre. Les contrles drivent de
la classe abstraite Control - http://msdn.microsoft.com/fr-fr/library/system.
windows.controls.control(v=vs.95).aspx. Elle sert de classe de base pour tous les
lments qui utilisent un modle pour dfinir leur apparence. Nous y reviendrons plus
tard, mais une des grandes forces du XAML est doffrir la possibilit de changer lapparence dun contrle, tout en conservant ses fonctionnalits. Les contrles qui hritent
de la classe Control peuvent se rpartir en trois grandes catgories.
Ceux qui drivent de la classe ContentControl - http://msdn.microsoft.com/
fr-fr/library/system.windows.controls.contentcontrol(v=vs.95).aspx
et qui permettent de contenir dautres objets. Cest le cas du bouton par exemple,
nous y reviendrons.
Il y a galement les contrles qui drivent de la classe ItemsControl
- http://msdn.microsoft.com/fr-fr/library/system.windows.controls.
itemscontrol(v=vs.95).aspx, qui servent afficher une liste dlments, cest
le cas de la ListBox par exemple, nous ltudierons plus loin.
Et enfin, il reste les contrles qui drivent directement de la classe Control, qui
ne contiennent pas dautres contrles ou qui naffichent pas de liste dlments,
comme par exemple le TextBlock ou le TextBox que nous avons vu.
Pour schmatiser, nous pouvons observer ce schma (incomplet) la figure 4.1.
Une dernire remarque avant de terminer, sur lutilisation des proprits. Nous avons
vu lcriture suivante pour par exemple modifier la valeur de la proprit Text du
contrle TextBlock :
1

< TextBlock Text = " Hello world " / >

Il est galement possible dcrire la proprit Text de cette faon :


1
2
3
4
5

48

< TextBlock >


< TextBlock . Text >
Hello world
</ TextBlock . Text >
</ TextBlock >

UTILISER LE DESIGNER POUR AJOUTER UN CHECKBOX

Figure 4.1 Hirarchie de classe pour les contrles


Cette criture nous sera trs utile lorsque nous aurons besoin de mettre des choses plus
complexes que des chanes de caractres dans nos proprits. Nous y reviendrons. Enfin,
une proprit possde gnralement une valeur par dfaut. Cest pour cela que notre
TextBox sait safficher mme si on ne lui prcise que sa proprit Text, elle possde
une couleur dcriture par dfaut, une taille dcriture par dfaut, etc.

Utiliser le designer pour ajouter un CheckBox


Revenons notre boite outils remplie de contrle. Elle se trouve gauche de lcran,
ainsi que vous pouvez le voir sur la figure 4.2.
Grce au designer, vous pouvez faire glisser un contrle de la bote outils dans le
rendu de la page. Celui-ci se positionnera automatiquement.
Prenons par exemple le contrle de case cocher que nous avons entre-aperu un peu
plus tt : CheckBox. Slectionnez-le et faites le glisser sur le rendu de la page (voir la
figure 4.3).
Le designer ma automatiquement gnr le code XAML correspondant :
1

< CheckBox Content = " CheckBox " HorizontalAlignment = " Left " Margin =
" 168 , 167 ,0 , 0 " VerticalAlignment = " Top " / >

Vous aurez srement des valeurs lgrement diffrentes, notamment au niveau de la


proprit Margin. Cest dailleurs en utilisant ces valeurs que le designer a pu me
positionner le contrle dans la grille. Remarquons que si javais fait glisser un Canvas
et ensuite la case cocher dans ce Canvas, jaurais plutt eu du code comme le suivant :
1

< Canvas H o ri z ontalAlignment = " Left " Height = " 100 " Margin = " 102 , 125
,0 , 0 " Ve rticalAlignment = " Top " Width = " 100 " >

49

CHAPITRE 4. LES CONTRLES

Figure 4.2 La bote outils des contrles dans Visual Studio

50

UTILISER LE DESIGNER POUR AJOUTER UN CHECKBOX

Figure 4.3 Ajout dun contrle CheckBox partir du designer

2
3

< CheckBox Content = " CheckBox " Canvas . Left = " 56 " Canvas . Top = "
40 " / >
</ Canvas >

Ici, il a utilis la proprit Canvas.Top et Canvas.Left pour positionner la case


cocher. Nous allons revenir sur ce positionnement un peu plus loin.
Il est possible de modifier les proprits de la case cocher, par exemple son contenu,
en allant dans la fentre de Proprits (voir la figure 4.4).
Si la fentre de proprits nest pas affiche, vous pouvez la faire apparaitre
en allant dans le menu Affichage, Fentre proprits.
Ici, je change la valeur de la proprit Content. Je peux observer les modifications sur
le rendu et dans le XAML. Remarquons que le designer est un outil utile pour crer
son rendu, par contre il ne remplace pas une bonne connaissance du XAML afin de
grer correctement le positionnement.
51

CHAPITRE 4. LES CONTRLES

Figure 4.4 Modification des proprits dun contrle partir de la fentre des proprits

Utiliser Expression Blend pour ajouter un ToggleButton


Jaurais aussi pu faire la mme chose dans Expression Blend qui est loutil de design
professionnel. Jai galement accs la bote outils (voir la figure 4.5).
Et de la mme faon, je peux faire glisser un contrle, disons le ToggleButton, sur
ma page. Jai galement accs ses proprits afin de les modifier. Ici, par exemple, je
modifie la couleur du ToggleButton (voir la figure 4.6).
Une fois revenu dans Visual Studio, je peux voir les modifications apportes depuis
Blend, avec par exemple dans mon cas :
1

< ToggleButton Content = " ToggleButton " HorizontalAlignment = " Left "
Margin = " 111 , 169 ,0 , 0 " VerticalAlignment = " Top " Background = " #
FFF71616 " BorderBrush = " # FF2DC50C " / >

Nous reviendrons sur Blend tout au long de ce cours.

En rsum
Il existe tout une hirarchie des contrles utilisables dans nos pages.
Les contrles sont accessibles depuis la barre doutils de Visual Studio ou dans
expression blend.
52

UTILISER EXPRESSION BLEND POUR AJOUTER UN TOGGLEBUTTON

Figure 4.5 Barre doutils des contrles dans Blend

Figure 4.6 Modification des couleurs du ToggleButton dans Blend

53

CHAPITRE 4. LES CONTRLES


Le designer de Visual Studio et celui de Blend peuvent nous faciliter la tche
dans le design de nos applications.

54

Chapitre

Le clavier virtuel
Difficult :
Le clavier virtuel est ce petit clavier qui apparat lorsque lon clique dans une zone de texte
modifiable, que nous avons pu voir dans notre Hello World. En anglais il se nomme le SIP,
pour Soft Input Panel, que lon traduit par clavier virtuel. Nous allons voir comment nous
en servir.

55

CHAPITRE 5. LE CLAVIER VIRTUEL

Afficher le clavier virtuel


Vous vous rappelez de notre Hello World ? Lorsque nous avons cliqu dans le TextBox,
nous avons vu apparatre ce fameux clavier virtuel (voir la figure 5.1).

Figure 5.1 Affichage du clavier virtuel


Il ny a quune seule solution pour afficher le clavier virtuel. Il faut que lutilisateur
clique dans une zone de texte modifiable. Et ce moment-l, le clavier virtuel apparat
en bas de lcran. Techniquement, il saffiche quand le contrle TextBox prend le focus
(lorsque lon clique dans le contrle) et il disparat lorsque celui-ci perd le focus (lorsquon clique en dehors du contrle). Il nest pas possible de dclencher son affichage
par programmation, ni son masquage, part en manipulant le focus. Pour afficher un
TextBox, on utilisera le XAML suivant :
1

< TextBox x : Name = " MonTextBox " / >

Intercepter les touches du clavier virtuel


Comme dj dit, il nest pas possible de manipuler le clavier. Par contre, on peut savoir
quand une touche est appuye en utilisant lvnement KeyDown ou KeyUp du TextBox.
Il sagit dvnements qui sont levs lorsquon appuie sur une touche ou lorsquon
relche la touche. Prenons par exemple le code suivant :
56

LES DIFFRENTS TYPES DE CLAVIER


1
2
3
4

< StackPanel >


< TextBox x : Name = " MonTextBox " KeyDown = " MonTextBox_KeyDown "
KeyUp = " MonTextBox_KeyUp " / >
< TextBlock x : Name = " Statut " / >
</ StackPanel >

Et le code behind :
1
2
3
4

private void MonTextBox_KeyDown ( object sender , KeyEventArgs e )


{
Statut . Text = " Touche appuy e : " + e . Key ;
}

5
6
7
8
9

private void MonTextBox_KeyUp ( object sender , KeyEventArgs e )


{
Statut . Text = " Touche relach e : " + e . Key ;
}

Nous aurons la figure 5.2.

Figure 5.2 Affichage de la touche relache dans lmulateur

Les diffrents types de clavier


Le clavier que nous avons vu est le clavier par dfaut. Nous avons notre disposition
dautres types de clavier, par exemple un clavier numrique permettant de saisir des
57

CHAPITRE 5. LE CLAVIER VIRTUEL


numros de tlphone (voir la figure 5.3).

Figure 5.3 Clavier virtuel de type numrique


Pour choisir le type de clavier afficher, nous allons utiliser la proprit InputScope
du contrle TextBox. Par exemple, pour afficher le clavier numrique, je vais utiliser :
1

< TextBox x : Name = " MonTextBox " InputScope = " Number " / >

La liste des diffrents claviers supports est disponible ici - http://msdn.microsoft.


com/fr-fr/library/system.windows.input.inputscopenamevalue.aspx.
Cela permet davoir un clavier plus adapt, si lon doit par exemple permettre de saisir
un @ pour un email, ou des caractres spciaux, etc. Sur la figure 5.4, vous pouvez
voir un clavier optimis pour la saisie dun email (type EmailUserName), avec un
arrobas (@) et un .fr .

En rsum
Le clavier virtuel saffiche lorsque lon clique dans une zone de texte modifiable.
Il existe plusieurs types de clavier notre disposition que nous pouvons choisir
grce la proprit InputScope.

58

LES DIFFRENTS TYPES DE CLAVIER

Figure 5.4 Clavier virtuel optimis pour la saisie dadresse email

59

CHAPITRE 5. LE CLAVIER VIRTUEL

60

Chapitre

Les conteneurs et le placement


Difficult :
Dans notre Hello World, lorsque nous avons parl du contrle TextBlock, nous avons dit
quil faisait partie du contrle StackPanel qui lui-mme faisait partie du contrle Grid. Ces
deux contrles sont en fait des conteneurs de contrles dont lobjectif est de regrouper des
contrles de diffrentes faons.
Les contrles conteneurs vont tre trs utiles pour organiser le look et lagencement de nos
pages. Il y en a quelques uns indispensables dcouvrir qui vont constamment vous servir
lors des vos dveloppements. Nous allons prsent les dcouvrir et voir comment nous en
servir.

61

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT

StackPanel
Le StackPanel par exemple, comme son nom peut le suggrer, permet dempiler les
contrles les uns la suite des autres. Dans lexemple suivant, les contrles sont ajouts
les uns en-dessous des autres :
1
2
3
4
5

< StackPanel >


< TextBlock Text = " Bonjour tous " / >
< Button Content = " Cliquez - moi " / >
< Image Source = " http :// open -e - education - 2013 . openclassrooms .
com / img / logos / logo - openclassrooms . png " / >
</ StackPanel >

Ce qui donne la figure 6.1.

Figure 6.1 Empilement vertical des contrles grce au StackPanel


O nous affichons un texte, un bouton et une image. Nous verrons un peu plus prcisment le contrle Image dans le chapitre suivant.
Notons au passage que Visual Studio et lmulateur peuvent trs facilement rcuprer
des contenus sur internet, sauf si vous utilisez un proxy. Ici par exemple, en utilisant
lURL dune image, je peux lafficher sans problme dans mon application, si celle-ci
est connecte internet bien sr.
Le contrle StackPanel peut aussi empiler les contrles horizontalement. Pour cela, il
suffit de changer la valeur dune de ses proprits :
62

STACKPANEL
1
2
3
4
5

< StackPanel Orientation = " Horizontal " >


< TextBlock Text = " Bonjour tous " / >
< Button Content = " Cliquez - moi " / >
< Image Source = " http :// open -e - education - 2013 . openclassrooms .
com / img / logos / logo - openclassrooms . png " / >
</ StackPanel >

Ici, nous avons chang lorientation de lempilement pour la mettre en horizontal. Ce


qui donne la figure 6.2.

Figure 6.2 Empilement horizontal des contrles


Ce qui ne rend pas trs bien ici. . . Pour lamliorer, nous pouvons ajouter dautres
proprits nos contrles, notamment les rduire en hauteur ou en largeur grce aux
proprits Height ou Width. Par exemple :
1
2
3
4
5

< StackPanel Orientation = " Horizontal " Height = " 40 " >
< TextBlock Text = " Bonjour tous " / >
< Button Content = " Cliquez - moi " / >
< Image Source = " http :// open -e - education - 2013 . openclassrooms .
com / img / logos / logo - openclassrooms . png " / >
</ StackPanel >

Ici, jai rajout une hauteur pour le contrle StackPanel, en fixant la proprit Height
40 pixels, ce qui fait que tous les sous-contrles se sont adapts cette hauteur. Nous
aurons donc la figure 6.3.
63

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT

Figure 6.3 Les contrles ont la taille fixe 40 pixels


Par dfaut, le contrle StackPanel essaie doccuper le maximum de place disponible
dans la grille dont il fait partie. Comme nous avons forc la hauteur, le StackPanel
va alors se centrer. Il est possible daligner le StackPanel en haut grce la proprit
VerticalAlignment :
1
2
3
4
5

< StackPanel Orientation = " Horizontal " Height = " 40 "


Verti calAlignment = " Top " >
< TextBlock Text = " Bonjour tous " / >
< Button Content = " Cliquez - moi " / >
< Image Source = " http :// open -e - education - 2013 . openclassrooms .
com / img / logos / logo - openclassrooms . png " / >
</ StackPanel >

Ce qui donne la figure 6.4.


Nous allons revenir sur lalignement un peu plus loin. Voil pour ce petit tour du
StackPanel !

ScrollViewer
Il existe dautres conteneurs, voyons par exemple le ScrollViewer. Il nous sert rendre
accessible des contrles qui pourraient tre masqus par un cran trop petit. Prenons
par exemple ce code XAML :
64

SCROLLVIEWER

Figure 6.4 Alignement vertical en haut du StackPanel

1
2
3
4
5
6
7
8
9
10
11

< ScrollViewer >


< StackPanel >
< TextBlock
< TextBlock
< TextBlock
< TextBlock
< TextBlock
< TextBlock
< TextBlock
</ StackPanel >
</ ScrollViewer >

Text = " Bonjour


Text = " Bonjour
Text = " Bonjour
Text = " Bonjour
Text = " Bonjour
Text = " Bonjour
Text = " Bonjour

tous
tous
tous
tous
tous
tous
tous

1"
2"
3"
4"
5"
6"
7"

Margin = " 40 "


Margin = " 40 "
Margin = " 40 "
Margin = " 40 "
Margin = " 40 "
Margin = " 40 "
Margin = " 40 "

/>
/>
/>
/>
/>
/>
/>

Nous crons 7 contrles TextBlock, contenant une petite phrase, qui doivent se mettre
les uns en-dessous des autres. Vous aurez devin que la proprit Margin permet de
dfinir une marge autour du contrle, jy reviendrai. Si nous regardons le rsultat, nous
pouvons constater quil nous manque un TextBlock (voir la figure 6.5).
Vous vous en doutez, il saffiche trop bas et nous ne pouvons pas le voir sur lcran car
il y a trop de choses. Le ScrollViewer va nous permettre de rsoudre ce problme. Ce
contrle gre une espce de dfilement, comme lorsque nous avons un ascenseur dans
nos pages web. Ce qui fait quil sera possible de naviguer de haut en bas sur notre
mulateur en cliquant sur lcran et en maintenant le clic tout en bougeant la souris
de haut en bas (voir la figure 6.6).
65

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT

Figure 6.5 Il manque un contrle lcran

Figure 6.6 Le ScrollViewer permet de faire dfiler lcran


66

GRID
Vous pouvez galement vous amuser faire dfiler le ScrollViewer horizontalement,
mais il vous faudra changer une proprit :
1
2
3

< ScrollViewer H o r i z o n t a l S c r o l l B a r V i s i b i l i t y = " Auto " >


< StackPanel Orientation = " Horizontal " >
...

Grid
Parlons prsent du contrle Grid. Cest un contrle trs utilis qui va permettre
de positionner dautres contrles dans une grille. Une grille peut tre dfinie par des
colonnes et des lignes. Il sera alors possible dindiquer dans quelle colonne ou quelle
ligne se positionne un contrle. Par exemple, avec le code suivant :
1
2
3
4
5
6
7
8
9
10
11
12

13

14

15

16

17

18

19

< Grid >


< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< TextBlock Text = " O " FontSize = " 50 " Grid . Row = " 0 " Grid . Column =
" 0 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >
< TextBlock Text = " O " FontSize = " 50 " Grid . Row = " 0 " Grid . Column =
" 1 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >
< TextBlock Text = " O " FontSize = " 50 " Grid . Row = " 0 " Grid . Column =
" 2 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >
< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 1 " Grid . Column =
" 0 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >
< TextBlock Text = " O " FontSize = " 50 " Grid . Row = " 1 " Grid . Column =
" 1 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >
< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 1 " Grid . Column =
" 2 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >
< TextBlock Text = " O " FontSize = " 50 " Grid . Row = " 2 " Grid . Column =
" 0 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >
< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 2 " Grid . Column =
" 1 " H orizontalAlignment = " Center " VerticalAlignment = "
Center " / >

67

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT


20

21

< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 2 " Grid . Column =
" 2 " HorizontalAlignment = " Center " VerticalAlignment = "
Center " / >
</ Grid >

Je dfinis une grille compose de 3 lignes sur 3 colonnes. Dans chaque case je pose un
TextBlock avec une valeur qui me simule un jeu de morpion. Ce quil est important de
remarquer ici cest que je dfinis le nombre de colonnes grce ColumnDefinition :
1
2
3
4
5

< Grid . ColumnDefinitions >


< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >

De la mme faon, je dfinis le nombre de lignes grce RowDefinition :


1
2
3
4
5

< Grid . RowDefinitions >


< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

Il y a donc 3 colonnes et 3 lignes. Chaque colonne a une largeur dun tiers de lcran.
Chaque ligne a une hauteur dun tiers de lcran. Je vais y revenir juste aprs. Pour indiquer quun contrle est la ligne 1 de la colonne 2, jutiliserai les proprits Grid.Row
et Grid.Column avec les valeurs 1 et 2. ( noter quon commence numroter partir
de 0, classiquement). Ce qui donnera la figure 6.7.
Pratique non ? Nous pouvons voir aussi que dans la dfinition dune ligne, nous positionnons la proprit Height. Cest ici que nous indiquerons la hauteur de chaque
ligne. Cest la mme chose pour la largeur des colonnes, cela se fait avec la proprit
Width sur chaque ColomnDefinition.
Ainsi, en utilisant ltoile, nous avons dit que nous voulions que le XAML soccupe de
rpartir toute la place disponible. Il y a trois toiles, chaque ligne et colonne a donc
un tiers de la place pour safficher. Dautres valeurs sont possibles. Il est par exemple
possible de forcer la taille une valeur prcise. Par exemple, si je modifie lexemple
prcdent pour avoir :
1
2
3
4
5
6
7
8
9
10

68

< Grid . RowDefinitions >


< RowDefinition Height = " * " / >
< RowDefinition Height = " 200 " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " 50 " / >
</ Grid . ColumnDefinitions >

GRID

Figure 6.7 Une grille de 3x3


Jindiquerai ici que la premire colonne aura une taille fixe de 100, la troisime une
taille fixe de 50 et la deuxime prendra la taille restante. De la mme faon, pour les
lignes, la deuxime est force 200 et les deux autres se rpartiront la taille restante,
savoir la moiti chacune.
Jen profite pour vous rappeler quun tlphone Windows Phone 7.5 a une rsolution
de 480 en largeur et de 800 en hauteur et quun tlphone Windows Phone 8 possde
trois rsolutions :
WVGA (800x480 pixels), comme Windows Phone 7.5
WXVGA (1280x768)
True 720p (1280x720)
Ainsi, sur un tlphone en WVGA la deuxime colonne aura une taille de 480 100
50 = 330. Ce qui donne une grille plutt disgracieuse, mais tant donn que chaque
contrle est align au centre, cela ne se verra pas trop. Pour bien le mettre en valeur, il
est possible de rajouter une proprit la grille lui indiquant que nous souhaitons voir
les lignes. Bien souvent, cette proprit ne servira qu des fins de dbogages. Il suffit
dindiquer :
1

< Grid ShowGridLines = " True " >

Par contre, les lignes saffichent uniquement dans lmulateur car le designer montre
dj ce que a donne (voir la figure 6.8).
69

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT

Figure 6.8 Affichage des lignes de la grille


Il est bien sr possible de faire en sorte quun contrle stende sur plusieurs colonnes
ou sur plusieurs lignes, ce moment-l, on utilisera la proprit Grid.ColumnSpan ou
Grid.RowSpan pour indiquer le nombre de colonnes ou lignes que lon doit fusionner.
Par exemple :
1

< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 1 " Grid . Column = " 0 "
H or i z ontalAlignment = " Center " VerticalAlignment = " Center " Grid
. ColumnSpan = " 3 " / >

la place de :
1
2
3

< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 1 " Grid . Column = " 0 "
H or i z ontalAlignment = " Center " VerticalAlignment = " Center " / >
< TextBlock Text = " O " FontSize = " 50 " Grid . Row = " 1 " Grid . Column = " 1 "
H or i z ontalAlignment = " Center " VerticalAlignment = " Center " / >
< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 1 " Grid . Column = " 2 "
H or i z ontalAlignment = " Center " VerticalAlignment = " Center " / >

Et nous avons donc la figure 6.9.


Avant de terminer sur les lignes et les colonnes, il est important de savoir quil existe une
autre valeur pour dfinir la largeur ou la hauteur, savoir la valeur Auto. Elle permet
de dire que cest la largeur ou la hauteur des contrles qui vont dfinir la hauteur dune
ligne ou dune colonne. Remarquez que par dfaut, un contrle saffichera la ligne 0
et la colonne 0 tant que son Grid.Row ou son Grid.Column nest pas dfini. Ainsi la
ligne suivante :
70

CANVAS

Figure 6.9 Une grille avec une case stirant sur 3 colonnes
1

< TextBlock Text = " X " FontSize = " 50 " Grid . Row = " 0 " Grid . Column = " 0 "
/>

est quivalente celle-ci :


1

< TextBlock Text = " X " FontSize = " 50 " / >

Voil pour ce petit tour de ce contrle si pratique quest la grille.

Canvas
Nous finirons notre aperu des conteneurs avec le Canvas. Au contraire des autres
conteneurs qui calculent eux mme la position des contrles, ici cest le dveloppeur qui
indique lemplacement dun contrle, de manire relative la position du Canvas. De
plus le Canvas ne calculera pas automatiquement sa hauteur et sa largeur en analysant
ses enfants, contrairement aux autres conteneurs. Ainsi si on met dans un StackPanel
un Canvas suivi dun bouton, le bouton sera affich par-dessus le Canvas, car ce dernier
aura une hauteur de 0 bien quil possde des enfants. Ainsi, lexemple suivant :
1
2
3

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Canvas >
< TextBlock Text = " Je suis en bas gauche " Canvas . Top = "
500 " / >

71

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT


4
5
6

< TextBlock Text = " Je suis en haut droite " Canvas . Left =
" 250 " Canvas . Top = " 10 " / >
</ Canvas >
</ Grid >

affichera la figure 6.10.

Figure 6.10 Positionnement absolu avec le Canvas


Nous nous servons des proprits Canvas.Top et Canvas.Left pour indiquer la position
du contrle relativement au Canvas.
Cest sans doute le conteneur qui permet le placement le plus simple comprendre, par
contre ce nest pas forcment le plus efficace, surtout pour sadapter plusieurs rsolutions ou lorsque nous retournerons lcran. Jen parlerai un peu plus loin. Remarquons
quune page doit absolument commencer par avoir un conteneur comme contrle racine
de tous les autres contrles. Cest ce que gnre par dfaut Visual Studio lorsquon cre
une nouvelle page. Il y met en loccurrence un contrle Grid.

Alignement
Lalignement permet de dfinir comment est align un contrle par rapport son
contenant, en gnral un panneau. Il existe plusieurs valeurs pour cette proprit :
Stretch (tir) qui est la valeur par dfaut
Left (gauche)
72

MARGES ET ESPACEMENT
Right (droite)
Center (centre)
Ainsi le code XAML suivant :
1
2
3
4
5
6
7
8
9
10
11

< Grid >


< TextBlock Text = " Gauche - Haut " HorizontalAlignment = " Left "
Vert icalAlignment = " Top " / >
< TextBlock Text = " Centre - Haut " HorizontalAlignment = " Center "
Vert icalAlignment = " Top " / >
< TextBlock Text = " Droite - Haut " HorizontalAlignment = " Right "
Vert icalAlignment = " Top " / >
< TextBlock Text = " Gauche - Centre " HorizontalAlignment = " Left "
Vert icalAlignment = " Center " / >
< TextBlock Text = " Centre - Centre " HorizontalAlignment = " Center
" V erticalAlignment = " Center " / >
< TextBlock Text = " Droite - Centre " HorizontalAlignment = " Right "
VerticalAlignment = " Center " / >
< TextBlock Text = " Gauche - Bas " HorizontalAlignment = " Left "
Vert icalAlignment = " Bottom " / >
< TextBlock Text = " Centre - Bas " HorizontalAlignment = " Center "
Vert icalAlignment = " Bottom " / >
< TextBlock Text = " Droite - Bas " HorizontalAlignment = " Right "
Vert icalAlignment = " Bottom " / >
</ Grid >

produira le rsultat que vous pouvez voir la figure 6.11.


Lorsquon utilise la valeur Stretch, les valeurs des proprits Width et Height peuvent
annuler leffet de ltirement. On peut voir cet effet avec le code suivant :
1
2
3
4

< Grid >


< Button Content = " Etir en largeur " Height = " 100 "
Vert icalAlignment = " Top " / >
< Button Content = " Etir en hauteur " Width = " 300 "
H or i zontalAlignment = " Left " / >
</ Grid >

Qui nous donne la figure 6.12.


Bien sr, un bouton avec que du Stretch remplirait ici tout lcran.
Les proprits dalignements nont pas dimpact dans un Canvas.

Marges et espacement
Avant de terminer, je vais revenir rapidement sur les marges. Je les ai rapidement
voques tout lheure. Pour mieux les comprendre, regardons cet exemple :
73

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT

Figure 6.11 Les diffrents alignements

Figure 6.12 Ltirement est annul par les proprits Height et Width
74

MARGES ET ESPACEMENT
1
2
3
4
5
6
7
8

< StackPanel >


< Rectangle Height = " 40 " Fill = " Yellow " / >
< StackPanel Orientation = " Horizontal " >
< TextBlock Text = " Mon texte " / >
< Rectangle Width = " 100 " Fill = " Yellow " / >
</ StackPanel >
< Rectangle Height = " 40 " Fill = " Yellow " / >
</ StackPanel >

Il donne la figure 6.13.

Figure 6.13 Un TextBlock sans marge


En rajoutant une marge au TextBlock, nous pouvons voir concrtement se dcaler le
texte :
1

< TextBlock Text = " Mon texte " Margin = " 50 " / >

Qui donne la figure 6.14.

Figure 6.14 Une marge de 50 autour du TextBlock


En fait, la marge prcdente rajoute une marge de 50 gauche, en haut, droite et en
bas. Ce qui est lquivalent de :
1

< TextBlock Text = " Mon texte " Margin = " 50 50 50 50 " / >

75

CHAPITRE 6. LES CONTENEURS ET LE PLACEMENT


Il est tout fait possible de choisir de mettre des marges diffrentes pour, respectivement, la marge gauche, en haut, droite et en bas :
1

< TextBlock Text = " Mon texte " Margin = " 0 10 270 100 " / >

Qui donne la figure 6.15.

Figure 6.15 La marge peut tre de taille diffrente en haut, en bas, gauche ou
droite du contrle
Bref, les marges aident positionner le contrle lemplacement voulu, trs utile pour
avoir un peu despace dans un StackPanel. Remarquez que nous avons aperu dans
ces exemples le contrle Rectangle qui permet, vous vous en doutez, de dessiner un
rectangle. Nous ltudierons un peu plus loin.

En rsum
Les conteurs contiennent des contrles et nous sont utiles pour les positionner
sur la page.
Chaque page doit possder un unique conteneur racine.
Les proprits dalignement, de marge et despacement nous permettent daffiner
nos positionnements dans les conteneurs.

76

Chapitre

Ajouter du style
Difficult :
Nous avons vu quon pouvait modifier les couleurs, la taille de lcriture. . . grce la fentre
des proprits dun contrle. Cela modifie les proprits des contrles et affecte leur rendu.
Cest trs bien. Mais imaginons que nous voulions changer les couleurs et lcriture de
plusieurs contrles, il va falloir reproduire ceci sur tous les contrles, ce qui dun coup est
plutt moins bien !
Cest l quintervient le style. Il correspond lidentification de plusieurs proprits par un
nom, que lon peut appliquer facilement plusieurs contrles.

77

CHAPITRE 7. AJOUTER DU STYLE

Afficher des images


Pour commencer, nous allons reparler du contrle Image. Ce nest pas vraiment un style
proprement parler, mais il va tre trs utile pour rendre nos pages un peu plus jolies.
Nous lavons rapidement utilis en montrant quil tait trs simple dafficher une image
prsente sur internet simplement en indiquant lURL de celle-ci. Il est galement trs
facile dafficher des images nous, embarques dans lapplication. Pour cela, jutilise
une petite image toute bte, reprsentant un cercle rouge (voir la figure 7.1).

Figure 7.1 Un cercle rouge sur fond blanc


Pour suivre cet exemple avec moi, je vous conseille de tlcharger cette image, en
cliquant ici - http://uploads.siteduzero.com/files/410001_411000/410620.png.
Ajoutons donc cette image la solution. Pour cela, je vais commencer par crer un
nouveau rpertoire Images sous le rpertoire Assets qui a t ajout lors de la cration
de la solution. Ensuite, nous allons ajouter un lment existant en faisant un clic droit
sur le projet. Je slectionne limage qui sajoute automatiquement la solution.
Jai cr un rpertoire pour que mes images soient mieux ranges et pour
illustrer le chemin daccs de celles-ci, mais ce nest pas du tout une tape
obligatoire.
Ici, il faut faire attention ce que dans les proprits de limage, laction de gnration
soit Contenu, ce qui est le paramtre par dfaut pour les projets ciblant Windows
Phone 8, mais pas Windows Phone 7 o cest laction de gnration Resource qui est
le paramtre par dfaut. Contenu permet dindiquer que limage sera un fichier part,
non intgre lassembly, nous y reviendrons la fin de la partie (voir la figure 7.2).
Nous pourrons alors trs simplement afficher limage en nous basant sur lURL relative
de limage dans la solution :
1

< Image x : Name = " MonImage " Source = " / Assets / Images / rond . png " Width
= " 60 " Height = " 60 " / >

noter que cela peut aussi se faire grce au code behind. Pour cela, supprimons la
proprit Source du XAML :
1

< Image x : Name = " MonImage " Width = " 60 " Height = " 60 " / >

Et chargeons limage dans le code de cette faon :


1

78

MonImage . Source = new BitmapImage ( new Uri ( " / Assets / Images / rond .
png " , UriKind . Relative ) ) ;

AFFICHER DES IMAGES

Figure 7.2 Limage doit avoir son action de gnration Contenu

79

CHAPITRE 7. AJOUTER DU STYLE


Remarque : pour utiliser la classe BitmapImage - http://msdn.microsoft.com/fr-fr/
library/system.windows.media.imaging.bitmapimage(v=vs.95).aspx, il faut ajouter le using suivant :
1

using System . Windows . Media . Imaging ;

Cela semble moins pratique, mais je vous lai prsent car nous utiliserons cette mthode
un petit peu plus loin. Dune manire gnrale, il sera toujours plus pertinent de passer
par le XAML que par le code !
Il nest pas possible dafficher des images GIF dans ce contrle lorsquon
dveloppe pour Windows Phone 7.5, seuls les formats JPG et PNG sont
supports. Par contre, le GIF est utilisable pour des projets Windows Phone
8 mais ne sanime pas.

Les ressources
Les ressources sont un mcanisme de XAML qui permet de rutiliser facilement des
objets ou des valeurs. Chaque classe qui drive de FrameworkElement dispose dune
proprit Resources, qui est en fait un dictionnaire de ressources. Chaque contrle
peut donc avoir son propre dictionnaire de ressources mais en gnral, on dfinit les
ressources soit au niveau de la page, soit au niveau de lapplication. Par exemple, pour
dfinir une ressource au niveau de la page, nous utiliserons la syntaxe suivante :
1
2
3
4
5

< phone : P h oneApplicationPage


x : Class = " HelloWorld . MainPage "
xmlns = " http :// schemas . microsoft . com / winfx / 2006 / xaml /
presentation "
[... plein de choses ...]
shell : SystemTray . IsVisible = " True " >

6
7
8
9

< phone : PhoneApplicationPage . Resources >


< SolidColorBrush x : Key = " BrushRouge " Color = " Red " / >
</ phone : PhoneApplicationPage . Resources >

10
11
12

<[... plein de choses dans la page ...] >


</ phone : PhoneApplicationPage >

Ici, jai cr un objet SolidColorBrush - http://msdn.microsoft.com/fr-fr/library/


system.windows.media.solidcolorbrush(v=vs.95).aspx, qui sert peindre une zone
dune couleur unie, dont la couleur est Rouge dans les ressources de ma page. Il est
obligatoire quune ressource possde un nom, ici je lai nomm BrushRouge. Je vais
dsormais pouvoir utiliser cet objet avec des contrles, ce qui donne :
1
2

80

< StackPanel >


< TextBlock Text = " Bonjour ma ressource " Foreground = " {
StaticResource BrushRouge } " / >

LES RESSOURCES
3
4

< Button Content = " Cliquez - moi , je suis rouge " Foreground = " {
StaticResource BrushRouge } " / >
</ StackPanel >

Et nous aurons la figure 7.3.

Figure 7.3 Utilisation dune ressource de type pinceau rouge


Alors, quy-a-t-il derrire ces ressources ?
La premire chose que lon peut voir cest la syntaxe particulire lintrieur de la
proprit ForeGround :
1

Foreground = " { StaticResource BrushRouge } "

Des accolades avec le mot-cl StaticResource. . . Cela signifie qu lexcution de lapplication, le moteur va aller chercher la ressource associe au nom BrushRouge et il va
la mettre dans la proprit Foreground de notre contrle.
On appelle la syntaxe entre accolades une extension de balisage XAML ,
en anglais : extension markup.
Ce moteur commence par chercher la ressource dans les ressources de la page et sil ne
la trouve pas, il ira chercher dans le dictionnaire de ressources de lapplication. Nous
avons positionn notre ressource dans la page, cest donc celle-ci quil utilise en premier.
81

CHAPITRE 7. AJOUTER DU STYLE


Remarquez que le dictionnaire de ressources, cest simplement une collection dobjets
associs un nom. Sil est dfini dans la page, alors il sera accessible pour tous les
contrles de la page. Sil est dfini au niveau de lapplication, alors il sera utilisable
partout dans lapplication. Vous aurez pu constater quici, notre principal intrt dutiliser une ressource est de pouvoir changer la couleur de tous les contrles en une seule
fois.
Nous pouvons mettre nimporte quel objet dans les ressources. Nous y avons mis un
SolidColorBrush afin que cela se voit, mais il est possible dy mettre un peu tout et
nimporte quoi. Pour illustrer ce point, nous allons utiliser le dictionnaire de ressource
de lapplication et y stocker une chane de caractre. Ouvrez donc le fichier App.xaml o
se trouve le dictionnaire de ressources. Nous pouvons ajouter notre chane de caractres
dans la section <Application.Resources> dj existante pour avoir :
1
2
3

< Application . Resources >


< system : String x : Key = " TitreApplication " > Hello World </ system
: String >
</ Application . Resources >

Dans le projet cr par dfaut pour Windows Phone 8, il y a dj une


ligne dans les ressources de lapplication : <local:LocalizedStrings
xmlns:local="clr-namespace:HelloWorld"
x:Key="LocalizedStrings"/> qui fait globalement la mme chose, sauf
que lobjet mis en ressource est une instance de la classe LocalizedStrings
qui se trouve la racine du projet.
Vous serez obligs de rajouter lespace de nom suivant en haut du fichier App.xaml :
1

xmlns : system = " clr - namespace : System ; assembly = mscorlib "

dans les proprits de lapplication de manire avoir :


1
2
3
4
5
6
7

< Application
x : Class = " HelloWorld . App "
xmlns = " http :// schemas . microsoft . com / winfx / 2006 / xaml /
presentation "
xmlns : x = " http :// schemas . microsoft . com / winfx / 2006 / xaml "
xmlns : phone = " clr - namespace : Microsoft . Phone . Controls ;
assembly = Microsoft . Phone "
xmlns : shell = " clr - namespace : Microsoft . Phone . Shell ; assembly =
Microsoft . Phone "
xmlns : system = " clr - namespace : System ; assembly = mscorlib " >

Pourquoi ? Parce que la classe String nest pas connue de lapplication. Il faut lui
indiquer o elle se trouve, en indiquant son espace de nom, un peu comme un using
C#. Pour cela on utilise la syntaxe prcdente pour dire que lespace de nom que jai
nomm system correspondra lespace de nom System de lassembly mscorlib.
Pour utiliser ma classe String, il faudra que je la prfixe de system : .
82

LES RESSOURCES
Bref, revenons notre ressource de type String. Je vais pouvoir lutiliser depuis nimporte quelle page vu quelle est dfinie dans le dictionnaire de ressources de lapplication, par exemple dans ma page principale :
1

< TextBlock Text = " { StaticResource TitreApplication } " Foreground =


" { StaticResource BrushRouge } " / >

Et nous aurons donc la figure 7.4.

Figure 7.4 Utilisation dune ressource de type chane de caractre

Le fichier App.xaml est lapplication ce que le fichier Mainpage.xaml


est la page MainPage. Il est accompagn de son code behind
App.xaml.cs et on peut voir que la classe App drive de la classe Application - http://msdn.microsoft.com/fr-fr/library/system.windows.
application(v=vs.95).aspx. Nous y reviendrons mais cest dans cette
classe que nous pourrons grer tout ce qui rapporte lapplication. Cest le
cas par exemple du dictionnaire de ressources, mais cest galement l que
nous pourrons grer les erreurs applicatives non interceptes dans le code et
plein dautres choses que nous dcouvrirons au fur et mesure.
83

CHAPITRE 7. AJOUTER DU STYLE

Les styles
Le style correspond lidentification de plusieurs proprits par un nom, que lon peut
appliquer facilement plusieurs contrles. Un style trouve donc tout fait naturellement sa place dans les dictionnaires de ressources que nous avons dj vus. Un style
est, comme une ressource, caractris par un nom et cible un type de contrle. Par
exemple, observons le style suivant :
1
2
3
4
5
6
7
8
9

< phone : P h oneApplicationPage . Resources >


< Style x : Key = " StyleTexte " TargetType = " TextBlock " >
< Setter Property = " Foreground " Value = " Green " / >
< Setter Property = " FontSize " Value = " 35 " / >
< Setter Property = " FontFamily " Value = " Comic Sans MS " / >
< Setter Property = " Margin " Value = " 0 20 0 20 " / >
< Setter Property = " HorizontalAlignment " Value = " Center "
/>
</ Style >
</ phone : P honeApplicationPage . Resources >

On remarque llment important TargetType="TextBlock" qui me permet dindiquer


que le style sapplique aux contrles TextBlock. La lecture du style nous renseigne sur
ce quil fait. Nous pouvons remarquer que la proprit Foreground aura la valeur Green,
que la proprit FontSize aura la valeur 35, etc. Pour que notre contrle bnficie de
ce style, nous pourrons utiliser encore la syntaxe suivante :
1

< TextBlock Text = " { StaticResource TitreApplication } " Style = " {


StaticResource StyleTexte } " / >

Ce qui nous donnera la figure 7.5.


Ah, mais a me rappelle quelque chose, on na pas dj vu des styles ? Et si, lorsque
nous crons une nouvelle application, Visual Studio nous cr le squelette dune page
dans le fichier MainPage.xaml et nous avons notamment le titre de la page dfini de
cette faon :
1
2
3
4

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,0 , 28
">
< TextBlock Text = " MON APPLICATION " Style = " { StaticResource
P h oneTextNormalStyle } " Margin = " 12 , 0 " / >
< TextBlock Text = " Hello World " Margin = "9 , -7 ,0 , 0 " Style = " {
StaticResource PhoneTextTitle1Style } " / >
</ StackPanel >

Vous pouvez dsormais comprendre que ces deux TextBlock utilisent les styles
PhoneTextNormalStyle et PhoneTextTitle1Style. Ce ne sont pas des styles que nous
avons crs. Il sagit de styles systmes, prsents directement dans le systme dexploitation et que nous pouvons utiliser comme bon nous semble.
Le style est un lment qui sera trs souvent utilis dans nos applications. Dfinir le
XAML associ ces styles est un peu rbarbatif. Heureusement, Blend peut nous aider
dans la cration de style. . . Prenons par exemple le code XAML suivant :
84

LES STYLES

Figure 7.5 Le style appliqu au TextBlock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< TextBlock Text = " Nom " Grid . Column = " 0 " Grid . Row = " 0 " / >
< TextBox Grid . Row = " 0 " Grid . Column = " 1 " / >
< TextBlock Text = " Pr nom " Grid . Column = " 0 " Grid . Row = " 1 " / >
< TextBox Grid . Row = " 1 " Grid . Column = " 1 " / >
< TextBlock Text = " Age " Grid . Column = " 0 " Grid . Row = " 2 " / >
< TextBox Grid . Row = " 2 " Grid . Column = " 1 " / >
</ Grid >

Qui donne la figure 7.6.


Si nous passons dans Blend, nous pouvons facilement crer un style en faisant un clic
droit sur un TextBlock et en choisissant de modifier le style, puis de crer un nouveau
style en choisissant de crer un lment vide (voir la figure 7.7).
85

CHAPITRE 7. AJOUTER DU STYLE

Figure 7.6 Aperu dun formulaire construit en XAML

Figure 7.7 Modification du style dans Blend

86

LES STYLES
Blend nous ouvre une nouvelle fentre o nous pouvons crer un nouveau style (voir
la figure 7.8).

Figure 7.8 Fentre de cration dun nouveau style


Nous devons fournir un nom au style puis nous pouvons indiquer quelle est la porte
du style, soit toute lapplication (ce que jai choisi), soit la page courante, soit un
dictionnaire de ressources dj existant.
Le style est cr dans le fichier App.xaml, comme nous lavons dj vu, qui est le fichier
de lancement de lapplication. Je peux aller modifier les proprits du style, par exemple
la couleur (voir la figure 7.9).

Figure 7.9 Modification de la couleur du style


Une fois le style termin, je peux retourner dans ma page pour appliquer ce style aux
autres contrles. Pour cela, jutilise le bouton droit sur le contrle, Modifier le style
, Appliquer la ressource, et je peux retrouver mon style tout en haut (voir la figure
7.10).
Figure 7.10 Notre nouveau style fait partie de la liste des styles
On remarque au passage quil existe plein de styles dj tout prts, ce sont des styles
systmes comme ceux que nous avons vu un peu plus haut et dont nous pouvons
87

CHAPITRE 7. AJOUTER DU STYLE


allgrement nous servir ! De retour dans le XAML, je peux constater quune proprit
a t rajoute mes TextBlock :
1

< TextBlock Text = " Pr nom " Grid . Column = " 0 " Grid . Row = " 1 " Style = " {
StaticResource TexteStyle } " / >

Cest la proprit Style bien videmment, qui va permettre dindiquer que lon applique
le style TexteStyle. Celui-ci est dfini dans le XAML du fichier App.xaml :
1
2
3
4
5

< Style x : Key = " TexteStyle " TargetType = " TextBlock " >
< Setter Property = " Foreground " Value = " # FF0B5EF0 " / >
< Setter Property = " FontFamily " Value = " Andy " / >
< Setter Property = " FontSize " Value = " 32 " / >
</ Style >

Ce qui correspond aux valeurs que jai modifies. Et voil, plutt simple non ? Remarquons avant de terminer que les styles peuvent hriter entre eux, ce qui permet de
complter ou de remplacer certaines valeurs. Prenons par exemple le XAML suivant :
1
2
3
4
5
6
7
8
9

< Style x : Key = " TexteStyle " TargetType = " TextBlock " >
< Setter Property = " Foreground " Value = " # FF0B5EF0 " / >
< Setter Property = " FontFamily " Value = " Andy " / >
< Setter Property = " FontSize " Value = " 32 " / >
</ Style >
< Style x : Key = " TexteStyle2 " TargetType = " TextBlock " BasedOn = " {
StaticResource TexteStyle } " >
< Setter Property = " FontSize " Value = " 45 " / >
< Setter Property = " HorizontalAlignment " Value = " Center " / >
</ Style >

Le deuxime style hrite du premier grce la proprit BaseOn. Notez que les styles
sont encore plus puissants que a, nous aurons loccasion de voir dautres utilisations
plus loin dans le cours.

Les thmes
Si vous avez jou avec lmulateur ou avec vos Windows Phone, vous avez pu vous rendre
compte que Windows Phone disposait de plusieurs thmes. Ouvrez votre mulateur et
faites glisser lcran sur la droite ou cliquez sur la flche en bas de lcran daccueil,
cliquez ensuite sur Paramtres (voir la figure 7.11).
Puis sur thme (voir la figure 7.12).
On obtient cet cran (voir la figure 7.13).
Il est possible de choisir le thme, soit sombre soit clair puis la couleur daccentuation
(voir la figure 7.14).
Quest-ce que a veut dire ? Eh bien cela veut dire quon ne peut pas faire nimporte
quoi avec les couleurs et surtout pas nimporte comment. Par exemple, si jcris du
88

LES THMES

Figure 7.11 Accs au menu de paramtrage de lmulateur

Figure 7.12 Accs au paramtrage des thmes


89

CHAPITRE 7. AJOUTER DU STYLE

Figure 7.13 Paramtrage des thmes

Figure 7.14 Modification de la couleur daccentuation


90

LES THMES
texte de couleur blanche, cela passera trs bien avec mon thme sombre, mais cela
deviendra invisible avec un thme clair.
Les contrles de Windows Phone savent grer ces diffrents thmes sans aucun problme grce aux styles systmes qui savent sadapter aux diffrents thmes, comme
par exemple les styles PhoneTextNormalStyle et PhoneTextTitle1Style. Ainsi, si
vous lancez votre application frachement cre en mode sombre, vous aurez les titres
suivants (voir la figure 7.15).

Figure 7.15 Le titre est blanc sur fond noir en mode sombre
Alors quen mode clair, vous aurez la figure 7.16.
Les couleurs sont diffrentes, cest le style qui gre les diffrents thmes. Il est possible de
dtecter le thme de lapplication afin dadapter nos designs, par exemple en changeant
une image ou en changeant une couleur, etc. Pour ce faire, on peut utiliser la technique
suivante :
1
2
3
4
5
6
7
8
9

Visibility d a r k Ba c k gr o u nd V i si b i li t y = ( Visibility ) Application .


Current . Resources [ " P h on e D ar k T he m e Vi s i bi l i ty " ];
if ( d a r k B a c k g r o un d V i si b i li t y == Visibility . Visible )
{
// le th me est sombre
}
else
{
// le th me est clair
}

91

CHAPITRE 7. AJOUTER DU STYLE

Figure 7.16 Le titre est noir sur fond blanc en mode clair
De mme, on peut rcuprer la couleur daccentuation choisie afin de lutiliser sur nos
pages, par exemple :
1
2

Color couleur = ( Color ) Application . Current . Resources [ "


PhoneAccentColor " ];
MonTextBox . Foreground = new SolidColorBrush ( couleur ) ;

Changer lapparence de son contrle


Il ny a pas que les styles qui permettent de changer lapparence dun contrle. Rappelezvous, nous avons dit que certains contrles drivaient de la classe ContentControl. Il
sagit de contrles qui contiennent dautres objets. Cest le cas du bouton par exemple.
Il est possible de modifier son apparence sans changer ses fonctionnalits. Cest une
des grandes forces du XAML. Il suffit de redfinir la proprit Content du bouton. . .
Jusqu prsent, un bouton ctait ce XAML ( lintrieur dun StackPanel) :
1

< Button Content = " Cliquez - moi ! " / >

Qui donnait la figure 7.17.


Nous avons mis une chane de caractres dans la proprit Content. Cette proprit
est de type object, il est donc possible dy mettre nimporte quoi. En loccurrence, on
peut y mettre un TextBlock qui donnera le mme rsultat visuel :
92

CHANGER LAPPARENCE DE SON CONTRLE

Figure 7.17 Un simple bouton


1
2
3
4
5

< Button >


< Button . Content >
< TextBlock Text = " Cliquez - moi " / >
</ Button . Content >
</ Button >

Si on peut mettre un TextBlock, on peut mettre nimporte quoi et cest a qui est
formidable. Par exemple, on peut facilement mettre une image. Reprenons notre rond
rouge du dbut du chapitre, puis utilisez le XAML suivant :
1
2
3
4
5
6
7
8

< Button >


< Button . Content >
< StackPanel >
< Image Source = " / Assets / Images / rond . png " Width = " 100 "
Height = " 100 " / >
< TextBlock Text = " Cliquez - moi ! " / >
</ StackPanel >
</ Button . Content >
</ Button >

Nous obtenons un bouton tout fait fonctionnel possdant une image et un texte (voir
la figure 7.18).
Nous navons rien eu dautre faire que de modifier lobjet Content et ce bouton
continue se comporter comme un bouton classique. Remarquons avant de terminer
quil est possible de pousser la personnalisation encore plus loin grce aux modles (en
anglais template) que nous verrons plus loin.

En rsum
Les styles sont un lment trs puissant nous permettant de modifier lapparence
de nos contrles.
On peut changer lapparence des contrles de type ContentControl pour crer
un autre look tout en conservant la fonctionnalit du contrle.
Il faut faire attention aux diffrents thmes dune application et toujours vrifier
93

CHAPITRE 7. AJOUTER DU STYLE

Figure 7.18 Un bouton avec une image


que ce quon souhaite afficher soit bien visible en fonction des thmes et de la
couleur daccentuation.

94

Chapitre

TP1 : Cration du jeu du plus ou du


moins
Difficult :
Bienvenue dans ce premier TP ! Vous avez pu dcouvrir dans les chapitres prcdents les
premires bases du XAML permettant la construction dapplications Windows Phone. Il est
grand temps de mettre en pratique ce que nous avons appris. Cest ici loccasion pour vous
de tester vos connaissances et de valider ce que vous appris en ralisant cet exercice.
Pour loccasion, nous allons raliser un petit jeu, le classique jeu du plus ou du moins.

95

CHAPITRE 8. TP1 : CRATION DU JEU DU PLUS OU DU MOINS

Instructions pour raliser le TP


Voici donc un petit TP sous forme de cration dun jeu simple qui va vous permettre
de vous entraner. Lide est de raliser le jeu classique du plus ou du moins. . . Je vous
rappelle les rgles. Lordinateur calcule un nombre alatoire et nous devons le deviner.
chaque saisie, il nous indique si le nombre saisi est plus grand ou plus petit que le
nombre trouver, ainsi que le nombre de coups. Une fois trouv, il nous indique que
nous avons gagn.
Nous allons donc pouvoir utiliser nos connaissances en XAML pour crer une interface
graphique permettant de raliser ce jeu. Nous aurons bien sr besoin dun TextBox
pour obtenir la saisie de lutilisateur. Vous pouvez ensuite utiliser un TextBlock pour
donner les instructions, qui pourront tre de la couleur daccentuation. De mme, vous
utiliserez un autre TextBlock pour afficher le nombre de coups. Vous pourrez utiliser
un bouton afin de vrifier le rsultat et un autre bouton pour recommencer une partie.
Pour rappel, vous pouvez obtenir un nombre alatoire en instanciant un objet Random
et en appelant la mthode Next :
1
2

Random random = new Random () ;


int valeurSecrete = random . Next (1 , 500 ) ;

Vous pouvez choisir les bornes que vous voulez, mais de 1 500 me parat pas trop mal.
Noubliez pas de grer le cas o lutilisateur saisit nimporte quoi. Nous ne voudrions
pas que notre premier jeu sur Windows Phone ait un bug qui fasse planter lapplication !
Cest vous de jouer. Bon courage.

Correction
Alors, comment tait ce TP ? Pas trop difficile, non ?
Alors, voyons ma correction. Il y a plusieurs faons de raliser ce TP ainsi quune
infinit de mise en page possible. Jai choisi un look trs simple, mais nhsitez pas
faire parler votre crativit.
Commenons par le XAML :
1
2
3
4
5
6

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
< RowDefinition Height = " Auto " / >
</ Grid . RowDefinitions >

7
8
9

10

96

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " TP du jeu du
plus ou du moins " Style = " { StaticResource
PhoneTextTitle2Style } " / >
</ StackPanel >

CORRECTION
11
12
13
14

15
16
17
18

19
20
21
22

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< TextBlock Text = " Veuillez saisir une valeur ( entre
0 et 500 ) " Style = " { StaticResource
PhoneTextNormalStyle } " HorizontalAlignment = "
Center " / >
< TextBox x : Name = " Valeur " InputScope = " Number " / >
< Button Content = " V rifier " Tap = " Button_Tap_1 " / >
< TextBlock x : Name = " Indications " Height = " 50 "
TextWrapping = " Wrap " / >
< TextBlock x : Name = " NombreDeCoups " Height = " 50 "
TextWrapping = " Wrap " Style = " { StaticResource
PhoneTextNormalStyle } " / >
</ StackPanel >
</ Grid >
< Button Content = " Rejouer " Tap = " Button_Tap_2 " Grid . Row = " 2 "
/>
</ Grid >

Il sagit de disposer mes contrles de manire obtenir ce rendu (voir la figure 8.1).

Figure 8.1 Rendu du TP du jeu du plus ou du moins dans lmulateur


Ce quil est important de voir ici cest que tous mes TextBlock possdent un style
qui sait grer les thmes, sauf celui pour les indications car cest par code que je
97

CHAPITRE 8. TP1 : CRATION DU JEU DU PLUS OU DU MOINS


vais positionner la couleur. Remarquez galement que le TextBox affichera un clavier
numrique grce lInputScope qui vaut Number.
Passons prsent au code behind :
1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
private Random random ;
private int valeurSecrete ;
private int nbCoups ;

6
7
8
9

public MainPage ()
{
I nitializeComponent () ;

10
11
12
13
14
15
16

random = new Random () ;


valeurSecrete = random . Next (1 , 500 ) ;
nbCoups = 0 ;
Color couleur = ( Color ) Application . Current . Resources [ "
PhoneAccentColor " ];
Indications . Foreground = new SolidColorBrush ( couleur ) ;

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

private void Button_Tap_1 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
int num ;
if ( int . TryParse ( Valeur . Text , out num ) )
{
if ( valeurSecrete == num )
{
Indications . Text = " Gagn !! " ;
}
else
{
nbCoups ++;
if ( valeurSecrete < num )
Indications . Text = " Trop grand ... " ;
else
Indications . Text = " Trop petit ... " ;
if ( nbCoups == 1 )
NombreDeCoups . Text = nbCoups + " coup " ;
else
NombreDeCoups . Text = nbCoups + " coups " ;
}
}
else
Indications . Text = " Veuillez saisir un entier ... " ;
}

43
44

98

private void Button_Tap_2 ( object sender , System . Windows .

CORRECTION

45
46
47
48
49
50
51
52

Input . GestureEventArgs e )
valeurSecrete = random . Next (1 , 500 ) ;
nbCoups = 0 ;
Indications . Text = string . Empty ;
NombreDeCoups . Text = string . Empty ;
Valeur . Text = string . Empty ;

La classe Color et la classe SolidColorBrush ncessitent limport suivant :


1

using System . Windows . Media ;

Le jeu en lui-mme ne devrait pas avoir pos trop de problmes. Lalgorithme est
classique, on commence par dterminer un nombre alatoire puis chaque demande
de vrification, on transforme la valeur saisie en entier, afin de vrifier que lutilisateur
na pas saisi nimporte quoi. Avec le clavier numrique, les erreurs sont limites, mais
elles sont encore possible car on demande des entiers et lutilisateur a la possibilit de
saisir des nombres virgule. Puis on compare et on indique le rsultat (voir la figure
8.2).

Figure 8.2 Une partie en cours de jeu . . .


Et voil pour notre premier TP. Vous avez pu voir comme il est finalement assez simple
de crer des petits programmes sur nos tlphones grce au XAML et au C#.
99

CHAPITRE 8. TP1 : CRATION DU JEU DU PLUS OU DU MOINS

100

Chapitre

Dessiner avec le XAML


Difficult :
En plus des contrles, le XAML possde les formes (en anglais Shape). Elles permettent
de dessiner diffrentes formes sur nos pages. Voyons prsent comment cela fonctionne.

101

CHAPITRE 9. DESSINER AVEC LE XAML

Dessin 2D
Il existe plusieurs types de formes. Elles sont reprsentes par des classes qui drivent toutes dune classe abstraite de base : Shape - http://msdn.microsoft.com/
fr-fr/library/system.windows.shapes.shape(v=vs.95).aspx. Shape est un lment affichable sur une page dans la mesure o elle drive, comme les contrles, de
FrameworkElement et de UIElement. Nous avons notre disposition :
Les ellipses et cercles via la classe Ellipse - http://msdn.microsoft.com/fr-fr/
library/system.windows.shapes.ellipse(v=vs.95).aspx
Les lignes, via la classe Line - http://msdn.microsoft.com/fr-fr/library/
system.windows.shapes.line(v=vs.95).aspx
Plusieurs lignes ou courbes connectes, via la classe Path - http:
//msdn.microsoft.com/fr-fr/library/system.windows.shapes.path(v=
vs.95).aspx. Path pouvant tre traduit en trac, il sagit dun trac de lignes
ou de courbes.
Des lignes connectes via la classe PolyLine - http://msdn.microsoft.com/
fr-fr/library/system.windows.shapes.polyline(v=vs.95).aspx
Les polygones, via la classe Polygon - http://msdn.microsoft.com/fr-fr/
library/system.windows.shapes.polygon(v=vs.95).aspx. La diffrence avec
le PolyLine est que la forme se termine en reliant le dernier trait au premier.
Des rectangles via la classe Rectangle - http://msdn.microsoft.com/fr-fr/
library/system.windows.shapes.rectangle(v=vs.95).aspx
Si vous vous rappelez, nous avons utilis la classe Rectangle dans un prcdent chapitre
pour illustrer les marges. Dessinons par exemple un carr et un cercle. Pour cela, je
peux utiliser les classes Rectangle et Ellipse :
1
2
3
4

< StackPanel >


< Rectangle Width = " 100 " Height = " 100 " Fill = " Aqua " / >
< Ellipse Height = " 100 " Width = " 100 " Fill = " Azure " / >
</ StackPanel >

Ce qui nous donne la figure 9.1.


Remarquons que la proprit Fill permet de colorer les formes. Nous allons y revenir.
Mais le plus simple est encore dutiliser Blend pour ce genre de choses. Vous avez accs
aux formes soit dans longlet des composants, soit en cliquant sur le rectangle (voir la
figure 9.2).
Blend est votre meilleur alli pour dessiner sur vos pages. Noubliez pas quil est capable
dexploiter le XAML que vous avez saisi la main dans Visual Studio, par exemple :
1
2
3

< Canvas >


< Line X1 = " 10 " Y1 = " 100 " X2 = " 150 " Y2 = " 100 " Stroke = " Blue "
StrokeThickness = " 15 " / >
</ Canvas >

qui va nous permettre de tracer une ligne bleue horizontale dpaisseur 15. Nous la
voyons apparatre dans Blend (voir la figure 9.3).
102

DESSIN 2D

Figure 9.1 Affichage dun rectangle et dune ellipse grce leurs contrles respectifs

Figure 9.2 Accs aux formes depuis Blend

103

CHAPITRE 9. DESSINER AVEC LE XAML

Figure 9.3 Affichage dune ligne bleue dans Blend


Je vais marrter l pour les exemples de formes car la documentation en ligne possde
des exemples qui sont plutt simples comprendre. Vous allez dailleurs voir dans le
prochain chapitre un exemple de polygone.

Pinceaux
Les pinceaux vont nous permettre de colorier nos formes. Nous avons rapidement vu
tout lheure que nous pouvions colorier nos formes grce la proprit Fill. Par
exemple, le XAML suivant :
1
2
3

< Canvas >


< Polygon Points = " 50 , 50 200 , 200 50 , 200 " Fill = " Aqua " Stroke =
" Blue " StrokeThickness = " 5 " / >
</ Canvas >

dessine un triangle rectangle de couleur Aqua dont le trait est bleu, dpaisseur 5 (voir
la figure 9.4).
En fait, Aqua et Blue sont des objets drivs de la classe Brush, en loccurrence
ici il sagit dune SolidColorBrush - http://msdn.microsoft.com/fr-fr/library/
system.windows.media.solidcolorbrush(v=vs.95).aspx. Comme on la dj vu,
on peut donc crire notre prcdent pinceau de cette faon :
104

PINCEAUX

Figure 9.4 Le triangle est color grce au pinceau Aqua

1
2
3
4
5

< Polygon Points = " 50 , 50 200 , 200 50 , 200 " Stroke = " Blue "
StrokeThickness = " 5 " >
< Polygon . Fill >
< SolidColorBrush Color = " Aqua " / >
</ Polygon . Fill >
</ Polygon >

Ce qui nous offre un meilleur contrle sur le pinceau. Nous pouvons par exemple changer
lopacit et la passer de 1 (valeur par dfaut) 0.4 par exemple :
1
2
3
4
5

< Polygon Points = " 50 , 50 200 , 200 50 , 200 " Stroke = " Blue "
StrokeThickness = " 5 " >
< Polygon . Fill >
< SolidColorBrush Color = " Aqua " Opacity = " 0 . 4 " / >
</ Polygon . Fill >
</ Polygon >

Et nous pouvons voir que la couleur est un peu plus transparente (voir la figure 9.5).

Figure 9.5 Lopacit joue sur la transparence du contrle


Toutes les proprits commenant par Stroke se rapportent au trait
de la forme. Par exemple, Stroke permet de modifier la couleur du
trait, StrokeThickness permet de modifier lpaisseur du trait ou encore
StrokeDash que nous navons pas vu qui permet de modifier lapparence
dun trait (pointills, flches aux extrmits, . . .).
105

CHAPITRE 9. DESSINER AVEC LE XAML


Vous vous en doutez, il existe dautres pinceaux que le pinceau uni. Nous avons galement notre disposition :
Un gradient linaire, via la classe LinearGradientBrush - http:
//msdn.microsoft.com/fr-fr/library/system.windows.media.
lineargradientbrush(v=vs.95).aspx
Un gradient radial, via la classe RadialGradientBrush - http://msdn.microsoft.
com/fr-fr/library/system.windows.media.radialgradientbrush(v=vs.95)
.aspx
Une image, via la classe ImageBrush - http://msdn.microsoft.com/fr-fr/
library/system.windows.media.imagebrush(v=vs.95).aspx
Une vido, via la classe VideoBrush - http://msdn.microsoft.com/fr-fr/
library/system.windows.media.videobrush(v=vs.95).aspx
Utilisons par exemple une ImageBrush pour afficher la mascotte dOpenClassrooms
dans notre triangle (voir la figure 9.6).

Figure 9.6 Zozor, la mascotte


Nous aurons le XAML suivant :
1
2
3
4
5

< Polygon Points = " 50 , 50 200 , 200 50 , 200 " Stroke = " Blue "
StrokeThickness = " 5 " >
< Polygon . Fill >
< ImageBrush ImageSource = " http :// uploads . siteduzero . com /
files / 337001_338000 / 337519 . png " / >
</ Polygon . Fill >
</ Polygon >

Qui donnera la figure 9.7.

Figure 9.7 Le triangle avec un pinceau utilisant limage de Zozor


Et voil comment utiliser une image comme pinceau. Sauf que ce triangle rectangle ne
lui rend vraiment pas honneur . . . ! Pour faire un dgrad, le mieux est dutiliser Blend.
106

PINCEAUX
Reprenons notre triangle rectangle et cliquez droite sur le pinceau de dgrad (voir
la figure 9.8).

Figure 9.8 Cration dun pinceau de dgrad


Il ne reste plus qu choisir les couleurs de votre dgrad. Il faut vous servir de la bande
en bas pour dfinir les diffrentes couleurs du dgrad (voir la figure 9.9).

Figure 9.9 Choix du dgrad


Et nous aurons un mgnifique triangle dgrad (voir la figure 9.10) !

Figure 9.10 Le triangle avec le pinceau dgrad


Notons que le XAML gnr est le suivant :
107

CHAPITRE 9. DESSINER AVEC LE XAML


1
2
3
4
5
6
7
8
9

< Polygon Points = " 50 , 50 200 , 200 50 , 200 " Stroke = " Blue "
StrokeThickness = " 5 " >
< Polygon . Fill >
< LinearGradientBrush EndPoint = " 0 .5 , 1 " StartPoint = " 0 .5 , 0
">
< GradientStop Color = " Black " Offset = " 0 " / >
< GradientStop Color = " # FF1FDC0C " Offset = " 1 " / >
< GradientStop Color = " # FFE8AD11 " Offset = " 0 . 488 " / >
</ LinearGradientBrush >
</ Polygon . Fill >
</ Polygon >

Voil pour ce petit tour des pinceaux.

Les transformations
Le XAML possde un systme de transformations qui permet dagir sur les contrles. Il
existe plusieurs types de transformations dites affines car elles conservent la structure
originale du contrle. Il est par exemple possible deffectuer :
une rotation grce la classe RotateTransform - http://msdn.microsoft.com/
fr-fr/library/system.windows.media.rotatetransform(v=vs.95).aspx
une translation grce la classe TranslateTransform - http://msdn.microsoft.
com/fr-fr/library/system.windows.media.translatetransform(v=vs.95)
.aspx
une mise lchelle grce la classe ScaleTransform - http://msdn.microsoft.
com/fr-fr/library/system.windows.media.scaletransform(v=vs.95).aspx
une inclinaison grce la classe SkewTransform - http://msdn.microsoft.
com/fr-fr/library/system.windows.media.skewtransform(v=vs.95).aspx
Une transformation matricielle grce la classe MatrixTransform http://msdn.microsoft.com/fr-fr/library/system.windows.media.
matrixtransform(v=vs.95).aspx
Par exemple, pour faire pivoter un bouton de 45, je peux utiliser le code suivant :
1
2
3
4
5
6
7
8
9

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< StackPanel >
< Button Content = " Cliquez - moi ! " >
< Button . RenderTransform >
< RotateTransform x : Name = " Rotation " Angle = " 45 "
CenterX = " 100 " CenterY = " 50 " / >
</ Button . RenderTransform >
</ Button >
</ StackPanel >
</ Grid >

Ce qui nous donne la figure 9.11.


Il suffit de renseigner la proprit RenderTransform du contrle, sachant que cette
proprit fait partie de la classe UIElement qui est la classe mre de tous les contrles
108

LES TRANSFORMATIONS

Figure 9.11 Rotation dun contrle de 45


affichables. Dans cette proprit, on met la classe RotateTransform en lui prcisant notamment langle de rotation et les coordonnes du centre de rotation. Illustrons encore
une transformation grce la classe ScaleTransform pour effectuer un grossissement
dun TextBlock :
1
2
3
4
5
6

< TextBlock Text = " Hello world " / >


< TextBlock Text = " Hello world " >
< TextBlock . RenderTransform >
< ScaleTransform ScaleX = " 3 " ScaleY = " 10 " / >
</ TextBlock . RenderTransform >
</ TextBlock >

Qui donne la figure 9.12.


Ces transformations peuvent se combiner grce la classe TransformGroup, par exemple
ici je combine une rotation avec une translation :
1
2
3
4
5
6
7
8

< TextBlock Text = " Hello world " >


< TextBlock . RenderTransform >
< TransformGroup >
< RotateTransform Angle = " 90 " / >
< TranslateTransform X = " 150 " Y = " 100 " / >
</ TransformGroup >
</ TextBlock . RenderTransform >
</ TextBlock >

109

CHAPITRE 9. DESSINER AVEC LE XAML

Figure 9.12 Mise lchelle du contrle


Et nous aurons la figure 9.13.
Sachant quil est possible de faire la mme chose avec une transformation composite, grce la classe CompositeTransform - http://msdn.microsoft.com/fr-fr/
library/system.windows.media.compositetransform(v=vs.95).aspx. Elle sutilise
ainsi :
1
2
3
4
5

< TextBlock Text = " Hello world " >


< TextBlock . RenderTransform >
< CompositeTransform TranslateX = " 150 " TranslateY = " 100 "
Rotation = " 90 " / >
</ TextBlock . RenderTransform >
</ TextBlock >

Voil pour les transformations. En soi elles ne sont pas toujours trs utiles, mais elles
rvlent toutes leurs puissances grce aux animations que nous dcouvrirons dans le
chapitre suivant.

En rsum
Le XAML possde plein de formes que nous pouvons utiliser pour dessiner dans
nos applications, comme le trait, lellipse, le rectangle, etc.
chaque forme peut tre applique une couleur de remplissage ou de traits
grce aux pinceaux.
110

LES TRANSFORMATIONS

Figure 9.13 Rotation combine une translation


Il est galement possible de faire subir des transformations un contrle comme
une rotation ou une translation.

111

CHAPITRE 9. DESSINER AVEC LE XAML

112

Chapitre

10

Crer des animations


Difficult :
Des contrles, du dessin . . . nous sommes presque prts raliser des jolies interfaces en
laissant parler notre crativit. Mais tout cela manque un peu de dynamique, de trucs qui
bougent et nous en mettent plein la vue.
Le XAML nous a entendu ! Grce lui, il est trs facile de crer des animations. Elles
vont nous servir mettre en valeur certains lments, ou raliser un effet de transition en
rajoutant du mouvement et de linteractivit. Bref, de quoi innover un peu et embellir vos
applications.
Nous allons dcouvrir dans ce chapitre comment tout cela fonctionne et comment raliser
nos propres animations directement en manipulant le XAML, ou encore grce loutil
professionnel de design : Expression Blend. Soyez prt ce que a bouge.

113

CHAPITRE 10. CRER DES ANIMATIONS

Principes gnraux des animations


Une animation consiste faire varier les proprits dun contrle dans un temps prcis.
Par exemple, si je veux faire bouger un contrle dans un Canvas, je vais pouvoir faire
varier les proprits Canvas.Top et Canvas.Left. De la mme faon, si je veux faire
disparaitre un lment avec ce que lon appelle communment leffet fade , je vais
pouvoir faire varier la proprit dopacit dun contrle.
Pour cela, le XAML possde plusieurs classes qui vont nous tre utiles. Des
classes permettant de faire varier une proprit de type couleur, une proprit de type double et une proprit de type Point qui sont respectivement
les classes ColorAnimation - http://msdn.microsoft.com/fr-fr/library/
system.windows.media.animation.coloranimation(v=vs.95).aspx,
DoubleAnimation
http://msdn.microsoft.com/fr-fr/library/system.windows.
media.animation.doubleanimation(v=vs.95).aspx
et
PointAnimation
http://msdn.microsoft.com/fr-fr/library/system.windows.media.animation.
pointanimation(v=vs.95).aspx.
Il nest possible danimer que ces trois types de valeur.

Pour fonctionner, elles ont besoin dune autre classe qui soccupe de contrler les animations afin dindiquer leurs cibles et la planification de lanimation. Il sagit de la
classe StoryBoard - http://msdn.microsoft.com/fr-fr/library/system.windows.
media.animation.storyboard(v=vs.95).aspx dont le nom explicite rappelle un peu
les projets de montage audio ou vido. Cest le mme principe, cest elle qui va cadencer
les diffrentes animations.

Cration dune animation simple (XAML)


Pour illustrer les animations, je vais vous montrer leffet de disparition ( fade ) appliqu un contrle. Crons donc un contrle, par exemple un StackPanel contenant un
bouton et un TextBlock. Jai besoin galement dun autre bouton qui va me permettre
de dclencher lanimation :
1
2
3
4
5
6
7
8
9
10

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< StackPanel x : Name = " LeStackPanel " >
< Button Content = " Cliquez - moi ! " / >
< TextBlock Text = " Je vais bient t dispara tre ... " / >
</ StackPanel >
< StackPanel Grid . Row = " 1 " >

114

CRATION DUNE ANIMATION SIMPLE (XAML)


11
12
13

< Button Content = " D marrer l ' animation " Tap = " Button_Tap "
/>
</ StackPanel >
</ Grid >

Nous allons maintenant crer notre Storyboard. Celui-ci doit se trouver en ressources.
Comme on la dj vu, vous pouvez le mettre en ressources de lapplication, de la page
ou bien en ressources dun contrle parent. Mettons-le dans les ressources de la grille :
1
2
3
4
5
6
7
8
9

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Grid . Resources >
< Storyboard x : Name = " MonStoryBoard " >
</ Storyboard >
</ Grid . Resources >

10
11
12
13
14
15
16
17
18

< StackPanel x : Name = " LeStackPanel " >


< Button Content = " Cliquez - moi ! " / >
< TextBlock Text = " Je vais bient t dispara tre ... " / >
</ StackPanel >
< StackPanel Grid . Row = " 1 " >
< Button Content = " D marrer l ' animation " Tap = " Button_Tap "
/>
</ StackPanel >
</ Grid >

Ce Storyboard doit avoir un nom afin dtre manipul par le clic sur le bouton. Il faut
maintenant dfinir lanimation :
1
2
3
4
5

< Storyboard x : Name = " MonStoryBoard " >


< DoubleAnimation From = " 1 . 0 " To = " 0 . 0 " Duration = " 0 : 0 : 2 "
Storyboard . TargetName = " LeStackPanel "
Storyboard . TargetProperty = " Opacity " / >
</ Storyboard >

Il sagit dune animation de type double o nous allons animer la proprit Opacity
pour la faire aller de la valeur 1 (visible) la valeur 0 (invisible) pendant une dure
de deux secondes, ciblant notre contrle nomm LeStackPanel. Il faut maintenant
dclencher lanimation lors du clic sur le bouton :
1
2
3
4

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
MonStoryBoard . Begin () ;
}

Difficile de vous faire une copie dcran du rsultat mais nhsitez pas essayer par
vous-mme (voir la figure 10.1).
115

CHAPITRE 10. CRER DES ANIMATIONS

Figure 10.1 Lanimation de lopacit fait disparatre le contrle


Il est possible de faire en sorte que lanimation se joue en boucle et de manire indfinie.
Il suffit de rajouter les proprits AutoReverse et RepeatBehavior. Par exemple, ici je
vais animer un bouton de manire ce quil se dplace de gauche droite et de droite
gauche indfiniment.
1
2
3
4
5
6
7
8
9
10

11
12

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Grid . Resources >
< Storyboard x : Name = " MonStoryBoard " >
< DoubleAnimation From = " 0 " To = " 200 " Duration = " 0 : 0 : 3 "
AutoReverse = " True "
RepeatBehavior = " Forever "
Storyboard . TargetName = "
MonBouton " Storyboard .
TargetProperty = " ( Canvas . Left
)"/>
</ Storyboard >
</ Grid . Resources >

13
14
15
16

116

< Canvas Width = " 480 " Height = " 500 " x : Name = " LeCanvas " >
< Button x : Name = " MonBouton " Content = " Cliquez - moi ! " / >
</ Canvas >

CRATION DUNE ANIMATION SIMPLE (XAML)


17
18
19
20

< StackPanel Grid . Row = " 1 " >


< Button Content = " D marrer l ' animation " Tap = " Button_Tap "
/>
</ StackPanel >
</ Grid >

Jen profite pour indiquer que pour animer une proprit complexe, il faut la
saisir entre parenthses. Je reviendrai sur ce type de proprit plus loin dans
le cours.
Nous pouvons contrler plus finement une animation. Jusqu prsent, nous avons utilis la mthode Begin() pour dmarrer une animation. Vous pouvez galement utiliser
la mthode Stop() pour arrter une animation, la mthode Pause() pour la mettre
en pause et la mthode Resume() pour la reprendre. Vous pouvez galement faire des
animations de transformations. Il suffit de combiner lutilisation des transformations
et dune DoubleAnimation. Par exemple, ici je vais faire tourner mon bouton de 90
degrs et le faire revenir sa position initiale :
1
2
3
4
5
6
7
8
9

10
11

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Grid . Resources >
< Storyboard x : Name = " MonStoryBoard " >
< DoubleAnimation From = " 0 " To = " 90 " Duration = " 0 : 0 : 1 "
AutoReverse = " True "
Storyboard . TargetName = " Rotation "
Storyboard . TargetProperty = " Angle
"/>
</ Storyboard >
</ Grid . Resources >

12
13
14
15
16
17
18
19
20
21
22
23

< StackPanel >


< Button x : Name = " MonBouton " Content = " Cliquez - moi ! " >
< Button . RenderTransform >
< RotateTransform x : Name = " Rotation " Angle = " 0 " / >
</ Button . RenderTransform >
</ Button >
</ StackPanel >
< StackPanel Grid . Row = " 1 " >
< Button Content = " D marrer l ' animation " Tap = " Button_Tap "
/>
</ StackPanel >
</ Grid >

Il suffit de cibler la proprit Angle de lobjet RotateTransform. Si vous voulez quune


animation dmarre partir dun certain temps, vous pouvez rajouter la proprit
BeginTime au Storyboard :
117

CHAPITRE 10. CRER DES ANIMATIONS


1
2
3
4

< Storyboard x : Name = " MonStoryBoard " BeginTime = " 0 : 0 : 2 " >
< DoubleAnimation From = " 0 " To = " 90 " Duration = " 0 : 0 : 1 "
AutoReverse = " True "
Storyboard . TargetName = " Rotation " Storyboard .
TargetProperty = " Angle " / >
</ Storyboard >

Par exemple ici, lanimation va durer une seconde et dmarrera deux secondes aprs son
dclenchement via la mthode Begin(). On peut contrler plus finement une animation
grce aux animations dites Key Frame qui permettent dindiquer diffrents moments
cls dune animation. Il est possible ainsi de spcifier la valeur de la proprit anime
un moment T. On utilisera les trois types danimations suivantes :
DoubleAnimationUsingKeyFrames
ColorAnimationUsingKeyFrames
PointAnimationUsingKeyFrames
Chacune de ces animations peut tre de trois types : Linear, Discret et Spline.
Lanimation linaire se rapproche des animations que nous avons vues prcdemment
dans la mesure o entre les moments cls, lanimation passera par toutes les valeurs
sparant les deux valeurs des moments cls. On pourrait illustrer ceci en simulant
un secouage de bouton afin dattirer lattention de lutilisateur. Le secouage va
consister faire une rotation de X degrs dans le sens horaire, puis revenir la position
initiale, puis faire la rotation de X degrs dans le sens anti horaire et enfin revenir la
position initiale. Il y a donc cinq moments cls dans cette animation :
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Grid . Resources >
< Storyboard x : Name = " MonStoryBoard " >
< D o u b l e A n i m a t i o n U s i n g K e y F r a m e s Storyboard .
TargetName = " Rotation " Storyboard . TargetProperty =
" Angle " RepeatBehavior = " 5x " >
< LinearDoubleKeyFrame Value = " 0 " KeyTime = " 00 : 00 :
00 " / >
< LinearDoubleKeyFrame Value = " 5 " KeyTime = " 00 : 00 :
00 . 1 " / >
< LinearDoubleKeyFrame Value = " 0 " KeyTime = " 00 : 00 :
00 . 2 " / >
< LinearDoubleKeyFrame Value = " -5 " KeyTime = " 00 : 00
: 00 . 3 " / >
< LinearDoubleKeyFrame Value = " 0 " KeyTime = " 00 : 00 :
00 . 4 " / >
</ DoubleAnimationUsingKeyFrames >
</ Storyboard >
</ Grid . Resources >

17
18

118

< StackPanel >

CRATION DUNE ANIMATION COMPLEXE (BLEND)


19
20
21
22
23
24
25
26
27
28

< Button x : Name = " MonBouton " Content = " Cliquez - moi ! "
Width = " 200 " Height = " 100 " >
< Button . RenderTransform >
< RotateTransform x : Name = " Rotation " Angle = " 0 "
CenterX = " 100 " CenterY = " 50 " / >
</ Button . RenderTransform >
</ Button >
</ StackPanel >
< StackPanel Grid . Row = " 1 " >
< Button Content = " D marrer l ' animation " Tap = " Button_Tap "
/>
</ StackPanel >
</ Grid >

tant donn que nous animons un angle, nous utiliserons la classe


DoubleAnimationUsingKeyFrames. Vu que nous voulons des transitions linaires pour
une animation de type double, nous pourrons utiliser la classe LinearDoubleKeyFrame
pour indiquer nos moments cls. Ainsi, jindique quau moment 0, langle sera de 0
degrs. Une fraction de seconde plus tard, langle sera de 5 degrs. Au bout de deux
fractions de seconde, langle sera nouveau 0 degrs. Une fraction de seconde plus
tard, langle sera de -5 degrs et enfin, une fraction de seconde plus tard, langle
reviendra sa position initiale.
noter que cette animation sera joue 5 fois grce la proprit RepeatBehavior="5x".
Il y aurait encore beaucoup de choses dire sur ce genre danimations, mais nous allons
prsent dcouvrir comment raliser des animations grce blend.

Cration dune animation complexe (Blend)


Expression Blend, en sa qualit de logiciel de design professionnel facilite la cration
danimations. Nous allons crer une petite animation inutile pour illustrer son fonctionnement. Repartez dune page toute neuve et ouvrez-l dans Expression Blend.
Pour rappel, cliquez-droit sur le fichier XAML et choisissez Ouvrir dans expression
blend. Nous allons prsent ajouter un bouton. Slectionnez le bouton dans la boite
outils et faites le glisser sur la surface de la page (voir la figure 10.2).
Allez maintenant dans le menu Fentre et choisissez espace de travail puis animation
afin de passer dans la vue ddie lanimation (voir la figure 10.3).
En bas, dans longlet Objets et chronologie, cliquez sur le plus pour crer une nouvelle animation (voir la figure 10.4).
Donnez un nom votre Storyboard, comme indiqu la figure 10.5.
Il apparat ensuite en bas droite la ligne de temps qui va nous permettre de dfinir
des images cls (voir la figure 10.6).
Dplacez le trait jaune qui est sous le chiffre zro pour le placer sous le chiffre un, en
le slectionnant par le haut de la ligne. Cela nous permet de dfinir une image cl la
premire seconde de lanimation. Nous allons dplacer le bouton vers le bas droite.
119

CHAPITRE 10. CRER DES ANIMATIONS

Figure 10.2 Ajout dun bouton partir de Blend

Figure 10.3 Changement de lespace de travail

Figure 10.4 Cration dune nouvelle animation


120

CRATION DUNE ANIMATION COMPLEXE (BLEND)

Figure 10.5 Nommage du storyboard

Figure 10.6 La ligne de temps du storyboard


Cela signifiera que pendant cette seconde, lanimation fera le trajet de la position 0
la position 1 correspondant au dplacement du bouton que nous avons ralis.
Pour voir comment rend lanimation, cliquez sur le petit bouton de lecture en haut de
la ligne de temps (voir la figure 10.7).

Figure 10.7 Dmarrage de lanimation


Je ne peux pas vous illustrer le rsultat, mais vous devriez voir votre rectangle se
dplacer de haut en bas droite. Essayez ! Nhsitez pas rduire le zoom si vous
ne voyez pas tout lcran du designer. Et voil, un dbut danimation plutt simple
faire !
Sauvegardez votre fichier et repassez dans Visual Studio. Vous pouvez voir que le fichier
121

CHAPITRE 10. CRER DES ANIMATIONS


XAML, aprs rechargement, contient dsormais un code qui ressemble au suivant :
1
2
3

5
6

< phone : P h oneApplicationPage . Resources >


< Storyboard x : Name = " MonStoryBoard " >
< DoubleAnimation Duration = " 0 : 0 : 1 " To = " 166 " Storyboard .
TargetProperty = " ( UIElement . RenderTransform ) .(
CompositeTransform . TranslateX ) " Storyboard .
TargetName = " button " d : IsOptimized = " True " / >
< DoubleAnimation Duration = " 0 : 0 : 1 " To = " 26 " Storyboard .
TargetProperty = " ( UIElement . RenderTransform ) .(
CompositeTransform . TranslateY ) " Storyboard .
TargetName = " button " d : IsOptimized = " True " / >
</ Storyboard >
</ phone : P honeApplicationPage . Resources >

7
8
9
10
11
12

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

13

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock Text = " MON APPLICATION " Style = " {
StaticResource PhoneTextNormalStyle } " Margin = " 12 , 0 "
/>
< TextBlock Text = " nom de la page " Margin = "9 , -7 ,0 , 0 "
Style = " { StaticResource PhoneTextTitle1Style } " / >
</ StackPanel >

14
15

16
17
18

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< Button x : Name = " button " Content = " Button "
HorizontalAlignment = " Left " Margin = " 137 , 68 ,0 , 0 "
VerticalAlignment = " Top " Rend erTran sformO rigin = " 0 .5 , 0
.5">
< Button . RenderTransform >
< CompositeTransform / >
</ Button . RenderTransform >
</ Button >
</ Grid >
</ Grid >

19
20

21
22
23
24
25
26

On peut voir quil nous a mis une CompositeTransform dans le bouton avec une
translation sur laxe des X et sur laxe des Y de une seconde. Remarquez la syntaxe
particulire qua utilis Blend :
1

< DoubleAnimation Duration = " 0 : 0 : 1 " To = " 166 " Storyboard .


TargetProperty = " ( UIElement . RenderTransform ) .(
Co mp ositeTransform . TranslateX ) " Storyboard . TargetName = "
button " d : IsOptimized = " True " / >

122

CRATION DUNE ANIMATION COMPLEXE (BLEND)


et notamment sur la proprit TargetProperty. Alors que dans mon exemple, javais
donn un nom la transformation pour animer une proprit de cette transformation, ici Blend a choisi danimer une proprit relative du bouton, nomm button.
Il dit quil va animer la proprit TranslateX de lobjet CompositeTransform faisant partie du RenderTransform correspondant au bouton, sachant que la proprit
RenderTransform fait partie de la classe de base UIElement.
Revenons Expression Blend pour rajouter une rotation. Plaons donc notre ligne
de temps sur la deuxime seconde. Je dplace mon bouton en bas gauche afin de
raliser une translation laquelle je vais combiner une rotation. Aller dans la fentre
de proprits du bouton et aller tout en bas pour cliquer sur transformer, et choisir
le deuxime onglet pour faire pivoter (voir la figure 10.8).

Figure 10.8 Rotation dun contrle via Blend


Vous pouvez dsormais choisir un angle, disons 40. Vous pouvez vrifier que la translation se fait en mme temps que la rotation en appuyant sur le bouton de lecture.
Terminons enfin notre mini boucle en dplaant la ligne de temps sur la troisime seconde et en faisant revenir le bouton la position premire et en rglant langle sur
360. Et voil, nous avons termin. Enfin. . . presque. Lanimation est prte mais rien
ne permet de la dclencher. Il existe une solution pour le faire avec Expression Blend,
via les comportements que lon trouve plus souvent sous le terme anglais de Behavior.
Jai choisi de ne pas en parler dans ce cours car cela ncessiterait pas mal dexplications
qui ne nous serviront pas particulirement pour la suite. Nous allons donc retourner
dans Visual Studio pour dmarrer manuellement lanimation, par exemple lors du clic
sur le bouton. Rajoutons donc lvnement clic directement dans notre bouton :
1

2
3
4
5

< Button x : Name = " button " Content = " Button " Height = " 77 " Margin = " 98
, 60 , 165 , 0 " VerticalAlignment = " Top " Rend erTran sform Origin = " 0 .
5 , 0 . 5 " Tap = " Button_Tap " >
< Button . RenderTransform >
< C om positeTransform / >
</ Button . RenderTransform >
</ Button >

Avec dans le code behind :


123

CHAPITRE 10. CRER DES ANIMATIONS


1
2
3
4

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
MonStoryBoard . Begin () ;
}

Et voil. Notre animation est termine !

Projections 3D
Chaque lment affichable avec le XAML peut subir une projection 3D. Cela consiste
donner une surface 2D une perspective 3D afin de raliser un effet visuel. Plutt
quun long discours, un petit exemple qui parlera de lui-mme. Prenons par exemple
une image, limage de la couverture de mon livre sur le C# :
1

< Image Source = " http :// uploads . siteduzero . com / files /
365001_366000 / 365350 . jpg " / >

Qui donne la figure 10.9.

Figure 10.9 Image de la couverture du livre pour apprendre le C# dans lmulateur


Pour lui faire subir un effet de perspective, nous pouvons utiliser le XAML suivant :
1

< Image Source = " http :// uploads . siteduzero . com / files /
365001_366000 / 365350 . jpg " >

124

PROJECTIONS 3D
2
3
4
5

< Image . Projection >


< PlaneProjection RotationX = " - 35 " RotationY = " - 35 "
RotationZ = " 15 " / >
</ Image . Projection >
</ Image >

qui lui fera subir une rotation de -35 autour de laxe des X, de -35 autour de laxe
des Y et de 15 autour de laxe des Z, ce qui donnera la figure 10.10.

Figure 10.10 Limage avec une projection 3D


Plutt sympa comme effet non ? Nous avons utilis la classe PlaneProjection - http://
msdn.microsoft.com/fr-fr/library/system.windows.media.planeprojection(v=
vs.95).aspx pour le raliser. Il existe une autre classe permettant de faire des projections suivant une matrice 3D, il sagit de la classe Matrix3DProjection - http://msdn.
microsoft.com/fr-fr/library/system.windows.media.matrix3dprojection(v=vs.
95).aspx mais je pense que vous ne vous servirez que de la projection plane.
Alors, cest trs joli comme a, mais combin une animation, cest encore mieux.
Prenons le XAML suivant :
1
2
3
4

< phone : P h on e A pplicationPage . Resources >


< Storyboard x : Name = " Sb " >
< DoubleAnimation Storyboard . TargetName = " Projection "
Storyboard . TargetProperty = " RotationZ "
From = " 0 " To = " 360 " Duration = " 0 : 0 : 5 "
/>

125

CHAPITRE 10. CRER DES ANIMATIONS


5
6
7
8
9
10
11
12
13
14
15

< DoubleAnimation Storyboard . TargetName = " Projection "


Storyboard . TargetProperty = " RotationY "
From = " 0 " To = " 360 " Duration = " 0 : 0 : 5 "
/>
</ Storyboard >
</ phone : P honeApplicationPage . Resources >
< StackPanel >
< Image Source = " http :// uploads . siteduzero . com / files /
365001_366000 / 365350 . jpg " >
< Image . Projection >
< PlaneProjection x : Name = " Projection " RotationX = " 0 "
RotationY = " 0 " RotationZ = " 0 " / >
</ Image . Projection >
</ Image >
</ StackPanel >

Vous vous doutez que je vais animer la rotation sur laxe des Y et sur laxe des Z de 0
360 degrs pendant une dure de 5 secondes . . . Difficile de vous montrer le rsultat,
mais je ne peux que vous encourager tester chez vous (voir la figure 10.11).

Figure 10.11 Animation dune projection 3D


Vous naurez bien sr pas oubli de dmarrer lanimation, par exemple depuis lvnement de chargement de page :
1
2
3
4
5

public MainPage ()
{
I ni t i alizeComponent () ;
Loaded += MainPage_Loaded ;
}

126

PROJECTIONS 3D
6
7
8
9
10

void MainPage_Loaded ( object sender , RoutedEventArgs e )


{
Sb . Begin () ;
}

En rsum
Le XAML possde un framework complexe danimation.
Blend se rvle un atout de qualit dans la ralisation danimations complexes.
Les projections 3D permettent dajouter un effet de perspective 3D dont le rendu
est plutt intressant.

127

CHAPITRE 10. CRER DES ANIMATIONS

128

Deuxime partie

Un mobile orient donnes

129

Chapitre

11

Une application plusieurs pages, la


navigation
Difficult :
Pour linstant, notre application est simple, avec une unique page. Il est bien rare quune
application nait quune seule page. . . Cest comme pour un site internet, imaginons que
nous ralisions une application mobile pour commander des produits, nous aurons une page
contenant la liste des produits par rayon, une page pour afficher la description dun produit,
une page pour commander. . .
Nous allons donc voir quil est possible de naviguer facilement entre les pages de notre
application grce au service de navigation de Windows Phone.

131

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION

Naviguer entre les pages


Avant de pouvoir naviguer entre des multiples pages, il faut effectivement avoir plusieurs pages ! Nous allons illustrer cette navigation en prenant pour exemple le site
OpenClassrooms. . . enfin, en beaucoup beaucoup moins bien.
Premire fonctionnalit, il faut pouvoir se loguer afin datteindre la page des cours.
Nous allons donc avoir deux pages, une qui permet de se loguer, et une qui permet
dafficher la liste des cours.
Commenons par la page pour se loguer et vu quelle existe, utilisons la page
MainPage.xaml :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

10

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " D
monstration de la navigation " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Page de Login "
Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle1Style } " / >
</ StackPanel >

11
12
13
14
15
16
17
18
19

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< TextBlock Text = " Saisir votre login "
HorizontalAlignment = " Center " / >
< TextBox x : Name = " Login " / >
< Button Content = " Se connecter " Tap = " Button_Tap " / >
</ StackPanel >
</ Grid >
</ Grid >

Noubliez pas de gnrer chaque fois les vnements des contrles, si ce


nest pas dj fait. Dans lexemple prcdent : Button_Tap.
Pour que cela soit plus simple, nous utilisons uniquement un login pour nous connecter.
Si nous affichons la page dans lmulateur, nous avons la figure 11.1.
Nous allons maintenant crer une deuxime page permettant dafficher la liste des
cours. Crons donc une autre page que nous nommons ListeCours.xaml. Pour cela,
nous faisons un clic droit sur le projet et choisissons dajouter un nouvel lment. Il
132

NAVIGUER ENTRE LES PAGES

Figure 11.1 Affichage de la page de login


suffit de choisir le modle de fichier Page en mode portrait Windows Phone et de lui
donner le bon nom (voir la figure 11.2).
Dans cette page, nous allons afficher simplement bonjour et que la page est en construction. Pour cela, un XAML trs minimaliste :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

10

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " D
monstration de la navigation " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Page des cours "
Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
P honeTextTitle1Style } " / >
</ StackPanel >

11
12
13

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< Grid . RowDefinitions >

133

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION

Figure 11.2 Ajout dune nouvelle page XAML dans le projet


< RowDefinition Height = " 200 " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< TextBlock x : Name = " Bonjour " Text = " Bonjour "
HorizontalAlignment = " Center " / >
< TextBlock Grid . Row = " 1 " Text = " Cette page est en
construction ... " / >
</ Grid >
</ Grid >

14
15
16
17
18
19
20

Retournons dans la mthode de clic sur le bouton de la premire page. Nous allons
utiliser le code suivant :
1
2
3
4
5

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
if (! string . IsNullOrEmpty ( Login . Text ) )
NavigationService . Navigate ( new Uri ( " / ListeCours . xaml " ,
UriKind . Relative ) ) ;
}

Nous utilisons le service de navigation et notamment sa mthode Navigate pour accder la page ListeCours.xaml, si le login nest pas vide. Grce cette mthode, nous
pouvons aller facilement sur la page en construction. Remarquons que si nous appuyons
sur le bouton en bas gauche du tlphone permettant de faire un retour arrire, alors
nous revenons la page prcdente. Si nous cliquons nouveau sur le retour arrire,
134

NAVIGUER ENTRE LES PAGES


alors nous quittons lapplication car il ny a pas de page prcdente. Bon, cest trs
bien tout a, mais si on pouvait afficher un bonjour personnalis, a serait pas plus
mal, avec par exemple le login saisi juste avant . . . Il y a plusieurs solutions pour faire
cela. Une des solutions consiste utiliser la query string. Elle permet de passer des
informations complmentaires une page, un peu comme pour les pages web. Pour
cela, on utilise la syntaxe suivante :
1

Page . xaml ? parametre1 = valeur1 & parametre2 = valeur2

Modifions donc notre mthode pour avoir :


1
2
3
4
5

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
if (! string . IsNullOrEmpty ( Login . Text ) )
Navig ationService . Navigate ( new Uri ( " / ListeCours . xaml ?
login = " + Login . Text , UriKind . Relative ) ) ;
}

Dsormais, la page ListeCours sera appele avec le paramtre login qui vaudra la
valeur saisie dans la TextBox. Pour rcuprer cette valeur, rendez-vous dans le code
behind de la seconde page o nous allons substituer la mthode appele lorsquon
navigue sur la page, il sagit de la mthode OnNavigatedTo, cette mthode faisant
partie de la classe PhoneApplicationPage. Nous aurons donc le code behind suivant :
1
2
3
4
5
6

public partial class ListeCours : PhoneApplicationPage


{
public ListeCours ()
{
I ni t i alizeComponent () ;
}

7
8
9
10
11
12

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
base . OnNavigatedTo ( e ) ;
}

Cest cet endroit que nous allons extraire la valeur du paramtre avec le code suivant :
1
2
3
4
5
6
7
8
9

protected override void OnNavigatedTo ( System . Windows . Navigation


. N a vi g at i onEventArgs e )
{
string login ;
if ( NavigationContext . QueryString . TryGetValue ( " login " , out
login ) )
{
Bonjour . Text += " " + login ;
}
base . OnNavigatedTo ( e ) ;
}

135

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION


On utilise la mthode TryGetValue en lui passant le nom du paramtre. Cette mthode fait partie de lobjet QueryString du contexte de navigation accessible via
NavigationContext. Ce qui nous donne la figure 11.3.

Figure 11.3 Affichage de la seconde page


Une autre solution pour passer des informations de page en page serait dutiliser le
dictionnaire dtat de lapplication afin de communiquer un contexte la page vers
laquelle nous allons naviguer. Il sagit dun objet accessible de partout o nous pouvons
stocker des informations et les lier une cl. Cela donne :
1
2
3
4
5
6
7
8

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
if (! string . IsNullOrEmpty ( Login . Text ) )
{
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ " login " ] = Login .
Text ;
NavigationService . Navigate ( new Uri ( " / ListeCours . xaml " ,
UriKind . Relative ) ) ;
}
}

Et pour rcuprer la valeur dans la deuxime page, nous ferons :


1
2

protected override void OnNavigatedTo ( System . Windows . Navigation


. N a vi g ationEventArgs e )
{

136

NAVIGUER ENTRE LES PAGES


3
4
5
6

string login = ( string ) P ho n eA pp li c at io nS e rv ic e . Current .


State [ " login " ];
Bonjour . Text += " " + login ;
base . OnNavigatedTo ( e ) ;

Lutilisation du dictionnaire dtat est trs pratique pour faire transiter un objet complexe qui sera difficilement reprsentable dans des paramtres de query string.
Attention : le dictionnaire dtat ne doit contenir que des informations srialisables.
Voil pour ce premier aperu du service de navigation. Remarquez que le XAML
possde galement un contrle qui permet de naviguer entre les pages, comme le
NavigationService. Il sagit du contrle HyperlinkButton - http://msdn.microsoft.
com/fr-fr/library/system.windows.controls.hyperlinkbutton(v=vs.95).aspx.
Il suffira de renseigner sa proprit NavigateUri. Compltons notre page ListeCours
pour rajouter en bas un HyperLinkButton qui renverra vers une page Contact.xaml :
1
2
3
4
5
6
7
8
9
10

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . RowDefinitions >
< RowDefinition Height = " 200 " / >
< RowDefinition Height = " * " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< TextBlock x : Name = " Bonjour " Text = " Bonjour "
H or i zontalAlignment = " Center " / >
< TextBlock Grid . Row = " 1 " Text = " Cette page est en
construction ... " / >
< HyperlinkButton Grid . Row = " 2 " Content = " Nous contacter "
NavigateUri = " / Contact . xaml " / >
</ Grid >

Puis crons une page Contact.xaml :


1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " D
monstration de la navigation " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Nous contacter "
Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
P honeTextTitle1Style } " / >

137

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION


10

</ StackPanel >

11
12
13
14
15
16
17
18

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< TextBlock Text = " Il n ' y a rien pour l ' instant ... "
/>
< Button Content = " Revenir la page pr c dente " Tap =
" Button_Tap " / >
</ StackPanel >
</ Grid >
</ Grid >

Ainsi, lorsque nous dmarrerons lapplication et aprs nous tre logus, nous pouvons
voir le bouton nous contacter en bas de la page (voir la figure 11.4).

Figure 11.4 Utilisation du contrle HyperLinkButton pour la navigation


Un clic dessus nous amne la page de contact (voir la figure 11.5).
Et voil, la navigation est rendue trs simple avec ce contrle, nous naviguons entre les
pages de notre application en nayant presque rien fait, part ajouter un contrle
HyperlinkButton. Il sait grer facilement une navigation avec des liens entre des
pages. Cest la forme de navigation la plus simple. Nous avons pu voir ainsi deux
faons diffrentes de naviguer entre les pages, via le contrle HyperlinkButton et via
le NavigationService. Puis nous avons vu deux faons diffrentes de passer des informations entre les pages, via la query string et via le dictionnaire dtat de lapplication.
138

GRER LE BOUTON DE RETOUR ARRIRE

Figure 11.5 Affichage de la page de contact


On remarque que la premire page safficher lorsquon dmarre une application est la page MainPage.xaml. Ceci est configurable en allant modifier
le fichier WMAppManifest.xml qui se trouve dans lexplorateur de solutions,
sous properties (voir la figure 11.6).
Double-cliquez dessus et une nouvelle page souvre permettant de saisir une autre page
de dmarrage (voir la figure 11.7).

Grer le bouton de retour arrire


Et pour revenir en arrire ? Nous lavons vu, il faut cliquer sur le bouton de retour
arrire qui fait ncessairement partie dun tlphone Windows Phone. Mais il est galement possible de dclencher ce retour arrire grce au service de navigation. Cest
cela que va servir le bouton que jai rajout dans la page Contact.xaml. Observons
lvnement associ au clic :
1
2
3
4

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
Navig ationService . GoBack () ;
}

139

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION

Figure 11.6 Le fichier WMAppManifest.xml dans lexplorateur de solutions

Figure 11.7 Le concepteur permettant de modifier la page XAML de dmarrage de


lapplication

140

GRER LE BOUTON DE RETOUR ARRIRE


Trs simple, il suffit de dclencher le retour arrire avec la mthode GoBack() du service
de navigation. Notez quil peut tre utile dans certaines situations de tester si un retour
arrire est effectivement possible. Cela se fait avec la proprit CanGoBack :
1
2
3
4
5

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
if ( NavigationService . CanGoBack )
Navig ationService . GoBack () ;
}

Il est galement possible de savoir si lutilisateur a appuy sur le fameux bouton de


retour arrire. ce moment-l, on passera dans la mthode OnBackKeyPress. Pour
pouvoir faire quelque chose lors de ce clic, on pourra substituer cette mthode dans
notre classe :
1
2
3
4

protected override void OnBackKeyPress ( System . ComponentModel .


CancelEventArgs e )
{
base . OnBackKeyPress ( e ) ;
}

Il est possible ici de faire ce que lon veut, comme afficher un message de confirmation
demandant si on veut rellement quitter cette page, ou sauvegarder des infos, etc. On
pourra annuler laction de retour arrire en modifiant la proprit Cancel de lobjet
CancelEventArgs true, si par exemple lutilisateur ne souhaite finalement pas revenir
en arrire. On peut galement choisir de rediriger vers une autre page si cest pertinent :
1
2
3

4
5
6
7
8

protected override void OnBackKeyPress ( System . ComponentModel .


CancelEventArgs e )
{
if ( MessageBox . Show ( " Vous n ' avez pas sauvegard votre
travail , voulez - vous vraiment quitter cette page ? " , "
Attention " , MessageBoxButton . OKCancel ) ==
MessageBoxResult . Cancel )
{
e . Cancel = true ;
}
base . OnBackKeyPress ( e ) ;
}

Qui donne la figure 11.8.


Et voil pour les bases de la navigation. Dune manire gnrale, il est de bon ton de
garder une mcanique de navigation fluide et cohrente. Il faut privilgier la navigation
intuitive pour que lutilisateur ne soit pas perdu dans un labyrinthe dcran. . .
141

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION

Figure 11.8 Affichage dune confirmation avant de quitter la page

Ajouter une image daccueil (splash screen)


Il est maintenant dun usage commun de faire en sorte quau dmarrage de son application il y ait une image pour faire patienter lutilisateur pendant que tout se charge.
On appelle cela en gnral de son nom anglais : Splash screen, que lon peut traduire
en image daccueil. On y trouve souvent un petit logo de lapplication ou de lentreprise qui la ralis, pourquoi pas le numro de version,. . . bref, des choses pour faire
patienter lutilisateur et lui dire que lapplication va bientt dmarrer. Avec Windows
Phone, il est trs facile de raliser ce genre dimage daccueil, il suffit de rajouter (si
elle nest pas dj prsente) une image sappelant SplashScreenImage.jpg la racine
du projet. Elle doit avoir son action de gnration Contenu (voir la figure 11.9).
Pour les applications Windows Phone 8, elle doit avoir la taille 768x1280 pixels alors
que pour les applications Windows Phone 7.X elle devra tre de 480x800 (voir la figure
11.10).
Vous noterez au passage mon talent de dessinateur et ma grande force exploiter toute
la puissance des formes de Paint.
142

AJOUTER UNE IMAGE DACCUEIL (SPLASH SCREEN)

Figure 11.9 Limage daccueil dans lexplorateur de solutions

Figure 11.10 Affichage de lcran daccueil dans lmulateur

143

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION

Le tombstonning
Avec Windows Phone 7 est apparu un changement radical dans la faon dont sont
traites les applications. Fini le multitche comme on le connaissait auparavant avec
un Windows classique, il ny a dsormais quune application qui sexcute la fois.
Cela veut dire que si je suis dans une application, que jappuie sur le bouton de menu
et que jouvre une nouvelle application, je nai pas deux applications qui tournent en
parallle, mais une seule. Ma premire application ne sest pas ferme non plus, elle
est passe dans un mode suspendu , voire dsactiv en fonction du contexte.
Ainsi, si je quitte ma seconde application en appuyant par exemple sur le bouton de
retour arrire, alors ma premire application qui tait en mode suspendu ou dsactiv,
se ractive et repasse dans le mode en cours dexcution.
Ce fonctionnement est conserv pour Windows Phone 8 et a t galement tendu pour
les applications Windows 8. Une application peut donc soit tre :

En cours dexcution
Suspendue
Dsactive
Non dmarre

Lorsque lapplication passe en mode suspendu, par exemple lorsque jappuie sur le
bouton de menu, elle reste intacte en mmoire. Cela veut dire quun retour arrire
pour retourner lapplication sera trs rapide et vous retrouverez votre application
comme elle se trouvait prcdemment.
Enfin, a, cest la thorie. En vrai, cest un peu plus compliqu que a. Cest le systme
dexploitation qui soccupe de grer les tats des applications en fonction notamment
de la mmoire disponible. En effet, Windows Phone peut choisir de dsactiver une
application suspendue si lapplication en cours dexcution a besoin de mmoire. De
la mme faon, lapplication peut avoir simplement t suspendue et se ractiver de
manire optimale si le systme dexploitation na pas eu besoin de mmoire. Une application dsactive a t termine par le systme dexploitation, bien souvent parce
quil avait besoin de mmoire. Quelques informations restent cependant disponibles et
si lutilisateur revient sur lapplication, celle-ci est alors redmarre depuis zro mais
ces informations sont accessibles afin de permettre de restaurer ltat de lapplication.
Tout ceci implique que lon ne peut jamais garantir que lutilisateur va fermer correctement une application ou que celle-ci va se terminer proprement, cest mme dailleurs
rarement le cas. Il peut y avoir plein de scnarios possibles. Par exemple votre utilisateur
est en train de saisir des informations dans votre application, il change dapplication
pour aller lire un mail, voir la mto, puis plus tard il revient votre application ; entre
temps il a reu un coup de tlphone, recharg son tlphone . . . Quest devenue notre
application ? Comment apporter lutilisateur tout le confort dutilisation possible et
lui garantir quil na pas perdu toute sa saisie ? Heureusement, lorsque lapplication
change dtat, nous pouvons tre prvenus grce des vnements. Ce sont des vnements applicatifs que lon retrouve dans le fichier dapplication que nous avons dj vu :
App.xaml. Par contre, ici nous allons nous intresser son code behind : App.xaml.cs
et notamment aux vnements dj gnrs pour nous. Ouvrez-le et vous pouvez voir :
144

LE TOMBSTONNING
1
2
3
4
5

// Code ex cuter lorsque l ' application d marre ( par exemple ,


partir de D marrer )
// Ce code ne s ' ex cute pas lorsque l ' application est r activ e
private void Applic ation_ Launc hing ( object sender ,
La un ch ing EventArgs e )
{
}

6
7
8
9
10
11

// Code ex cuter lorsque l ' application est activ e ( affich e


au premier plan )
// Ce code ne s ' ex cute pas lorsque l ' application est d marr e
pour la premi re fois
private void Applic ation_ Activ ated ( object sender ,
Ac ti va ted EventArgs e )
{
}

12
13
14
15
16
17

// Code ex cuter lorsque l ' application est d sactiv e ( envoy


e l ' arri re - plan )
// Ce code ne s ' ex cute pas lors de la fermeture de l '
application
private void Ap p li ca t io n_ De a ct iv at e d ( object sender ,
D e a c t i v a t edEventArgs e )
{
}

18
19
20
21
22
23

// Code ex cuter lors de la fermeture de l ' application ( par


exemple , lorsque l ' utilisateur clique sur Pr c dent )
// Ce code ne s ' ex cute pas lorsque l ' application est d sactiv
e
private void Application_Closing ( object sender ,
ClosingEventArgs e )
{
}

Les commentaires gnrs parlent deux-mmes. Ces mthodes sont donc lendroit idal
pour faire des sauvegardes de contexte. Voici la figure 11.11 un schma rcapitulatif
des diffrents tats.

1
2
3
4
5
6

Action
Dmarrage
Dsactivation
Reprise rapide
Reprise lente
Fermeture
Fermeture force par lOS

vnement applicatif
Launching
Deactivated
Activated
Activated
Closing
145

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION

Figure 11.11 Les diffrents tats dune application Windows Phone


Lvnement Closing nest pas lev lorsque cest le systme dexploitation
qui choisit de terminer lapplication, par manque de ressources par exemple.
Imaginons que je sois en train de saisir un long texte dans mon application, que mon
tlphone sonne et que je doive partir durgence. Lapplication va commencer par passer en mode suspendu. Si je retourne dans mon application rapidement en appuyant
sur le retour arrire, alors je vais retrouver mon texte intact. Par contre, si celle-ci est
dsactive par le systme dexploitation, alors je peux dire adieu ma saisie. Et l, je
risque de maudire cette application et ne plus jamais lutiliser. Et cest encore pire si
cest moi qui relance depuis zro lapplication depuis le menu, je ne pourrais mme pas
maudire le systme dexploitation. Une solution est de sauvegarder ce texte au fur et
mesure de sa saisie. Comme a, si jamais lapplication est dsactive, je pourrai alors
profiter des vnements applicatifs pour enregistrer ce texte dans la mmoire du tlphone, afin de le repositionner si jamais lutilisateur r-ouvre lapplication. Mais avant
cela, essayons de bien comprendre le processus dactivation/dsactivation et modifions
MainPage.xaml pour avoir ceci :
1
2
3
4
5

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< StackPanel >
< TextBox / >
</ StackPanel >
</ Grid >

Et dans le code-behind :
146

LE TOMBSTONNING
1
2
3
4

public partial class MainPage : PhoneApplicationPage


{
private bool _estNouvelleInstance = false ;
private string laPage ; // info conserver

5
6
7
8
9
10

public MainPage ()
{
I ni t i alizeComponent () ;
_ e s t N ouvelleInstance = true ;
}

11
12
13
14
15
16
17
18
19
20
21
22
23

protected override void OnNavigatedFrom ( System . Windows .


Navigation . NavigationEventArgs e )
{
if ( e . NavigationMode != System . Windows . Navigation .
NavigationMode . Back )
{
// l ' appli est d sactiv e , la page est quitt e
State [ " MainPage " ] = laPage ;
}
else
{
// on quitte l ' appli en appuyant sur back
}
}

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
if ( _ estNouvelleInstance )
{
if ( State . Count > 0 )
{
// application a t d sactiv e par l ' OS , on
peut acc der au dictionnaire d ' tat
// pour restaurer les infos
laPage = ( string ) State [ " MainPage " ];
}
else
{
// quivalent un d marrage de l ' appli
laPage = " MainPage " ;
}
}
else
{
// la page est gard e en m moire , pas besoin d '
aller lire le dictionnaire d ' tat
}
_ e s t N ouvelleInstance = false ;

147

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION


46
47

Jespre que les commentaires sont assez explicites.


Ce quil faut retenir cest que si

nous dmarrons lapplication en appuyant sur F5 , que nous saisissons un texte dans
le TextBox et que nous appuyons sur le bouton menu, alors lapplication est suspendue
(voir la figure 11.12).

Figure 11.12 Appui sur le bouton de menu pour que lapplication soit suspendue
Nous sommes alors passs dans la mthode OnNavigatedTo et nous sommes dans le cas
o _estNouvelleInstance vaut vrai et que le dictionnaire dtat est vide (dmarrage
de lapplication). Lorsquon clique sur le bouton de menu, alors nous passons dans la
mthode OnNavigatedFrom qui indique que la page est dsactive. Vous arrivez alors
sur la page daccueil de votre mulateur (ou de votre Windows Phone). Un petit clic
sur le bouton de retour vous ramne sur notre application avec la zone de texte qui
correspond ce que nous avons saisi. On repasse dans la mthode OnNavigatedTo avec
_estNouvelleInstance qui vaut faux. Ceci prouve bien que lapplication est intacte
en mmoire et que nous ne sommes pas repasss dans le constructeur de la classe. Il ny
a rien faire car lapplication est exactement la mme quavant son changement dtat.
Il ne reste plus qu cliquer nouveau sur la flche de retour pour fermer lapplication
et ainsi repasser dans la mthode OnNavigatedFrom mais cette fois-ci dans le else,
quand e.NavigationMode vaut NavigationMode.Back. Remarquez que le dbogueur
est toujours en route et quil faut larrter. Voil pour ltat suspendu.
Pour simuler ltat dsactiv, il faut aller dans les proprits du projet en faisant un
148

LE TOMBSTONNING
clic droit dessus, puis proprits. On arrive sur lcran des proprits du projet, cliquez
sur dboguer et vous pouvez alors cocher la case qui permet de forcer passer dans
ltat dsactiv (Tombstone) lorsque lon suspend lapplication (voir la figure 11.13).

Figure 11.13 Activer le tombstoning de lapplication



Maintenant, lorsque vous allez appuyer sur F5 , saisir un texte dans la TextBox, puis
appuyer sur le bouton de menu, lapplication sera dsactive, comme si ctait le systme dexploitation qui le faisait pour librer de la mmoire. Revenez en arrire pour
rveiller lapplication, nous pouvons constater que le TextBox est vide. Ltat de lapplication nest pas conserv et nous passons cette fois-ci dans la mthode OnNavigatedTo
mais lorsque le dictionnaire dtat nest pas vide. Il contient en loccurrence ce que
nous avons associ la chane MainPage . Nous allons donc pouvoir nous servir du
dictionnaire dtat pour restaurer ltat de lapplication lorsque celle-ci est dsactive.
Noubliez pas quil ny a rien faire lorsque lapplication est suspendue.

Allez modifier le XAML pour donner un nom notre TextBox :


1

< TextBox x : Name = " LeTextBox " / >

Puis modifiez le code behind, tout en conservant le squelette de MainPage, pour avoir
ceci :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private bool _estNouvelleInstance = false ;

4
5
6
7
8
9

public MainPage ()
{
I ni t i alizeComponent () ;
_ e s t N ouvelleInstance = true ;
}

10

149

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION


protected override void OnNavigatedFrom ( System . Windows .
Navigation . NavigationEventArgs e )
{
if ( e . NavigationMode != System . Windows . Navigation .
NavigationMode . Back )
{
// l ' appli est d sactiv e , la page est quitt e
State [ " MonTexte " ] = LeTextBox . Text ;
}
else
{
// on quitte l ' appli en appuyant sur back
}
}

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
if ( _estNouvelleInstance )
{
if ( State . Count > 0 )
{
// application a t d sactiv e par l ' OS , on
peut acc der au dictionnaire d ' tat pour
restaurer les infos
LeTextBox . Text = ( string ) State [ " MonTexte " ];
}
else
{
// quivalent un d marrage de l ' appli
}
}
else
{
// la page est gard e en m moire , pas besoin d '
aller lire le dictionnaire d ' tat
}
_ estNouvelleInstance = false ;
}

Et voil. Ltat de la zone de texte est restaur grce :


1

LeTextBox . Text = ( string ) State [ " MonTexte " ];

Qui est excut lorsque lapplication est dsactive. Vous me direz quon sembte
peut-tre un peu pour rien. Ne pourrait-on pas remplacer la mthode OnNavigatedTo
par :
1

150

protected override void OnNavigatedTo ( System . Windows . Navigation


. N a vi g ationEventArgs e )

LE TOMBSTONNING
2

3
4
5

if ( State . ContainsKey ( " MonTexte " ) )


LeTextBox . Text = ( string ) State [ " MonTexte " ];

Car finalement, peu importe si on est en mode dmarr, suspendu ou dsactiv, tout
ce qui nous intresse cest que le TextBox soit rempli si jamais il la t auparavant.
Je vous dirais oui, mais. . . Ici, lappel au dictionnaire dtat est assez rapide, mais
imaginons que nous ayons besoin daller lire une information sur internet (ce que nous
apprendrons faire trs bientt), ou effectuer un calcul complexe, ou quoi que ce soit,
ce nest pas la peine de le faire si nous possdons dj linformation. Cela fluidifie
le redmarrage de lapplication, vite de consommer des datas pour rien, etc. Veillez
toujours ne pas faire des choses inutiles et gardez lesprit quun tlphone a des
ressources limites.
Et les vnements applicatifs ?

Ils servent aussi a. Lorsque lapplication est suspendue ou dsactive, nous avons
vu que lvnement application Deactivated tait lev. Cest galement lemplacement
idal pour faire persister des informations dans le dictionnaire dtat :
1
2
3
4

private void Ap p li ca t io n_ De a ct iv at e d ( object sender ,


D e a c t i v a t edEventArgs e )
{
P h o n e A p p l i ca t io nS er v ic e . Current . State [ " DateDernierLancement
" ] = DateTime . Now ;
}

De la mme faon, lorsque lapplication est ractive, que ce soit en reprise rapide
(depuis ltat suspendu) ou en reprise lente (depuis ltat dsactive), lvnement
applicatif Activated est lev. Cest galement un endroit idal pour prparer nouveau
ltat de notre application :
1
2
3
4
5

6
7
8

private void Applic ation_ Activ ated ( object sender ,


Ac ti va ted EventArgs e )
{
if ( P h o n e A pp li c at io n Se rv ic e . Current . State . ContainsKey ( "
D a t e D ernierLancement " ) )
{
DateTime dernierLancement = ( DateTime )
P ho ne Ap p li ca ti o nS er vi c e . Current . State [ "
D ateDernierLancement " ];
if ( DateTime . Now - dernierLancement > TimeSpan .
FromMinutes ( 30 ) )
{
// si ca fait plus de trente minutes que l '
application est d sactiv e , mon information n '
est peut - tre plus jour

151

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION


9
10
11
12

T e l e c h a r g e r L e s N o u v e l l e s D o n n e e s () ;

Remarquez que le dictionnaire dtat est accessible via la proprit PhoneApplicationService.Current.State["..."] depuis nimporte quel emplacement et via la proprit State["..."] depuis une page
(qui drive de PhoneApplicationPage). Cest la mme chose.
Mais bon, il y a encore un petit problme ! tant donn que nous ne matrisons pas le
passage en dsactiv, encore moins la fermeture force de lapplication par le systme
dexploitation, ou encore le redmarrage manuel de lapplication par lutilisateur (sil
relance lapplication depuis la page daccueil), nous risquons de perdre nouveau toutes
les informations que notre utilisateur a saisi mais cette fois-ci, pas parce que nous avons
mal gr la dsactivation, mais parce que lapplication sest termine !
La solution est dutiliser la mme mcanique mais en faisant persister les informations
dans la mmoire du tlphone ddie lapplication. Il sagit du rpertoire local (en
anglais Local folder ), galement connu sous son ancien nom : IsolatedStorage. Voyez
cela un peu comme des fichiers sur un disque dur o nous pouvons enregistrer ce que
bon nous semble. Puisque cette information persiste entre les dmarrages successifs de
lapplication, il sera possible denregistrer ltat de notre application afin par exemple
que lutilisateur ne perde pas toute sa saisie. Je ne vais pas dtailler le code qui permet
denregistrer des informations dans la mmoire du tlphone, car jy reviendrai dans
un prochain chapitre.
Avant de terminer, sachez quil existe un autre tat, ltat Obscured, que lon peut
traduire par obscurci . Il sagit dun tat o une partie de lcran est masqu, par
exemple lorsquon reoit un SMS, un appel, une notification, etc. Lapplication reste
dans un tat o elle est en cours dexcution, mais lapplication peut devenir difficile
utiliser. Imaginons que votre utilisateur soit en plein compte rebours final, prt
vaincre le boss final et quil reoive un SMS juste au moment o il va gagner et qu
cause de cela il reoit la flche fatale juste en plein milieu du cur. . ., il va maudire votre
application, juste titre ! Pour viter cela, il est possible dtre notifi lorsque lapplication passe en mode Obscured, ce qui nous laisse par exemple lopportunit de faire une
pause dans notre compte rebours final . . . Pour cela, rendez-vous dans le constructeur
de la classe App pour vous abonner aux vnements Obscured et Unobscured :
1
2
3

public App ()
\{
// plein de choses ...

4
5
6
7

RootFrame . Obscured += RootFrame_Obscured ;


RootFrame . Unobscured += RootFrame_Unobscured ;

8
9

152

private void RootFrame_Unobscured ( object sender , EventArgs e )

LE TOMBSTONNING
10

11
12

// L application quitte le mode Obscured

13
14
15
16
17
18

private void RootFrame_Obscured ( object sender ,


Obscu redEventArgs e )
{
// L application passe en mode Obscured
bool estVerouille = e . IsLocked ;
}

Remarquez que le paramtre de type ObscuredEventArgs permet de savoir si lcran


est verrouill ou non.

En rsum
Il est possible de naviguer de page en page dans une application grce au service
de navigation.
Le contrle HyperlinkButton permet de dmarrer une navigation trs simplement.
Il est possible de faire transiter des informations de contexte entre les pages ; on
pourra utiliser la query string ou le dictionnaire dtat.
Pour dclencher un retour arrire par code, on utilisera la mthode GoBack()
du NavigationService.
On peut ajouter facilement une image daccueil une application Windows
Phone en utilisant une image JPEG, nomme SplashScreenImage.jpg.
Il est trs important de grer correctement les diffrents tats par lesquels peut
passer une application afin de fournir une exprience dutilisation la plus optimale.

153

CHAPITRE 11. UNE APPLICATION PLUSIEURS PAGES, LA NAVIGATION

154

Chapitre

12

TP 2 : Crer une animation de


transition entre les pages
Difficult :
Maintenant que nous avons vu comment naviguer entre les pages et que nous savons faire
des animations, il est temps de nous entrainer dans un contexte combin. Le but bien sr
est de vrifier si vous avez bien compris ces prcdents chapitres et de vous exercer.
Ce TP va nous permettre de crer une animation de transition entre des pages et par la
mme occasion embellir nos applications avec tout notre savoir.

155

CHAPITRE 12. TP 2 : CRER UNE ANIMATION DE TRANSITION ENTRE


LES PAGES

Instructions pour raliser le TP


Nous allons donc raliser une animation de transition entre des pages, histoire que nos
navigations soient un peu plus jolies. Vous allez crer une application avec deux pages.
Vous pouvez mettre ce que vous voulez sur les pages, une image, du texte. . ., mais il
faudra que la premire page possde un bouton permettant de dclencher la navigation
vers la seconde page. Lanimation fera glisser le contenu de la page vers le bas jusqu
sortir de lcran tout en rduisant lopacit jusqu la disparition. Lanimation ne sera
pas trop longue, disons seconde, voire 1 seconde (ce qui est dj long !). Laffichage
de la seconde page subira aussi une transition. Lanimation fera apparatre le contenu
de la page comme si elle apparaissait den haut et lopacit augmentera pour devenir
compltement visible. Bref, linverse de la premire transition.
Vous vous le sentez ? Alors, vous de jouer.
Si vous vous le sentez un peu moins, je vais vous donner quelques indications pour
raliser ce TP sereinement. La premire chose faire est danimer le contenu de la
page. Dans tous nos exemples, nous avons utilis un conteneur racine (bien souvent
une Grid), qui contient tous les lments de la page. Il suffit de faire porter lanimation
sur ce contrle pour faire tout disparatre. Ensuite, mme si cela fonctionne, il est
plus propre dattendre la fin de lanimation pour dclencher la navigation. Il faut donc
sabonner lvnement de fin danimation et ce moment-l dclencher la navigation.
Enfin, pour dmarrer la seconde animation, il faudra le faire depuis la mthode qui est
appele lorsquon arrive sur la seconde page. Voil, vous savez tout.

Correction
Passons la correction, maintenant que tout le monde a ralis ce dfi haut la main. Il
sagit dans un premier temps de crer deux pages diffrentes. Vous avez pu y mettre ce
que vous vouliez, il fallait juste un moyen de pouvoir naviguer sur une autre page. La
premire chose faire est donc de crer lanimation qui va permettre de faire disparaitre
lgamment la premire page. Il sagit dune animation qui cible le conteneur de premier
niveau de notre page, dans mon cas une Grid. tant donn que je vais avoir besoin de
faire une translation, je vais dfinir une classe TranslateTransform dans la proprit
RenderTransform de ma grille :
1
2
3
4
5
6
7

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RenderTransform >
< TranslateTransform x : Name = " Translation " / >
</ Grid . RenderTransform >
[... reste du code supprim pour plus de lisibilit ...]
< Button Content = " Aller la page 2 " Tap = " Button_Tap " / >
</ Grid >

Mon Storyboard sera dclar dans les ressources de ma page :


1
2

< phone : P h oneApplicationPage . Resources >


< Storyboard x : Name = " CachePage " >

156

CORRECTION
3
4
5
6
7
8

< DoubleAnimation Storyboard . TargetName = " LayoutRoot "


Storyboard . TargetProperty = " Opacity "
From = " 1 " To = " 0 " Duration = " 0 : 0 : 0 . 5 "
/>
< DoubleAnimation Storyboard . TargetName = " Translation "
Storyboard . TargetProperty = " Y "
From = " 0 " To = " 800 " Duration = " 0 : 0 : 0 . 5
" />
</ Storyboard >
</ phone : P ho n e ApplicationPage . Resources >

Lanimation consiste faire varier la proprit Opacity de la grille et la proprit Y de


lobjet de translation. Puis il faut dmarrer cette animation lors du clic sur le bouton
sachant quauparavant, je vais mabonner lvnement de fin danimation afin de
pouvoir dmarrer la navigation ce moment-l :
1
2
3
4
5
6
7

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I ni t i alizeComponent () ;
CachePage . Completed += CachePage_Completed ;
}

private void CachePage_Completed ( object sender , EventArgs e


)
{
Navig ationService . Navigate ( new Uri ( " / Page2 . xaml " ,
UriKind . Relative ) ) ;
}

9
10
11
12
13

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
CachePage . Begin () ;
}

14
15
16
17
18
19

Lvnement de fin danimation est videmment lvnement Completed, cest dans


la mthode associe que je dclencherai la navigation via la mthode Navigate du
NavigationService. Passons maintenant la deuxime page. Le principe est le mme,
voici le XAML qui nous intresse :
1
2
3
4

< phone : P h on e A pplicationPage . Resources >


< Storyboard x : Name = " AffichePage " >
< DoubleAnimation Storyboard . TargetName = " LayoutRoot "
Storyboard . TargetProperty = " Opacity "
From = " 0 " To = " 1 " Duration = " 0 : 0 : 0 . 5 "
/>

157

CHAPITRE 12. TP 2 : CRER UNE ANIMATION DE TRANSITION ENTRE


LES PAGES
< DoubleAnimation Storyboard . TargetName = " Translation "
Storyboard . TargetProperty = " Y "
From = " - 800 " To = " 0 " Duration = " 0 : 0 : 0 .
5" />
</ Storyboard >
</ phone : P honeApplicationPage . Resources >

5
6
7
8
9
10
11
12
13
14
15

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RenderTransform >
< TranslateTransform x : Name = " Translation " / >
</ Grid . RenderTransform >
[... code supprim pour plus de lisibilit ...]
</ Grid >

Cette fois-ci on incrmente lopacit et on passe de -800 0 pour les valeurs de la


translation de Y. Cot code behind nous aurons :
1
2
3
4
5
6

public partial class Page2 : PhoneApplicationPage


{
public Page2 ()
{
I nitializeComponent () ;
}

7
8
9
10
11
12
13

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
AffichePage . Begin () ;
base . OnNavigatedTo ( e ) ;
}

On redfinit la mthode qui est appele lorsquon navigue sur la page afin de dmarrer
lanimation. Et voil ! Quoique. . . ce nest pas tout fait complet en ltat car si vous
revenez sur la page prcdente en appuyant sur le bouton de retour arrire, vous vous
rendrez compte que la page est vide. Eh oui, nous avons fait disparaitre la page lors de
lanimation de transition ! Nous devons donc arrter cette fameuse animation lorsque
nous revenons sur la page avec :
1
2
3
4
5

protected override void OnNavigatedTo ( System . Windows . Navigation


. N a vi g ationEventArgs e )
{
CachePage . Stop () ;
base . OnNavigatedTo ( e ) ;
}

Et voil, prsentes comme jai pu, nos deux animations (voir la figure 12.1).
158

CORRECTION

Figure 12.1 Rendu des animations de transitions

159

CHAPITRE 12. TP 2 : CRER UNE ANIMATION DE TRANSITION ENTRE


LES PAGES
Remarquez que jai choisi la valeur 800 pour lanimation car jai considr
que la plupart des rsolutions de tlphones taient du 800. Cest videment
une mauvaise ide car je ne connais pas le comportement sur les tlphones
qui ont une autre rsolution. Il aurait t judicieux de fixer la valeur la
rsolution de lcran, sauf que nous ne savons pas encore comment faire.
Avant de terminer ce TP, notez que le toolkit pour Windows Phone, que nous dcouvrirons plus loin, possde tout un lot de transitions prtes lemploi.

160

Chapitre

13

Les proprits de dpendances et


proprits attaches
Difficult :
Il est temps de vous dire la vrit sur les proprits. Jusqu prsent, jai fait comme si
toutes les proprits que nous avons vues taient des proprits classiques au sens C#.
Elles sont en fait plus volues que a.

161

CHAPITRE 13. LES PROPRITS DE DPENDANCES ET PROPRITS


ATTACHES

Les proprits de dpendances


De leurs noms anglais dependency properties , ces proprits sont plus complexes
que la proprit de base de C# que nous connaissons, savoir :
1
2
3
4

public class MaClasse


{
public int MaPropriete { get ; set ; }
}

Avec une telle classe, il est possible dutiliser cette proprit ainsi :
1
2
3

MaClasse maClasse = new MaClasse () ;


maClasse . MaPropriete = 6 ;
int valeur = maClasse . MaPropriete ;

En XAML, nous avons dit que le code suivant :


1

< TextBlock Text = " Je suis un texte " Foreground = " Red " / >

correspondait au code C# suivant :


1
2
3

TextBlock monTextBlock = new TextBlock () ;


monTextBlock . Text = " Je suis un texte " ;
monTextBlock . Foreground = new SolidColorBrush ( Colors . Red ) ;

Ici, on ne le voit pas mais ces deux proprits sont en fait des proprits de dpendances.
Le vrai appel qui sopre derrire est quivalent :
1
2
3

TextBlock monTextBlock = new TextBlock () ;


monTextBlock . SetValue ( TextBlock . TextProperty , " Je suis un texte
");
monTextBlock . SetValue ( TextBlock . ForegroundProperty , new
SolidColorBrush ( Colors . Red ) ) ;

La mthode SetValue est hrite de la classe de base DependencyObject dont hritent


tous les UIElement. La proprit de dpendance tend les fonctionnalits dune proprit classique C#. Cest la base notamment du systme de liaison de donnes que
nous dcouvrirons plus loin. Cela permet de traiter la valeur dune proprit en fonction du contexte de lobjet et dtre potentiellement dtermine partir de plusieurs
sources de donnes. La proprit de dpendance peut galement avoir une valeur par
dfaut et des informations de description.
Ce quil faut retenir cest que la proprit de dpendance est une proprit
volue gre par le moteur XAML qui va permettre de grer les styles, les
animations, la liaison de donnes, . . .
Pour obtenir la valeur dune proprit de dpendance, on pourra utiliser :
1

162

string text = ( string ) monTextBlock . GetValue ( TextBlock .


TextProperty ) ;

LES PROPRITS ATTACHES

Les proprits attaches


Le mcanisme de proprit attache permet de rajouter des proprits un contexte
donn. Prenons par exemple le XAML suivant :
1
2
3

< Canvas >


< TextBlock Text = " Je suis un texte " Canvas . Top = " 150 " Canvas .
Left = " 80 " / >
</ Canvas >

Il est possible dindiquer la position du TextBlock dans le Canvas. Or, le TextBlock ne


possde pas de proprit Canvas.Top ou Canvas.Left. Il sagit de proprits attaches
qui vont permettre dindiquer des informations pour le TextBlock, dans le contexte de
son conteneur, le Canvas. Cest bien le Canvas qui va se servir de ces proprits pour
placer correctement le TextBlock. Le mme principe sapplique la grille par exemple,
si vous vous rappelez, afin dindiquer dans quelle ligne ou colonne se place un contrle :
1
2
3
4
5
6
7
8

< Grid >


< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< TextBlock Grid . Column = " 0 " Text = " Je suis un texte " / >
< TextBlock Grid . Column = " 1 " Text = " Je suis un autre texte " / >
</ Grid >

On utilise ici la proprit attache Grid.Column pour indiquer la grille quel endroit
il faut placer nos TextBlock.
Allez, jarrte de vous embter avec ces proprits volues. Jen ai rapidement parl
pour que vous sachiez que quand je parle de proprits, je parle en fait de quelque
chose dun peu plus pouss que ce que nous connaissions dj.
Ce quil faut retenir cest quil y a un systme complexe derrire qui est presque invisible
pour une utilisation de dbutant. Si le sujet vous intresse, nhsitez pas poser des
questions ou aller chercher des informations sur internet.
Sachez enfin quil est possible de crer nos propres proprits de dpendances et nos
propres proprits attaches, mais nous sortons du cadre dbutant et je ne le montrerai
pas dans ce cours.

En rsum
Les proprits de dpendances offrent un mcanisme plus complet que la proprit classique C#.
Ces proprits sont utilises par le moteur XAML pour grer les styles, la liaison
de donnes, etc.
En tant que dbutant, nous navons pas vraiment besoin de savoir ce quil se
cache l dessous.
163

CHAPITRE 13. LES PROPRITS DE DPENDANCES ET PROPRITS


ATTACHES

164

Chapitre

14

O est mon application ?


Difficult :
Nous avons commenc crer des applications, mais. . . que sont-elles rellement ? Lorsque
nous crons des programmes en C# pour Windows, nous obtenons des fichiers excutables dont lextension est .exe. Mais pour un tlphone, comment a marche ? Et lorsque
jajoute des images dans ma solution, o finissent-elles ? Voyons prsent les subtilits de
la construction dapplications pour Windows Phone.

165

CHAPITRE 14. O EST MON APPLICATION ?

Le .XAP
Pour illustrer ce chapitre, crons un nouveau projet, que nous nommerons par exemple
DemoXap. Lorsque nous compilons notre application pour Windows Phone, celle-ci se
gnre par dfaut dans un sous rpertoire de notre projet : DemoXap\Bin\Debug, Debug
tant le mode de compilation par dfaut lorsque nous crons une solution. Nous verrons
dans la dernire partie comment passer le mode de compilation en Release.
Toujours est-il que dans ce rpertoire, il va sy gnrer plein de choses, mais une seule
nous intresse vraiment ici, cest le fichier qui porte le nom du projet et dont lextension
est .xap. Dans mon cas, il sappelle DemoXap_Debug_AnyCPU.xap car mon mode de
compilation est Debug, Any Cpu (voir la figure 14.1).

Figure 14.1 Le mode de compilation est Debug, Any Cpu


Quest-ce donc que ce fichier ? En fait, cest une archive au format compress (zip) qui
va contenir tous les fichiers dont notre application va avoir besoin. Si vous louvrez avec
votre dcompresseur prfr, vous pourrez voir que cette archive contient les fichiers
suivants :
Assets
Tiles
FlipCycleTileLarge.png
FlipCycleTileMedium.png
FlipCycleTileSmall.png
IconicTileMediumLarge.png
IconicTileSmall.png
AlignmentGrid.png
ApplicationIcon.png
AppManifest.xaml
DemoXap.dll
WMAppManifest.xml
Plein de choses que nous retrouvons dans notre solution. En fait, tout ce qui est du
XAML et du code a t compil dans lassembly DemoXap.dll, les fichiers de Manifest
sont laisss dans larchive car ce sont eux qui donnent les instructions concernant la
configuration de lapplication. Et ensuite, il y a les quelques images.
Ajoutons un nouveau projet notre solution (clic droit sur la solution > Ajouter >
Nouveau projet), de type bibliothque de classes Windows Phone, que nous nommons
MaBibliotheque. Ciblez la plate-forme 8.0 et faites une rfrence cette assembly
depuis votre application DemoXap. Compilez et vous pourrez retrouver cette assembly
166

AFFICHAGE DIMAGES EN RESSOURCES


dans le .xap gnr. Nous retrouvons donc toutes les assemblys dont le projet a besoin
dans ce .xap.
Ajoutez prsent un nouvel lment dj existant votre projet et allez chercher une
image par exemple. Elle sajoute dans la solution et si vous compilez et que vous ouvrez
le fichier .xap, alors cette image apparait dedans. Cest parce que votre image est par
dfaut ajoute avec laction de gnration Contenu . Si vous changez cette action
de gnration et que vous mettez Resource , alors cette fois-ci, elle napparait plus
dans le .xap. En fait, un fichier inclus en tant que Resource est compil lintrieur
de son assembly. Si vous avez t attentifs aux fichiers, vous aurez pu constater que
lassembly (DemoXap.dll) est beaucoup plus grosse lorsque limage est compile en
tant que ressource. Tandis que si elle est compile en tant que contenu, alors celle-ci
fait partie du .xap.

Affichage dimages en ressources


Ceci implique des contraintes. Si nous voulons afficher par code une image, nous avons
vu que nous pouvions compiler limage en tant que contenu et utiliser le code suivant :
1

< Image x : Name = " MonImage " / >

et
1

MonImage . Source = new BitmapImage ( new Uri ( " / monimage . png " ,
UriKind . Relative ) ) ;

Ceci sexplique simplement. tant donn que limage est inclue dans le .xap, il va
pouvoir aller la chercher tranquillement lemplacement /monimage.png, donc la
racine du package. Essayez dsormais de changer laction de gnration ressource, et
vous verrez que limage ne saffiche plus. En effet, limage nest plus cet emplacement
mais compile lintrieur de lassembly. Il est quand mme possible daccder son
contenu, mais cela demande daller lire lintrieur de lassembly, ce que lon peut faire
de cette faon :
1

MonImage . Source = new BitmapImage ( new Uri ( " / DemoXap ; component /


monimage . png " , UriKind . Relative ) ) ;

Bien sr, si limage nest pas place la racine, mais dans un sous rpertoire, il faudra
indiquer le sous rpertoire dans lURL de limage.
Remarquez que dune manire gnrale, il vaudra mieux positionner le plus
souvent possible laction de gnration contenu afin de rduire la taille de
lassembly et ainsi augmenter les performances de chargement de celle-ci. Si
les images sont en ressources, elles se chargeront plus vite. Si elles sont en
contenu, alors cest lapplication qui se chargera plus vite. De mme, pour
des raisons de performances, vous aurez intrt utiliser des jpg partout sauf
si vous avez besoin de transparence.
167

CHAPITRE 14. O EST MON APPLICATION ?

Accder au flux des ressources


Il est possible daccder en lecture aux ressources. Cela peut tre intressant par
exemple pour lire un fichier texte, xml ou autre. Le fichier doit bien sr avoir laction de gnration Ressource. Puis vous pouvez utiliser le code suivant :
1
2
3
4

St re am Res ourceInfo sr = Application . GetResourceStream ( new Uri ( "


/ DemoXap ; component / MonFichier . txt " , UriKind . Relative ) ) ;
StreamReader s = new StreamReader ( sr . Stream ) ;
MonTextBlock . Text = s . ReadToEnd () ;
s . Close () ;

En rsum
Une application Windows Phone est gnre sous la forme dune archive dont
lextension est .xap.
On peut embarquer des lments dans notre assembly en positionnant laction
de gnration Resource.
Un lment compil avec laction de gnration Contenu sera disponible directement dans le .xap.

168

Chapitre

15

ListBox
Difficult :
La ListBox est un lment incontournable dans la cration dapplications pour Windows
Phone. Elle permet un puissant affichage dune liste dlment. Voyons tout de suite de
quoi il sagit car vous allez vous en servir trs souvent !

169

CHAPITRE 15. LISTBOX

Un contrle majeur
Utilisons notre designer prfr pour rajouter une ListBox dans notre page et nommonsl ListeDesTaches, ce qui donne le XAML suivant :
1
2

< ListBox x : Name = " ListeDesTaches " >


</ ListBox >

Une ListBox permet dafficher des lments sous la forme dune liste. Pour ajouter des
lments, on peut utiliser le XAML suivant :
1
2
3
4
5

< ListBox x : Name = " ListeDesTaches " >


< ListBoxItem Content = " Arroser les plantes " / >
< ListBoxItem Content = " Tondre le gazon " / >
< ListBoxItem Content = " Planter les tomates " / >
</ ListBox >

Ce qui donne la figure 15.1.

Figure 15.1 Une ListBox contenant 3 lments positionns par XAML


Il est cependant plutt rare dnumrer les lments dune ListBox directement dans
le XAML. En gnral, ces lments viennent dune source dynamique construite depuis
le code behind. Pour cela, il suffit dalimenter la proprit ItemSource avec un objet
implmentant IEnumerable, par exemple une liste. Supprimons les ListBoxItem de
notre ListBox et utilisons ce code behind :
1

List < string > chosesAFaire = new List < string >

170

UN CONTRLE MAJEUR
2
3
4
5
6
7

" Arroser les plantes " ,


" Tondre le gazon " ,
" Planter les tomates "

};
ListeDesTaches . ItemsSource = chosesAFaire ;

Il ne reste plus qu dmarrer lapplication. Nous pouvons voir que la ListBox sest
automatiquement remplie avec nos valeurs (voir la figure 15.2).

Figure 15.2 La ListBox est remplie depuis le code-behind


Et ceci sans rien faire de plus. Ce quil est important de remarquer, cest que si nous
ajoutons beaucoup dlments notre liste, alors celle-ci gre automatiquement un ascenseur pour pouvoir faire dfiler la liste. La ListBox est donc une espce de ScrollViewer qui contient une liste dlments dans un StackPanel. Tout ceci est gr nativement
par la ListBox.
Nous avons quand mme rencontr un petit truc trange. Dans le XAML, nous avons
mis la ListBox, mais la liste est vide, rien ne saffiche dans le designer. Ce qui nest
pas trs pratique pour crer notre page. Cest logique car lalimentation de la ListBox
est faite dans le constructeur, cest--dire lorsque nous dmarrons notre application.
Nous verrons plus loin comment y remdier. Lautre souci, cest que si vous essayez de
mettre des choses un peu plus complexes quune chane de caractre dans la ListBox,
par exemple un objet :
1

public partial class MainPage : PhoneApplicationPage

171

CHAPITRE 15. LISTBOX


2

3
4
5

public MainPage ()
{
I nitializeComponent () ;

6
7
8
9
10
11
12
13
14
15
16

List < ElementAFaire > chosesAFaire = new List <


ElementAFaire >
{
new ElementAFaire { Priorite = 1 , Description =
Arroser les plantes " } ,
new ElementAFaire { Priorite = 2 , Description =
Tondre le gazon " } ,
new ElementAFaire { Priorite = 1 , Description =
Planter les tomates " } ,
new ElementAFaire { Priorite = 3 , Description =
Laver la voiture " } ,
};
ListeDesTaches . ItemsSource = chosesAFaire . OrderBy ( e
e . Priorite ) ;

"
"
"
"
=>

17
18
19
20
21
22

public class ElementAFaire


{
public int Priorite { get ; set ; }
public string Description { get ; set ; }
}

vous aurez laffichage suivant (voir la figure 15.3).


La ListBox affiche la reprsentation de lobjet, cest--dire le rsultat de sa mthode
ToString(). Ce qui est un peu moche ici. Vous me direz, il suffit de substituer la
mthode ToString() avec quelque chose comme a :
1
2
3
4

public class ElementAFaire


{
public int Priorite { get ; set ; }
public string Description { get ; set ; }

5
6
7
8
9
10

public override string ToString ()


{
return Priorite + " - " + Description ;
}

Et laffaire est rgle ! Et je vous rpondrai oui, parfait. Sauf que cela ne fonctionne que
parce que nous affichons du texte ! Et si nous devions afficher du texte et une image ?
Ou du texte et un bouton ?
172

GRER LES MODLES

Figure 15.3 Cest la reprsentation de lobjet qui saffiche ici dans la ListBox

Grer les modles


Cest l quinterviennent les modles, plus couramment appels en anglais : template.
Ils permettent de personnaliser le rendu de son contrle. Le contrle garde toute sa
logique mais peut nous confier le soin de grer laffichage, si nous le souhaitons. Cest
justement ce que nous voulons faire.
Lutilisation de template est diffrente de la redfinition de la proprit
Content que nous avons dj fait dans la partie prcdente. Dune manire
gnrale, on redfinit la proprit Content pour redfinir le look gnral dun
contrle alors quon utilise les templates pour redfinir lapparence de plusieurs lments dune collection.
Nous allons donc redfinir laffichage de chaque lment de la liste. Pour cela, plutt
que dafficher un simple texte, nous allons en profiter pour afficher une image pour la
priorit et le texte de la description. Si la priorit est gale 1, alors nous afficherons
un rond rouge, sinon un rond vert (voir les figures 15.4 et 15.5).

Figure 15.4 Image rouge


173

CHAPITRE 15. LISTBOX

Figure 15.5 Image verte


Crez un rpertoire Images sous le rpertoire Assets par exemple et ajoutez les deux
images en tant que Contenu, comme nous lavons dj fait. Vous pouvez les tlcharger
la rouge ici - http://uploads.siteduzero.com/files/411001\T1\textbackslash{}_
412000/411562.png, et la verte l - http://uploads.siteduzero.com/files/411001\
T1\textbackslash{}_412000/411563.png.
La premire chose faire est de dfinir le modle des lments de la ListBox. Cela se
fait avec le code suivant :
1
2
3
4
5
6
7
8
9
10

< ListBox x : Name = " ListeDesTaches " >


< ListBox . ItemTemplate >
< DataTemplate >
< StackPanel Orientation = " Horizontal " >
< Image Source = " { Binding Image } " Width = " 30 "
Height = " 30 " / >
< TextBlock Text = " { Binding Description } " Margin =
" 20 0 0 0 " / >
</ StackPanel >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >

On redfinit la proprit ItemTemplate, cest--dire le modle dun lment de la liste.


Puis lintrieur de la balise DataTemplate, on peut rajouter nos propres contrles.
Ici, il y a un conteneur, le StackPanel, qui contient une image et une zone de texte
en lecture seule. Le DataTemplate doit toujours contenir un seul contrle. Vu que
vous avez lil, vous avez remarqu des extensions de balisage XAML lintrieur du
contrle Image et du contrle TextBlock. Je vais y revenir dans le prochain chapitre.
En attendant, nous allons modifier lgrement le code behind de cette faon :
1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;

6
7
8
9
10
11

174

List < ElementAFaire > chosesAFaire


ElementAFaire >
{
new ElementAFaire { Priorite
Arroser les plantes " } ,
new ElementAFaire { Priorite
Tondre le gazon " } ,
new ElementAFaire { Priorite
Planter les tomates " } ,

= new List <


= 1 , Description = "
= 2 , Description = "
= 1 , Description = "

GRER LES MODLES


12

};

13

new ElementAFaire { Priorite = 3 , Description = "


Laver la voiture " } ,

14
15

16

ListeDesTaches . ItemsSource = chosesAFaire . OrderBy ( e = >


e . Priorite ) . Select ( e = > new ElementAFaireBinding {
Description = e . Description , Image = ObtientImage ( e .
Priorite ) }) ;

17
18
19
20
21
22
23
24

private BitmapImage ObtientImage ( int priorite )


{
if ( priorite <= 1 )
return new BitmapImage ( new Uri ( " / Assets / Images / vert
. png " , UriKind . Relative ) ) ;
return new BitmapImage ( new Uri ( " / Assets / Images / rouge .
png " , UriKind . Relative ) ) ;
}

Pour rappel, la classe BitmapImage se trouve dans lespace de nom


System.Windows.Media.Imaging. Vrifiez galement que le lURL passe la
classe BitmapImage correspond lemplacement o vous avez ajout les images.
Nous aurons besoin de la nouvelle classe suivante :
1
2
3
4
5

public class ElementAFaireBinding


{
public BitmapImage Image { get ; set ; }
public string Description { get ; set ; }
}

Le principe est de construire des lments numrables partir de notre liste. Il sagit
dy mettre un nouvel objet qui possde une proprit Description et une proprit
Image qui contient un objet BitmapImage construit partir de la valeur de la priorit
de la tche. Il est important de constater que la classe contient des proprits qui ont
les mmes noms que ce quon a crit dans lextension de balisage vue plus haut.
1
2

< Image Source = " { Binding Image } " Width = " 30 " Height = " 30 " / >
< TextBlock Text = " { Binding Description } " Margin = " 20 0 0 0 " / >

Jy reviendrai plus tard, mais nous avons ici fait ce quon appelle un binding, que lon
peut traduire par une liaison de donnes. Nous indiquons que nous souhaitons mettre la
valeur de la proprit Image de llment courant dans la proprit Source de limage
et la valeur de la proprit Description de llment courant dans la proprit Text
du TextBlock. Rappelez-vous, llment courant est justement un objet spcial qui
contient ces proprits.
Si nous excutons le code, nous obtenons donc la figure 15.6.
Magique ! Le seul dfaut viendrait de mes images qui ne sont pas transparentes. . .
175

CHAPITRE 15. LISTBOX

Figure 15.6 Les images saffichent dans la ListBox grce au modle


Remarquons quil est obligatoire de crer une nouvelle classe contenant les
proprits Description et Image. Il naurait pas t possible dutiliser un
type anonyme car le type anonyme nest pas public mais internal. Dans
ce cas, il aurait fallu rajouter une instruction particulire permettant de dire
que les classes qui font la liaison de donnes ont le droit de voir les classes
internes. Ce qui est beaucoup plus compliqu que ce que nous avons fait !
Sachez quil existe beaucoup de contrles qui utilisent ce mme mcanisme de modle,
nous aurons loccasion den voir dautres. Cest une fonctionnalit trs puissante qui
nous laisse beaucoup de contrle sur le rendu de nos donnes.

Slection dun lment


La ListBox gre galement un autre point intressant : savoir quel lment est slectionn. Cela se fait en toute logique grce un vnement. Pour que cela soit plus
simple, enlevons nos templates et modifions le XAML pour avoir :
1
2
3
4
5

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " 100 " / >
</ Grid . RowDefinitions >

176

SLECTION DUN LMENT


6
7
8
9

< ListBox x : Name = " ListeDesTaches " SelectionChanged = "


ListeDesTaches_SelectionChanged ">
</ ListBox >
< TextBlock x : Name = " Selection " Grid . Row = " 1 " / >
</ Grid >

Notre grille a donc deux lignes, la premire contenant la ListBox et la seconde un


TextBlock. Remarquons lvnement SelectionChanged qui est associ la mthode
ListeDesTaches_SelectionChanged. Dans le code behind, nous aurons :
1
2
3
4
5
6
7
8
9
10
11
12
13

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I ni t i alizeComponent () ;
List < string > chosesAFaire = new List < string >
{
" Arroser les plantes " ,
" Tondre le gazon " ,
" Planter les tomates "
};
ListeDesTaches . ItemsSource = chosesAFaire ;
}

14
15
16
17
18
19
20

private void L i s t e D e s T a c h e s _ S e l e c t i o n C h a n g e d ( object sender ,


S e l e c ti o n C h a n g e d E ve n t A r g s e )
{
if ( e . AddedItems . Count > 0 )
Selection . Text = e . AddedItems [ 0 ]. ToString () ;
}

Ce qui va nous permettre dafficher dans le TextBlock la valeur de ce que nous avons
choisi (voir la figure 15.7).
Nous voyons au passage que la slection est mise en valeur automatiquement dans la
ListBox.
Remarquez qutant donn que notre ListBox a un nom et donc une variable pour
la manipuler, il est galement dobtenir la valeur slectionne grce linstruction
suivante :
1
2
3
4

private void L i s t e D e s T a c h e s _ S e l e c t i o n C h a n g e d ( object sender ,


S e l e c t i o n C h a n g e d Ev e n t A r g s e )
{
Selection . Text = ListeDesTaches . SelectedItem . ToString () ;
}

Ce qui est dailleurs plus propre. Lobjet SelectedItem est du type object et sera
du type de ce que nous avons mis dans la proprit ItemsSource. tant donn que
nous avons mis une liste de chane de caractres, SelectedItem sera une chane, nous
pouvons donc faire :
177

CHAPITRE 15. LISTBOX

Figure 15.7 lment slectionn dans la ListBox


1
2
3
4

private void L i s t e D e s T a c h e s _ S e l e c t i o n C h a n g e d ( object sender ,


S e l e c t io n C h a n g e d E ve n t A r g s e )
{
Selection . Text = ( string ) ListeDesTaches . SelectedItem ;
}

Dans lexemple prcdent, avec les templates, SelectedItem aurait bien sr


t du type ElementAFaireBinding.
De la mme faon, lindex de llment slectionn sera accessible grce la proprit
SelectedIndex et sera du type entier. Ce qui permet par exemple de prslectionner
une valeur dans notre ListBox au chargement de celle-ci. Ainsi, pour slectionner le
deuxime lment, je pourrais faire :
1
2
3
4
5
6
7
8
9

public MainPage ()
{
I ni t i alizeComponent () ;
List < string > chosesAFaire = new List < string >
{
" Arroser les plantes " ,
" Tondre le gazon " ,
" Planter les tomates "
};

178

SLECTION DUN LMENT


10
11
12

ListeDesTaches . ItemsSource = chosesAFaire ;


ListeDesTaches . SelectedIndex = 1 ;

Ou encore :
1
2
3
4
5
6
7
8
9
10
11
12

public MainPage ()
{
I ni t i al i zeComponent () ;
List < string > chosesAFaire = new List < string >
{
" Arroser les plantes " ,
" Tondre le gazon " ,
" Planter les tomates "
};
ListeDesTaches . ItemsSource = chosesAFaire ;
ListeDesTaches . SelectedValue = chosesAFaire [ 1 ];
}

sachant que le fait dinitialiser la slection dun lment par code dclenche lvnement de changement de slection ; ce qui nous arrange pour que notre TextBlock soit
rempli. Pour viter ceci, il faudrait associer la mthode lvnement de changement
de slection aprs avoir slectionn llment. Cela revient enlever la dfinition de
lvnement dans le XAML :
1
2

< ListBox x : Name = " ListeDesTaches " >


</ ListBox >

Et sabonner lvnement depuis le code behind, aprs avoir initialis llment


slectionn :
1
2
3
4
5
6
7
8
9
10
11
12
13

public MainPage ()
{
I ni t i al i zeComponent () ;
List < string > chosesAFaire = new List < string >
{
" Arroser les plantes " ,
" Tondre le gazon " ,
" Planter les tomates "
};
ListeDesTaches . ItemsSource = chosesAFaire ;
ListeDesTaches . SelectedValue = chosesAFaire [ 1 ];
ListeDesTaches . SelectionChanged +=
ListeDesTaches_SelectionChanged ;
}

Pour finir sur la slection dun lment, il faut savoir que la ListBox peut permettre
de slectionner plusieurs lments en changeant sa proprit SelectionMode et en la
passant Multiple :
1

< ListBox x : Name = " ListeDesTaches " SelectionChanged = "


L i s t e D e s T a c h e s _ S e l e c t i o n C h a n g e d " SelectionMode = " Multiple " >

179

CHAPITRE 15. LISTBOX


</ ListBox >

Et nous pourrons rcuprer les diffrentes valeurs slectionnes grce la collection


SelectedItems :
1
2
3
4
5
6
7
8
9

private void L i s t e D e s T a c h e s _ S e l e c t i o n C h a n g e d ( object sender ,


S e l e c t io n C h a n g e d E ve n t A r g s e )
{
string selection = string . Empty ;
foreach ( string choix in ListeDesTaches . SelectedItems )
{
selection += choix + " ; " ;
}
Selection . Text = selection ;
}

Trs bien tout a !

En rsum
La ListBox est un contrle trs utile qui permet dafficher une liste dlments
trs facilement.
Il est possible de personnaliser efficacement le rendu de chaque lment dune
ListBox grce au systme de modles.
La ListBox est un contrle complet qui possde tout une gestion de llment
slectionn.

180

Chapitre

16

La manipulation des donnes


(DataBinding & Converters)
Difficult :
Sil y a vraiment un chapitre o il faut tre attentif, cest bien celui-l. La liaison de
donnes (ou databinding en anglais) est une notion indispensable et incontournable pour
toute personne souhaitant raliser des applications XAML srieuses. Nous allons voir dans
ce chapitre de quoi il sagit et comment le mettre en place.

181

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)

Principe du Databinding
Le databinding se traduit en franais par liaison de donnes . Il sagit de la possibilit de lier un contrle des donnes. Le principe consiste indiquer un contrle o il
peut trouver sa valeur et celui-ci se dbrouille pour lafficher. Nous lavons entre-aperu
dans le chapitre prcdent avec la ListBox, il est temps de creuser un peu son fonctionnement. Techniquement, le moteur utilise un objet de type Binding - http://msdn.
microsoft.com/fr-fr/library/system.windows.data.binding(v=vs.95).aspx qui
associe une source de donnes un lment de destination, do lemploi du mot raccourci binding pour reprsenter la liaison de donnes. Le binding permet de positionner
automatiquement des valeurs aux proprits des contrles en fonction du contenu de la
source de donnes. En effet, il est trs frquent de mettre des valeurs dans des TextBox,
dans des TextBlock ou dans des ListBox, comme nous lavons fait. Le binding est l
pour faciliter tout ce qui peut tre automatisable et risque derreurs. De plus, si la
source de donnes change, il est possible de faire en sorte que le contrle soit automatiquement mis jour. Inversement, si des modifications sont faites depuis linterface,
alors on peut tre notifi automatiquement des changements.

Le binding des donnes


Pour illustrer le fonctionnement le plus simple du binding, nous allons lier une zone de
texte modifiable (TextBox) une proprit dune classe. Puisque le TextBox travaille
avec du texte, il faut crer une proprit de type string sur une classe. Cette classe sera
le contexte de donnes du contrle. Crons donc la nouvelle classe suivante :
1
2
3
4

public class Contexte


{
public string Valeur { get ; set ; }
}

Et une instance de cette classe dans notre code behind :


1
2
3

public partial class MainPage : PhoneApplicationPage


{
private Contexte contexte ;

public MainPage ()
{
I nitializeComponent () ;

5
6
7
8
9
10
11
12

contexte = new Contexte { Valeur = " Nicolas " };


DataContext = contexte ;

Nous remarquons la fin du constructeur que la proprit DataContext de la page


est initialise avec notre contexte de donnes, tape obligatoire permettant de lier la
182

LE BINDING DES DONNES


page au contexte de donnes. Chaque objet FrameworkElement possde une proprit
DataContext et chaque lment enfant dun autre lment hrite de son contexte de
donnes implicitement. Cest pour cela quici on initialise notre contexte de donnes
au niveau de la page afin que tous les lments contenus dans la page hritent de ce
contexte de donnes.
Il ne reste plus qu ajouter un contrle TextBox qui sera li cette proprit :
1

< TextBox Text = " { Binding Valeur } " Height = " 80 " / >

Cela se fait grce lexpression de balisage {Binding}. Lorsque nous excutons notre
application, nous pouvons voir que la TextBox sest correctement remplie avec la chane
de caractres Nicolas (voir la figure 16.1).

Figure 16.1 La valeur du TextBox est lie la proprit Valeur


Et tout a automatiquement, sans avoir besoin de positionner la valeur de la proprit
Text depuis le code behind.
Quest-ce qua fait le moteur de binding ? Il est all voir dans son contexte (proprit
DataContext) puis il est all prendre le contenu de la proprit Valeur de ce contexte
pour le mettre dans la proprit Text du TextBox, cest--dire la chane Nicolas .
Il faut faire attention car dans le XAML nous crivons du texte, si nous orthographions
mal Valeur, par exemple en oubliant le u :
1

< TextBox Text = " { Binding Valer } " Height = " 80 " / >

183

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
Alors la liaison de donnes naura pas lieu car la proprit est introuvable sur lobjet
Contexte. Ce qui est vrai ! Valer nexiste pas. Il ny a pas de vrification la
compilation, cest donc au moment de lexcution que nous remarquerons labsence
du binding. La seule information que nous aurons, cest dans la fentre de sortie du
dbogueur, o nous aurons :
1

System . Windows . Data Error : BindingExpression path error : ' Valer


' property not found on ' DemoPartie2 . Contexte ' ' DemoPartie2 .
Contexte '
( HashCode = 54897010 ) . BindingExpression : Path = ' Valer ' DataItem = '
DemoPartie2 . Contexte ' ( HashCode = 54897010 ) ; target element is
' System . Windows . Controls . TextBox '
( Name = ' ') ; target property is ' Text ' ( type ' System . String ') ..

indiquant que la proprit na pas t trouve.


Attention, le binding ne fonctionne quavec une proprit ayant la visibilit
public.
Il est galement possible de dfinir un binding par code behind, pour cela enlevez
lexpression de balisage dans le XAML et donnez un nom votre contrle :
1

< TextBox x : Name = " MonTextBox " Height = " 80 " / >

puis utilisez le code behind suivant :


1
2
3

public partial class MainPage : PhoneApplicationPage


{
private Contexte contexte ;

public MainPage ()
{
I nitializeComponent () ;

5
6
7
8
9
10
11
12
13

MonTextBox . SetBinding ( TextBox . TextProperty , new Binding


( " Valeur " ) ) ;
contexte = new Contexte { Valeur = " Nicolas " };
DataContext = contexte ;

La classe Binding fait partie de lespace de nom System.Windows.Data que


vous devrez ajouter avec un using.
On en profite pour constater que le binding se fait bien avec une proprit de dpendance, ici TextBox.TextProperty.
Le binding est trs pratique pour quun contrle se remplisse avec la bonne valeur.
184

LE BINDING DES DONNES


Il est possible de modifier la valeur affiche dans la zone de texte trs facilement en
modifiant la valeur du contexte depuis le code. Pour cela, changeons le XAML pour
ajouter un bouton qui va nous permettre de dclencher ce changement de valeur :
1
2
3
4

< StackPanel >


< TextBox Text = " { Binding Valeur } " Height = " 80 " / >
< Button Content = " Changer valeur " Tap = " Button_Tap " / >
</ StackPanel >

Et dans lvnement de Tap, faisons :


1
2
3
4
5
6
7

private void button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
if ( contexte . Valeur == " Nicolas " )
contexte . Valeur = " J r mie " ;
else
contexte . Valeur = " Nicolas " ;
}

Par contre, il va manquer quelque chose. Un moyen de dire la page h, jai


modifi un truc, il faut que tu regardes si tu es impact . a, cest le rle de
linterface INotifyPropertyChanged - http://msdn.microsoft.com/fr-fr/library/
system.componentmodel.inotifypropertychanged.aspx. Notre classe de contexte
doit implmenter cette interface et faire en sorte que quand on modifie la proprit,
elle lve lvnement qui va permettre linterface de se mettre jour. Notre classe de
contexte va donc devenir :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public class Contexte : IN oti fy Pro pe rty Cha ng ed


{
private string valeur ;
public string Valeur
{
get
{
return valeur ;
}
set
{
if ( value == valeur )
return ;
valeur = value ;
N oti fyPro pertyC hange d ( " Valeur " ) ;
}
}

18
19

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

20
21
22
23

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )

185

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
24
25
26

PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (


nomPropriete ) ) ;

Noubliez pas de rajouter le using System.ComponentModel;

Limplmentation de linterface INotifyPropertyChanged est trs classique


dans les applications XAML, aussi cette faon de faire est souvent factorise
dans des classes mres. Vous le verrez dans le chapitre suivant.
Ici, lorsque nous affectons une valeur la proprit, la mthode NotifyPropertyChanged
est appele en passant en paramtre le nom de la proprit de la classe quil faut rafraichir sur la page. Attention, cest une erreur classique de ne pas avoir le bon nom de
proprit en paramtres, faites-y attention.
Notez quavec la version 8.0 du SDK (en fait, grce au framework 4.5), il est possible
dutiliser une autre solution pour implmenter INotifyPropertyChanged sans avoir
linconvnient de devoir passer une chane de caractre en paramtre. Il suffit dutiliser
lattribut de mthode CallerMemberName - http://msdn.microsoft.com/fr-Fr/
library/system.runtime.compilerservices.callermembernameattribute.aspx,
qui permet dobtenir le nom de la proprit (ou mthode) qui a appel notre mthode,
en loccurrence il sagira justement du nom de la proprit quon aurait pass en
paramtre :
1
2
3
4
5
6
7
8

public class Contexte : IN oti fy Pro per ty Cha ng ed


{
private string valeur ;
public string Valeur
{
get { return valeur ; }
set { Noti fyPro pertyC hange d ( ref valeur , value ) ; }
}

9
10

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

11
12
13
14
15
16

public void N otifyP roper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

17
18
19

186

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{

LE BINDING DES DONNES


if ( object . Equals ( variable , valeur ) ) return false ;

20
21
22
23
24
25
26

variable = valeur ;
N o t i f yPro pertyC hange d ( nomPropriete ) ;
return true ;

La syntaxe est plus lgante et il ny a pas de risque de mal orthographier le nom de la


proprit. Par contre, je suis dsol pour ceux qui suivent le cours avec la version 7.1
du SDK, il faudra continuer utiliser la solution que jai prsente juste avant.
Note, il faudra inclure lespace de nom :
1

using System . Runtime . CompilerServices ;

Relanons lapplication, nous pouvons voir que le clic sur le bouton entrane bien le
changement de valeur dans la TextBox.
Ok, cest bien beau tout a, mais nest-ce pas un peu compliqu par rapport
ce quon a dj fait, savoir modifier directement la valeur de la proprit
Text ?
Effectivement, dans ce cas-l, on pourrait juger que cest sortir lartillerie lourde pour
pas grand-chose. Cependant cest une bonne pratique dans la mesure o on automatise
le processus de mise jour de la proprit. Vous aurez remarqu que lon ne manipule
plus directement le contrle mais une classe qui na rien voir avec le TextBox. Et
quand il y a plusieurs valeurs mettre jour dun coup, cest dautant plus facile. De
plus, nous pouvons faire encore mieux avec ce binding grce la bidirectionnalit de la
liaison de donnes. Par exemple, modifions le XAML pour rajouter encore un bouton :
1
2
3
4
5

< StackPanel >


< TextBox Text = " { Binding Valeur , Mode = TwoWay } " Height = " 80 " / >
< Button Content = " Changer valeur " Tap = " Button_Tap " / >
< Button Content = " Afficher valeur " Tap = " Button_Tap_1 " / >
</ StackPanel >

La mthode associe ce nouveau clic affichera la valeur du contexte :


1
2
3
4

private void button_Tap_1 ( object sender , System . Windows . Input .


GestureEventArgs e )
{
MessageBox . Show ( contexte . Valeur ) ;
}

On utilise pour cela la mthode MessageBox.Show qui affiche une petite bote de dialogue minimaliste. Les lecteurs attentifs auront remarqu que jai enrichi le binding
sur la Valeur en rajoutant un Mode=TwoWay. Ceci permet dindiquer que le binding
seffectue dans les deux sens. Cest--dire que si je modifie la proprit de la classe
de contexte, alors linterface est mise jour. Inversement, si je modifie la valeur de la
187

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
TextBox avec le clavier virtuel, alors la proprit de la classe est galement mise jour.
Cest cela que va servir notre deuxime bouton. Dmarrez lapplication, modifiez la
valeur du champ avec le clavier virtuel et cliquez sur le bouton permettant dafficher
la valeur (voir la figure 16.2).

Figure 16.2 La valeur est affiche grce la liaison de donnes bidirectionnelle


La valeur est bien rcupre. Vous pouvez faire le test en enlevant le mode TwoWay,
vous verrez que vous ne rcuprerez pas la bonne valeur. Plutt pas mal non ?
Maintenant que nous avons un peu mieux compris le principe du binding, il est temps
de prciser un point important. Pour illustrer le fonctionnement du binding, jai cr
une classe puis jai cr une variable lintrieur de cette classe contenant une instance
de cette classe. Puis jai reli cette classe au contexte de donnes de la page. En gnral,
on utilise ce dcoupage dans une application utilisant le patron de conception MVVM
(Model-View-ViewModel ). Je parlerai de ce design pattern dans le prochain chapitre.
Remarquez que lon voit souvent la construction o cest la classe de la page
qui sert de contexte de donnes de la page. Cela veut dire quon peut modifier
lexemple prcdent pour que a soit la classe MainPage qui implmente linterface
INotifyPropertyChanged, ce qui donne :
1
2
3
4
5

public partial class MainPage : PhoneApplicationPage ,


I N o t i fy Pr ope rty Ch ang ed
{
private string valeur ;
public string Valeur
{

188

LE BINDING DES DONNES


6
7

get { return valeur ; }


set { Not ifyPro perty Change d ( ref valeur , value ) ; }

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

10
11

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

12
13
14
15
16
17

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

18
19
20
21
22
23
24

25

variable = valeur ;
N o t i f yPro pertyC hange d ( nomPropriete ) ;
return true ;

26

public MainPage ()
{
I ni t i alizeComponent () ;

27
28
29
30
31
32

33

Valeur = " Nicolas " ;


DataContext = this ;

34
35
36
37
38
39
40
41
42
43
44
45
46

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
if ( Valeur == " Nicolas " )
Valeur = " J r mie " ;
else
Valeur = " Nicolas " ;
}
private void Button_Tap_1 ( object sender , System . Windows .
Input . GestureEventArgs e )
{
MessageBox . Show ( Valeur ) ;
}

La classe Contexte na plus de raison dtre. Tout est port par la classe reprsentant
la page. On affecte donc lobjet this la proprit DataContext de la page. Cette
construction est peut-tre un peu plus perturbante dun point de vue architecture
o on a tendance mlanger les responsabilits dans la classe mais elle a lavantage
189

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
de simplifier pas mal le travail. Personnellement jemploie trs rarement ce genre de
construction (jutilise plutt un driv de la premire solution), mais je lutiliserai de
temps en temps dans ce cours pour simplifier le code.
Notre ListBox fonctionne galement avec le binding. Il suffit dutiliser lexpression de
balisage avec la proprit ItemsSource :
1

< ListBox ItemsSource = " { Binding Prenoms } " / >

Nous aurons bien sr dfini la proprit Prenoms dans notre contexte :


1
2
3
4
5
6
7
8

public partial class MainPage : PhoneApplicationPage ,


I N o t i fy Pr ope rty Ch ang ed
{
private List < string > prenoms ;
public List < string > Prenoms
{
get { return prenoms ; }
set { Noti fyPro pertyC hange d ( ref prenoms , value ) ; }
}

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

10
11

public void N otifyP roper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

12
13
14
15
16
17

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

18
19
20
21
22
23
24

25

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

26

public MainPage ()
{
I nitializeComponent () ;

27
28
29
30
31
32
33
34

Prenoms = new List < string > { " Nicolas " , " J r mie " , "
Delphine " };
DataContext = this ;

Ce qui donne la figure 16.3.


190

LE BINDING DES DONNES

Figure 16.3 Liaison de donnes avec une ListBox


Le binding est encore plus puissant que a, voyons encore un point intressant. Il sagit
de la capacit de lier une proprit dun contrle la proprit dun autre contrle.
Par exemple, mettons un TextBlock en plus de notre ListBox :
1
2
3
4
5
6
7
8

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< ListBox x : Name = " ListeBoxPrenoms " ItemsSource = " { Binding
Prenoms } " / >
< TextBlock Grid . Column = " 1 " Text = " { Binding ElementName =
ListeBoxPrenoms , Path = SelectedItem } " Foreground = " Red " / >
</ Grid >

Regardons lexpression de binding du TextBlock, nous indiquons que nous voulons lier la valeur du TextBlock la proprit SelectedItem du contrle nomm
ListeBoxPrenoms. Ici cela voudra dire que lorsque nous slectionnerons un lment
dans la ListBox, alors celui-ci sera automatiquement affich dans le TextBlock, sans
avoir rien dautre faire comme le montre la figure 16.4.
Tout simplement !
Voil pour cet aperu du binding. Nous nen avons pas vu toutes les subtilits mais ce
que nous avons tudi ici vous sera grandement utile et bien souvent suffisant dans vos
191

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)

Figure 16.4 Liaison de donnes entre proprits de contrles


futures applications Windows Phone !

Binding et mode design


Vous vous rappelez notre ListBox quelques chapitres avant ? Nous avions cr une
ListBox avec une liste de choses faire. Cette liste de choses faire tait alimente
par la proprit ItemsSource dans le constructeur de la page. Le problme cest que
notre ListBox tait vide en mode design. Du coup, pas facile pour faire du style, pour
mettre dautres contrles, etc. Le binding va nous permettre de rsoudre ce problme.
Prenons le code XAML suivant :
1
2
3
4
5
6
7
8
9
10

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< ListBox ItemsSource = " { Binding ListeDesTaches } " >
< ListBox . ItemTemplate >
< DataTemplate >
< StackPanel Orientation = " Horizontal " >
< Image Source = " { Binding Image } " Width = " 30 "
Height = " 30 " / >
< TextBlock Text = " { Binding Description } "
Margin = " 20 0 0 0 " / >
</ StackPanel >
</ DataTemplate >
</ ListBox . ItemTemplate >

192

BINDING ET MODE DESIGN


11
12

</ ListBox >


</ Grid >

Avec dans le code behind :


1
2
3
4
5
6
7
8

public partial class MainPage : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
private IEnumerable < ElementAFaireBinding > listeDesTaches ;
public IEnumerable < ElementAFaireBinding > ListeDesTaches
{
get { return listeDesTaches ; }
set { Not ifyPro perty Change d ( ref listeDesTaches , value ) ;
}
}

9
10

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

11
12
13
14
15
16

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

17
18
19
20

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

21
22
23
24
25

variable = valeur ;
N o t i f yPro pertyC hange d ( nomPropriete ) ;
return true ;

26
27
28
29

public MainPage ()
{
I ni t i alizeComponent () ;

30
31
32
33
34
35
36
37

List < ElementAFaire > chosesAFaire


ElementAFaire >
{
new ElementAFaire { Priorite
Arroser les plantes " } ,
new ElementAFaire { Priorite
Tondre le gazon " } ,
new ElementAFaire { Priorite
Planter les tomates " } ,
new ElementAFaire { Priorite
Laver la voiture " } ,
};

= new List <


= 1 , Description = "
= 2 , Description = "
= 1 , Description = "
= 3 , Description = "

193

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
38

ListeDesTaches = chosesAFaire . OrderBy ( e = > e . Priorite ) .


Select ( e = > new ElementAFaireBinding { Description =
e . Description , Image = ObtientImage ( e . Priorite ) }) ;

39

40
41

42

DataContext = this ;

43
44
45
46
47
48
49
50

private BitmapImage ObtientImage ( int priorite )


{
if ( priorite <= 1 )
return new BitmapImage ( new Uri ( " / Assets / Images / vert
. png " , UriKind . Relative ) ) ;
return new BitmapImage ( new Uri ( " / Assets / Images / rouge .
png " , UriKind . Relative ) ) ;
}

51
52
53
54
55
56
57
58
59
60
61

public class ElementAFaire


{
public int Priorite { get ; set ; }
public string Description { get ; set ; }
}
public class ElementAFaireBinding
{
public BitmapImage Image { get ; set ; }
public string Description { get ; set ; }
}

Pour linstant, rien na chang, la ListBox est toujours vide en mode design. Sauf que
nous avons galement la possibilit de lier le mode design un contexte de design.
Crons donc une nouvelle classe :
1
2
3
4
5
6
7
8
9

10

11

public class MainPageDesign


{
public IEnumerable < ElementAFaireBinding > ListeDesTaches
{
get
{
return new List < ElementAFaireBinding >
{
new ElementAFaireBinding { Image = new
BitmapImage ( new Uri ( " / Assets / Images / vert . png
" , UriKind . Relative ) ) , Description = "
Arroser les plantes " } ,
new ElementAFaireBinding { Image = new
BitmapImage ( new Uri ( " / Assets / Images / rouge .
png " , UriKind . Relative ) ) , Description = "
Tondre le gazon " } ,
new ElementAFaireBinding { Image = new
BitmapImage ( new Uri ( " / Assets / Images / rouge .

194

BINDING ET MODE DESIGN

12

13
14
15
16

}; ;

png " , UriKind . Relative ) ) , Description = "


Planter les tomates " } ,
new ElementAFaireBinding { Image = new
BitmapImage ( new Uri ( " / Assets / Images / vert . png
" , UriKind . Relative ) ) , Description = " Laver
la voiture " } ,

Cette classe ne fait que renvoyer une proprit ListeDesTaches avec des valeurs de
design. Compilez et rajoutez maintenant dans le XAML linstruction suivante avant le
conteneur de plus haut niveau :
1
2
3

<d : DesignProperties . DataContext >


< design : MainPageDesign / >
</ d : DesignProperties . DataContext >

Ceci permet de dire que le contexte de design est aller chercher dans la classe
MainPageDesign. Attention, la classe MainPageDesign nest pas connue de la page !
Il faut lui indiquer o elle se trouve, en indiquant son espace de nom, un peu
comme un using C#. Cette proprit se rajoute dans les proprits de la page,
<phone:PhoneApplicationPage> :
1
2
3
4
5

< phone : P h on e A pplicationPage


x : Class = " DemoPartie2 . MainPage "
[... plein de choses ...]
xmlns : design = " clr - namespace : DemoPartie2 "
shell : SystemTray . IsVisible = " True " >

Avec cette criture, je lui dis que le raccourci design correspond lespace de nom
DemoPartie2.
Nous commenons voir apparatre des choses dans le designer de Visual Studio (voir
la figure 16.5).
Avouez que cest beaucoup plus pratique pour raliser le design de sa page.
Dans la mesure o le contexte de la page et le contexte de design doivent
contenir des proprits communes, cest une bonne ide de faire en sorte
quils implmentent tous les deux une interface qui contienne les proprits
implmenter des deux cts. . .
Avant de terminer, il faut savoir que Blend est galement capable de nous gnrer des
donnes de design sans que lon ait forcment besoin de crer une classe spcifique. Pour
illustrer ceci, repartez dune nouvelle page vide. Puis ouvrez la page dans Expression
Blend.
195

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)

Figure 16.5 Le designer affiche les donnes de design grce la liaison de donnes

196

BINDING ET MODE DESIGN


Si vous suivez ce cours pas pas, vous vous trouvez srement dans lespace
de travail animation. Revenez lespace de travail design (menu Fentre >
Espaces de travail > Design).
Cliquez sur longlet donnes, puis sur licne tout droite crer des exemples de
donnes (voir la figure 16.6).

Figure 16.6 Cration des exemples de donnes dans Blend


Cliquez sur nouvel exemple de donnes dans le menu propos. Indiquez un nom pour
la source de donnes et choisissez de la dfinir dans ce document uniquement (voir la
figure 16.7).

Figure 16.7 Cration de la source de donnes


Nous obtenons notre source de donnes, comme vous pouvez le voir la figure 16.8.

Figure 16.8 La source de donnes est visibles dans lcran de donnes


197

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
Elle est compose dune collection dobjets contenant 2 proprits. Property1 est de
type chane de caractres et Property2 est de type boolen (on peut le voir en cliquant
sur les petits boutons droite). Renommez la premire en Description et la seconde
en Image, puis cliquez sur licne droite pour changer le type de la proprit de la
seconde, comme sur la figure 16.9.

Figure 16.9 Modification du type de la proprit


Et choisissez le type Image (voir la figure 16.10).

Figure 16.10 Le type de la donnes est dsormais Image


Nous avons cr ici un jeu de donnes, stockes sous la forme dun fichier XAML. Il
est possible de modifier les donnes en cliquant sur le bouton modifier les exemples
de valeurs (voir la figure 16.11).
Nous obtenons une fentre de ce style (voir la figure 16.12).
On peut y mettre nos propres valeurs. Ici, jai chang le premier lment pour lui
indiquer la valeur que je voulais et limage que je souhaitais, mais il est galement
possible dindiquer un rpertoire pour slectionner les images. Maintenant, il est temps
dutiliser nos donnes. Slectionnez la collection et faites-la glisser dans la fentre de
design, comme indiqu sur la figure 16.13.
Il vous cre automatiquement une ListBox avec les nouvelles donnes de la source de
donnes (voir la figure 16.14).
198

BINDING ET MODE DESIGN

Figure 16.11 Modification des exemples de valeurs

Figure 16.12 Fentre de modification des exemples de valeurs

199

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)

Figure 16.13 La source de donnes est glisse dans le designer

Figure 16.14 Une ListBox est automatiquement cre partir de la source de donnes

200

UTILISER LOBSERVABLECOLLECTION

Utiliser lObservableCollection
Avant de terminer sur la liaison de donnes, reprenons un exemple simplifi de notre
liste de taches. Avec le XAML suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " 100 " / >
</ Grid . RowDefinitions >
< ListBox ItemsSource = " { Binding ListeDesTaches } " >
< ListBox . ItemTemplate >
< DataTemplate >
< StackPanel Orientation = " Horizontal " >
< TextBlock Text = " { Binding Priorite } " Margin
= " 20 0 0 0 " / >
< TextBlock Text = " { Binding Description } "
Margin = " 20 0 0 0 " / >
</ StackPanel >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
< Button Content = " Ajouter un l ment " Tap = " Button_Tap " Grid .
Row = " 1 " / >
</ Grid >

O nous affichons notre liste des tches avec la valeur de la priorit et la description
dans des TextBlock. Nous disposons galement dun bouton en bas pour rajouter un
nouvel lment. Le code behind sera :
1
2
3
4
5
6
7
8

public partial class MainPage : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
private List < ElementAFaire > listeDesTaches ;
public List < ElementAFaire > ListeDesTaches
{
get { return listeDesTaches ; }
set { Not ifyPro perty Change d ( ref listeDesTaches , value ) ;
}
}

9
10

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

11
12
13
14
15
16

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

17

201

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
private bool NotifyPropertyChanged <T >( ref T variable , T
valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

18
19
20
21
22
23
24

25

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

26

public MainPage ()
{
I nitializeComponent () ;

27
28
29
30

List < ElementAFaire > chosesAFaire


ElementAFaire >
{
new ElementAFaire { Priorite
Arroser les plantes " } ,
new ElementAFaire { Priorite
Tondre le gazon " } ,
new ElementAFaire { Priorite
Planter les tomates " } ,
new ElementAFaire { Priorite
Laver la voiture " } ,
};

31
32
33
34
35
36
37

= new List <


= 1 , Description = "
= 2 , Description = "
= 1 , Description = "
= 3 , Description = "

38

ListeDesTaches = chosesAFaire . OrderBy ( e = > e . Priorite ) .


ToList () ;

39
40
41

42

DataContext = this ;

43
44
45
46
47
48

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
ListeDesTaches . Add ( new ElementAFaire { Priorite = 1 ,
Description = " Faire marcher ce binding ! " }) ;
}

49
50
51
52
53
54

public class ElementAFaire


{
public int Priorite { get ; set ; }
public string Description { get ; set ; }
}

La diffrence avec la version prcdente est que nous utilisons une


List<ElementAFaire> comme type dobjet li la source de donnes de la
ListBox. Nous pouvons galement voir que dans lvnement de clic sur le bouton,
202

UTILISER LOBSERVABLECOLLECTION
nous ajoutons un nouvel lment la liste des taches, en utilisant la mthode Add()
de la classe List<>. Si nous excutons notre application et que nous cliquons sur
le bouton, un lment est rajout la liste, sauf que rien nest visible dans notre
ListBox. Problme !
Ah oui, cest vrai, nous navons pas inform la page que la ListBox devait se mettre
jour. Pour ce faire, il faudrait modifier lvnement de clic sur le bouton de cette faon :
1
2
3

List < ElementAFaire > nouvelleListe = new List < ElementAFaire >(
ListeDesTaches ) ;
nouvelleListe . Add ( new ElementAFaire { Priorite = 1 , Description
= " Faire marcher ce binding ! " }) ;
ListeDesTaches = nouvelleListe ;

Cest--dire crer une copie de la liste, ajouter un nouvel lment et affecter cette
nouvelle liste la proprit ListDesTaches. Ce qui devient peu naturel . . .
Cest parce que la liste nimplmente pas INotifyCollectionChanged qui permet denvoyer des vnements sur lajout ou la suppression dun lment dans une liste. Heureusement il existe une autre classe dans le framework .NET qui implmente dj ce
comportement, il sagit de la classe ObservableCollection - http://msdn.microsoft.
com/fr-fr/library/vstudio/ms668604(v=vs.95).aspx. Il sagit dune liste volue
prenant en charge les mcanismes de notification automatiquement lorsque nous faisons
un ajout la collection, lorsque nous supprimons un lment, etc. Changeons donc le
type de notre proprit de liaison :
1
2
3
4
5
6

private ObservableCollection < ElementAFaire > listeDesTaches ;


public ObservableCollection < ElementAFaire > ListeDesTaches
{
get { return listeDesTaches ; }
set { N o t ifyPro perty Change d ( ref listeDesTaches , value ) ; }
}

Remarque
:
vous
devez
importer
System.Collections.ObjectModel.

lespace

de

nom

Dans le constructeur, il faudra changer linitialisation de la liste :


1

ListeDesTaches = new ObservableCollection < ElementAFaire >(


chosesAFaire . OrderBy ( e = > e . Priorite ) ) ;

Et dsormais, lors du clic, il suffira de faire :


1

ListeDesTaches . Add ( new ElementAFaire { Priorite = 1 ,


Description = " Faire marcher ce binding ! " }) ;

Ce qui est quand mme beaucoup plus simple. Plutt pratique cette
ObservableCollection. Elle nous simplifie normment la tche lorsquil sagit
de faire des oprations sur une collection et quun contrle doit tre notifi de ce
changement. Cest le complment idal pour toute ListBox qui se respecte. De plus,
203

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
avec lObservableCollection, notre ListBox ne sest pas compltement rafrachie,
elle a simplement ajout un lment. Avec la mthode prcdente, cest toute la liste
qui se met jour dun coup, ce qui pnalise un peu les performances.
Alors pourquoi je ne lai pas utilis avant ? Parce que je considre quil est important de
comprendre ce que lon a fait. Le binding fonctionne avec tout ce qui est numrable,
comme la List<> ou nimporte quoi implmentant IEnumerable<>. Cest ce que jai
illustr au dbut du chapitre. Lorsquon a besoin uniquement de remplir un contrle et
quil ne va pas se mettre jour, ou pas directement, utiliser une liste ou un IEnumerable
est le plus simple et le plus performant. Cela permet galement de ne pas avoir besoin
dinstancier une ObservableCollection. Si bien sr, il y a beaucoup dopration sur
la liste, suppression, mise jour, ajout, . . . il sera beaucoup plus pertinent dutiliser
une ObservableCollection. Mais il faut faire attention lutiliser correctement. . .
Imaginons par exemple que je veuille mettre jour toutes mes priorits. . . Comme je
suis en avance, je rajoute un bouton me permettant daugmenter la priorit de 1 pour
chaque lment :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " 150 " / >
</ Grid . RowDefinitions >
< ListBox ItemsSource = " { Binding ListeDesTaches } " >
< ListBox . ItemTemplate >
< DataTemplate >
< StackPanel Orientation = " Horizontal " >
< TextBlock Text = " { Binding Priorite } " Margin
= " 20 0 0 0 " / >
< TextBlock Text = " { Binding Description } "
Margin = " 20 0 0 0 " / >
</ StackPanel >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
< StackPanel Grid . Row = " 1 " >
< Button Content = " Ajouter un l ment " Tap = " Button_Tap "
/>
< Button Content = " Augmenter les priorit s " Tap = "
Button_Tap_1 " / >
</ StackPanel >
</ Grid >

Et dans la mthode du clic, je peux faire :


1
2
3
4
5
6
7

private void Button_Tap_1 ( object sender , RoutedEventArgs e )


{
foreach ( ElementAFaire element in ListeDesTaches )
{
element . Priorite ++;
}
}

204

UTILISER LOBSERVABLECOLLECTION
Sauf quaprs un clic sur notre bouton, on se rend compte que lObservableCollection
est mise jour mais pas la ListBox. . . Aarrrgghhhh ! Alors que notre
ObservableCollection tait cense rsoudre tous nos problmes de notification . . .
Cest l o il est important davoir compris ce quon faisait rellement . . . Ici, ce nest
pas la collection que lon a modifie (pas dajout, pas de suppression, . . .), mais bien
lobjet contenu dans la collection. Il doit donc implmenter INotifyPropertyChanged,
ce qui donne :

1
2
3

public class ElementAFaire : INo ti fyP ro per tyC ha nge d


{
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

5
6
7
8
9
10

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

11
12
13
14
15
16
17

18

variable = valeur ;
N o t i f yPro pertyC hange d ( nomPropriete ) ;
return true ;

19
20
21
22
23
24
25
26
27

private int priorite ;


public int Priorite
{
get { return priorite ; }
set { Not ifyPro perty Change d ( ref priorite , value ) ; }
}
public string Description { get ; set ; }

Il faut galement notifier du changement lors de laccs la proprit Priorite. En


toute logique, il faudrait galement le faire sur la proprit Description, mais vu que
nous ne nous en servons pas ici, je vous fais grce de ce changement (voir la figure
16.15).
LObservableCollection est donc une classe puissante mais qui peut nous jouer
quelques tours si son fonctionnement nest pas bien matris.
205

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)

Figure 16.15 La collection est mise jour grce limplmentation de linterface


INotifyPropertyChanged

Les converters
Parfois, lorsque nous faisons des liaisons de donnes, la source de donnes ne correspond
pas exactement ce que nous souhaitons afficher ; la preuve juste au-dessus. Nous
voulions afficher une image dans une ListBox mais nous navions notre disposition
quun chiffre reprsentant une priorit. Pour y remdier, nous avions construit un objet
spcial avec directement les bonnes valeurs via la classe ElementAFaireBinding :
1
2
3
4
5
6
7

List < ElementAFaire > chosesAFaire


{
new ElementAFaire { Priorite
les plantes " } ,
new ElementAFaire { Priorite
gazon " } ,
new ElementAFaire { Priorite
les tomates " } ,
new ElementAFaire { Priorite
voiture " } ,
};

= new List < ElementAFaire >


= 1 , Description = " Arroser
= 2 , Description = " Tondre le
= 1 , Description = " Planter
= 3 , Description = " Laver la

8
9

206

listeDesTaches . ItemsSource = chosesAFaire . OrderBy ( e = > e .


Priorite ) . Select ( e = > new ElementAFaireBinding { Description
= e . Description , Image = ObtientImage ( e . Priorite ) }) ;

LES CONVERTERS
Cest une bonne faon de faire mais il existe une autre solution qui consiste appliquer
un convertisseur lors de la liaison de donnes. Appels converters en anglais, ils font
en sorte de transformer une donne en une autre, adapte ce que lon souhaite lier.
Un exemple sera plus clair quun long discours. Prenons par exemple le cas o lon
souhaite masquer une zone de lcran en fonction dune valeur. Laffichage / masquage
dun contrle, cest le rle de la proprit Visibility qui a la valeur Visible par
dfaut ; pour tre invisible, il faut que la valeur soit Collapsed :
1
2
3
4
5
6

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< StackPanel >
< TextBlock Text = " Je suis visible " Visibility = " Collapsed
" />
< Button Content = " Masquer / Afficher " Tap = " Button_Tap " / >
</ StackPanel >
</ Grid >

Ces deux valeurs font partie de lnumration Visibility - http://msdn.microsoft.


com/fr-fr/library/system.windows.uielement.visibility(v=vs.95).aspx. Sauf
que pour nous, il est beaucoup plus logique de travailler avec un boolen : sil est vrai,
le contrle est visible, sinon il est invisible.
Cest l que va servir le converter, il va permettre de transformer true en Visible
et false en Collapsed. Pour crer un tel converter, nous allons ajouter une nouvelle classe : VisibilityConverter. Cette classe doit implmenter linterface IValueConverter - http://msdn.microsoft.com/fr-fr/library/system.windows.data.
ivalueconverter(v=vs.95).aspx qui force implmenter une mthode de conversion de bool vers Visibility et inversement une mthode qui transforme Visibility
en bool. Voici donc une telle classe :
1
2
3
4
5
6

public class VisibilityConverter : IValueConverter


{
public object Convert ( object value , Type targetType , object
parameter , CultureInfo culture )
{
return ( bool ) value ? Visibility . Visible : Visibility .
Collapsed ;
}

7
8
9
10
11
12
13

public object ConvertBack ( object value , Type targetType ,


object parameter , CultureInfo culture )
{
Visibility visibility = ( Visibility ) value ;
return ( visibility == Visibility . Visible ) ;
}

IValueConverter fait partie de lespace de nom System.Windows.Data,


CultureInfo fait partie de lespace de nom System.Globalization et
Visibility de System.Windows.
207

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
Le code nest pas trs compliqu. La seule chose peut-tre nouvelle pour certains est
lutilisation de loprateur ternaire ? . Lcriture suivante :
Visibility v = visibility ? Visibility . Visible : Visibility .
Collapsed ;

permet de remplacer :
1
2
3
4
5

Visibility v ;
if ( visibility )
v = Visibility . Visible ;
else
v = Visibility . Collapsed ;

Il value le boolen (ou la condition) gauche du point dinterrogation. Si le rsultat


est vrai, alors il prend le premier oprande, sinon il prend le second, situ aprs les
deux points.
Bref, une fois ceci fait, il va falloir dclarer le converter dans notre page XAML. Cela
se fait dans les ressources et ici, je le mets dans les ressources de la page :
1
2
3

< phone : P h oneApplicationPage . Resources >


< converter : VisibilityConverter x : Key = " VisibilityConverter "
/>
</ phone : P honeApplicationPage . Resources >

Ce code doit tre prsent au mme niveau que le conteneur de base de la page, cest-dire lintrieur de la balise <phone:PhoneApplicationPage>.
Attention, la classe converter nest pas connue de la page, il faut ajouter un espace de
nom dans les proprits de la page : xmlns:converter="clr-namespace:DemoPartie2"
Il ne reste plus qu crer une proprit pour le binding dans notre classe :
1
2
3
4
5
6

private bool textBlockVisible ;


public bool TextBlockVisible
{
get { return textBlockVisible ; }
set { Noti fyPro pertyC hange d ( ref textBlockVisible , value ) ; }
}

Et modifier sa valeur dans lvnement de clic sur le bouton :


1
2
3
4

private void Button_Tap ( object sender , RoutedEventArgs e )


{
TextBlockVisible = ! TextBlockVisible ;
}

Maintenant, nous devons indiquer le binding dans le XAML et que nous souhaitons
utiliser un converter, cela se fait avec :
1

< TextBlock Text = " Je suis visible " Visibility = " { Binding
TextBlockVisible , Converter ={ StaticResource
V is i b ilityConverter }} " / >

208

LES CONVERTERS
Nous lui indiquons ici que le converter est accessible en ressources par son nom :
VisibilityConverter. Noublions pas de donner la valeur initiale du boolen, par
exemple dans le constructeur, ainsi que dalimenter le DataContext :
1
2
3
4
5
6

public MainPage ()
{
I ni t i al i zeComponent () ;
TextBlockVisible = false ;
DataContext = this ;
}

Et voil, le fait de changer la valeur du boolen influe bien sur la visibilit du contrle,
comme vous pouvez le constater la figure 16.16.

Figure 16.16 Le converter permet de modifier la visibilit du contrle grce une


liaison de donnes
Il est assez courant dcrire des converters pour se simplifier la tche, par contre cela
rajoute un temps de traitement qui en fonction des cas peut tre important. Si cest
possible, prfrez les cas o la donne est correctement prpare ds le dbut, pensez que
les smartphones nont pas autant de puissance que votre machine de dveloppement. . .

En rsum
La liaison de donnes, binding en anglais, est un lment fondamental des applications XAML et permet dassocier une source de donnes un contrle.
209

CHAPITRE 16. LA MANIPULATION DES DONNES (DATABINDING &


CONVERTERS)
On utilise lextention de balisage {Binding} pour dclarer une liaison de donnes
depuis une proprit dun contrle dans le XAML.
Il est possible damliorer le mode design de ses pages grce au binding.
LObservableCollection est une liste volue qui gre automatiquement les notifications en cas de changement dans la liste.
Les converters sont un mcanisme qui permet de transformer une donne en une
autre au moment du binding.

210

Chapitre

17

MVVM
Difficult :
Passons maintenant MVVM. . . Si vous tes dbutants en XAML, je ne vous cache pas
que ce chapitre risque dtre difficile apprhender. Il sagit de concepts avancs quil nest
pas ncessaire de maitriser immdiatement. Au contraire, dune manire gnrale, il faut
dj pas mal de pratique avant de pouvoir utiliser les concepts prsents dans ce chapitre.
Mais nhsitez pas le lire quand mme et y revenir plus tard, cela vous sera toujours
utile.
Trs la mode, MVVM est un patron de conception (design pattern en anglais) qui sest
construit au fur et mesure que les dveloppeurs craient des applications utilisant le
XAML. MVVM signifie Model-View-ViewModel, nous allons dtailler son principe et son
fonctionnement dans ce chapitre.

211

CHAPITRE 17. MVVM

Principe du patron de conception


La premire chose savoir est quest-ce quun patron de conception ? Trs connu sous
son appellation anglaise, design pattern , un patron de conception constitue une
solution prouve et reconnue comme une bonne pratique un problme rcurrent
dans la conception dapplications informatiques. En gnral, il dcrit une modlisation
de classes utilises pour rsoudre un problme prcis. Il existe beaucoup de patrons de
conceptions, comme le populaire MVC (Modle-Vue-Contrleur), trs utilis dans la
ralisation de site webs.
Ici, le patron de conception MVVM est particulirement adapt la ralisation dapplications utilisant le XAML, comme les applications pour Windows Phone, mais galement les applications Silverlight, Windows 8 ou WPF. Il permet darchitecturer efficacement une application afin den faciliter la maintenabilit et la testabilit. Certains
auront tendance considrer MVVM comme un ensemble de bonnes pratiques plutt quun vrai patron de conception. Ce qui est important, cest quen le comprenant
vous allez amliorer votre productivit dans la ralisation dapplications consquentes.
Voyons prsent de quoi il sagit.
MVVM signifie Model-View-ViewModel.
Model, en franais le modle , correspond aux donnes. Il sagit en gnral
de plusieurs classes qui permettent daccder aux donnes, comme une classe
Client, une classe Commande, etc. Peu importe la faon dont on remplit ces
donnes (base de donnes, service web,. . .), cest ce modle qui est manipul
pour accder aux donnes.
View, en franais la vue , correspond tout ce qui sera affich, comme la
page, les boutons, etc. En pratique, il sagit du fichier .xaml.
View Model, que lon peut traduire en modle de vue , cest la colle entre le
modle et la vue. Il sagit dune classe qui fournit une abstraction de la vue. Ce
modle de vue, que jappellerai dsormais view-model, sappuie sur la puissance
du binding pour mettre disposition de la vue les donnes du modle. Il soccupe
galement de grer les commandes que nous verrons un peu plus loin.
Voici la figure 17.1 un schma reprsentant ce patron de conception.
Le but de MVVM est de faire en sorte que la vue neffectue aucun traitement, elle ne
doit faire quafficher les donnes prsentes par le view-model. Cest le view-model qui
a en charge de faire les traitements et daccder au modle.
Et si on se tentait une petite mtaphore pour essayer de comprendre un peu mieux ?
Essayons de reprsenter MVVM travers un jeu, disons une machine sous dun
casino.
Mon modle correspondra aux diffrentes valeurs internes des images de la machine sous, dont le fameux 7 qui fait gagner le gros lot.
Ma vue correspondra la carcasse de la boite sous et surtout aux images qui
saffichent. Il sagira de tout ce que lon voit.
Mon view-model correspondra aux engrenages qui relient les images la machine
sous et qui transforment une valeur interne en image affichable sur la machine
212

PRINCIPE DU PATRON DE CONCEPTION

Figure 17.1 Le patron de conception MVVM

213

CHAPITRE 17. MVVM


sous.
Sans ces engrenages, mes images ne peuvent pas saccrocher au cadre de la machine
sous et tout se casse la figure, on ne voit rien sur la vue. Lorsque ces engrenages
sont prsents, on peut voir les donnes lies la vue (grce au binding). Et je peux
agir sur mon modle par lintermdiaire de commandes, en loccurrence le levier de
la machine sous. Je tire sur le levier, une commande du view-model est active, les
images tournent, le modle se met jour (les valeurs internes ont changes) et la vue est
mise jour automatiquement. Je peux voir que les trois sept sont aligns. JACKPOT.
Plus comprhensible ? Nhsitez pas me proposer dautres mtaphores en commentaires pour expliquer MVVM.

Premire mise en place de MVVM


Nous avons commenc pratiquer un peu MVVM au chapitre prcdent, en fournissant
un contexte dans une classe spare. Ce contexte est le view-model, il prpare les
donnes afin quelles soient affichables par la vue. Si on veut tre un peu plus prcis
et utiliser un langage plus proche des patrons de conception, on pourrait dire que le
view-model adapte le modle pour la vue. Commenons par crer un nouveau projet
pour mettre en place une version simplifie de MVVM. Comme lide est de sparer les
responsabilits, nous allons en profiter pour crer des rpertoires pour notre modle,
nos vues et nos view-models. Crons donc les rpertoires et les fichiers suivants :
Model
Client.cs
ServiceClient.cs
View
VoirClientView.xaml
ViewModel
VoirClientViewModel.cs
Et profitons-en pour supprimer le fichier MainPage.xaml, de manire avoir la mme
architecture que sur la figure 17.2.
Par convention, vous aurez compris que le modle se place dans le rpertoire Model,
que les vues se placent dans le rpertoire View et que les view-models se placent dans
le rpertoire ViewModel. De mme, on suffixera les vues par View et les view-models
par ViewModel . En ltat, notre application ne pourra pas dmarrer ainsi, car notre
application va essayer de dmarrer en naviguant sur le fichier MainPage.xaml, que nous
avons supprim. Nous devons donc lui indiquer un nouveau point dentre. Cela se fait
depuis le fichier WMAppManifest.xml qui est sous le rpertoire Properties. Ouvrezle et modifiez la Page de navigation, comme nous lavons dj fait, pour y mettre :
View/VoirClientView.xaml.
Cela nous permet de dire que lapplication doit dmarrer en affichant la page
VoirClientView.xaml. Commenons par crer un modle ultra simple, qui consiste
en une classe client qui contient un prnom, un ge et un boolen indiquant sil est un
bon client :
214

PREMIRE MISE EN PLACE DE MVVM

Figure 17.2 Architecture de la solution MVVM


1
2
3
4
5
6

public class Client


{
public string Prenom { get ; set ; }
public int Age { get ; set ; }
public bool EstBonClient { get ; set ; }
}

Ainsi quun service qui va nous simuler le chargement dun client :


1
2
3
4
5
6
7

public class ServiceClient


{
public Client Charger ()
{
return new Client { Prenom = " Nico " , Age = 30 ,
EstBonClient = true };
}
}

Maintenant, ralisons la vue. Nous allons simplement afficher le prnom et lge du


client. Ceux-ci seront sur un fond vert si le client est un bon client et en rouge si cest
un mauvais client. Quelque chose comme a :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >

6
7
8

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " PageTitle " Text = " Fiche client "
Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
P honeTextTitle1Style } " / >
</ StackPanel >

10

215

CHAPITRE 17. MVVM


< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
Background = " { Binding BonClient } " >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< TextBlock Text = " Pr nom : " / >
< TextBlock Grid . Column = " 1 " Text = " { Binding Prenom } " / >
< TextBlock Grid . Row = " 1 " Text = " Age : " / >
< TextBlock Grid . Column = " 1 " Grid . Row = " 1 " Text = " { Binding
Age } " / >
</ Grid >
</ Grid >

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

On utilise les expressions de balisage pour indiquer les valeurs grce au binding.
Sauf quil est difficile de se rendre compte ainsi si la vue est bien construite, car
il nous manque les valeurs de design. Qu cela ne tienne, nous savons dsormais comment crer un contexte de design. Crons un nouveau rpertoire sous le
rpertoire ViewModel que nous appelons Design et une nouvelle classe sappelant
DesignVoirClientViewModel.cs qui contiendra les valeurs de design suivantes :
1
2
3
4
5
6

public class D e s i g n Vo i r C l i e n t V i ew M o d e l
{
public string Prenom
{
get { return " Nico " ; }
}

public int Age


{
get { return 30 ; }
}

8
9
10
11
12
13
14
15
16
17

public SolidColorBrush BonClient


{
get { return new SolidColorBrush ( Color . FromArgb ( 100 , 0 ,
255 , 0 ) ) ; }
}

Pour rappel, SolidColorBrush se trouve dans lespace de nom System.Windows.Media.


Il faut ensuite lier le contexte de design au view-model de design :
1
2
3

<d : DesignProperties . DataContext >


< design : D es i g n V o i r C l ie n t V i e w M o d e l / >
</ d : DesignProperties . DataContext >

216

PREMIRE MISE EN PLACE DE MVVM


sans oublier dimporter lespace de nom correspondant :
1

xmlns : design = " clr - namespace : DemoMvvm . ViewModel . Design "

Ainsi, nous pourrons avoir en mode design le rsultat affich la figure 17.3.

Figure 17.3 Affichage des donnes en mode design grce MVVM


Ce qui est le rsultat attendu. Chouette ! Passons enfin au view-model. Nous avons vu
quil devait implmenter linterface INotifyPropertyChanged :
1
2
3
4
5
6
7
8

public class VoirClientViewModel : I No tif yPr op ert yC han ged


{
private string prenom ;
public string Prenom
{
get { return prenom ; }
set { Not ifyPro perty Change d ( ref prenom , value ) ; }
}

9
10
11
12
13
14
15

private int age ;


public int Age
{
get { return age ; }
set { Not ifyPro perty Change d ( ref age , value ) ; }
}

16
17
18

private SolidColorBrush bonClient ;


public SolidColorBrush BonClient

217

CHAPITRE 17. MVVM


{

19
20
21

22

get { return bonClient ; }


set { Noti fyPro pertyC hange d ( ref bonClient , value ) ; }

23

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

24
25

public void N otifyP roper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

26
27
28
29
30
31

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

32
33
34
35
36
37
38
39
40

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

Rien de sorcier, nous dfinissons galement les proprits Prenom, Age et BonClient.
Reste charger notre modle depuis notre view-model et affecter les proprits du
view-model partir des valeurs du modle :
1
2
3
4

public void ChargeClient ()


{
ServiceClient service = new ServiceClient () ;
Client client = service . Charger () ;

5
6
7
8
9
10
11
12

Prenom = client . Prenom ;


Age = client . Age ;
if ( client . EstBonClient )
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 , 0 ,
255 , 0 ) ) ;
else
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 , 255
, 0, 0));

Il nous manque une dernire chose, hautement indispensable, qui est de lier la vue au
view-model. Pour linstant, nous avons vu que nous pouvions le faire depuis le codebehind de la vue, avec :
1
2
3

public partial class VoirClientView : PhoneApplicationPage


{
public VoirClientView ()

218

PREMIRE MISE EN PLACE DE MVVM


{

4
5
6
7
8

I ni t i alizeComponent () ;
DataContext = new VoirClientViewModel () ;

Il y a une autre solution qui vite de passer par le code, en utilisant le XAML :
1
2
3

< phone : P h on e A pplicationPage . DataContext >


< viewmodel : VoirClientViewModel / >
</ phone : P ho n e ApplicationPage . DataContext >

Ce qui revient au mme, vu quon positionne la proprit DataContext de la page


une instance du view-model. Cest cette solution que nous allons privilgier ici. Vous
naurez bien sr pas oubli dinclure lespace de nom qui va bien :
1

xmlns : viewmodel = " clr - namespace : DemoMvvm . ViewModel "

Revenons prsent un peu sur ce que nous avons fait. Nous avons cr une vue, la
page XAML, lie lexcution au view-model VoirClientViewModel et lie en design
au pseudo view-model DesignVoirClientViewModel. Le pseudo view-model de design
expose des donnes en dur, pour nous permettre davoir des donnes dans le designer
alors que le view-model utilise et transforme le model pour exposer les mmes donnes.
Une bonne pratique ici serait de dfinir une interface avec les donnes exposer et
que nos deux view-models limplmentent. Crons donc un rpertoire Interface dans le
rpertoire ViewModel et crons linterface IVoirClientViewModel :
1
2
3
4
5
6

public interface IVoirClientViewModel


{
string Prenom { get ; set ; }
int Age { get ; set ; }
SolidColorBrush BonClient { get ; set ; }
}

Nos deux view-models doivent implmenter cette interface :


1
2
3
4

public class VoirClientViewModel : INotifyPropertyChanged ,


I V o i r C l i e ntViewModel
{
...
}

Et :
1
2
3
4
5
6
7

public class D e s i g n V oi r C l i e n t V i ew M o d e l : IVoirClientViewModel


{
public string Prenom
{
get { return " Nico " ; }
set { }
}

219

CHAPITRE 17. MVVM


public int Age
{
get { return 30 ; }
set { }
}

9
10
11
12
13
14
15
16
17
18
19
20

public SolidColorBrush BonClient


{
get { return new SolidColorBrush ( Color . FromArgb ( 100 , 0 ,
255 , 0 ) ) ; }
set { }
}

Vous aurez remarqu que nous avons rajout le mutateur set dans le view-model de
design, et quelle na besoin de rien faire.
Nous pouvons encore faire une petite amlioration. Ici, elle est mineure car nous navons
quun seul view-model, mais elle sera intressante ds que nous en aurons plusieurs.
En effet, chaque view-model doit implmenter linterface INotifyPropertyChanged et
avoir le code suivant :
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

1
2
3
4
5
6
7

public void N otifyP roper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

8
9
10
11

private bool NotifyPropertyChanged <T >( ref T variable , T valeur ,


[ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

12
13
14
15
16

variable = valeur ;
N o t i f yPro perty Change d ( nomPropriete ) ;
return true ;

Nous pouvons factoriser ce code dans une classe de base dont vont driver tous les
view-models. Crons pour cela un rpertoire FrameworkMvvm et dedans, mettons-y la
classe ViewModelBase :
1
2
3

public class ViewModelBase : INo ti fyP rop er tyC ha nge d


{
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

4
5
6

220

public void N otifyP roper tyChan ged ( string nomPropriete )


{

LES COMMANDES
7
8

if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;

10

public bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

11
12
13
14
15
16
17
18
19

variable = valeur ;
N o t i f yPro pertyC hange d ( nomPropriete ) ;
return true ;

Noubliez pas de passer la mthode NotifyPropertyChanged en public. Il ne reste plus


qu supprimer ce code du view-model et de le faire hriter de cette classe de base :
1
2
3
4

public class VoirClientViewModel : ViewModelBase ,


I V o i r C l i e ntViewModel
{
...
}

Les commandes
Bon, cest trs bien tout a, mais nous navons fait quune partie du chemin. . . MVVM
ce nest pas que du binding avec une sparation entre la vue et les donnes. Il faut tre
capable de faire des actions. Imaginons que nous souhaitions charger les donnes du
client suite un appui sur un bouton. Avant MVVM, nous aurions utilis un vnement
sur le clic du bouton, puis nous aurions charg les donnes dans le code-behind et nous
les aurions affiches sur notre page. Avec notre dcoupage, le view-model nest pas
au courant dune action sur linterface, car cest un fichier part. Il nest donc pas
directement possible de raliser une action dans le view-model lors dun clic sur le
bouton. On peut rsoudre ce problme dune premire faon trs simple mais pas tout
fait parfaite. Crons tout dabord un bouton dans notre XAML :
1
2
3
4
5
6
7
8
9

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
Background = " { Binding BonClient } " >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >

221

CHAPITRE 17. MVVM


</ Grid . ColumnDefinitions >
< TextBlock Text = " Pr nom : " / >
< TextBlock Grid . Column = " 1 " Text = " { Binding Prenom } " / >
< TextBlock Grid . Row = " 1 " Text = " Age : " / >
< TextBlock Grid . Column = " 1 " Grid . Row = " 1 " Text = " { Binding Age }
" />
< Button Grid . Row = " 2 " Grid . ColumnSpan = " 2 " Content = " Charger
client " Tap = " Button_Tap " / >
</ Grid >

10
11
12
13
14
15
16

Voyons prsent comment rsoudre simplement ce problme. Il suffit dutiliser lvnement Tap et de faire quelque chose comme ceci dans le code-behind de la page, dans
la mthode associe lvnement de clic :
1
2
3
4
5
6

public partial class VoirClientView : PhoneApplicationPage


{
public VoirClientView ()
{
I nitializeComponent () ;
}

7
8
9
10
11
12

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
(( IVoirClientViewModel ) DataContext ) . ChargeClient () ;
}

On utilise la proprit DataContext pour rcuprer linstance du view-model. En effet,


noubliez pas que nous avons li la vue au view-model via la proprit DataContext.
Cela implique de rajouter une mthode ChargeClient() dans linterface et de la dfinir
dans notre view-model ainsi que dans celui de design :
1
2
3
4
5
6
7

public interface IVoirClientViewModel


{
string Prenom { get ; set ; }
int Age { get ; set ; }
SolidColorBrush BonClient { get ; set ; }
void ChargeClient () ;
}

8
9
10
11
12
13
14
15

public class D e s i g n Vo i r C l i e n t V i ew M o d e l : IVoirClientViewModel


{
public string Prenom
{
get { return " Nico " ; }
set { }
}

16
17
18

222

public int Age


{

LES COMMANDES
19
20

21

get { return 30 ; }
set { }

22

public SolidColorBrush BonClient


{
get { return new SolidColorBrush ( Color . FromArgb ( 100 , 0 ,
255 , 0 ) ) ; }
set { }
}

23
24
25
26
27
28
29
30
31
32

public void ChargeClient ()


{
}

33
34
35
36

public class VoirClientViewModel : ViewModelBase ,


I V o i r C l i e ntViewModel
{
[... code supprim pour plus de clart ...]

37

public V o irClientViewModel ()
{
}

38
39
40
41

public void ChargeClient ()


{
ServiceClient service = new ServiceClient () ;
Client client = service . Charger () ;

42
43
44
45
46
47
48
49
50
51
52
53
54

Prenom = client . Prenom ;


Age = client . Age ;
if ( client . EstBonClient )
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 ,
0 , 255 , 0 ) ) ;
else
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 ,
255 , 0 , 0 ) ) ;

Vous pouvez tester cette solution, cela fonctionne. Cependant, elle est imparfaite car
cela cre un couplage entre la vue et le view-model, cest--dire que la vue connait
linstance du view-model car cest effectivement elle qui linstancie avec la dclaration
que nous avons vue dans le XAML. Mais bien quimparfaite, ncartez pas non plus
compltement cette solution de votre esprit, elle reste bien pratique et ne rajoute pas
tant de code que a dans la vue (de plus, ce code ne fait pas de traitement, il fonctionne
juste comme un relai du traitement).
223

CHAPITRE 17. MVVM


Pour rsoudre plus proprement ce problme, il y a une autre solution : les
commandes. Les commandes correspondent des actions faites sur la vue.
Le XAML dispose dun mcanisme lger de gestion de commandes via linterface
ICommand
http://msdn.microsoft.com/fr-fr/library/system.
windows.input.icommand(v=vs.95).aspx. Par exemple, le contrle bouton possde (par hritage) une proprit Command du type ICommand http://msdn.microsoft.com/fr-fr/library/system.windows.controls.
primitives.buttonbase.command(v=vs.95).aspx permettant dinvoquer une
commande lorsque le bouton est appuy.
Ainsi, il sera possible de remplacer :
1

< Button Grid . Row = " 2 " Grid . ColumnSpan = " 2 " Content = " Charger
client " Tap = " Button_Tap " / >

par :
1

< Button Grid . Row = " 2 " Grid . ColumnSpan = " 2 " Content = " Charger
client " Command = " { Binding ChargerClientCommand } " / >

Ici, nous avons enlev lvnement Tap et la mthode associe (vous pouvez donc supprimer cette mthode dans le code behind) pour la remplacer par un binding dune
commande. Cette commande devra tre dfinie dans le view-model :
public ICommand ChargerClientCommand { get ; private set ; }

Noubliez pas dinclure :


using System . Windows . Input ;

Elle sera du type ICommand et videmment en lecture seule afin que seul le viewmodel puisse instancier la commande. Cette commande doit ensuite tre relie une
mthode et pour faire cela, nous allons utiliser un dlgu et plus particulirement
un dlgu de type Action. Crons donc une classe qui implmente ICommand et qui
associe la commande une action. Nous pouvons placer cette classe dans le rpertoire FrameworkMvvm et la nommer RelayCommand (ce nom nest pas choisi au hasard,
vous verrez plus loin pourquoi). Linterface ICommand impose de dfinir la mthode
CanExecute qui permet dindiquer si la commande peut tre excute ou non. Nous
allons renvoyer vrai dans tous les cas pour cet exemple. Elle oblige galement dfinir un vnement CanExecuteChanged que nous nallons pas utiliser et une mthode
Execute qui appellera la mthode associe la commande. La classe RelayCommand
pourra donc tre :
1
2
3

public class RelayCommand : ICommand


{
private readonly Action actionAExecuter ;

4
5
6
7
8

224

public RelayCommand ( Action action )


{
actionAExecuter = action ;
}

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT


9

public bool CanExecute ( object parameter )


{
return true ;
}

10
11
12
13
14

public event EventHandler CanExecuteChanged ;

15
16
17
18
19
20
21

public void Execute ( object parameter )


{
actionAExecuter () ;
}

Ce qui fait que nous allons pouvoir instancier un objet du type RelayCommand que
nous stockerons dans la proprit ChargerClientCommand dans notre view-model, par
exemple depuis le constructeur :
1
2
3
4

public V o ir C l ientViewModel ()
{
C h a r g e r C l ientCommand = new RelayCommand ( ChargeClient ) ;
}

Et voil, la mthode ChargeClilent() reste la mme que prcdemment. Vous pouvez


donc la supprimer de linterface, du view-model de design et mme si vous le souhaitez,
vous pouvez la passer en prive dans le view-model. En ce qui concerne la proprit
ChargerClientCommand, vous pouvez la dclarer dans linterface mais ce nest pas
forcment utile car le view-model de design na pas besoin de connaitre la commande
et ne saura dailleurs pas quoi mettre comme mthode dedans.
Super ces commandes sauf que la proprit Command que nous avons vu est prsente sur
le contrle ButtonBase, dont hrite le contrle Button ainsi que le contrle de case
cocher, CheckBox. Elle nest pas prsente par contre sur tous les autres contrles. Nous
allons tre bloqus pour associer une commande un autre vnement, par exemple la
slection dun lment dune ListBox. Nous allons voir plus loin comment rsoudre ce
problme.

Les frameworks la rescousse : MVVM-Light


Entre la classe ViewModelBase et la classe RelayCommand, nous sommes en train
dcrire un vrai mini framework pour nous aider implmenter MVVM. Alors, je
vous le dis tout de suite, nous allons nous arrter l dans limplmentation de ce
framework. . . car dautres lont dj fait ; et en mieux ! Il existe beaucoup de framework pour utiliser MVVM et certains sont utilisables avec Windows Phone, comme
par exemple le populaire MVVM Light Toolkit, que vous pouvez trouver ici - http:
//mvvmlight.codeplex.com/.
Malgr sa dnomination, il nest en fait pas si light que a car il est utilisable avec
WPF, Silverlight, Windows Phone et Windows 8. Je ne vais pas faire un cours entier
225

CHAPITRE 17. MVVM


sur ce toolkit, car cela demanderait beaucoup trop de pages. Nous allons cependant
regarder quelques points intressants qui pourraient vous servir dans une application
Windows Phone. Aprs, vous de voir si vous avez besoin dembarquer la totalit du
framework ou si vous pouvez simplement vous inspirer de quelques ides. . .
Toujours est-il quil y a beaucoup de bonnes choses dans ce toolkit. Vous vous rappelez
de nos deux classes ViewModelBase et RelayCommand ? Et bien, MVVM Light possde
galement ces deux classes. . . et en plus fournies ! Par exemple la classe RelayCommand.
Nous en avons crit une version ultra simplifie. Celle de ce toolkit est complte et permet mme dy associer un paramtre. Cela peut-tre utile lorsque la mme commande
est associe plusieurs boutons et que chaque bouton possde un paramtre diffrent.
De mme, ce toolkit propose une solution au problme que jvoquais plus haut,
savoir de pouvoir relier nimporte quel vnement une commande.
Il propose galement une solution pour rsoudre le problme de couplage entre la vue et
le view-model que nous avons rencontr prcdemment. Il utilise en effet un patron de
conception prvu pour ce genre de cas, le service locator, qui est un patron de conception
dinversion de dpendance. Je ne parlerai pas en dtail de ce patron de conception ici,
vous pouvez trouver plus dinformations en anglais sur ce site : http://martinfowler.
com/articles/injection.html#UsingAServiceLocator. Sachez simplement que le
principe gnral est davoir un gros objet contenant une liste de services et qui sait
en retrouver un partir dune cl. Cette cl peut tre de plusieurs sortes, comme une
chane de caractre, un type (en gnral une interface), etc. Pour viter le couplage
entre la vue et le view-model, ce patron de conception permettra de faire en sorte
que la vue connaisse simplement une cl afin dobtenir son view-model, sans forcment
connatre linstance et le type du view-model.
Remarquez que pour moi, ce couplage fort entre la vue et le view-model nest pas un
vrai problme, au risque de faire hurler les puristes. Le but premier thorique est de
pouvoir ventuellement changer facilement de view-model pour une mme vue, sans
avoir tout modifier. En pratique, il savre quil existe toujours un et un seul viewmodel associ une vue, et que cest toujours le mme. Cest pourquoi je trouve que
la liaison du view-model depuis le XAML propose dans le chapitre prcdent est bien
souvent suffisante.

Installation du toolkit
Voyons prsent comment crer une application MVVM Light. Nous allons installer la dernire version stable du framework lheure o jcris ces lignes, savoir la
version 4.1.25. Le plus simple pour tlcharger les bibliothques est de passer par NuGet, qui est un gestionnaire de package .NET open source permettant de tlcharger
des bibliothques externes trs facilement. Pour cela, allez dans le menu outils, puis
gestionnaire de package de bibliothques, puis Grer les packages NuGet pour la
solution, comme indiqu la figure 17.4.
Vous arrivez dans NuGet o vous allez pouvoir cliquer sur En ligne gauche et commencer rechercher le MVVM Light Toolkit (voir la figure 17.5).
226

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT

Figure 17.4 Dmarrer NuGet

Figure 17.5 Installation du Mvvm Light Toolkit via NuGet

227

CHAPITRE 17. MVVM

Vous devez bien sr tre connects internet pour utiliser NuGet.

Vous pouvez choisir dinstaller la version complte ou uniquement la bibliothque. La


version complte contient les binaires du toolkit, les templates et les snippets.
Les binaires sont indispensables pour utiliser le toolkit. Il sagit des assemblys contenant le code du toolkit. Les templates sont des modles de projet permettant de crer
facilement une application utilisant MVVM Light. Le projet ainsi cr contient dj les
bonnes rfrences, un premier exemple de vue, de locator, etc. Les snippets sont des
extraits de code qui sont utiliss pour pouvoir crer plus rapidement des applications
MVVM.
Comme je ne souhaite pas vous prsenter tout MVVM Light et que je veux me concentrer sur la comprhension des lments du patron de conception, je vais simplement
installer les binaires en choisissant MVVM Light Libraries only. Cliquez sur Installer.
Aprs tlchargement (rapide) il me propose de linstaller dans ma solution en cours,
comme vous pouvez le voir sur la figure 17.6.

Figure 17.6 Installation de Mvvm Light Toolkit


Faites Ok et acceptez galement la licence. Et voil, cest install !
Vous pouvez aussi tout classiquement tlcharger linstalleur sur le site.

Vous pouvez voir que dans votre projet, les assemblys suivantes ont automatiquement
t rfrences :
228

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT

GalaSoft.MvvmLight.Extras.WP8.dll
GalaSoft.MvvmLight.WP8.dll
Microsoft.Practices.ServiceLocation
System.Windows.Interactivity.dll

sachant que la dernire est une assembly de Blend et que lavant dernire permet
dutiliser le service locator.
Recrez alors un rpertoire View pour y mettre votre page VoirClientView.xaml,
un rpertoire Model avec la classe Client et un rpertoire ViewModel avec une classe
VoirClientViewModel. La classe Client est la mme que prcdemment, le service
aussi. Nous allons simplement lui rajouter une interface en plus :
1
2
3
4
5
6

public class Client


{
public string Prenom { get ; set ; }
public int Age { get ; set ; }
public bool EstBonClient { get ; set ; }
}

7
8
9
10
11

public interface IServiceClient


{
Client Charger () ;
}

12
13
14
15
16
17
18
19

public class ServiceClient : IServiceClient


{
public Client Charger ()
{
return new Client { Prenom = " Nico " , Age = 30 ,
EstBonClient = true };
}
}

Puis nous allons rajouter une classe de design pour notre service. Au contraire de ce
que nous avons fait dans le chapitre prcdent, ce nest pas ici le view-model qui sera
en mode design, mais le model :
1
2
3
4
5
6
7

public class DesignServiceClient : IServiceClient


{
public Client Charger ()
{
return new Client { Prenom = " Nico " , Age = 30 ,
EstBonClient = true };
}
}

Ici, la classe ServiceClient et la classe DesignServiceClient sont identiques, mais dans une vraie application, la classe ServiceClient irait
srement chercher les donnes en base ou autre. . .
229

CHAPITRE 17. MVVM


Puis notre view-model va hriter de ViewModelBase qui se situe dans lespace de nom
GalaSoft.MvvmLight :
using GalaSoft . MvvmLight ;

1
2
3
4
5

public class VoirClientViewModel : ViewModelBase


{
}

Le service locator
Il est temps de crer le service locator afin de bnficier dun couplage faible entre
notre vue et le view-model, mais galement entre le view-model et le service, ce qui
nest pas indispensable mais ne fait pas de mal. Pour cela, crons une nouvelle classe
ViewModelLocator, je la place la racine du projet :
1
2
3
4
5

public class ViewModelLocator


{
static ViewModelLocator ()
{
ServiceLocator . SetLocatorProvider (() = > SimpleIoc .
Default ) ;

if ( ViewModelBase . IsInDesignModeStatic )
SimpleIoc . Default . Register < IServiceClient ,
DesignServiceClient >() ;
else
SimpleIoc . Default . Register < IServiceClient ,
ServiceClient >() ;

7
8
9
10
11
12

13

SimpleIoc . Default . Register < VoirClientViewModel >() ;

14
15
16
17
18
19

public VoirClientViewModel VoirClientVM


{
get { return ServiceLocator . Current . GetInstance <
VoirClientViewModel >() ; }
}

Noubliez
pas
dimporter
les
espaces
de
nom
Microsoft.Practices.ServiceLocation, GalaSoft.MvvmLight.Ioc et
GalaSoft.MvvmLight.
Elle contient un constructeur statique qui soccupe de linversion de dpendance en ellemme, savoir associer le service client de design avec linterface lorsquon est en mode
design, et le vrai service sinon. De mme, il enregistre une instance du view-model. Il
230

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT


possde galement une proprit permettant daccder linstance du ViewModel :
VoirClientVM.
Jai choisi dappeler la proprit VoirClientVM alors que je laurai naturellement appele VoirClientViewModel, afin de bien voir la diffrence entre
la proprit et la classe du view-model.
Ce locator devra tre dclar dans les ressources de lapplication, ainsi il sera accessible
depuis nimporte quelle vue. Modifiez-donc le fichier App.xaml pour avoir :
1
2

< Application . Resources >


<... / >

3
4
5

< vm : ViewModelLocator x : Key = " Locator " d : IsDataSource = " true "
/>
</ Application . Resources >

Sans oublier de dclarer lespace de nom :


1

xmlns : vm = " clr - namespace : DemoMvvmLight "

et dinclure les dclarations suivantes, quil nest pas ncessaire de comprendre :


1
2
3

xmlns : mc = " http :// schemas . openxmlformats . org / markup compatibility / 2006 "
xmlns : d = " http :// schemas . microsoft . com / expression / blend / 2008 "
mc : Ignorable = " d "

Ceci correspond limplmentation du locator fourni dans les exemples du toolkit. Il


existe plusieurs implmentations possibles de ce locator, plus ou moins parfaites.
Lier la vue au view-model
Maintenant, pour lier le modle la vue il suffira de modifier la proprit Datacontext
de votre page pour avoir :
1
2
3
4

< phone : P h on e A pplicationPage


x : Class = " DemoMvvmLight . View . VoirClientView "
...
DataContext = " { Binding VoirClientVM , Source ={ StaticResource
Locator }} " >

La liaison seffectue donc sur la proprit publique VoirClientVM du locator, trouv


en ressource. Modifions notre vue pour afficher notre client :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >

231

CHAPITRE 17. MVVM


< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " PageTitle " Text = " Fiche client "
Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle1Style } " / >
</ StackPanel >

7
8

9
10

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
Background = " { Binding BonClient } " >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< TextBlock Text = " Pr nom : " / >
< TextBlock Grid . Column = " 1 " Text = " { Binding Prenom } " / >
< TextBlock Grid . Row = " 1 " Text = " Age : " / >
< TextBlock Grid . Column = " 1 " Grid . Row = " 1 " Text = " { Binding
Age } " / >
</ Grid >
</ Grid >

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Maintenant, passons au view-model. Il ressemble beaucoup notre prcdent viewmodel :


1
2
3

public class VoirClientViewModel : ViewModelBase


{
private readonly IServiceClient serviceClient ;

4
5
6
7
8
9
10

private string prenom ;


public string Prenom
{
get { return prenom ; }
set { Noti fyPro pertyC hange d ( ref prenom , value ) ; }
}

11
12
13
14
15
16
17

private int age ;


public int Age
{
get { return age ; }
set { Noti fyPro pertyC hange d ( ref age , value ) ; }
}

18
19
20
21
22
23

232

private SolidColorBrush bonClient ;


public SolidColorBrush BonClient
{
get { return bonClient ; }
set { Noti fyPro pertyC hange d ( ref bonClient , value ) ; }

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT


}

24
25

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

26
27
28
29
30
31
32

33

variable = valeur ;
R a i s e PropertyChanged ( nomPropriete ) ;
return true ;

34

public V o irClientViewModel ( IServiceClient service )


{
serviceClient = service ;

35
36
37
38
39
40
41
42
43
44
45
46
47

Client client = serviceClient . Charger () ;


Prenom = client . Prenom ;
Age = client . Age ;
if ( client . EstBonClient )
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 ,
0 , 255 , 0 ) ) ;
else
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 ,
255 , 0 , 0 ) ) ;

La diffrence vient bien sr en premier lieu de lhritage ViewModelBase mais surtout


de lapplication de linversion de dpendance dans le constructeur du view-model. Ainsi,
notre view-model rcupre automatiquement une instance de notre service. Notons
aussi la diffrence dans la mthode NotifyPropertyChanged qui doit appeler maintenant la mthode RaisePropertyChanged de la classe de base, ViewModelBase. Cette
mthode correspond notre mthode NotifyPropertyChanged mais on se demande
pourquoi le crateur du toolkit na pas crit de surcharge de cette mthode utilisant
les nouveauts du framework 4.5 et notamment lattribut CallerMemberName, du coup
nous sommes toujours obligs dutiliser une variation de la mthode NotifyPropertyChanged.
Noubliez pas de faire en sorte que ce soit la bonne vue qui saffiche lors du dmarrage
de lapplication et vous pourrez observer votre application qui fonctionne grce au
toolkit MVVM Light. Bravo !
Vous aurez galement constat quen mode design, nous pouvons aussi voir
des donnes.
Jusque-l, nous ne sommes pas trop dpayss part dans lutilisation du service locator.
233

CHAPITRE 17. MVVM


Les commandes
Passons maintenant aux commandes sur le bouton. Je vous ai indiqu quil est possible
de passer un paramtre un bouton. Pour illustrer ce fonctionnement, nous allons crer
une nouvelle page qui affichera une liste de clients. Modifions notre service pour avoir
la mthode suivante :
1
2
3
4
5

public interface IServiceClient


{
Client Charger () ;
List < Client > ChargerTout () ;
}

6
7
8
9
10
11
12

public class ServiceClient : IServiceClient


{
public Client Charger ()
{
return new Client { Prenom = " Nico " , Age = 30 ,
EstBonClient = true };
}

13
14
15
16
17
18
19
20
21
22
23

public List < Client > ChargerTout ()


{
return new List < Client >
{
new Client { Age = 30 , EstBonClient = true , Prenom
= " Nico " } ,
new Client { Age = 20 , EstBonClient = false , Prenom
= " J r mie " } ,
new Client { Age = 30 , EstBonClient = true , Prenom
= " Delphine " }
};
}

Et pareil dans la classe DesignServiceClient.

Rajoutons une nouvelle vue dans le rpertoire View que nous allons appeler ListeClientsView.x
Et enfin, crons un nouveau view-model que nous appellerons ListeClientsViewModel
et qui hritera de ViewModelBase. Noubliez pas de dclarer le nouveau view-model
dans le locator :
1
2
3
4
5

public class ViewModelLocator


{
static ViewModelLocator ()
{
ServiceLocator . SetLocatorProvider (() = > SimpleIoc .
Default ) ;

6
7

234

if ( ViewModelBase . IsInDesignModeStatic )

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT


8

else

9
10

SimpleIoc . Default . Register < IServiceClient ,


DesignServiceClient >() ;
SimpleIoc . Default . Register < IServiceClient ,
ServiceClient >() ;

11
12
13

14

SimpleIoc . Default . Register < VoirClientViewModel >() ;


SimpleIoc . Default . Register < ListeClientsViewModel >() ;

15

public V o irClientViewModel VoirClientVM


{
get { return ServiceLocator . Current . GetInstance <
VoirClientViewModel >() ; }
}

16
17
18
19
20
21
22
23
24
25

public L i st eClien tsView Model ListeClientsVM


{
get { return ServiceLocator . Current . GetInstance <
ListeClientsViewModel >() ; }
}

Votre vue va possder une ListBox dont le template contiendra un bouton :


1
2
3
4
5

6
7
8
9

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< ListBox ItemsSource = " { Binding ListeClients } " >
< ListBox . ItemTemplate >
< DataTemplate >
< Button Content = " Qui suis - je ? " Command = " {
Binding QuiSuisJeCommand } " CommandParameter =
" { Binding } " / >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
</ Grid >

Le code XAML prcdent contient volontairement une erreur que nous corrigerons un peu plus loin.
Nous voyons que notre ListBox est lie une proprit ListeClients qui devra tre
prsente dans notre view-model. Le bouton prsent dans le template possde une commande lie la commande QuiSuisJeCommand. Remarquons la proprit CommandParameter
qui permet de positionner llment en cours comme paramtre de la commande. Liez
ensuite le view-model la vue en modifiant la proprit Datacontext avec notre nouvelle proprit dans le locator :
1

DataContext = " { Binding ListeClientsVM , Source ={ StaticResource


Locator }} " >

235

CHAPITRE 17. MVVM


Passons notre view-model dsormais :
1
2
3
4
5
6
7
8

public class L isteCl ients ViewMo del : ViewModelBase


{
private List < Client > listeClients ;
public List < Client > ListeClients
{
get { return listeClients ; }
set { Noti fyPro pertyC hange d ( ref listeClients , value ) ; }
}

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

10
11
12
13
14
15
16

17

variable = valeur ;
R aisePropertyChanged ( nomPropriete ) ;
return true ;

18

public ICommand QuiSuisJeCommand { get ; set ; }

19
20

public Liste Clien tsView Model ( IServiceClient service )


{
ListeClients = service . ChargerTout () ;

21
22
23
24
25

26

QuiSuisJeCommand = new RelayCommand < Client >( QuiSuisJe ) ;

27
28
29
30
31
32

private void QuiSuisJe ( Client client )


{
MessageBox . Show ( " Je suis " + client . Prenom ) ;
}

Remarque, la classe RelayCommand ncessite limport suivant :


1

using GalaSoft . MvvmLight . Command ;

Nous avons cr la proprit ListeClients que nous avons alimente dans le constructeur grce au modle. Puis nous voyons comment dfinir une commande qui accepte
un paramtre. En loccurrence, ici le paramtre sera du type Client car cest le type
que nous passons dans :
1

CommandParameter = " { Binding } "

Lextension de balisage {Binding} prend ici llment courant de la proprit numrable ListeClients qui est bien de type Client. Une fois le bouton cliqu, nous
pourrons alors afficher le client slectionn grce la boite de message MessageBox.
Vrifions cela en dmarrant lapplication. Noubliez pas de changer le point dentre de lapplication dans le fichier WMAppManifest.xml afin quil dmarre sur la vue
236

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT


ListeClientsView.xaml. Sauf que. . . cela ne marche pas. Le clic sur le bouton ne fait
rien ! Si vous observez bien la fentre de sortie de Visual Studio, vous pouvez constater
lerreur suivante, erreur que vous aurez loccasion de souvent voir :
1

System . Windows . Data Error : BindingExpression path error : '


QuiSuisJeCommand ' property not found on ' DemoMvvmLight . Model
. Client ' ' DemoMvvmLight . Model . Client '
( HashCode = 23288300 ) . BindingExpression : Path = ' QuiSuisJeCommand '
DataItem = ' DemoMvvmLight . Model . Client ' ( HashCode = 23288300 ) ;
target element is ' System . Windows . Controls . Button '
( Name = ' ') ; target property is ' Command ' ( type ' System . Windows .
Input . ICommand ') ..

Une erreur de binding. . . Argh ! On nous indique que la proprit QuiSuisJeCommand


est introuvable sur lobjet Client. Ce qui est vrai en soit, notre classe Client ne
possde pas cette commande ! Mais nous, ce que nous voulions cest que la commande
QuiSuisJeCommand soit celle du view-model. Comme ce que nous avions fait prcdemment. Cest rageant, on fait pareil, mais cela ne fonctionne pas ! Essayons de comprendre
pourquoi.
Je vous donne un indice : Datacontext.
En effet, dans notre exemple prcdent, notre commande est lie un bouton dont le
contexte est celui hrit de celui de la page, car il na pas t chang. Nous avions li la
proprit Datacontext de la page au view-model et tous les Datacontext des objets
de la page ont hrit de ce contexte. Ici, nous avons chang le contexte de la ListBox
pour lui fournir une liste de clients. Dans chaque template des lments de la ListBox,
nous sommes sur un objet Client et plus sur le view-model. Cest pour cela quil ne
trouve pas la proprit QuiSuisJeCommand et quil ne peut pas faire correctement le
binding. Cest une erreur trs classique que lon peut rsoudre de plusieurs faons. La
premire est de faire en sorte que le contexte courant puisse connatre le view-model.
En loccurrence, on pourrait ajouter une proprit pointant sur le view-model lobjet
Client ou mme mieux crer un nouvel objet ddi au binding qui contient cette
proprit. Crons donc une classe ClientBinding :
1
2
3
4
5
6
7

public class ClientBinding


{
public string Prenom { get ; set ; }
public int Age { get ; set ; }
public bool EstBonClient { get ; set ; }
public L i st eClien tsView Model ViewModel { get ; set ; }
}

Puis changeons le type de la proprit ListeClient pour avoir une List<ClientBinding>.


Et modifions le constructeur pour avoir :
1
2
3

public L i s t e C lien tsView Model ( IServiceClient service )


{
ListeClients = service . ChargerTout () . Select ( c = > new
ClientBinding { Age = c . Age , EstBonClient = c .
EstBonClient , Prenom = c . Prenom , ViewModel = this }) .
ToList () ;

237

CHAPITRE 17. MVVM


4
5
6

QuiSuisJeCommand = new RelayCommand < ClientBinding >(


QuiSuisJe ) ;

Remarquez que jai aussi chang le type gnrique de RelayCommand pour avoir un
ClientBinding vu que dsormais, cest un objet de ce type qui est li au paramtre
de la commande. Ce qui implique galement de changer le type du paramtre de la
mthode QuiSuisJe() :
1
2
3
4

private void QuiSuisJe ( ClientBinding client )


{
MessageBox . Show ( " Je suis " + client . Prenom ) ;
}

(noubliez pas de rajouter le using de lespace de nom System.Linq;).


Chaque client possde donc une proprit ViewModel contenant le view-model en cours
grce this. Il ne reste plus qu modifier lextension de balisage pour avoir :
1

< Button Content = " Qui suis - je ? " Command = " { Binding ViewModel .
QuiSuisJeCommand } " CommandParameter = " { Binding } " / >

Et voil, cela fonctionne. Mais cela implique pas mal de changement. . . Lautre solution
est de lier directement la proprit Command la proprit QuiSuisJeCommand de la
proprit du locator pour accder au view-model. Plus besoin de classe intermdiaire
qui contient une rfrence vers le view-model. Il suffit dcrire :
1

< Button Content = " Qui suis - je ? " Command = " { Binding Source ={
StaticResource Locator } , Path = ListeClientsVM .
QuiSuisJeCommand } " CommandParameter = " { Binding } " / >

Et le rsultat est le mme, ainsi que vous pouvez le constater sur la figure 17.7.
Pouvoir passer un paramtre une commande est trs pratique dans ce genre de situation. La classe RelayCommand du toolkit nous aide bien pour rcuprer ce paramtre,
que lon retrouve en paramtre de la mthode QuiSuisJe(). Elle sait faire encore une
chose intressante, savoir de permettre de savoir si la commande est utilisable ou
pas. Rappelez-vous, cest ce que nous avions vu plus haut. Il sagissait de la mthode
CanExecute de linterface ICommand. Javais dcid arbitrairement que cette mthode
renverrait toujours vrai. La classe RelayCommand de MVVM Light permet dassocier
une condition la possibilit dexcuter une commande. Par exemple, on pourrait imaginer quon ne puisse cliquer sur le bouton que des clients qui sont des bons clients. Cest
de la sgrgation, mais cest comme a. Les mauvais clients resteront inconnus ! Pour
ce faire, on utilisera le deuxime paramtre du constructeur de la classe RelayCommand
qui permet de dfinir une mthode qui servira de prdicat permettant dindiquer si la
commande peut tre excute ou non. Ici, nous aurons simplement besoin de renvoyer la
valeur du boolen EstBonClient, mais cela pourrait tre une mthode plus complexe.
Notre instanciation de commande devient donc :
1

238

QuiSuisJeCommand = new RelayCommand < Client >( QuiSuisJe ,


C an E x ecuteQuiSuisJe ) ;

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT

Figure 17.7 Affichage dun message suite lactivation de la commande


avec :
1
2
3
4
5
6

private bool CanExecuteQuiSuisJe ( Client client )


{
if ( client == null )
return false ;
return client . EstBonClient ;
}

Ainsi, non seulement il ne sera pas possible dexcuter la commande en cliquant sur le
bouton, mais le bouton est galement gris, signe de son tat dsactiv (voir la figure
17.8).
Plutt pratique quand un bouton doit tre dsactiv. Juste avant de terminer ce point,
remarquons que le prdicat associ la possibilit dexcution dune commande est
excut une unique fois. Si jamais notre client venait devenir un bon client, notre
bouton resterait dans un tat dsactiv car il naura pas t mis au courant de ce
changement. ce moment-l, MVVM Light fournit une mthode qui permet ce
prdicat de se rvaluer et ainsi modifier ventuellement ltat du bouton. Il sagit de
la mthode RaiseCanExecuteChanged. On pourra lutiliser ainsi :
1

(( RelayCommand ) QuiSuisJeCommand ) . Ra ise Ca nEx ecu te Cha ng ed () ;

Continuons cet aperu de MVVM Light et de ses commandes en vous indiquant comment relier nimporte quel vnement une commande. Par exemple, lvnement de
239

CHAPITRE 17. MVVM

Figure 17.8 Le bouton est gris quand la commande nest pas utilisable
slection dun lment dans une ListBox. Premire chose faire, modifier ma vue pour
ne plus avoir ce bouton, mais simplement pour avoir le prnom du client afin quil soit
facilement slectionnable :
1
2
3
4
5
6
7

< ListBox ItemsSource = " { Binding ListeClients } " >


< ListBox . ItemTemplate >
< DataTemplate >
< TextBlock Text = " { Binding Prenom } " Margin = " 10 30 0
0" />
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >

Avant, pour savoir quand un lment dun ListBox est slectionn, on se serait abonn
lvnement SelectionChanged. Grce MVVM Light, on peut utiliser laction
EventToCommand :
1
2
3
4

< ListBox ItemsSource = " { Binding ListeClients } " >


< Interactivity : Interaction . Triggers >
< Interactivity : EventTrigger EventName = " SelectionChanged
" >
< Command : EventToCommand Command = " { Binding
S el ec ti o nE le m en tC om m an d } " P as sEv en tAr gsT oC omm an d
= " True " / >
</ Interactivity : EventTrigger >

240

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT


6
7
8
9
10
11
12

</ Interactivity : Interaction . Triggers >


< ListBox . ItemTemplate >
< DataTemplate >
< TextBlock Text = " { Binding Prenom } " Margin = " 10 30 0
0" />
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >

Sachant quil faudra importer les espaces de noms suivants :


1
2

xmlns : Command = " clr - namespace : GalaSoft . MvvmLight . Command ;


assembly = GalaSoft . MvvmLight . Extras . WP8 "
xmlns : Interactivity = " clr - namespace : System . Windows . Interactivity
; assembly = System . Windows . Interactivity "

Le XAML est un peu plus verbeux, je vous le concde. Le principe est dutiliser les
triggers de Blend qui correspondent au dclenchement dun vnement, de prciser
lvnement choisi dans la proprit EventName et on pourra alors se brancher sur
nimporte quel vnement, ici lvnement SelectionChanged.
Reste dfinir la commande dans le view-model :
1

public ICommand Se le c ti on El e me nt Co m ma nd { get ; set ; }

et linstancier, par exemple dans le constructeur :


1

S e l e c t i o n E l e m en tC om m an d = new RelayCommand <


SelectionChangedEventArgs >( OnSelectionElement ) ;

Avec la mthode associe :


1
2
3

private void OnSelectionElement ( S e l e c t i o n Ch a n g e d E v e n t Ar g s args )


{
}

Remarquez que nous avons positionn la proprit PassEventArgsToCommand de EventToCommand


true et que nous pouvons ainsi obtenir largument de lvnement en paramtre de la
commande. En loccurrence, pour lvnement SelectionChanged, nous obtenons un
paramtre du type SelectionChangedEventArgs.
La messagerie
a commence ressembler quelque chose, mais quest-ce quon pourrait bien faire
une fois un client slectionn ? Vous me voyez venir. . . on pourrait naviguer sur la vue
VoirClientView.xaml et afficher le dtail du client. Rien de plus simple, nous savons
naviguer dans notre application, on la vu dans la partie prcdente. . . sauf que nous
nous heurtons un problme de taille. Vous devinez ?
Le service de navigation est une proprit de la PhoneApplicationPage. cause de
notre sparation des responsabilits, la mthode associe la commande se trouve dans
le view-model qui na aucune connaissance de la vue.
241

CHAPITRE 17. MVVM

Aie. Comment retrouver notre service de navigation ?

Ceci fait partie dun problme plus gnral, savoir : comment faire pour que le viewmodel puisse agir sur la prsentation part en utilisant les mcanismes du binding ?
La navigation se retrouve exactement dans ce cas-l. Cest le code-behind qui aurait
normalement pris en charge cette navigation, sauf que l, cest impossible. On retrouve
un cas similaire lorsque lon cherche afficher une boite de dialogue, autre que la
MessageBox.
MVVM Light propose une solution pour rsoudre cet pineux problme travers son
systme de messagerie. Ce systme offre la possibilit de pouvoir communiquer de
manire dcouple entre un view-model et sa vue ou entres view-models. Le principe
est que lmetteur envoie un message au systme de messagerie, qui le diffuse ceux qui
sy sont abonn. Dans notre cas, il faut donc que le code-behind sabonne au message :
1
2
3
4
5
6
7

public partial class ListeClientsView : PhoneApplicationPage


{
public ListeClientsView ()
{
I nitializeComponent () ;
Messenger . Default . Register < Client >( this , AfficheClient )
;
}

8
9
10
11
12
13
14

private void AfficheClient ( Client client )


{
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ " Client " ] =
client ;
NavigationService . Navigate ( new Uri ( " / View /
VoirClientView . xaml " , UriKind . Relative ) ) ;
}

Cela se fait grce la mthode Register du Messenger, qui se trouve dans lespace
de nom GalaSoft.MvvmLight.Messaging. On indique que lon sabonne aux messages
qui prendront un client en paramtre et que dans ce cas, la mthode AfficheClient
est appele. La mthode AfficheClient fait une navigation toute simple, comme on
la dj vu.
Il faut maintenant que le view-model mette le message, mais avant a, nous allons
ajouter une liaison de donnes pour rcuprer llment slectionn de la ListBox.
Remarque, on pourrait le faire avec la valeur de largument, mais cest plus propre
de faire comme a. Donc changeons notre ListBox pour avoir la liaison sur llment
slectionn :
1

< ListBox ItemsSource = " { Binding ListeClients } " SelectedItem = " {


Binding Selection , Mode = TwoWay } " >

242

LES FRAMEWORKS LA RESCOUSSE : MVVM-LIGHT


tant donn que notre proprit sera mise jour partir de linterface, le binding doit
tre dans les deux sens, do le mode TwoWay. Ajoutons la proprit Selection dans
le view-model :
1
2
3
4
5
6

private Client selection = null ;


public Client Selection
{
get { return selection ; }
set { N o t ifyPro perty Change d ( ref selection , value ) ; }
}

Il ny a plus qu envoyer le message depuis la commande de slection. On utilise pour


cela la mthode Send du Messenger :
1
2
3
4

private void OnSelectionElement ( S e l e c t i o n Ch a n g e d E v e n t Ar g s args )


{
Messenger . Default . Send ( Selection ) ;
}

Rsumons.

Lutilisateur slectionne un lment de la ListBox


La commande associe lvnement est dclenche sur le view-model
Le view-model met un message avec le client slectionn
Le code-behind de la vue, qui sest abonn ce type de message, reoit le message
mit par le view-model et dclenche la navigation

Pour terminer proprement la petite application, il faudrait que la vue qui affiche un
client utilise les donnes positionnes dans le dictionnaire dtat. Alors, comment feriezvous ?
Il y a plusieurs solutions, je vous propose la plus simple. Nous allons profiter quun
message est diffus chaque slection dun lment pour mettre jour les proprits
du view-model :
1
2
3
4
5
6

public V o ir C l ientViewModel ()
{
Messenger . Default . Register < Client >( this , MetAJourClient ) ;
Client client = ( Client ) P ho ne A pp li c at io nS e rv ic e . Current .
State [ " Client " ];
MetAJourClient ( client ) ;
}

7
8
9
10
11
12
13
14
15

private void MetAJourClient ( Client client )


{
Prenom = client . Prenom ;
Age = client . Age ;
if ( client . EstBonClient )
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 , 0 ,
255 , 0 ) ) ;
else
BonClient = new SolidColorBrush ( Color . FromArgb ( 100 , 255
, 0, 0));

243

CHAPITRE 17. MVVM


16

Il suffit de sabonner galement la rception de ce message afin de mettre jour


les proprits avec le nouveau client courant. Ce message est bien celui mit par le
view-model de la liste des clients et cest le view-model qui permet de voir un client
qui le reoit aprs sy tre abonn. Il faudra faire attention linitialisation o nous
utiliserons le dictionnaire dtat pour rcuprer la premire slection.
Notons enfin que vous devez rinitialiser la proprit Selection afin que la ListBox ne
conserve pas la slection lors du retour arrire sur la page :
1
2
3
4
5
6
7

private void OnSelectionElement ( S e l e c t i o n C ha n g e d E v e n t Ar g s args )


{
if ( Selection == null )
return ;
Messenger . Default . Send ( Selection ) ;
Selection = null ;
}

Voil pour ce petit tour de MVVM Light. Nous avons vu lessentiel de ce toolkit qui
propose des solutions pour aider la mise en place du patron de conception MVVM.
Noubliez pas que malgr sa dnomination de light, cest un framework complet qui
prend sa place (110 ko). Il est en fait light par rapport dautres framework, comme
PRISM qui est utilis avec WPF. A utiliser en connaissance de cause.

Dautres frameworks MVVM


MVVM Light nest pas le seul framework aidant la mise en place de MVVM. Cest
assurment lun des plus connus, mais dautres existent respectant plus ou moins bien
le patron de conception et apportant des outils diffrents. Citons par exemple :

Calcium - http://calcium.codeplex.com/
Caliburn Micro - http://caliburnmicro.codeplex.com/
Catel - http://catel.codeplex.com/
nRoute - http://nroute.codeplex.com/
Simple MVVM Toolkit - http://simplemvvmtoolkit.codeplex.com/
UltraLight.mvvm - http://ultralightmvvm.codeplex.com/

Je ne peux pas bien sur tous les prsenter et dailleurs je ne les ai pas tous tests :-
. Jaime bien lUltraLight.mvvm qui, via son locator, offre une liaison avec la page ce
qui permet facilement de dmarrer une navigation sans passer par lutilisation dune
messagerie. Nhsitez pas les tester pour vous faire votre propre opinion et pour vous
permettre de voir ce que vous souhaitez garder de MVVM et ce dont vous pouvez vous
passer.
244

FAUT-IL UTILISER SYSTMATIQUEMENT MVVM ?

Faut-il utiliser systmatiquement MVVM ?


MVVM est complexe apprhender. Pour bien le comprendre, il faut pratiquer. Ce
nest que petit petit que vous verrez vraiment de quoi vous avez besoin et quel
moment. Jimagine que pour linstant, vous avez limpression que MVVM pose plus
de problmes quil nen rsout et quon se complique la vie pour pas grand-chose.
Franchement, le coup de la navigation et de la messagerie, cest cens nous simplifier la
vie ? Il est vrai que lorsque lon ralise des petites applications, respecter parfaitement le
patron de conception MVVM est sans doute un peu dmesur. Cela implique toute une
mcanique qui est plutt longue mettre en place et parfois ennuyeuse, pour un gain
pas forcment vident. Noubliez cependant pas que le but premier de MVVM est de
sparer les responsabilits, notamment en sparant les donnes de la vue. Cela facilite
les oprations de maintenance en limitant lternel problme du plat de spaghetti o
la moindre correction a des impacts sur un autre bout de code. Mon avis sur MVVM
est que peu importe si vous ne respectez pas parfaitement MVVM, le principe de ce
pattern est de vous aider dans la ralisation de votre application et surtout dans sa
maintenabilit.
Lintrt galement est quil devient possible de faire des tests unitaires sur le viewmodel, sans avoir besoin de charger lapplication et de cliquer partout. Cela permet
de tester chaque fonctionnalit, dans un processus automatis, ce qui dans une grosse
application est un atout considrable pour viter les rgressions. En tous cas, nayez
crainte. Vous ntes pas obligs de pratiquer MVVM tout de suite. En tant que dbutant, vous aurez plutt intrt commencer crer des applications et ensuite
chercher appliquer des bonnes pratiques. Dans ce cas, nhsitez pas revenir lire ce
chapitre .
Remarque : par souci de simplicit et de concision, je ne respecterai pas
MVVM dans la suite de ce cours.

En rsum
MVVM est un patron de conception qui aide la ralisation dapplications
denvergure utilisant le XAML.
Des frameworks gratuits nous aident la mise en place de ce patron de conception en fournissant des bibliothques remplies de classes prouves.
Le respect et lutilit de MVVM se dcouvrent en pratiquant. Ne soyez pas
forcment trop presss de respecter parfaitement MVVM.

245

CHAPITRE 17. MVVM

246

Chapitre

18

Gestion des tats visuels


Difficult :
Maintenant que nous connaissons les templates, nous allons revenir sur un point qui vous
sera srement utile dans le dveloppement de vos applications. Il sagit de la gestion des
tats visuels. Mais quest-ce donc ? Prenons un bouton par exemple, il possde plusieurs
tats visuels. Il y a ltat quand il est cliqu, ltat quand il est dsactiv et ltat lorsquil
est au repos.
Cest la mme chose pour une ListBox, nous avons par exemple vu un tat o un lment
est slectionn, qui est dailleurs mis en avant grce la couleur daccentuation du tlphone. Tous les contrles ont potentiellement plusieurs reprsentations visuelles en fonction
de leurs tats. Voyons voir comment cela fonctionne.

247

CHAPITRE 18. GESTION DES TATS VISUELS

Les tats dun contrle


Observons les tats de notre bouton plus en dtail. la figure 18.1, vous pouvez le voir
dans son tat normal, au repos.

Figure 18.1 Etat du bouton au repos


la figure 18.2, vous voyez son tat quand il est cliqu.

Figure 18.2 Etat du bouton cliqu


Et sur la figure 18.3, lorsquil est dsactiv et donc non cliquable.

Figure 18.3 Etat du bouton dsactiv


Ils correspondent respectivement aux tats :
Normal
Pressed
Disabled
248

LES TATS DUN CONTRLE


Chaque contrle dispose de diffrents tats. Les tats changent automatiquement en
fonction dune action utilisateur ou dune proprit. Par exemple, cest en cliquant sur
le bouton que celui-ci passe de ltat Normal Pressed et en relchant le clic que
celui-ci passe de Pressed Normal, changeant au passage lapparence du contrle.
Pour ltat dsactiv, ce changement se fait quand on passe la proprit IsEnabled
false. Le bouton possde dautres tats qui ne sont pas utiliss pour Windows Phone,
comme ltat Focused et ltat Unfocused qui correspondent au fait que le bouton ait
le focus ou pas, ce qui ne sert pas vraiment dans une application pour Windows Phone,
ainsi que ltat MouseOver qui correspond au passage de la souris sur le bouton.
On dit quun contrle possde le focus quand il est la cible des actions de
saisie de lutilisateur, comme le clavier. Cest trs valable pour une application
PC, mais beaucoup moins pour une application de tlphone.
Ces trois tats du bouton sont un hritage de Silverlight pour PC, ils ne servent pas
avec le XAML pour Windows Phone. Un contrle peut tre dans plusieurs tats
la fois, par exemple si ctait valable on pourrait envisager quil soit dans un tat
Pressed et un tat o il ait le focus. Par contre, dautres tats sont exclusifs, il nest
bien sr pas possible que le bouton soit dans ltat Pressed et dans ltat Normal. . .
Pour reprsenter ceci, les tats appartiennent des groupes. Le bouton ne peut avoir
quun seul tat par groupe. En loccurrence, notre bouton possde deux groupes avec
les tats suivants :
Groupe FocusStates :
Focused
Unfocused
Groupe CommonStates :

Pressed
Disabled
Normal
MouseOver

chaque tat donc son apparence. . . ce qui implique que nous pouvons modifier les
apparences de chaque tat via des templates que nous allons dfinir dans un style.
Pour comprendre, le plus simple est dutiliser Blend. Ajoutons un bouton dans notre
XAML :
1
2
3
4
5

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< StackPanel >
< Button x : Name = " But " Content = " Cliquez - moi ! " / >
</ StackPanel >
</ Grid >

Puis dmarrons Blend en faisant un clic droit sur le projet, puis ouvrir dans expression
blend. Une fois ouvert, cliquez sur le bouton pour le slectionner et faites un clic droit
pour modifier une copie du modle, comme indiqu la figure 18.4.
Cela permet de crer un style automatiquement (voir la figure 18.5).
249

CHAPITRE 18. GESTION DES TATS VISUELS

Figure 18.4 Modification du modle du bouton dans Blend

Figure 18.5 Cration du style par dfaut du bouton

250

LES TATS DUN CONTRLE


Maintenant, nous pouvons voir dans longlet Etats, les diffrents tats du bouton,
comme vous pouvez le constater sur la figure 18.6.

Figure 18.6 Les diffrents tats du bouton


Remarquons que le XAML est dsormais :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

< phone : P h on e A pplicationPage


x : Class = " DemoEtatVisuel . MainPage "
... >
< phone : PhoneApplicationPage . Resources >
< Style x : Key = " ButtonStyle1 " TargetType = " Button "
>
< Setter Property = " Background " Value = "
Transparent " / >
< Setter Property = " BorderBrush " Value = " {
StaticResource PhoneForegroundBrush } " / >
< Setter Property = " Foreground " Value = " {
StaticResource PhoneForegroundBrush } " / >
< Setter Property = " BorderThickness " Value = " {
StaticResource PhoneBorderThickness } " / >
< Setter Property = " FontFamily " Value = " {
StaticResource Ph o ne Fo nt F am il yS e mi Bo l d } " / >
< Setter Property = " FontSize " Value = " {
StaticResource PhoneFontSizeMedium } " / >
< Setter Property = " Padding " Value = " 10 ,5 , 10 , 6 " / >
< Setter Property = " Template " >
< Setter . Value >
< ControlTemplate TargetType = " Button " >
< Grid Background = " Transparent " >
< VisualStateManager . VisualStateGroups >
< VisualStateGroup x : Name = " CommonStates "
>
< VisualState x : Name = " Normal " / >
< VisualState x : Name = " MouseOver " / >
< VisualState x : Name = " Pressed " >

251

CHAPITRE 18. GESTION DES TATS VISUELS


22
23

24

25
26

27

28
29
30
31
32
33

34

35
36

37

38
39

40
41
42
43
44
45

252

< Storyboard >


< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = "
Foreground " Storyboard . TargetName = "
ContentContainer " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " { StaticResource
PhoneButtonBasePressedForegroundBrush
}"/>
</ ObjectAnimationUsingKeyFrames >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = "
Background " Storyboard . TargetName = "
ButtonBackground " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " { StaticResource
PhoneAccentBrush } " / >
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >
< VisualState x : Name = " Disabled " >
< Storyboard >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = "
Foreground " Storyboard . TargetName = "
ContentContainer " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " { StaticResource
PhoneDisabledBrush } " / >
</ ObjectAnimationUsingKeyFrames >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = "
BorderBrush " Storyboard . TargetName = "
ButtonBackground " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " { StaticResource
PhoneDisabledBrush } " / >
</ ObjectAnimationUsingKeyFrames >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = "
Background " Storyboard . TargetName = "
ButtonBackground " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " Transparent " / >
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >
</ VisualStateGroup >
< VisualStateGroup x : Name = " FocusStates "
/>

LES TATS DUN CONTRLE


46
47

48

49
50
51
52
53
54
55

</ VisualStateManager . VisualStateGroups >


< Border x : Name = " ButtonBackground "
BorderBrush = " { TemplateBinding
BorderBrush } " BorderThickness = " {
TemplateBinding BorderThickness } "
Background = " { TemplateBinding
Background } " CornerRadius = " 0 " Margin
= " { StaticResource
P h on e T ou c h Ta r g et O v er h a ng } " >
< ContentControl x : Name = "
ContentContainer " ContentTemplate = " {
TemplateBinding ContentTemplate } "
Content = " { TemplateBinding Content } "
Foreground = " { TemplateBinding
Foreground } "
HorizontalContentAlignment ="{
TemplateBinding
H o r i z o n t a l C o n t e n t A l i g n m e n t } " Padding
= " { TemplateBinding Padding } "
V e rt i c al C o nt e n tA l i gn m e nt = " {
TemplateBinding
V e rt i c al C o nt e n tA l i gn m e nt } " / >
</ Border >
</ Grid >
</ ControlTemplate >
</ Setter . Value >
</ Setter >
</ Style >
</ phone : PhoneApplicationPage . Resources >

56
57
58
59
60
61

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

62
63
64

65
66

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12
, 17 ,0 , 28 " >
< TextBlock Text = " MON APPLICATION " Style = " {
StaticResource PhoneTextNormalStyle } " Margin = " 12
,0"/>
< TextBlock Text = " nom de la page " Margin = "9 , -7 ,0 , 0 "
Style = " { StaticResource PhoneTextTitle1Style } " / >
</ StackPanel >

67
68
69
70

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 ,
12 , 0 " >
< StackPanel >
< Button x : Name = " But " Content = " Cliquez - moi ! "
Style = " { StaticResource ButtonStyle1 } " / >

253

CHAPITRE 18. GESTION DES TATS VISUELS


</ StackPanel >
</ Grid >
</ Grid >
</ phone : PhoneApplicationPage >

71
72
73
74

Beaucoup de choses, mais ce quil faut regarder prcisment cest que Blend a dclar
un nouveau template du bouton, celui-ci est simplement :
1

< Border x : Name = " ButtonBackground " BorderBrush = " { TemplateBinding


BorderBrush } " BorderThickness = " { TemplateBinding
BorderThickness } " Background = " { TemplateBinding Background } "
CornerRadius = " 0 " Margin = " { StaticResource
P h o n e T o u c hT a r ge t O v er h a ng } " >
< ContentControl x : Name = " ContentContainer "
ContentTemplate = " { TemplateBinding ContentTemplate } "
Content = " { TemplateBinding Content } " Foreground = " {
TemplateBinding Foreground } "
H o r i z o n t a l C o n t e n t A l i g n m e n t = " { TemplateBinding
H o r i z o n t a l C o n t e n t A l i g n m e n t } " Padding = " {
TemplateBinding Padding } " V er t i ca l C on t e nt A l i gn m e nt = "
{ TemplateBinding V e r ti c a lC o n te n t Al i g nm e n t } " / >
</ Border >

Bref, un contrle Border contenant un contrle ContentControl, ce qui est prcisment


le look du bouton tel que nous le connaissons.
Lextension de balisage TemplateBinding permet de lier la valeur dune
proprit dans un modle afin de la dfinir comme valeur dune autre proprit
expose dans le contrle. Typiquement ici, le fait de dfinir par exemple la
proprit Foreground sur le contrle Button va en fait appliquer cette valeur
au contrle ContentControl.
Ensuite, il faut regarder lintrieur de la balise <VisualStateManager.VisualStateGroups>.
lintrieur sont dfinis tous les groupes dtats du contrle :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

< V is ua lSt ateManager . VisualStateGroups >


< VisualStateGroup x : Name = " CommonStates " >
< VisualState x : Name = " Normal " / >
< VisualState x : Name = " MouseOver " / >
< VisualState x : Name = " Pressed " >
< Storyboard >
...
</ Storyboard >
</ VisualState >
< VisualState x : Name = " Disabled " >
< Storyboard >
...
</ Storyboard >
</ VisualState >
</ VisualStateGroup >

254

MODIFIER UN TAT
16
17

< VisualStateGroup x : Name = " FocusStates " / >


</ V is ual St at eManager . VisualStateGroups >

Ce quon peut voir cest que les tats dfinissent une animation qui permet de changer
la valeur de certaines proprits lorsque le contrle change dtat. Par exemple, en
passant ltat Pressed, nous pouvons constater que la proprit Foreground passe
la valeur de PhoneButtonBasePressedForegroundBrush.
Voil comment sont dfinis les tats, dans des modles.

Modifier un tat
Ceci nous permet de faire ce que nous voulons avec les tats des contrles afin damliorer le look de nos boutons. Rappelez-vous dans la premire partie, nous avions modifi
lapparence dun bouton en modifiant sa proprit Content :
1
2
3
4
5
6
7
8

< Button x : Name = " But " >


< Button . Content >
< StackPanel >
< Image Source = " rouge . png " Width = " 100 " Height = " 100 "
/>
< TextBlock Text = " Cliquez - moi ! " / >
</ StackPanel >
</ Button . Content >
</ Button >

Comme on peut sen rendre compte maintenant que nous avons vu le modle original du
contrle, nous navons modifi que ce qui correspondait au contenu du ContentControl.
Le cadre est donc conserv, mais plus encore, la mme animation sur le fond du cadre
existe toujours. Ceci ne correspond peut-tre pas ce que nous souhaitons avoir lorsque
le bouton est cliqu. En loccurrence, moi ce que je voudrais, cest que le rond rouge
devienne vert et que le texte passe cliqu . Pour cela, il suffit de modifier le template
de ltat Pressed de notre contrle. . . Reprenons donc notre bouton :
1

< Button x : Name = " But " Content = " Cliquez - moi ! " Style = " {
StaticResource ButtonStyle1 } " / >

et modifions ses templates lintrieur de son style :


1
2
3
4
5
6
7
8
9

< ControlTemplate TargetType = " Button " >


< Grid Background = " Transparent " >
< V is ualStateManager . VisualStateGroups >
< VisualStateGroup x : Name = " CommonStates " >
< VisualState x : Name = " Normal " / >
< VisualState x : Name = " MouseOver " / >
< VisualState x : Name = " Pressed " >
< Storyboard >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = " Source "
Storyboard . TargetName = " MonRond " >

255

CHAPITRE 18. GESTION DES TATS VISUELS


< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " vert . png " / >
</ ObjectAnimationUsingKeyFrames >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = " Text "
Storyboard . TargetName = " MonTexte " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " Cliqu :) " / >
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >
< VisualState x : Name = " Disabled " >
< Storyboard >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = "
Visibility " Storyboard . TargetName = "
MonRond " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " Collapsed " / >
</ ObjectAnimationUsingKeyFrames >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = " Text "
Storyboard . TargetName = " MonTexte " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 "
Value = " Pas touche ... " / >
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >
</ VisualStateGroup >
</ VisualStateManager . VisualStateGroups >
< Border x : Name = " ButtonBackground " BorderBrush = " {
TemplateBinding BorderBrush } " BorderThickness = " {
TemplateBinding BorderThickness } " Background = " {
TemplateBinding Background } " CornerRadius = " 0 " Margin
= " { StaticResource P h o ne T o uc h T ar g e tO v e rh a n g } " >
< StackPanel >
< Image x : Name = " MonRond " Source = " rouge . png "
Width = " 100 " Height = " 100 " / >
< TextBlock x : Name = " MonTexte " Text = " Cliquez - moi
!" />
</ StackPanel >
</ Border >
</ Grid >
</ ControlTemplate >

10
11
12

13
14
15
16
17
18
19

20
21
22

23
24
25
26
27
28
29

30
31
32
33
34
35
36

Vous pouvez voir que jai modifi lapparence du contrle pour quil contienne notre
image et notre texte :
1

< Border x : Name = " ButtonBackground " BorderBrush = " { TemplateBinding


BorderBrush } " BorderThickness = " { TemplateBinding
BorderThickness } " Background = " { TemplateBinding Background } "

256

MODIFIER UN TAT

2
3
4
5
6

CornerRadius = " 0 " Margin = " { StaticResource


P h o n e T o u c h Ta r g et O v er h a ng } " >
< StackPanel >
< Image x : Name = " MonRond " Source = " rouge . png " Width = " 100 "
Height = " 100 " / >
< TextBlock x : Name = " MonTexte " Text = " Cliquez - moi ! " / >
</ StackPanel >
</ Border >

Puis, dans ltat Pressed, jai anim les proprits Source de limage et Text du
TextBlock pour charger une nouvelle image et changer le texte :
1
2
3

4
5
6

7
8
9
10

< VisualState x : Name = " Pressed " >


< Storyboard >
< O b j e c t A n i m a t i o n U s i n g K e y F r a m e s Storyboard .
TargetProperty = " Source " Storyboard . TargetName = "
MonRond " >
< D is cre teO bj ect Ke yFr ame KeyTime = " 0 " Value = " vert . png
"/>
</ ObjectAnimationUsingKeyFrames >
< O b j e c t A n i m a t i o n U s i n g K e y F r a m e s Storyboard .
TargetProperty = " Text " Storyboard . TargetName = "
MonTexte " >
< D is cre teO bj ect Ke yFr ame KeyTime = " 0 " Value = " Cliqu
:) " / >
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >

Bien sr, il faudra rajouter les images rouge.png et vert.png la solution. De la mme
faon, dans ltat Disabled, jai chang la visibilit de limage pour la faire disparaitre,
puis jai anim le texte pour le changer :
1
2
3

4
5
6

7
8
9
10

< VisualState x : Name = " Disabled " >


< Storyboard >
< O b j e c t A n i m a t i o n U s i n g K e y F r a m e s Storyboard .
TargetProperty = " Visibility " Storyboard . TargetName = "
MonRond " >
< D is cre teO bj ect Ke yFr ame KeyTime = " 0 " Value = "
Collapsed " / >
</ ObjectAnimationUsingKeyFrames >
< O b j e c t A n i m a t i o n U s i n g K e y F r a m e s Storyboard .
TargetProperty = " Text " Storyboard . TargetName = "
MonTexte " >
< D is cre teO bj ect Ke yFr ame KeyTime = " 0 " Value = " Pas
touche ... " / >
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >

Du coup, dans mon XAML, je me retrouve avec mon bouton :


257

CHAPITRE 18. GESTION DES TATS VISUELS


1

< Button x : Name = " But " Content = " Cliquez - moi ! " Style = " {
StaticResource ButtonStyle1 } " / >

Qui, lorsquil est au repos, ressemble ce que vous pouvez voir sur la figure 18.7.

Figure 18.7 Etat du bouton modifi au repos


Lorsquil est cliqu, il est plutt ainsi (voir la figure 18.8).
Et lorsquil est dsactiv, il est comme vous pouvez le voir la figure 18.9.
Ici, nous avons fait les modifications des animations des diffrents tats directement en XAML, mais il aurait t tout fait possible dutiliser Blend pour le
faire. Cest notamment plus pratique lorsquil y a des animations complexes.

Changer dtat
Bon. . . javoue ! Dans le chapitre prcdent jai trich ! Mais ne le dites personne. Pour
faire mes copies dcrans, je nai pas cherch appuyer sur la touche de copie dcran
tout en maintenant le clic sur le bouton. . . trop compliqu, je fais attention ltat. . .
de mes doigts. :- Jai donc pour loccasion chang ltat du bouton par code. Et oui,
il ny a pas que les actions de lutilisateur qui peuvent changer ltat dun contrle. Il
est trs simple de changer ltat dun contrle avec une ligne de code. On utilise pour
cela le VisualStateManager - http://msdn.microsoft.com/fr-fr/library/system.
258

CHANGER DTAT

Figure 18.8 Etat du bouton modifi lorsquil est cliqu

Figure 18.9 Etat du bouton modifi lorsquil est dsactiv


259

CHAPITRE 18. GESTION DES TATS VISUELS


windows.visualstatemanager(v=vs.95).aspx. Par exemple, pour passer sur ltat
Pressed, je peux utiliser :
Vi su al Sta teManager . GoToState ( But , " Pressed " , true ) ;

Il suffit de connatre le nom de ltat atteindre et le tour est jou.

Crer un nouvel tat


Et vous savez quoi ? Il est mme possible de rajouter des tats un contrle. Imaginons
par exemple que je souhaite que mon bouton puisse tre dans un tat TailleReduite
et un autre TailleNormale o vous laurez compris notre bouton pourra avoir deux
apparences diffrentes en fonction de son tat. Ce nouvel tat sera bien sr cumulatif
aux autres tats, comme Pressed ou Disabled. Comme nous lavons vu, il va falloir
commencer par crer un nouveau groupe dtat. Pour le rajouter, il suffit de se placer lintrieur de la proprit <VisualStateManager.VisualStateGroups>. Et nous
aurons par exemple :
1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19

< V is ua lSt ateManager . VisualStateGroups >


< VisualStateGroup x : Name = " TailleStates " >
< VisualState x : Name = " TailleNormale " / >
< VisualState x : Name = " TailleReduite " >
< Storyboard >
< O b j e c t A n i m a t i o n U s i n g K e y F r a m e s Storyboard .
TargetProperty = " Width " Storyboard . TargetName
= " ButtonBackground " >
< D is cre teO bj ect Ke yFr am e KeyTime = " 0 " Value = "
100 " / >
</ ObjectAnimationUsingKeyFrames >
</ Storyboard >
</ VisualState >
</ VisualStateGroup >
< VisualStateGroup x : Name = " CommonStates " >
< VisualState x : Name = " Normal " / >
< VisualState x : Name = " MouseOver " / >
< VisualState x : Name = " Pressed " >
...
</ VisualState >
</ VisualStateGroup >
</ V is ua lStateManager . VisualStateGroups >

Ici, jai simplement choisi de rduire la taille de la proprit Width du contrle Border
afin de rduire la taille du bouton. Le voici la figure 18.10 donc dans un tat Pressed
et TailleReduite.
Pour obtenir cela, dans le XAML, jaurai toujours mon bouton, mais jai galement
rajout une case cocher pour pouvoir positionner ltat rduit :
1

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >

260

CRER UN NOUVEL TAT

Figure 18.10 Le bouton est dans ltat Pressed et TailleReduite

2
3
4

5
6

< StackPanel >


< Button x : Name = " But " Content = " Cliquez - moi ! " Style = " {
StaticResource ButtonStyle1 } " / >
< CheckBox Margin = " 0 100 0 0 " Content = " Taille r duite ? "
Checked = " CheckBox_Checked " Unchecked = "
CheckBox_Unchecked " / >
</ StackPanel >
</ Grid >

Avec dans le code-behind :


1
2
3
4

private void CheckBox_Checked ( object sender , RoutedEventArgs e )


{
Vi su al Sta teManager . GoToState ( But , " TailleReduite " , true ) ;
}

5
6
7
8
9

private void CheckBox_Unchecked ( object sender , RoutedEventArgs


e)
{
Vi su al Sta teManager . GoToState ( But , " TailleNormale " , true ) ;
}

261

CHAPITRE 18. GESTION DES TATS VISUELS


On peut galement facilement ajouter un nouvel tat grce Blend, nous le
verrons dans la partie suivante lors de la gestion de lorientation.

En rsum
Un contrle peut possder plusieurs tats, comme un bouton qui peut tre cliqu
ou non cliqu.
chaque tat est associe une reprsentation visuelle diffrente, quil est possible
de modifier grce aux templates.
Il est possible de crer un nouvel tat ou un nouveau groupe dtat pour un
contrle.
On change un tat par code grce la classe VisualStateManager.
Blend peut se rvler trs utile dans la cration ou la modification dtats.

262

Chapitre

19

Le traitement des donnes


Difficult :
Gnralement, dans nos applications, nous allons avoir besoin de traiter des donnes. Des
clients, des commandes ou tout autre chose qui a toute sa place dans une ListBox ou dans
dautres contrles. Nous venons en plus de parler de modle dans le chapitre sur MVVM en
simulant un peu maladroitement un chargement de donnes, avec des donnes en dur dans
le code. Dans une application de gestion, les donnes voluent au fur et mesure. La liste
de clients sagrandit, le nombre de produits du catalogue augmente, les prix changent, etc.
Bref, nous allons avoir besoin de grer des donnes, aussi nous allons nous attarder dans
ce chapitre considrer diffrentes solutions pour rcuprer des donnes et les manipuler.

263

CHAPITRE 19. LE TRAITEMENT DES DONNES

HttpRequest & WebClient


Dans une application pour mobile, il y a souvent beaucoup doccasions pour rcuprer
des informations disponibles sur internet, notamment avec la prsence de plus en plus
importante du cloud. Rcuprer des donnes sur internet consiste gnralement en trois
choses :
Demander la rcupration de donnes.
Attendre la fin du tlchargement.
Interprter ces donnes.
Dans nos applications Windows Phone, la rcupration de donnes est obligatoirement
asynchrone pour viter de bloquer lapplication et garder une interface fonctionnelle.
Cela veut dire quon va lancer le tlchargement et tre notifi de la fin par un vnement. Cest ce moment-l que lon pourra interprter les donnes. Il y a plusieurs faons de faire des appels, la premire est dutiliser les classes WebClient - http://msdn.
microsoft.com/fr-fr/library/system.net.webclient.aspx et HttpWebRequest http://msdn.microsoft.com/fr-fr/library/system.net.httpwebrequest.aspx.
Si vous avez simplement besoin de rcuprer une chane (ou du XML brut) le plus simple
est dutiliser la classe WebClient. Par exemple, la page internet suivante - http://fr.
openclassrooms.com/uploads/fr/ftp/windows_phone/script_nico.php renvoie la
chane Bonjour tout le monde . Pour y accder, nous pouvons crire le code suivant :
1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;

6
7
8
9

10

WebClient client = new WebClient () ;


client . D ow n lo ad St r in gC om p le te d +=
client_DownloadStringCompleted ;
client . DownloadStringAsync ( new Uri ( " http :// fr .
openclassrooms . com / uploads / fr / ftp / windows_phone /
script_nico . php " ) ) ;

11
12
13
14
15
16
17
18
19
20
21
22

264

private void c l i e n t _ D o w n l o a d S t r i n g C o m p l e t e d ( object sender ,


DownloadStringCompletedEventArgs e)
{
if ( e . Error == null )
{
string texte = e . Result ;
MessageBox . Show ( texte ) ;
}
else
{
MessageBox . Show ( " Impossible de r cup rer les donn
es sur internet : " + e . Error ) ;
}

HTTPREQUEST & WEBCLIENT


23
24

Ce qui donne le rsultat de la figure 19.1.

Figure 19.1 Affichage dune donne rcupre sur internet


Le principe est de sabonner lvnement de fin de tlchargement, de dclencher le
tlchargement asynchrone et de rcuprer le rsultat. Ici, il ny a pas de traitement
faire pour interprter les donnes, vu que nous souhaitons afficher directement la
chane rcupre.
La mthode DownloadStringAsync fonctionne trs bien pour tout ce qui est texte
brut. Si vous voulez tlcharger des donnes binaires, vous pourrez utiliser la mthode OpenReadAsync. La classe WebClient est parfaite pour faire des tlchargements
simples mais elle devient vite limite lorsque nous devons faire des oprations plus
pointues sur les requtes, comme modifier les enttes HTTP ou envoyer des donnes
en POST, etc. Cest l que nous allons utiliser la classe HttpWebRequest. Elle offre
un contrle plus fin sur la requte web. Nous allons illustrer ceci en faisant une requte sur le formulaire PHP du cours PHP du site OpenClassrooms, disponible cet
emplacement : http://fr.openclassrooms.com/uploads/formulaire.php
Le principe est denvoyer des donnes en POST, notamment la donne : "prenom=Nicolas".
Le code est le suivant :
1

< TextBlock x : Name = " Resultat " TextWrapping = " Wrap " / >

Avec le code behind qui suit :


265

CHAPITRE 19. LE TRAITEMENT DES DONNES


1
2
3

public MainPage ()
{
I ni t i alizeComponent () ;

HttpWebRequest requete = ( HttpWebRequest ) HttpWebRequest .


Create ( " http :// fr . openclassrooms . com / uploads / fr / ftp /
mateo21 / php / form_text / formulaire . php " ) ;
requete . Method = " POST " ;
requete . ContentType = " application /x - www - form - urlencoded " ;

6
7
8
9
10

requete . Beg inGetR eques tStrea m ( DebutReponse , requete ) ;

11
12
13
14
15
16

private void DebutReponse ( IAsyncResult resultatAsynchrone )


{
HttpWebRequest requete = ( HttpWebRequest ) resultatAsynchrone
. AsyncState ;
Stream postStream = requete . EndGetRequestStream (
re sultatAsynchrone ) ;
string donneesAEnvoyer = " prenom = Nicolas " ;

17
18
19
20
21
22

byte [] tableau = Encoding . UTF8 . GetBytes ( donneesAEnvoyer ) ;


postStream . Write ( tableau , 0 , donneesAEnvoyer . Length ) ;
postStream . Close () ;
requete . BeginGetResponse ( FinReponse , requete ) ;

23
24
25
26
27
28

private void FinReponse ( IAsyncResult resultatAsynchrone )


{
HttpWebRequest requete = ( HttpWebRequest ) resultatAsynchrone
. AsyncState ;
WebResponse webResponse = requete . EndGetResponse (
re sultatAsynchrone ) ;
Stream stream = webResponse . GetResponseStream () ;

29

StreamReader streamReader = new StreamReader ( stream ) ;


string reponse = streamReader . ReadToEnd () ;
stream . Close () ;
streamReader . Close () ;
webResponse . Close () ;

30
31
32
33
34
35
36
37

Dispatcher . BeginInvoke (() = > Resultat . Text = reponse ) ;

Stream et StreamReader sont dans lespace de nom System.IO. Encoding


est dans lespace de nom System.Text.
266

HTTPREQUEST & WEBCLIENT


Ce code peut paratre un peu compliqu, mais cest toujours le mme pour faire ce genre
de requte. On commence par crer la requte en lui indiquant la mthode POST.
Ensuite dans la premire mthode on crit les donnes envoyer dans le flux et on
invoque la requte asynchrone. Dans la deuxime mthode, on rcupre le retour (voir
la figure 19.2).

Figure 19.2 Envoi dun formulaire POST une page PHP et affichage du retour
Ici, le retour est du HTML, ce qui est normal vu que ce formulaire a t prvu pour
une page web. Il aurait pu tre judicieux dinterprter le rsultat, en retirant les balises
HTML par exemple. . .
Attention, un tlchargement fait avec HttpWebRequest sexcute sur un
thread en arrire-plan, cela veut dire que si nous voulons mettre jour linterface, nous aurons besoin dutiliser le dispatcher pour re-basculer sur le thread
de linterface.
Cest ce que nous avons fait ici avec :
1

Dispatcher . BeginInvoke (() = > resultat . Text = reponse ) ;

Ce qui permet de mettre jour la proprit Text du TextBlock.


267

CHAPITRE 19. LE TRAITEMENT DES DONNES


Remarquons que si nous utilisons la classe WebClient dans un thread diffrent
de celui permettant de mettre jour linterface, le rsultat sera aussi dans un
thread diffrent, il faudra donc utiliser un dispatcher dans ce cas aussi. Nous
reviendrons sur ces notions de Thread et de Dispatcher dans une prochaine
partie.
Nous pouvons galement consommer facilement des services web avec une application
Windows Phone. Un service web est une espce dapplication web qui rpond des
requtes permettant dappeler une mthode avec des paramtres et de recevoir en
rponse le retour de la mthode.
Mais comment appeler un service web ? Quelle adresse ? Comment connaiton le nom des mthodes appeler ? Comment indiquer les paramtres et les
valeurs que lon souhaite passer ?
Eh oui, il faut une syntaxe dfinie, sinon on peut faire ce que lon veut. Cest l quinterviennent les organismes de standardisation. Ils ont dfini plusieurs normes permettant
de dcrire le format des changes. Cest le cas par exemple du protocole SOAP - http:
//fr.wikipedia.org/wiki/SOAP qui est bas sur du XML. Il est associ au WSDL http://fr.wikipedia.org/wiki/Web_Services_Description_Language qui permet
de dcrire le service web. Nous avons notre disposition galement les services web de
type REST - http://fr.wikipedia.org/wiki/Representational_State_Transfer
qui exposent les fonctionnalits comme des URL.
Pour illustrer ce fonctionnement, nous allons utiliser un service web gratuit qui permet
de transformer des tempratures, par exemple de degrs Celsuis en degr Fahrenheit.
Ce service web est disponible ladresse suivante : http://www.webservicex.net/
ConvertTemperature.asmx, et plus prcisment, sa description est accessible sur http:
//www.webservicex.net/ConvertTemperature.asmx?WSDL.
Nous devons ensuite ajouter une rfrence web, pour cela faites un clic droit sur les
rfrences et ajoutez une rfrence de service, comme indiqu sur la figure 19.3.

Figure 19.3 Ajout dune rfrence de service

Ensuite, il faut saisir ladresse du WSDL http://www.webservicex.net/ConvertTemperature.


asmx?WSDL et cliquer sur Aller (voir la figure 19.4).
268

HTTPREQUEST & WEBCLIENT

Figure 19.4 Fentre dajout de la rfrence de service

Remarquez que je laisse lespace de noms la valeur ServiceReference1, mais nhsitez


pas le changer si besoin. Vous aurez alors besoin dinclure cet espace de nom afin
de pouvoir appeler le service web. Une fois valid, Visual Studio nous gnre un proxy
en se basant sur le WSDL du service web. Ce proxy va soccuper dencapsuler tout la
logique dappel du web service pour nous simplifier la tche.
Remarque : un proxy est une ou plusieurs classes qui se placent entre deux
autres pour faciliter ou surveiller leurs changes. Le proxy est en fait un
patron de conception que lon peut dcrire assez facilement avec un exemple
du monde rel, dans le cas o deux personnes qui ne parlent pas la mme
langue vont recourir un interprte pour les faire communiquer entre elles.
Cet interprte, cest le proxy.
Cela veut dire concrtement que Visual Studio travaille pour nous. partir de lURL
de nos services web, il va analyser le type des donnes qui doivent tre passes en
paramtres ainsi que les donnes que lon obtient en retour et gnrer des classes qui
leurs correspondent. De mme, il gnre tout une classe qui encapsule les diffrents
appels aux diffrentes mthodes du service web. Cest cette classe que lon appelle un
proxy car elle sert de point dentre pour tous les appels des mthodes du service web.
Toutes ces classes ont t cres dans lespace de nom que nous avons indiqu.
Nous pourrons alors simplement lutiliser comme ceci :
269

CHAPITRE 19. LE TRAITEMENT DES DONNES


1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;

6
7

8
9

10

TemperatureService . C o n v e r t T e m p e r a t u r e S o a p C l i e n t client
= new TemperatureService .
C o n v e r t T e m p e r a t u r e S o a p C l i e n t () ;
client . ConvertTempCompleted +=
client_ConvertTempCompleted ;
client . ConvertTempAsync ( 25 , TemperatureService .
TemperatureUnit . degreeCelsius , TemperatureService .
TemperatureUnit . degreeFahrenheit ) ;

11
12
13
14
15
16
17
18
19

private void c l i e n t _ C o n v e r t T e m p C o m p l e t e d ( object sender ,


Te mperatureService . C o n v e r t T e m p C o m p l e t e d E v e n t A r g s e )
{
if ( e . Error == null )
{
MessageBox . Show ( e . Result . ToString () ) ;
}
}

Ce qui donnera la figure 19.5.


Et voil, 25 degr Celsuis font 77 degr Fahrenheit !

Linq-To-Json
Parlons prsent un peu des services REST. Je ne prsenterai pas comment faire
un appel REST parce que vous savez dj le faire, dans la mesure o il sagit dune
simple requte HTTP. Par contre, ce quil est intressant dtudier ce sont les solutions pour interprter le rsultat dun appel REST. De plus en plus, les services REST
renvoient du JSON car cest un format de description de donnes beaucoup moins
verbeux que le XML. .NET sait interprter le JSON mais malheureusement lassembly Silverlight System.Json.dll nest pas porte pour Windows Phone. Nous pouvons quand mme lutiliser mais cest nos risques et prils. Elle a t installe avec
le SDK de Silverlight, chez moi cet emplacement : C:\Program Files\Microsoft
SDKs\Silverlight\v4.0\Libraries\Client\System.Json.dll.
Cette assemby nous permet de faire du Linq To Json et davoir accs aux objets JsonObject - http://msdn.microsoft.com/fr-fr/library/system.json.jsonobject(v=vs.
95).aspx et JsonArray - http://msdn.microsoft.com/fr-fr/library/system.json.
jsonarray(v=vs.95).aspx. Lorsque vous allez rfrencer cette assembly, vous aurez
le message davertissement prsent dans la figure 19.6 :
270

LINQ-TO-JSON

Figure 19.5 Rsultat de la conversion degr Celsuis en degr Fahrenheit

Figure 19.6 Fentre davertissement lors de la rfrence System.Json.dll

271

CHAPITRE 19. LE TRAITEMENT DES DONNES


Vous pouvez cliquer sur oui pour continuer. Nous allons illustrer le fonctionnement de
ces objets travers un appel REST qui consistera raliser une recherche avec le moteur
de recherche Google. Par exemple, si je veux chercher la chane openclassrooms sur
Google, je pourrai utiliser le service web REST suivant : http://ajax.googleapis.
com/ajax/services/search/web?v=1.0&q=openclassrooms Qui me renverra quelque
chose comme :
1

2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

272

" responseData " :{


" results " :[
{
" GsearchResultClass " : " GwebSearch " ,
" unescapedUrl " : " http :// fr . openclassrooms . com / " ,
" url " : " http :// fr . openclassrooms . com / " ,
" visibleUrl " : " fr . openclassrooms . com " ,
" cacheUrl " : " http :// www . google . com / search ? q \
u003dcache : Wy40FwwsrHMJ : fr . openclassrooms . com " ,
" title " : " \ u003cb \ u003eOpenClassrooms \ u003c / b \ u003e ,
Le Site du Z ro - Les cours les plus ouverts du
Web " ,
[...]
},
{
" GsearchResultClass " : " GwebSearch " ,
" unescapedUrl " : " http :// fr . openclassrooms . com /
informatique / cours " ,
" url " : " http :// fr . openclassrooms . com / informatique /
cours " ,
[...]
},
[...]
],
" cursor " :{
" resultCount " : " 119 000 " ,
" pages " :[
{
" start " : " 0 " ,
" label " : 1
},
[...]
],
" estimatedResultCount " : " 119000 " ,
" currentPageIndex " :0 ,
[...]
" searchResultTime " : "0 , 21 "
}
},
" responseDetails " : null ,
" responseStatus " : 200

LINQ-TO-JSON
Le format JSON est relativement comprhensible lil nu, mais sa lecture fait un peu
mal aux yeux. Nous pouvons quand mme dcrypter que le rsultat contient une srie de
valeurs correspondant la recherche. Construisons une mini application qui effectuera
le tlchargement des donnes, vous savez faire, on utilise la classe WebClient :
1
2
3
4
5
6
7
8

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I ni t i alizeComponent () ;
WebClient client = new WebClient () ;
client . D ow n lo ad St r in gC o mp le te d +=
client_DownloadStringCompleted ;
client . DownloadStringAsync ( new Uri ( " http :// ajax .
googleapis . com / ajax / services / search / web ? v = 1 . 0 & q =
openclassrooms " ) ) ;
}

10
11
12
13
14
15
16
17
18

private void c l i e n t _ D o w n l o a d S t r i n g C o m p l e t e d ( object sender ,


DownloadStringCompletedEventArgs e)
{
if ( e . Error == null )
{
JsonObject json = ( JsonObject ) JsonObject . Parse ( e .
Result ) ;
}
}

Nous allons pouvoir obtenir un objet JsonObject grce la mthode statique Parse.
Le JsonObject est en fait une espce de dictionnaire o nous pouvons accder des
valeurs partir de leur cl. Par exemple, vous pouvez voir que le json rcupr possde
la proprit responseData qui contient une sous proprit cursor, contenant ellemme la proprit resultCount fournissant le nombre de rsultats de la requte. Nous
pouvons y accder de cette faon :
1
2

JsonObject json = ( JsonObject ) JsonObject . Parse ( e . Result ) ;


string nombreResultat = json [ " responseData " ][ " cursor " ][ "
resultCount " ];

Dans ce cas, chaque JsonObject renvoie un nouvel JsonObject qui est lui-mme toujours cette espce de dictionnaire. En effet, responseData, cursor et resultCount
sont des proprits simples. Ce nest pas le cas par contre de la proprit results qui
est un tableau. On utilisera alors un JsonArray pour pouvoir le parcourir. Et cest l
que Linq To Json rentre en action, nous allons pouvoir requter sur ce tableau. Par
exemple :
1
2
3

List < string > resultats = new List < string >() ;
JsonObject json = ( JsonObject ) JsonObject . Parse ( e . Result ) ;
string nombreResultat = json [ " responseData " ][ " cursor " ][ "
resultCount " ];

273

CHAPITRE 19. LE TRAITEMENT DES DONNES


4
5
6
7

var listeResultat =
from resultat in ( JsonArray ) ( json [ " responseData " ][ "
results " ])
where (( string ) resultat [ " content " ]) . IndexOf ( " cours " ,
StringComparison . C u rr e n tC u l tu r e Ig n o re C a se ) >= 0
select resultat ;

8
9
10
11
12

foreach ( var resultat in listeResultat )


{
resultats . Add ( resultat [ " titleNoFormatting " ]) ;
}

Ici, je rcupre les titres de chaque rsultats dont le contenu contient le mot cours, sans
faire attention la casse. Ainsi, avec une ListBox :
1

< ListBox x : Name = " MaListeBox " / >

Je pourrai les afficher :


1

MaListeBox . ItemsSource = resultats ;

Et obtenir le rsultat illustr dans la figure 19.7.

Figure 19.7 Rsultat de la recherche google dans la ListBox


Vous aurez remarqu que traiter du JSON de cette faon nest pas formidable. Cest plutt lourd mettre en place. Heureusement, il y a une autre solution plus intressante et
qui na pas besoin daller chercher lassembly System.Json.dll. Il sagit de transformer
274

LINQ-TO-JSON

le JSON obtenu en objet. Cela peut se faire avec le DataContractJsonSerializer - http:


//msdn.microsoft.com/fr-fr/library/system.runtime.serialization.json.datacontractjs
aspx du framework.NET, qui se trouve dans lassembly System.Servicemodel.Web.
Celui-ci souffre cependant de quelques limitations. On pourra le remplacer avec la
bibliothque open-source JSON.NET que lon peut tlcharger sur codeplex - http:
//json.codeplex.com/. Tlchargez la dernire version et rfrencez lassembly dans
votre projet, la version Windows Phone bien sr ou alors utilisez NuGet (voir figure
19.8).

Figure 19.8 Installation de Json.NET via NuGet


La premire chose faire est de regarder la rponse renvoye car nous allons avoir besoin
de construire un ou plusieurs objets mappant ce rsultat. Nous pouvons les construire
la main ou bien profiter du fait que certaines personnes ont ralis des outils pour
nous simplifier la vie. Allons par exemple sur le site - http://json2csharp.com/, o
nous pouvons copier le rsultat de la requte. Ce site nous gnre les classes suivantes :
1
2
3
4
5

public class Page


{
public int label { get ; set ; }
public string start { get ; set ; }
}

6
7
8
9
10
11

public class Cursor


{
public int currentPageIndex { get ; set ; }
public string estimatedResultCount { get ; set ; }
public string moreResultsUrl { get ; set ; }

275

CHAPITRE 19. LE TRAITEMENT DES DONNES


12
13
14
15

public List < Page > pages { get ; set ; }


public string resultCount { get ; set ; }
public string searchResultTime { get ; set ; }

16
17
18
19
20
21
22
23
24
25
26
27

public class Result


{
public string GsearchResultClass { get ; set ; }
public string cacheUrl { get ; set ; }
public string content { get ; set ; }
public string title { get ; set ; }
public string titleNoFormatting { get ; set ; }
public string unescapedUrl { get ; set ; }
public string url { get ; set ; }
public string visibleUrl { get ; set ; }
}

28
29
30
31
32
33

public class ResponseData


{
public Cursor cursor { get ; set ; }
public List < Result > results { get ; set ; }
}

34
35
36
37
38
39
40

public class RootObject


{
public ResponseData responseData { get ; set ; }
public object responseDetails { get ; set ; }
public int responseStatus { get ; set ; }
}

que nous pouvons inclure dans notre projet. Ces classes reprsentent exactement le
rsultat de la requte sous la forme de plusieurs objets. Il ne reste plus qu faire un
appel web comme on la vu :
1
2
3
4
5
6
7

public MainPage ()
{
I ni t i alizeComponent () ;
WebClient client = new WebClient () ;
client . D ow n lo ad St r in gC om p le te d +=
client_DownloadStringCompleted ;
client . DownloadStringAsync ( new Uri ( " http :// ajax . googleapis .
com / ajax / services / search / web ? v = 1 . 0 & q = openclassrooms " ) ) ;
}

8
9
10
11
12
13

private void c l i e n t _ D o w n l o a d S t r i n g C o m p l e t e d ( object sender ,


DownloadStringCompletedEventArgs e)
{
if ( e . Error == null )
{
RootObject resultat = JsonConvert . DeserializeObject <
RootObject >( e . Result ) ;

276

LA BIBLIOTHQUE DE SYNDICATION
14
15
16

MaListeBox . ItemsSource = resultat . responseData . results .


Select ( r = > r . titleNoFormatting ) ;

et utiliser la classe JsonConvert pour dsrialiser le JSON rcupr et le mettre dans


les classes qui ont t gnres.
Srialis signifie que linstance dun objet subit une transformation afin
de pouvoir tre stocke au format texte, ou binaire. Inversement, la dsrialisation permet de reconstruire une instance dun objet partir de ce texte ou
de binaire.
Ensuite, jextraie uniquement le titre pour lafficher dans ma ListBox. Et le tour est
jou ! Grce la bibliothque JSON.NET, nous pouvons facilement interprter des
donnes JSON venant dinternet afin dtre efficace dans nos applications. Remarquez
que maintenant que nous avons des objets, nous pouvons utiliser les extensions Linq
pour requter sur les informations issues du JSON.

La bibliothque de Syndication
Maintenant que nous savons rcuprer des donnes depuis internet, pourquoi ne pas
essayer den faire quelque chose dun peu intressant ? Comme un lecteur de flux RSS
par exemple. . . Vous connaissez sans doute tous le RSS - http://fr.wikipedia.org/
wiki/RSS, cest ce format qui nous permet de nous abonner nos blogs favoris afin
dtre avertis des nouveaux articles publis. Le flux RSS est produit sous forme de XML
standardis, contenant des informations sur le nom du site, les billets qui le composent,
le titre du billet, la description, etc. Le site OpenClassrooms possde bien videmment
des flux RSS, comme le flux dactualit de son blog disponible cet emplacement :
http://www.simple-it.fr/blog/feed/. Si vous naviguez sur ce lien, vous pouvez
facilement voir le titre du site, la description, ainsi que les diffrentes actualits ; et
tout a au format XML.
Prenons un autre site pour lexemple, le blog de lquipe Windows Phone. Le flux
RSS est accessible via cette page : http://blogs.windows.com/windows_phone/b/
windowsphone/rss.aspx.
Vous savez dj rcuprer du XML grce la classe WebClient. Je vais en profiter
pour vous communiquer une petite astuce dont je nai pas parl dans les chapitres
prcdents. Il est possible de fournir un objet de contexte la requte de tlchargement
et ainsi pouvoir utiliser la mme mthode pour lvnement de fin de tlchargement
et identifier ainsi diffrentes requtes. Il suffit de lui passer en paramtre de lappel la
mthode DownloadStringAsync. Cet objet de contexte sera rcupr dans lvnement
de fin de tlchargement, dans la proprit UserState de lobjet rsultat :
1
2

public partial class MainPage : PhoneApplicationPage


{

277

CHAPITRE 19. LE TRAITEMENT DES DONNES


private WebClient client ;

3
4

public MainPage ()
{
I nitializeComponent () ;

5
6
7
8
9
10
11

12

client = new WebClient () ;


client . D ow n lo ad St r in gC om p le te d +=
client_DownloadStringCompleted ;
client . DownloadStringAsync ( new Uri ( " http :// www . simple it . fr / blog / feed / " ) , " OC " ) ;

13

private void c l i e n t _ D o w n l o a d S t r i n g C o m p l e t e d ( object sender ,


DownloadStringCompletedEventArgs e)
{
if ( e . Error == null )
{
if (( string ) e . UserState == " OC " )
{
// ce sont les donn es venant du flux du blog
OpenClassrooms

14
15
16
17
18
19
20
21

// lancer le t l chargement suivant


client . DownloadStringAsync ( new Uri ( " http ://
windowsteamblog . com / windows_phone / b /
windowsphone / rss . aspx " ) , " WP " ) ;

22
23

24
25
26
27
28
29
30
31

}
if (( string ) e . UserState == " WP " )
{
// ce sont les donn es venant du flux blog de l
' quipe windows phone
}

Ceci nous permettra dutiliser la mme mthode pour lvnement de fin de tlchargement. Nous avons maintenant besoin dinterprter les donnes du flux XML retourn.
tant donn que les flux RSS sont standards, il existe une bibliothque Silverlight qui
permet de travailler avec ce genre de flux. Cest la bibliothque System.ServiceModel.Syndicati
Encore une fois, cette assembly na pas t crite pour Windows Phone, mais elle est
quand mme utilisable avec nos applications Windows Phone. Pour lutiliser, nous
devons ajouter une rfrence celle-ci. Elle se trouve dans le rpertoire suivant :
C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client.
Comme pour System.Json.dll, vous aurez une boite de dialogue davertissement o
vous pouvez cliquer sur Oui (voir la figure 19.9).
Nous allons donc pouvoir charger lobjet de Syndication partir du rsultat, cet ob278

LA BIBLIOTHQUE DE SYNDICATION

Figure 19.9 Fentre davertissement


tem.ServiceModel.Syndication.dll

lors

de

la

rfrence

Sys-

jet sera du type SyndicationFeed - http://msdn.microsoft.com/fr-fr/library/


system.servicemodel.syndication.syndicationfeed(v=vs.95).aspx. Pour commencer, je me rajoute une variable prive :
1

private List < SyndicationFeed > listeFlux ;

que jinitialise dans le constructeur :


1

listeFlux = new List < SyndicationFeed >() ;

Noubliez pas de rajouter le using qui va bien :


1

using System . ServiceModel . Syndication ;

puis je consolide ma liste partir du retour de lappel web :


1
2
3
4
5
6
7
8

private void c l i e n t _ D o w n l o a d S t r i n g C o m p l e t e d ( object sender ,


DownloadStringCompletedEventArgs e)
{
if ( e . Error == null )
{
if (( string ) e . UserState == " OC " )
{
// ce sont les donn es venant du flux du blog
OpenClassrooms
AjouteFlux ( e . Result ) ;

9
10
11

12
13
14
15
16
17

// lancer le t l chargement suivant


client . DownloadStringAsync ( new Uri ( " http ://
windowsteamblog . com / windows_phone / b / windowsphone
/ rss . aspx " ) , " WP " ) ;

}
if (( string ) e . UserState == " WP " )
{
// ce sont les donn es venant du flux blog de l '
quipe windows phone
AjouteFlux ( e . Result ) ;
}

279

CHAPITRE 19. LE TRAITEMENT DES DONNES


18
19

20
21
22
23
24
25
26
27

private void AjouteFlux ( string flux )


{
StringReader stringReader = new StringReader ( flux ) ;
XmlReader xmlReader = XmlReader . Create ( stringReader ) ;
SyndicationFeed feed = SyndicationFeed . Load ( xmlReader ) ;
listeFlux . Add ( feed ) ;
}

Pour charger un flux de syndication, il suffit dutiliser la mthode Load de la classe


SyndicationFeed en lui passant un XmlReader, prsent dans lespace de nom System.Xml.
Lobjet StringReader permet de lire un flux et de lexploiter en tant que
conteur de caractres. De mme, lobjet XmlReader permet de traiter ce flux
sous la forme de donnes XML.
Et voil, nous avons cr une liste dobjet SyndicationFeed qui possde les lments du
flux RSS du blog OpenClassrooms ainsi que ceux du blog de lquipe Windows Phone.
Chaque objet SyndicationFeed contient une liste de billets, sous la forme dobjets SyndicationItem - http://msdn.microsoft.com/fr-fr/library/system.servicemodel.
syndication.syndicationitem(v=vs.95).aspx. Par exemple, la date de publication
est accessible via la proprit PublishDate dun SyndicationItem. Le titre du billet
est accessible via la proprit Title.Text. . .
Nous pourrons par exemple afficher le titre de chaque post, ainsi que sa date dans une
ListBox :
1
2
3
4
5
6
7
8
9
10

< ListBox x : Name = " LaListeBox " >


< ListBox . ItemTemplate >
< DataTemplate >
< StackPanel Orientation = " Horizontal " >
< TextBlock Text = " { Binding PublishDate } " / >
< TextBlock Text = " { Binding Title . Text } " / >
</ StackPanel >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >

Il nous faudra lier la proprit ItemsSource de la ListBox , par exemple une ObservableCollec
que nous construisons une fois le dernier flux reu, et qui sera trie par ordre de date
de publication de la plus rcente la plus ancienne :
1
2
3
4
5

if (( string ) e . UserState == " WP " )


{
// ce sont les donn es venant du flux blog de l ' quipe
windows phone
AjouteFlux ( e . Result ) ;
ObservableCollection < SyndicationItem > listeBillets = new
ObservableCollection < SyndicationItem >() ;

280

ASYNCHRONISME AVANC
6
7
8
9
10
11
12
13
14

foreach ( SyndicationFeed flux in listeFlux )


{
foreach ( SyndicationItem billet in flux . Items )
{
listeBillets . Add ( billet ) ;
}
}
LaListeBox . ItemsSource = listeBillets . OrderByDescending (
billet = > billet . PublishDate ) ;

Ce qui donnera le rsultat prsent dans la figure 19.10.

Figure 19.10 Affichage du flux RSS dans la ListBox


La prsentation laisse dsirer, mais cest fait exprs. Nous en restons l pour linstant,
mais ne vous inquitez pas, vous allez y revenir bientt.

Asynchronisme avanc
Bon, cest trs bien les mthodes asynchrones, mais cest une gymnastique un peu
complique. On sabonne un vnement de fin de tlchargement puis on dmarre
le tlchargement et quand le tlchargement est termin, on exploite le rsultat dans
une autre mthode perdant au passage le contexte de lappel. Alors oui. . . il y a des
281

CHAPITRE 19. LE TRAITEMENT DES DONNES


astuces, comme celle que nous venons de voir. . . mais je vous dis pas les nuds au
cerveau lorsquil y a des appels dans tous les sens !
Bonne nouvelle, avec Windows Phone 8 il est possible de se simplifier grandement
lasynchronisme. En fait, cest surtout le framework 4.5 quil faut remercier, mais peu
importe, si vous crez une application pour Windows Phone 8, vous pourrez bnficier
de 2 formidables nouveaux petits mot-cls : async et await.
Certaines API de Windows Phone 8 ont t rcrites pour tirer parti de ces nouveaux
mots cls, cest le cas par exemple de certaines oprations sur les fichiers qui peuvent
prendre du temps et que nous allons voir juste aprs. a aurait galement pu tre le
cas pour les classes daccs Internet comme WebClient et HttpWebRequest, mais
malheureusement celles-ci nont pas t rcrites. Heureusement, nous pouvons crire
un wrapper pour bnficier de ces lments avec la classe WebClient. Ceci va nous
permettre de nous simplifier grandement lasynchronisme.
Je ne vais pas rentrer dans le dtail des Task car il ne sagit pas dun cours
sur le C# et au fond ce nest pas vraiment ce qui nous intresse ici.
Ce qui va nous intresser cest la construction suivante, que nous avons vue au tout
dbut de ce chapitre :
1
2
3

public MainPage ()
{
I ni t i alizeComponent () ;

4
5
6
7

WebClient client = new WebClient () ;


client . D ow n lo ad St r in gC om p le te d +=
client_DownloadStringCompleted ;
client . DownloadStringAsync ( new Uri ( " http :// fr .
openclassrooms . com / uploads / fr / ftp / windows_phone /
script_nico . php " ) ) ;

9
10
11
12
13
14
15
16
17
18
19
20
21

private void c l i e n t _ D o w n l o a d S t r i n g C o m p l e t e d ( object sender ,


DownloadStringCompletedEventArgs e)
{
if ( e . Error == null )
{
string texte = e . Result ;
MessageBox . Show ( texte ) ;
}
else
{
MessageBox . Show ( " Impossible de r cup rer les donn es
sur internet : " + e . Error ) ;
}
}

282

ASYNCHRONISME AVANC
Commenons par utiliser la classe TaskCompletionSource - http://msdn.microsoft.
com/fr-fr/library/vstudio/dd449174.aspx dans une mthode dextension :
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public static class Extensions


{
public static Task < string > D ow nl oa d St ri n gT as kA s yn c ( this
WebClient webClient , Uri uri )
{
TaskCompletionSource < string > taskCompletionSource = new
TaskCompletionSource < string >() ;
DownloadStringCompletedEventHandler
d o wn l o ad C o mp l e te d H an d l er = null ;
d o w n l oa d C o mp l e te d H an d l er = (s , e ) = >
{
webClient . Do wn l oa dS tr i ng Co mp l et ed -=
d o wn l o ad C o mp l e te d H an d l er ;
if ( e . Error != null )
taskCompletionSource . TrySetException ( e . Error ) ;
else
taskCompletionSource . TrySetResult ( e . Result ) ;
};

15

webClient . Do wn l oa dS tr i ng Co mp l et ed +=
d o wn l o ad C o mp l e te d H an d l er ;
webClient . DownloadStringAsync ( uri ) ;

16
17
18
19
20
21

return taskCompletionSource . Task ;

Le principe est dencapsuler lappel DownloadStringAsync et de renvoyer un objet


de type Task<string>, string tant le type de ce que lon rcupre en rsultat de
lappel. Ainsi, nous allons pouvoir remplacer lappel du dbut par :
1
2
3
4
5

public MainPage ()
{
I ni t i al i zeComponent () ;
L a n c e T e l e c ha r g em e n tA s y nc () ;
}

6
7
8
9
10

11
12

private async void La n c eT e l ec h a rg e m en t A sy n c ()


{
WebClient client = new WebClient () ;
string texte = await client . Do w nl oa dS t ri ng T as kA sy n c ( new Uri
( " http :// fr . openclassrooms . com / uploads / fr / ftp /
windows_phone / script_nico . php " ) ) ;
MessageBox . Show ( texte ) ;
}

La mthode LanceTelechargementAsync doit possder le mot-cl async avant son


type de retour pour indiquer quelle est asynchrone et doit galement par conven283

CHAPITRE 19. LE TRAITEMENT DES DONNES


tion avoir son nom qui se termine par Async. Nous appelons la mthode dextension
DownloadStringTaskAsync et nous attendons son rsultat de manire asynchrone grce
au mot-cl await. Pas de mthode appele une fois le tlchargement termin. . . Juste
un mot-cl qui nous permet dattendre le rsultat de manire asynchrone.
Plutt simplifi comme construction, non ?
Allez, pour le plaisir, on se simplifie le tlchargement des flux RSS que lon a fait juste
au dessus ? Gardons toujours la mme classe dextension et crivons dsormais :
1
2
3

public MainPage ()
{
I ni t i alizeComponent () ;

4
5
6

L a n c e L e T e l e c h a r g e m e n t A s y n c () ;

7
8
9
10

private async void L a n c e L e T e l e c h a r g e m e n t A s y n c ()


{
listeFlux = new List < SyndicationFeed >() ;

11

client = new WebClient () ;


string rss = await client . D ow nl oa d St ri ng T as kA sy n c ( new Uri ( "
http :// www . simple - it . fr / blog / feed / " ) ) ;
AjouteFlux ( rss ) ;
rss = await client . Do wn l oa dS tr i ng Ta sk A sy nc ( new Uri ( " http ://
windowsteamblog . com / windows_phone / b / windowsphone / rss .
aspx " ) ) ;
AjouteFlux ( rss ) ;

12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27

ObservableCollection < SyndicationItem > listeBillets = new


ObservableCollection < SyndicationItem >() ;
foreach ( SyndicationFeed flux in listeFlux )
{
foreach ( SyndicationItem billet in flux . Items )
{
listeBillets . Add ( billet ) ;
}
}
LaListeBox . ItemsSource = listeBillets . OrderByDescending (
billet = > billet . PublishDate ) ;

Cest quand mme bien plus clair !


Il ny a plus de traitement derreur dans la construction prcdente. Vous
pourrez alors encadrer lappel dans un try/catch.

284

LE RPERTOIRE LOCAL

Le rpertoire local
Il nest pas toujours possible daccder internet pour rcuprer des infos ou les mettre
jour. Nous avons encore notre disposition un emplacement pour faire persister de
linformation : lintrieur du tlphone.
On appelle cet emplacement le rpertoire local (local folder en anglais ou anciennement
isolated storage) dans la mesure o nous navons pas accs directement au systme
de fichiers, mais plutt un emplacement mmoire isol dont nous pouvons ignorer
le fonctionnement. Tout ce quil faut savoir cest quil est possible dy stocker des
informations, comme du texte mais aussi des objets srialiss.
Il y a deux grandes faons dutiliser le rpertoire local. La plus simple est dutiliser le
dictionnaire ApplicationSettings. On peut y ranger des objets qui seront associs
une chane de caractres. Par exemple, en imaginant que lon cre une application
o lon demande le nom de notre utilisateur, il pourra tre judicieux dviter de le
redemander chaque ouverture de lapplication. . . Pour cela, nous pouvons le faire
persister dans le rpertoire local. On utilisera alors :
1

I s o l a t e d S t o r a ge Se tt i ng s . ApplicationSettings [ " prenom " ] = "


Nicolas " ;

Nb : pour utiliser la classe IsolatedStorageSettings, nous devons inclure :


1

using System . IO . IsolatedStorage ;

Jassocie ici la chane de caractres prenom la chane de caractres Nicolas .


Au prochain dmarrage de lapplication, on pourra vrifier si le prnom existe dj en
tentant daccder sa cl prenom . Sil y a quelque chose dassoci cette cl, on
pourra le rcuprer pour viter de demander re-saisir le prnom de lutilisateur :
1
2

if ( I s o l a t e d S t o ra ge S et ti ng s . ApplicationSettings . Contains ( "


prenom " ) )
prenom = ( string ) Is ol at e dS to r ag eS et t in gs .
A pp l icationSettings [ " prenom " ];

Nous pouvons mettre des objets complexes dans le rpertoire local, pas seulement des
chanes de caractres :
1
2
3
4
5

public class Utilisateur


{
public int Age { get ; set ; }
public string Prenom { get ; set ; }
}

6
7
8

Utilisateur nicolas = new Utilisateur { Age = 30 , Prenom = "


Nicolas " };
I s o l a t e d S t o r a ge Se tt i ng s . ApplicationSettings [ " utilisateur " ] =
nicolas ;

Et de la mme faon, on pourra rcuprer cette valeur trs facilement :


285

CHAPITRE 19. LE TRAITEMENT DES DONNES


1
2

if ( I s o l a t ed S to ra ge S et ti ng s . ApplicationSettings . Contains ( "


utilisateur " ) )
nicolas = ( Utilisateur ) I s ol at e dS to ra g eS et ti n gs .
A pplicationSettings [ " utilisateur " ];

Attention, le rpertoire local fonctionne trs bien avec lmulateur la seule


condition de ne pas fermer brutalement votre application, en arrtant le dbogueur par exemple, en fermant lmulateur ou en ayant une exception. Pour
que le rpertoire local persiste dune utilisation lautre, il faut que vous
conserviez lmulateur ouvert et que vous terminiez lapplication en cliquant
sur le bouton retour, jusqu ce quil ny ait plus rien dans la pile des pages
et que lapplication se termine. Sinon, vous devrez appeler la mthode de
sauvegarde explicite suivante :
I s o l a t e d S t or ag e Se tt i ng s . ApplicationSettings . Save () ;

Le stockage dobjets dans le rpertoire local est quand mme trs pratique. Vous vous
servirez trs souvent de ce fonctionnement simple et efficace. Parfois vous aurez peuttre besoin dun peu plus de contrle sur ce que vous voulez stocker. ce moment-l,
on peut se servir du rpertoire local comme dun flux classique, comme lorsque lon
souhaite enregistrer des donnes dans un fichier.
Regardons lenregistrement :
1

2
3
4
5
6
7
8

using ( I s o l a t ed S t o r a g e F i le S t r e a m stream = new


I s o l a t ed S t o r a g e F i le S t r e a m ( " sauvegarde . txt " , FileMode . Create ,
I so l atedStorageFile . G e t U s e r S t o r e F o r A p p l i c a t i o n () ) )
{
using ( StreamWriter writer = new StreamWriter ( stream ) )
{
writer . WriteLine ( 30 ) ;
writer . WriteLine ( " Nicolas " ) ;
}
}

Puis la lecture :
1

2
3
4
5
6
7
8

using ( I s o l a t ed S t o r a g e F i le S t r e a m stream = new


I s o l a t ed S t o r a g e F i le S t r e a m ( " sauvegarde . txt " , FileMode . Open ,
I so l a tedStorageFile . G e t U s e r S t o r e F o r A p p l i c a t i o n () ) )
{
using ( StreamReader reader = new StreamReader ( stream ) )
{
int age = Convert . ToInt32 ( reader . ReadLine () ) ;
string prenom = reader . ReadLine () ;
}
}

Cela ressemble beaucoup des oprations dcriture et de lecture dans un fichier classique. . . Enfin, si besoin, on pourra supprimer le fichier :
286

LE RPERTOIRE LOCAL
1
2
3

Iso l a te d St o ra geFile racine = IsolatedStorageFile .


G e t U s e r S t o r e F o r A p p l i c a t i o n () ;
if ( racine . FileExists ( " sauvegarde . txt " ) )
racine . DeleteFile ( " sauvegarde . txt " ) ;

Lobjet IsolatedStorageFile que lon rcupre avec la mthode GetUserStoreForApplication


permet de crer un fichier, un rpertoire, de le supprimer, etc. Bref, quasiment tout ce
que permet un systme de fichier classique.
noter que cette technique est tout fait approprie pour stocker des images par
exemple, pour viter davoir les re-tlcharger chaque fois.
Remarquez quau contraire dune application Silverlight sexcutant dans un
navigateur, le rpertoire local dans une application Windows Phone na pas
de limite de taille. Tant que le tlphone dispose encore de mmoire, celle-ci
est toute vous.
Enfin, avec Windows Phone 8 nous pouvons tirer parti des nouvelles API de stockages
asynchrones. Celles-ci nous permettent de ne pas bloquer le thread courant lorsque
nous avons besoin de lire et crire potentiellement de grosses donnes dans le rpertoire
local. Voici par exemple comment crire des donnes dans le rpertoire local de manire
asynchrone :
1
2
3

private async void SauvegardeAsync ()


{
IStorageFolder applicationFolder = ApplicationData . Current .
LocalFolder ;

4
5

6
7
8
9
10
11

IStorageFile storageFile = await applicationFolder .


CreateFileAsync ( " sauvegarde . txt " ,
C r e a t io n Co ll is i on Op ti o n . ReplaceExisting ) ;
using ( Stream stream = await storageFile .
O p e n S tr e am Fo rW r it eA sy n c () )
{
byte [] bytes = Encoding . UTF8 . GetBytes ( " 30 ; Nicolas " ) ;
await stream . WriteAsync ( bytes , 0 , bytes . Length ) ;
}

Et voici comment lire les donnes prcdemment sauvegardes :


1
2
3

private async void LectureAsync ()


{
IStorageFolder applicationFolder = ApplicationData . Current .
LocalFolder ;

4
5
6

IStorageFile storageFile = await applicationFolder .


GetFileAsync ( " sauvegarde . txt " ) ;
I Ra n d om A ccessStream accessStream = await storageFile .
OpenReadAsync () ;

287

CHAPITRE 19. LE TRAITEMENT DES DONNES


8
9
10
11
12
13
14
15
16
17

using ( Stream stream = accessStream . AsStreamForRead (( int )


accessStream . Size ) )
{
byte [] bytes = new byte [ stream . Length ];
await stream . ReadAsync ( bytes , 0 , bytes . Length ) ;
string chaine = Encoding . UTF8 . GetString ( bytes , 0 , bytes
. Length ) ;
string [] tableau = chaine . Split ( '; ') ;
int age = int . Parse ( tableau [ 0 ]) ;
string prenom = tableau [ 1 ];
}

Ici, jai choisi de stocker mes donnes sous la forme de texte spars par des points
virgules, mais libre vous de spcifier le format de fichier de votre choix. Nous remarquons lutilisation des mots-cls async et await, dignes tmoins de lasynchronisme de
ces mthodes.

En rsum
Le XAML/C# pour Windows Phone dispose de toute une gamme de solutions
pour utiliser des donnes depuis internet ou en local sur le tlphone.
Les classes HttpRequest et WebClient permettent de faire des requtes sur le
protocole HTTP.
On utilise la bibliothque open-source JSON.NET pour interprter les donnes
au format JSON.
Il est trs facile dexploiter des flux RSS grce la bibliothque de syndication.
Lasynchronisme est grandement facilit dans Windows Phone 8 grce aux motscls async et await.
Le rpertoire local correspond un emplacement sur le tlphone, ddi notre
application, o lon peut faire persister de linformation entre les divers lancements dune application.

288

Troisime partie

Une bibliothque de contrles

289

Chapitre

20

Panorama et Pivot
Difficult :
Nous avons vu dans la partie prcdente que nous pouvions naviguer entre les pages,
cest bien ! Mais sachez que nous pouvons galement naviguer entre les donnes. Cest
encore mieux. Cest l quinterviennent deux contrles trs utiles qui permettent de naviguer
naturellement entre des donnes : le contrle Panorama et le contrle Pivot. Le contrle
Panorama sert en gnral voir un petit bout dun plus gros cran, qui ne rentre pas dans
lcran du tlphone. Le principe est quon peut mettre beaucoup dinformations sur une
grosse page et la mcanique du contrle Panorama incite lutilisateur se dplacer avec
le doigt sur le reste du plus gros cran. Le contrle Pivot quant lui permet plutt de
voir la mme donne sur plusieurs pages. La navigation entre les pages se fait en faisant
glisser le doigt, comme si lon tournait une page. Par exemple pour une application mto,
la premire page permet dafficher la mto du jour, la page suivante permet dafficher la
mto de demain, etc.
Dcouvrons prsent ces deux contrles.

291

CHAPITRE 20. PANORAMA ET PIVOT

Panorama
Le panorama est donc un contrle qui sert voir un petit bout dun plus gros cran
dont la taille dpasse celle de lcran du tlphone. On lillustre souvent avec une image
de ce genre (voir la figure 20.1).

Figure 20.1 Reprsentation du contrle Panorama


Vous vous rappelez lintroduction du cours et le passage sur les hubs ? Ce contrle est
exactement le mme. Nous pouvons lintgrer dans nos applications et tirer parti de
son lgance et de ses fonctionnalits. Pour dcouvrir le panorama, le plus simple est de
crer un nouveau projet. Vous avez srement constat que Visual Studio nous proposait
de crer diffrents modles de projet, dont un projet sappelant Application Panorama Windows Phone et un autre sappelant Application Pivot Windows Phone .
Choisissons le projet Application Panorama Windows Phone , ainsi quindiqu la
figure 20.2.
Si nous dmarrons immdiatement lapplication, nous pouvons voir quelle contient
un panorama existant. Wahou. . . bon ok, passons sur la relative traduction des divers
lments.
Ce quil faut remarquer ici, cest quil est possible de faire glisser lcran en cours de
gauche droite, affichant trois lments en tout, et en boucle, sachant que le troisime
lment occupe plus despace quun cran (voir la figure 20.3).
Il faut galement remarquer que laffichage de chaque cran incite lutilisateur aller
voir ce quil y a droite. En effet, on peut voir que le titre nest pas complet. Pareil
292

PANORAMA

Figure 20.2 Cration dun projet Panorama

Figure 20.3 La panorama du projet exemple, compos de 3 crans

293

CHAPITRE 20. PANORAMA ET PIVOT


pour le carr jaune, on se doute quil doit y avoir quelque chose ct. . . Bref, tout est
fait pour donner envie daller voir ce quil y a plus loin. Cest le principe du panorama.
Voyons prsent le XAML qui a t gnr pour obtenir cet cran :
1
2
3
4

< phone : Panorama Title = " mon application " >


< phone : Panorama . Background >
< ImageBrush ImageSource = " / DemoPanorama ; component / Assets
/ PanoramaBackground . png " / >
</ phone : Panorama . Background >

5
6
7
8
9
10
11
12

<! - - l ment un de panorama - - >


< phone : PanoramaItem Header = " first item " >
<! - - Liste simple trait avec habillage du texte - - >
< phone : LongListSelector Margin = "0 ,0 , - 22 , 0 " ItemsSource =
" { Binding Items } " >
[...]
</ phone : LongListSelector >
</ phone : PanoramaItem >

13
14
15
16

17
18
19
20

<! - - l ment deux de panorama - - >


< phone : PanoramaItem >
<! - - Liste double trait avec espace r serv pour une
image et habillage du texte utilisant un en - t te
flottant qui d file avec le contenu - - >
< phone : LongListSelector Margin = "0 , - 38 , - 22 , 2 "
ItemsSource = " { Binding Items } " >
[...]
</ phone : LongListSelector >
</ phone : PanoramaItem >

21
22
23
24
25
26
27
28
29

<! - - l ment trois de panorama - - >


< phone : PanoramaItem Header = " third item " Orientation = "
Horizontal " >
<! - - Double largeur de panorama avec espaces r serv s
pour grandes images - - >
< Grid >
[...]
</ Grid >
</ phone : PanoramaItem >
</ phone : Panorama >

Ce quon peut constater dj cest quil est compos de trois parties qui sont toutes
les trois des PanoramaItem. Un PanoramaItem - http://msdn.microsoft.com/fr-fr/
library/microsoft.phone.controls.panoramaitem(v=vs.92).aspx correspond donc
une vue de la totalit du Panorama. La navigation se passe entre ces trois lments.
Nous pouvons dailleurs voir dans le designer le rendu du premier PanoramaItem. Vous
pouvez galement voir le second en allant vous positionner dans le XAML au niveau
du second PanoramaItem, et de mme pour le troisime. Plutt pas mal, le rendu est
assez fidle.
294

PANORAMA
Nous pouvons galement constater que le contrle Panorama - http://msdn.microsoft.
com/fr-fr/library/microsoft.phone.controls.panorama(v=vs.92).aspx est dfini dans un espace de noms diffrent de ceux que nous avons dj utiliss, on voit
notamment quil est prfix par phone qui correspond :
1

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


Microsoft . Phone "

Ce contrle se situe donc dans lespace de noms Microsoft.Phone.Controls et dans


lassembly Microsoft.Phone.
Pour les utilisateurs du SDK pour Windows Phone 7, le contrle panorama se
situe dans lassembly Microsoft.Phone.Controls et doit tre explicitement
rfrence.
Le panorama possde un titre qui est affich tout en haut du contrle, ici, en haut de la
page. On le remplit via la proprit Title. Vous pouvez dailleurs constater que ce titre
nest pas affich en entier et que cela nous incite encore aller voir plus droite sil ny
a pas autre chose. Ce titre est une proprit de contenu que nous pouvons remplacer
par nimporte quoi, comme avec le bouton. titre dexemple, remplacez la proprit
Title par :
1
2
3
4
5

< phone : Panorama >


< phone : Panorama . Title >
< Image Source = " / Assets / ApplicationIcon . png " Margin = " 150
60 0 - 30 " / >
</ phone : Panorama . Title >
...

Vous pouvez voir le rsultat la figure 20.4.

Figure 20.4 Le titre est un contrle de contenu


De la mme faon, vous pouvez mettre un fond dcran au panorama via la proprit BackGround. Notez que limage doit absolument avoir son action de gnration
Resource, sinon elle risque de ne pas apparatre immdiatement et dtre charge de
manire asynchrone.
Chaque lment du panorama a un titre, reprsent par la proprit Header. Dans le
deuxime, on peut remarquer que le titre commence par un s et quil dpasse du
295

CHAPITRE 20. PANORAMA ET PIVOT


premier lment. Tout ceci est fait automatiquement sans que lon ait faire quoi que
ce soit de supplmentaire.
Nous pouvons crer autant de PanoramaItem que nous le voulons et y mettre ce que
nous voulons. Ici, il a t mis des listes de type LongListSelector, et dans le troisime
lment une grille mais cela pourrait tre nimporte quoi dautre vu que le Panorama fait
office de conteneur. Soyez vigilant quant lutilisation du contrle Panorama. Il doit tre
utilis des emplacements judicieux afin de ne pas perturber lutilisateur. Bien souvent,
il est utilis comme page daccueil dune application. Vous vous doutez bien quon peut
faire beaucoup de choses avec ce panorama. Il est par exemple possible de sabonner
lvnement de changement de PanoramaItem, ou se positionner directement sur un
lment prcis du panorama. Illustrons ceci avec le XAML suivant :
1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

< phone : Panorama x : Name = " MonPanorama " Title = " Mes t ches " Loaded =
" Panorama_Loaded " SelectionChanged = "
P a n o r a ma _ S e l e c t i o nC h a n g e d " >
< phone : PanoramaItem Header = " Accueil " >
< StackPanel >
< TextBlock Text = " Blablabla " HorizontalAlignment = "
Center " / >
< Button Content = " Allez aujourd ' hui " Tap = "
Button_Tap " Margin = " 0 50 0 0 " / >
</ StackPanel >
</ phone : PanoramaItem >
< phone : PanoramaItem Header = " Aujourd ' hui " >
< ListBox >
< ListBoxItem > Tondre la pelouse </ ListBoxItem >
< ListBoxItem > Arroser les plantes </ ListBoxItem >
</ ListBox >
</ phone : PanoramaItem >
< phone : PanoramaItem Header = " Demain " >
< StackPanel >
< TextBlock Text = " Passer l ' aspirateur " Margin = " 30 50
0 60 " / >
< TextBlock Text = " Laver la voiture " Margin = " 30 50 0
60 " / >
</ StackPanel >
</ phone : PanoramaItem >
</ phone : Panorama >

Mon panorama contient trois lments, un accueil, des tches pour aujourdhui et des
tches pour demain. Je me suis abonn lvnement de chargement du panorama ainsi
qu lvnement de changement de slection. Ici, cela fonctionne un peu comme une
ListBox. Notons galement que lcran daccueil possde un bouton avec un vnement
de clic. Passons au code-behind prsent :
1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;

296

PANORAMA
}

6
7

private void Panorama_Loaded ( object sender , RoutedEventArgs


e)
{
if ( I so l at ed St o ra ge S et ti ng s . ApplicationSettings .
Contains ( " PageCourante " ) )
{
MonPanorama . DefaultItem = MonPanorama . Items [( int )
I so la t ed St or a ge Se tt i ng s . ApplicationSettings [ "
PageCourante " ]];
}
}

8
9
10
11
12

13
14
15

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
MonPanorama . DefaultItem = MonPanorama . Items [ 1 ];
}

16
17
18
19
20
21
22
23
24
25

private void P a n o r a m a _S e l e c t i o n C h an g e d ( object sender ,


S e l e c ti o n C h a n g e d Ev e n t A r g s e )
{
I s o l a te d St or a ge Se tt i ng s . ApplicationSettings [ "
PageCourante " ] = MonPanorama . SelectedIndex ;
}

La mthode Panorama_SelectionChanged est appele chaque changement de slection. Dans cette mthode, je stocke dans le rpertoire local lindex de la page en
cours, obtenu comme pour la ListBox avec la proprit SelectedIndex. Ce qui me
permet, au chargement du panorama, de me repositionner sur la dernire page visite
sil y en a une. Cela se fait grce la proprit DefaultItem que je renseigne avec le
PanoramaItem trouv lindice de la proprit Items, indice qui est celui stock dans le
rpertoire local. De la mme faon, je peux me positionner sur un PanoramaItem choisi
lorsque je clique sur le bouton. Mme si cest un peu plus rare, il est possible dutiliser
le binding avec le contrle Panorama. Reproduisons plus ou moins notre exemple prcdent en utilisant un contexte de donnes (je retire la page accueil et le bouton, ce
sera plus simple). Tout dabord le XAML :
1

2
3
4
5
6
7

< phone : Panorama x : Name = " MonPanorama " Title = " Mes t ches " Loaded =
" Panorama_Loaded " SelectionChanged = "
P a n o r a m a _ S e l e c t i on C h a n g e d " ItemsSource = " { Binding ListeEcrans
}">
< phone : Panorama . HeaderTemplate >
< DataTemplate >
< TextBlock Text = " { Binding Titre } " / >
</ DataTemplate >
</ phone : Panorama . HeaderTemplate >
< phone : Panorama . ItemTemplate >

297

CHAPITRE 20. PANORAMA ET PIVOT


< DataTemplate >
< ListBox ItemsSource = " { Binding ListeDesTaches } " >
< ListBox . ItemTemplate >
< DataTemplate >
< TextBlock Text = " { Binding } " Margin = " 0
20 0 0 " / >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
</ DataTemplate >
</ controls : Panorama . ItemTemplate >
</ controls : Panorama >

8
9
10
11
12
13
14
15
16
17
18

Ici, cest comme pour la ListBox. Le contrle Panorama possde aussi des modles,
que nous pouvons utiliser. Il y a le modle HeaderTemplate qui nous permet de dfinir
un titre et le modle ItemTemplate qui nous permet de grer le contenu. Le contrle
Panorama a sa proprit ItemsSource qui est lie la proprit ListeEcrans que nous
retrouvons dans le code-behind :
1
2
3

public partial class MainPage : PhoneApplicationPage ,


I N o t i fy Pr ope rty Ch ang ed
{
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

4
5
6
7
8
9
10
11
12

private void Notify Prope rtyCha nged ( String propertyName )


{
P r o p e r t y C h a n g e d E v e n t H a n d l e r handler = PropertyChanged ;
if ( null != handler )
{
handler ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
propertyName ) ) ;
}
}

13
14
15
16

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

17
18
19
20
21

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

22
23
24
25
26
27
28

298

private List < Ecran > _listeEcrans ;


public List < Ecran > ListeEcrans
{
get { return _listeEcrans ; }
set { Noti fyPro pertyC hange d ( ref _listeEcrans , value ) ; }
}

PANORAMA
29

public MainPage ()
{
I ni t i alizeComponent () ;

30
31
32
33

ListeEcrans = new List < Ecran >


{
new Ecran
{
Titre = " Aujourd ' hui " ,
ListeDesTaches = new List < string > { " Tondre
la pelouse " , " Arroser les plantes " }
},
new Ecran
{
Titre = " Demain " ,
ListeDesTaches = new List < string > { " Passer
l ' aspirateur " , " Laver la voiture " }
}
};

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

49

DataContext = this ;

50

private void Panorama_Loaded ( object sender , RoutedEventArgs


e)
{
if ( I so l at ed St o ra ge S et ti ng s . ApplicationSettings .
Contains ( " PageCourante " ) )
{
MonPanorama . DefaultItem = MonPanorama . Items [( int )
I so la t ed St or a ge Se tt i ng s . ApplicationSettings [ "
PageCourante " ]];
}
}

51
52
53
54
55

56
57
58
59
60
61
62
63

private void P a n o r a m a _S e l e c t i o n C h an g e d ( object sender ,


S e l e c ti o n C h a n g e d Ev e n t A r g s e )
{
I s o l a te d St or a ge Se tt i ng s . ApplicationSettings [ "
PageCourante " ] = MonPanorama . SelectedIndex ;
}

Avec la classe Ecran suivante :


1
2
3
4
5

public class Ecran


{
public string Titre { get ; set ; }
public List < string > ListeDesTaches { get ; set ; }
}

299

CHAPITRE 20. PANORAMA ET PIVOT


Et le tour est jou. Vous navez plus qu dmarrer lapplication pour obtenir ce rsultat
(voir la figure 20.5).

Figure 20.5 Contrle Panorama et Binding

Pivot
Passons maintenant lautre contrle trs pratique, le Pivot, qui est un peu le petit
frre du panorama. Il permet plutt de voir la mme donne sur plusieurs pages. La
navigation entre les pages se fait en faisant glisser le doigt, comme si lon tournait une
page. On pourrait le comparer un contrle de gestion donglets. On passe longlet
suivant en faisant glisser son doigt. . .
Voyons prsent comme fonctionne le contrle Pivot - http://msdn.microsoft.
com/fr-fr/library/microsoft.phone.controls.pivot(v=vs.92).aspx. Pour cela,
crez le deuxime type de projet que nous avons vu, savoir Application Pivot Windows Phone et dmarrons lapplication exemple (voir la figure 20.6). On constate
quon peut galement naviguer en faisant glisser la page sur la droite ou sur la gauche
avec le doigt (ou la souris).
Ici, visuellement, il y a seulement le titre des pages qui nous renseigne sur la prsence
dun autre lment. Voyons prsent le code XAML :
1
2

< phone : Pivot Title = " MON APPLICATION " >


<! - - l ment un de tableau crois dynamique - - >

300

PIVOT

Figure 20.6 Rendu du projet Pivot exemple


3
4
5
6
7
8

< phone : PivotItem Header = " first " >


<! - - Liste double trait avec habillage du texte - - >
< phone : LongListSelector Margin = "0 ,0 , - 12 , 0 " ItemsSource =
" { Binding Items } " >
[... code supprim pour plus de clart ...]
</ phone : LongListSelector >
</ phone : PivotItem >

9
10
11
12
13
14
15
16
17

<! - - l ment deux de tableau crois dynamique - - >


< phone : PivotItem Header = " second " >
<! - - Liste double trait , aucun habillage du texte - - >
< phone : LongListSelector Margin = "0 ,0 , - 12 , 0 " ItemsSource =
" { Binding Items } " >
[... code supprim pour plus de clart ...]
</ phone : LongListSelector >
</ phone : PivotItem >
</ phone : Pivot >

Ici, le principe est le mme que pour le Panorama. Le contrle Pivot est compos de
deux PivotItem - http://msdn.microsoft.com/fr-fr/library/microsoft.phone.
controls.pivotitem(v=vs.92).aspx, chacun faisant office de container. Dedans il
y a un LongListSelector mais tout autre contrle y trouve sa place. Encore une
fois, cest la proprit Header qui va permettre de donner un titre la page. Vous
pouvez galement voir dans le designer les diffrents rendus des PivotItem en vous
301

CHAPITRE 20. PANORAMA ET PIVOT


positionnant dans le XAML leurs niveaux. Tout comme pour le panorama, vous
pouvez allgrement modifier les diffrentes proprits, Title, Background. . . afin de
personnaliser ce contrle. De mme, il possde des vnements bien pratiques pour tre
notifi dun changement de vue et galement de quoi se positionner sur celle que lon
veut. Il est galement possible dutiliser le binding avec ce contrle, et cest dailleurs ce
que vous aurez tendance souvent faire. Reprenons lexemple de la liste des tches qui
est particulirement adapt au contrle Pivot et amliorons cet exemple. Voici dans
un premier temps le XAML :
1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< phone : Pivot Title = " Mes t ches " SelectedIndex = " { Binding
Index , Mode = TwoWay } " Loaded = " Pivot_Loaded " ItemsSource = "
{ Binding ListeEcrans } " >
< phone : Pivot . HeaderTemplate >
< DataTemplate >
< TextBlock Text = " { Binding Titre } " / >
</ DataTemplate >
</ phone : Pivot . HeaderTemplate >
< phone : Pivot . ItemTemplate >
< DataTemplate >
< ListBox ItemsSource = " { Binding ListeDesTaches } "
>
< ListBox . ItemTemplate >
< DataTemplate >
< TextBlock Text = " { Binding } " Margin =
" 0 20 0 0 " / >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
</ DataTemplate >
</ phone : Pivot . ItemTemplate >
</ phone : Pivot >
</ Grid >

Cela ressemble beaucoup ce que nous avons fait pour le panorama. Une des premires
diffrences vient de la proprit SelectedIndex du Pivot, qui fonctionne comme pour
la ListBox. Je lai lie une proprit Index en mode TwoWay afin que la mise
jour de la proprit depuis le code-behind affecte le contrle mais quinversement, un
changement de valeur depuis le contrle mette jour la proprit. Du coup, je nai plus
besoin de lvnement de changement de slection qui existe galement sur le contrle
Pivot. Le reste du XAML est semblable au Panorama. Passons au code-behind :
1
2
3

public partial class MainPage : PhoneApplicationPage ,


I N o t i fy Pr ope rty Ch ang ed
{
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

4
5
6
7

302

private void Notify Prope rtyCha nged ( String propertyName )


{
P r o p e r t y C h a n g e d E v e n t H a n d l e r handler = PropertyChanged ;

PIVOT
8
9
10
11
12

if ( null != handler )
{
handler ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
propertyName ) ) ;
}

13
14
15
16

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

17
18
19
20
21

variable = valeur ;
N o t i f yPro pertyC hange d ( nomPropriete ) ;
return true ;

22
23
24
25
26
27
28

private List < Ecran > _listeEcrans ;


public List < Ecran > ListeEcrans
{
get { return _listeEcrans ; }
set { Not ifyPro perty Change d ( ref _listeEcrans , value ) ; }
}

29
30
31
32
33
34
35
36
37
38
39

private int _index ;


public int Index
{
get { return _index ; }
set
{
I so la te d St or a ge Se tt i ng s . ApplicationSettings [ "
PageCourante " ] = value ;
N oti fyPro pertyC hange d ( ref _index , value ) ;
}
}

40
41
42
43

public MainPage ()
{
I ni t i alizeComponent () ;

44
45
46
47
48
49
50
51
52
53

ListeEcrans = new List < Ecran >


{
new Ecran
{
Titre = " Aujourd ' hui " ,
ListeDesTaches = new List < string > { " Tondre la
pelouse " , " Arroser les plantes " }
},
new Ecran
{

303

CHAPITRE 20. PANORAMA ET PIVOT


54
55
56

};

57

Titre = " Demain " ,


ListeDesTaches = new List < string > { " Passer l '
aspirateur " , " Laver la voiture " }

58
59

60

DataContext = this ;

61
62
63
64
65
66
67
68
69

private void Pivot_Loaded ( object sender , RoutedEventArgs e )


{
if ( I s ol at ed S to ra ge S et ti ng s . ApplicationSettings .
Contains ( " PageCourante " ) )
{
Index = ( int ) I so l at ed St o ra ge S et ti ng s .
ApplicationSettings [ " PageCourante " ];
}
}

Nous voyons que jai rajout une proprit Index, et qu lintrieur de son modificateur, jenregistre dans le rpertoire local la valeur de la slection. Ensuite, au chargement du pivot et lorsque la valeur existe, je positionne la proprit Index la valeur
enregistre lors dune visite prcdente. Et voil, vous pouvez admirer le rendu la
figure 20.7.
Vous pourriez trouver que les deux contrles se ressemblent, et ce nest pas compltement faux. Je vous rappelle juste que le contrle Panorama permet dafficher plusieurs
donnes sur une seule grosse page alors que le contrle Pivot est utilis pour prsenter
la mme donne sur plusieurs pages. Vous apprhenderez au fur et mesure la subtile
diffrence entre ces deux contrles.

En rsum
Le Panorama et le Pivot permettent de naviguer lintrieur des donnes.
Le Panorama sert voir un petit bout dun plus gros cran dont la taille dpasse
celle de lcran du tlphone.
Le Pivot permet plutt de voir la mme donne sur plusieurs pages.
Chaque changement de vue se fait grce un glissement de doigt.
Le contrle Pivot est particulirement bien adapt au binding.

304

PIVOT

Figure 20.7 Le binding du contrle Pivot

305

CHAPITRE 20. PANORAMA ET PIVOT

306

Chapitre

21

Navigateur web
Difficult :
Malgr tous les superbes contrles dont dispose Windows Phone, vous allez parfois avoir
besoin dafficher du HTML ou bien directement une page web. Cest ainsi que le SDK
de Windows Phone dispose dun contrle bien pratique : le WebBrowser - http://msdn.
microsoft.com/fr-fr/library/microsoft.phone.controls.webbrowser(v=vs.92)
.aspx. Vous pouvez le voir comme un mini Internet Explorer que lon peut mettre o on
veut dans une page et qui na pas toute la gestion des barres dadresses, des favoris, . . .
Si nos tlphones possdent dj un navigateur web, en loccurrence Internet Explorer,
quoi pourrait bien servir un tel contrle ?
Les scnarios sont divers, cela peut aller de laffichage dun billet issu dun flux RSS une
authentification via un formulaire HTML, sur un rseau social par exemple. Ou pourquoi pas
un jeu en HTML5 ? De plus, il est galement possible de communiquer entre le Javascript
dune page web et notre page XAML. Regardons tout cela de plus prs.

307

CHAPITRE 21. NAVIGATEUR WEB

Naviguer sur une page web


Tout dabord, il nous faut ce fameux contrle. Vous pouvez le mettre dans votre XAML
via la boite outils, comme indiqu la figure 21.1.

Figure 21.1 Le contrle WebBrowser dans la boite outils


Ou comme nous en avons dsormais lhabitude, directement dans le XAML :
1

< phone : WebBrowser x : Name = " MonWebBrowser " / >

Il est ensuite possible de naviguer sur une page web grce la mthode Navigate()
qui prend une URI en paramtre :
1
2
3
4

public MainPage ()
{
I ni t i alizeComponent () ;
MonWebBrowser . Navigate ( new Uri ( " http :// fr . openclassrooms .
com / informatique / cours / apprenez -a - developper - en - c " ,
UriKind . Absolute ) ) ;
}

Ici, jeffectue la navigation dans le constructeur de la page. La page internet saffiche


donc comme on peut le voir sur la figure 21.2.
(si bien sr vous tes connects internet !)

Evnements de navigation
Le contrle WebBrowser possde galement des vnements qui permettent de savoir
par exemple quand une page est charge, il sagit de lvnement Navigated. Ce qui est
pratique si lon souhaite afficher un message dattente, ou si on veut nafficher vraiment
le WebControl quune fois la page compltement charge. Il y a galement un autre
vnement intressant qui permet de savoir si la navigation a chou, par exemple si
lutilisateur ne capte plus internet. Il sagit de lvnement NavigationFailed. Cet
vnement nous fournit notamment une exception qui peut nous donner plus dinformations sur lerreur.
Il est toujours intressant dindiquer lutilisateur si la navigation a chou. Il est
308

NAVIGATION INTERNE

Figure 21.2 La page web saffiche dans le contrle

galement appropri de lui proposer un bouton lui permettant de retenter sa navigation,


si jamais il se trouve nouveau dans une zone de couverture.

Navigation interne
Mais nous pouvons galement crer notre propre HTML et lafficher grce la mthode
NavigateToString :
1
2
3
4
5
6
7
8
9
10

public MainPage ()
{
I ni t i al i zeComponent () ;
MonWebBrowser . NavigateToString (
@ " < html >
< body >
<h3 > Bonjour HTML ! </ h3 >
</ body >
</ html > " ) ;
}

Qui donnera la figure 21.3.


309

CHAPITRE 21. NAVIGATEUR WEB

Figure 21.3 Affichage direct de HTML


Le caractre @ est utilis ici pour pouvoir avoir une chane de caractres
dfinie sur plusieurs lignes. Vous pouvez tout faire crire tout en ligne ou
faire des concatnations avec le caractre \n.
Vous avouerez que ce nest pas trs pratique de devoir saisir le HTML dans le code.
Cela serait plus pratique de pouvoir intgrer un fichier HTML notre application et
de naviguer dessus. Pour ce faire, il va falloir dans un premier temps intgrer le fichier
dans le rpertoire local. Commencez dj par ajouter un nouveau fichier de type texte
au projet que vous nommez hello.html et qui possde le code suivant :
1
2
3
4
5

< html >


< body >
<h3 > Bonjour HTML local ! </ h3 >
</ body >
</ html >

Ensuite, dans les proprits du fichier, vrifiez que laction de gnration est contenu .
Puis, on peut utiliser le code suivant pour ajouter un fichier HTML dans le rpertoire
local :
1
2
3
4

public MainPage ()
{
I ni t i alizeComponent () ;
I n t e g r e H t m l D a n s R e p e r t o i r e L o c a l S i N o n P r e s e n t ( " hello . html " ) ;

310

COMMUNIQUER ENTRE XAML ET HTML


5
6

MonWebBrowser . Navigate ( new Uri ( " hello . html " , UriKind .


Relative ) ) ;

7
8
9
10

private void I n t e g r e H t m l D a n s R e p e r t o i r e L o c a l S i N o n P r e s e n t ( string


nomFichier )
{
I so l a te d StorageFile systemeDeFichier = IsolatedStorageFile .
G e t U s e r S t o r e F o r A p p l i c a t i o n () ;

11

if (! systemeDeFichier . FileExists ( nomFichier ) )


{
// lecture du fichier depuis les ressources
St re amResourceInfo sr = Application . GetResourceStream (
new Uri ( nomFichier , UriKind . Relative ) ) ;

12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30

using ( StreamReader reader = new StreamReader ( sr . Stream


))
{
string html = reader . ReadToEnd () ;
using ( I s o l a te d S t o r a g e F i le S t r e a m stream = new
I s o l a te d S t o r a g e F il e S t r e a m ( nomFichier , FileMode .
Create , IsolatedStorageFile .
G e t U s e r S t o r e F o r A p p l i c a t i o n () ) )
{
using ( StreamWriter writer = new StreamWriter (
stream ) )
{
writer . Write ( html ) ;
writer . Close () ;
}
}
}

Le principe est de vrifier la prsence du fichier. Sil nexiste pas alors on le lit depuis
les ressources comme on la dj vu, puis on crit le contenu dans le rpertoire local. Il
ne restera plus qu naviguer sur ce fichier cr grce la mthode Navigate comme
on peut le voir plus haut.

Communiquer entre XAML et HTML


Le WebBrowser a aussi la capacit de faire communiquer la page web et la page
XAML. Plus particulirement, il est possible dinvoquer une mthode Javascript depuis notre page XAML et inversement, la page web peut dclencher un vnement
dans notre application. Pour que ceci soit possible, vous devez positionner la proprit IsScriptEnabled true dans votre WebBrowser et vous abonner lvnement
311

CHAPITRE 21. NAVIGATEUR WEB


ScriptNotify :
1

< phone : WebBrowser x : Name = " MonWebBrowser " IsScriptEnabled = " True "
ScriptNotify = " M o n W e b B r o w s e r _ S c r i p t N o t i f y " / >

Pour que cela soit plus simple, je vais illustrer le fonctionnement avec du HTML et du
Javascript que jembarquerai dans mon application avec la mthode montre prcdemment. Mais il est bien sr possible de faire la mme chose avec une page sur internet.
Lvnement ScriptNotify est lev lorsque la page web invoque la mthode Javascript
window.external.notify(). Cette mthode accepte un paramtre sous la forme dune
chane de caractres qui pourra tre rcupre dans lvnement ScriptNotify. Pour
lillustrer, modifions notre page hello.html pour avoir :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<! DOCTYPE html PUBLIC " -// W3C // DTD XHTML 1 . 0 Transitional // EN "
" http :// www . w3 . org / TR / xhtml1 / DTD / xhtml1 - transitional . dtd " >
< html xmlns = " http :// www . w3 . org / 1999 / xhtml " >
< head >
< title > Demo javascript </ title >
< script type = " text / javascript " >
function EnvoyerMessage ()
{
window . external . notify ( " Bonjour , je suis " + prenom .
value ) ;
resultat . innerHTML = " Message bien envoy " ;
}
</ script >
</ head >
< body >
<h3 > Communication entre page web et XAML </ h3 >
<p > Saisissez votre pr nom : </p >
< input type = " text " id = " prenom " / >
< input type = " button " value = " Dire bonjour " onclick = "
EnvoyerMessage () ; " / >
< div id = " resultat " / >
</ body >
</ html >

Cette page possde une zone de texte pour saisir un prnom et un bouton qui invoque
la mthode Javascript EnvoyerMessage(). Cette mthode rcupre la valeur du champ
saisi, la concatne la chane Bonjour, je suis et lenvoie notre application Windows Phone via la mthode window.external.notify. Enfin, elle affiche un message
sur la page web pour indiquer que le message a bien t envoy. Ct code-behind,
nous avons juste naviguer sur notre page et rcuprer la valeur envoye :
1
2
3
4
5
6

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;
I n t e g r e H t m l D a n s R e p e r t o i r e L o c a l S i N o n P r e s e n t ( " hello . html "
, true ) ;

312

COMMUNIQUER ENTRE XAML ET HTML


7

MonWebBrowser . Navigate ( new Uri ( " hello . html " , UriKind .


Relative ) ) ;

private void I n t e g r e H t m l D a n s R e p e r t o i r e L o c a l S i N o n P r e s e n t (
string nomFichier , bool force )
{
I so l a tedStorageFile systemeDeFichier =
I solatedStorageFile . G e t U s e r S t o r e F o r A p p l i c a t i o n () ;

10
11
12
13

if (! systemeDeFichier . FileExists ( nomFichier ) || force )


{
// lecture du fichier depuis les ressources
StreamResourceInfo sr = Application .
GetResourceStream ( new Uri ( nomFichier , UriKind .
Relative ) ) ;

14
15
16
17

18
19
20
21
22

23
24
25
26
27
28
29
30
31

32

using ( StreamReader reader = new StreamReader ( sr .


Stream ) )
{
string html = reader . ReadToEnd () ;
using ( I s o l a te d S t o r a g e F il e S t r e a m stream = new
I s o l a t ed S t o r a g e F i l eS t r e a m ( nomFichier ,
FileMode . Create , IsolatedStorageFile .
G e t U s e r S t o r e F o r A p p l i c a t i o n () ) )
{
using ( StreamWriter writer = new
StreamWriter ( stream ) )
{
writer . Write ( html ) ;
writer . Close () ;
}
}
}

33
34
35
36
37
38

private void M o n W e b B r o w s e r _ S c r i p t N o t i f y ( object sender ,


NotifyEventArgs e )
{
MessageBox . Show ( e . Value ) ;
}

Notez que jai rajout un paramtre la mthode permettant de stocker la page HTML
dans le rpertoire local, qui force lenregistrement afin dtre sr davoir toujours la
dernire version. Rappelez-vous, nous recevons la valeur envoye par la page web grce
lvnement ScriptNotify. Lorsque cette chane est reue, on laffiche simplement
avec une boite de message. Dmarrons lapplication, la page web saffiche, comme vous
pouvez le voir la figure 21.4.
313

CHAPITRE 21. NAVIGATEUR WEB

Figure 21.4 La page web avant envoi du message


Notez que nous sommes un peu obligs de zoomer pour pouvoir voir le contenu de la
page web et cliquer sur le bouton. Pour rappel, le zoom seffectue avec deux doigts, en
posant les doigts sur lcran et en les tirant vers lextrieur. Ce mouvement sappelle
le Pinch-to-zoom. Il est galement possible de double-cliquer dans lmulateur afin de
produire le mme effet. Nous verrons comment corriger ce problme plus loin. Saisissez
une valeur et cliquez sur le bouton. Le message est bien envoy par la page HTML et
est bien reu par notre application, comme en tmoigne la figure 21.5.
Mais alors, cette page HTML illisible, on ne peut pas un peu lamliorer ? Il y a plusieurs
solutions pour ce faire. La premire est dutiliser un tag meta que le navigateur va
interprter. Cette balise se met lintrieur de la balise <head> :
1

< meta name = " viewport " content = " width = device - width , user scalable = no " / >

Elle permet de dfinir la taille de la fentre. On peut y mettre une valeur numrique
allant de 320 10000 ou lajuster directement la taille du tlphone avec la valeur
device-width. Remarquez, que la proprit user-scalable empchera lutilisateur de
pouvoir zoomer dans la page. Voici le rendu la figure 21.6.
Ce qui est beaucoup plus lisible ! Lautre solution est dutiliser une proprit CSS pour
ajuster la taille du texte :
1

< h3 style = " -ms - text - size - adjust : 300 % " > Communication entre page
web et XAML </ h3 >

314

COMMUNIQUER ENTRE XAML ET HTML

Figure 21.5 Lapplication Windows Phone reoit un message de la page web

Figure 21.6 Le zoom est adapt la largeur de la page


315

CHAPITRE 21. NAVIGATEUR WEB


Ici, jaugmente la taille de cette balise de 300%. Remarquez que cette balise est incompatible avec la balise viewport.
Il est possible de faire communiquer nos deux lments galement dans lautre sens.
Ainsi, nous pouvons invoquer une mthode Javascript prsente sur la page web depuis
le code C#. Modifions notre page pour quelle possde une mthode Javascript qui
accepte deux paramtres :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<! DOCTYPE html PUBLIC " -// W3C // DTD XHTML 1 . 0 Transitional // EN "
" http :// www . w3 . org / TR / xhtml1 / DTD / xhtml1 - transitional . dtd " >
< html xmlns = " http :// www . w3 . org / 1999 / xhtml " >
< head >
< meta name = " viewport " content = " width = device - width , user scalable = no " / >
< title > Demo javascript </ title >
< script type = " text / javascript " >
function ReceptionMessage ( texte , heure )
{
resultat . innerHTML = texte + " . Il est " + heure ;
return " OK " ;
}
</ script >
</ head >
< body >
<h3 > En attente d ' un envoi ... </ h3 >
< div id = " resultat " / >
</ body >
</ html >

Rajoutons ensuite dans notre XAML un bouton qui va permettre denvoyer des informations la page web :
1
2
3
4
5
6
7
8

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< phone : WebBrowser x : Name = " MonWebBrowser " IsScriptEnabled = "
True " ScriptNotify = " M o n W e b B r o w s e r _ S c r i p t N o t i f y " / >
< Button Content = " Envoyer l ' heure " Tap = " Button_Tap " Grid . Row
="1" />
</ Grid >

Et dans le code-behind nous aurons :


1
2
3

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
string retour = ( string ) MonWebBrowser . InvokeScript ( "
ReceptionMessage " , " Bonjour depuis Windows Phone " ,
DateTime . Now . ToShortTimeString () ) ;
}

316

COMMUNIQUER ENTRE XAML ET HTML


On utilise la mthode InvokeScript de lobjet WebBrowser en lui passant en paramtre
le nom de la mthode Javascript appeler. Puis nous pouvons passer ensuite autant
de paramtres que nous le souhaitons, sous la forme dune chane de caractres. Il faut
bien sr quil y ait autant de paramtres que peut accepter la mthode Javascript. Ce
qui donne la figure 21.7.

Figure 21.7 Rception par la page HTML du message envoy par lapplication
Windows Phone
Notez que le Javascript peut renvoyer une valeur au code C#. Ici je renvoie la chane
OK, que je stocke dans la variable retour pour une ventuelle interprtation. Remarquez quil est galement possible de passer des objets complexes grce au JSON. Le
principe consiste envoyer une version srialise dun objet la page web et la page
web dsrialise cet objet pour pouvoir lutiliser. Utilisons donc la bibliothque opensource JSON.NET que nous avions prcdemment utilise afin de srialiser un objet.
Rfrencez lassembly Newtonsoft.Json.dll et crez une classe avec des proprits :
1
2
3
4
5

public class Informations


{
public string Message { get ; set ; }
public DateTime Date { get ; set ; }
}

Puis, srialisez un objet pour lenvoyer notre mthode Javascript :


1

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )

317

CHAPITRE 21. NAVIGATEUR WEB


2

3
4
5

Informations informations = new Informations { Message = "


Bonjour depuis Windows Phone " , Date = DateTime . Now };
string objetSerialise = JsonConvert . SerializeObject (
informations ) ;
Retour retour = JsonConvert . DeserializeObject < Retour >((
string ) MonWebBrowser . InvokeScript ( " ReceptionMessage " ,
objetSerialise ) ) ;

De la mme faon, on pourra dsrialiser le retour de la mthode, dans lobjet Retour


suivant :
1
2
3
4
5

public class Retour


{
public string Resultat { get ; set ; }
public bool Succes { get ; set ; }
}

Il ne reste plus qu modifier le Javascript de la page :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

<! DOCTYPE html PUBLIC " -// W3C // DTD XHTML 1 . 0 Transitional // EN "
" http :// www . w3 . org / TR / xhtml1 / DTD / xhtml1 - transitional . dtd " >
< html xmlns = " http :// www . w3 . org / 1999 / xhtml " >
< head >
< meta name = " viewport " content = " width = device - width , user scalable = no " / >
< title > Demo javascript </ title >
< script type = " text / javascript " >
function ReceptionMessage ( objet )
{
var infos = JSON . parse ( objet ) ;
var d = new Date ( infos . Date ) ;
resultat . innerHTML = infos . Message + " . Il est " + d .
getHours () + 'h ' + d . getMinutes () ;
var retour = {
Resultat : " OK " ,
Succes : true
}
return JSON . stringify ( retour ) ;
}
</ script >
</ head >
< body >
<h3 > En attente d ' un envoi ... </ h3 >
< div id = " resultat " / >
</ body >
</ html >

Pour dsrialiser du JSON cot javascript, on utilise la mthode JSON.parse et pour


srialiser, on utilisera JSON.stringify. Et voil. Plutt puissant nest-ce pas ?
318

COMMUNIQUER ENTRE XAML ET HTML

En rsum
Le contrle WebBrowser nous permet facilement dafficher du HTML dans nos
applications Windows Phone.
Il est possible dinteragir entre lapplication et la page web grce du Javascript.
Grce au JSON, nous pourrons passer des paramtres complexes entre lapplication et la page web.

319

CHAPITRE 21. NAVIGATEUR WEB

320

Chapitre

22

TP : Cration dun lecteur de flux RSS


simple
Difficult :
Oula, mais a fait longtemps quon a pas un peu test nos connaissances dans un contexte
bien concret. Il est temps dy remdier avec ce TP o nous allons mettre en pratique
les derniers lments que nous avons tudis. Avec la cl, un petit dbut dapplication
sympathique pour lire nos flux RSS. Allez, cest parti, vous de travailler.

321

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE

Instructions pour raliser le TP


Vous lavez compris, nous allons raliser une petite application qui va nous permettre
de lire nos flux RSS prfrs. Voici ce que devra faire lapplication :
Lapplication possdera une page de menu qui permettra soit daller voir les flux
dj enregistrs dans lapplication, soit daller en ajouter de nouveaux.
La page de saisie de nouveau flux offrira un moyen de saisir et de faire persister
des URL de flux RSS.
La page de consultation possdera une ListBox prsentant la liste de tous les
titres, image et description des flux.
Lors du clic sur un lment, elle renverra sur une nouvelle page contenant un
contrle Pivot, branch encore une fois sur la liste de tous les flux et automatiquement positionn sur le flux slectionn dans la ListBox. Ce qui permettra de
naviguer de flux en flux grce un glissement de doigt. Ce pivot prsentera le
titre du blog ainsi quune ListBox contenant la liste des titres des billets classs
par ordre dcroissant de dernire mise jour.
La slection dun billet de cette ListBox renverra sur une page contenant un
WebBrowser et affichant ce fameux billet
Voil pour lnonc de ce TP. Il va nous permettre de vrifier que nous avons bien
compris comment accder des donnes sur internet, comment utiliser la bibliothque
de syndication, comment utiliser la ListBox et le Pivot avec le binding. Vous devrez
galement utiliser le rpertoire local - bien sr, nous nallons pas re-saisir nos flux RSS
chaque lancement dapplication -, la navigation et le contrle WebBrowser. Bref, que
des bonnes choses !
Vous vous sentez prt pour relever ce dfi ? Alors, nhsitez pas vous lancer. Petit
bonus ? Utiliser une solution pour marquer diffremment les billets qui ont t lus des
billets qui ne lont pas encore t. . . Vous pouvez bien sr si vous le souhaitez respecter
le patron de conception MVVM, mais ceci nest pas une obligation.
Si cela vous effraie un peu (et a peut !), essayons de dcortiquer un peu les diffrents
lments.
La page de menu ne devrait pas poser de problmes, il suffit dutiliser le service
de navigation.
La page de saisie dURL de flux RSS ne devrait pas non plus poser de problmes.
Pensez juste faire persister toute modification dans le rpertoire local.
Passons la page qui affiche la liste des flux RSS dans une ListBox. Le point
critique consiste charger les diffrents flux RSS, mais a, vous devriez savoir le faire car nous lavons vu dans la partie prcdente. Noubliez pas que
la classe WebClient ne peut tlcharger quun lment la fois, il va donc falloir attendre que le tlchargement prcdent soit termin. Ensuite, tant donn
quon doit afficher le titre, limage et la description, il est tout fait opportun de crer une classe ddie qui ne contiendra que les lments que nous
souhaitons afficher. Le titre est disponible dans la proprit Title.Text dun
SyndicationFeed, limage via la proprit ImageUrl et la description via la proprit Description.Text. Chaque flux possdera une liste de billets qui contien322

CORRECTION
dront la date de dernire publication (proprit LastUpdatedTime.DateTime),
le titre (proprit Title.Text) et lURL du billet (premier lment de la liste
Links, proprit Uri). Ensuite tout est une histoire de binding, ce qui nest pas
forcment le plus facile mais cest un point trs important o vous devez vous
entraner.
Enfin, la page avec le WebBrowser ne devrait pas tre trop complique faire,
le chapitre sur le WebBrowser tant tout frachement lu.
Noubliez pas que vous pouvez utiliser le dictionnaire dtat pour communiquer entre
les pages. Allez, jen ai beaucoup dit. Pour le reste, cest vous de chercher un peu.
Bon courage.

Correction
Ahhhh, une premire vraie application. Enfin ! Pas si facile finalement ? On se rend
compte quil y a plein de petites choses, plein de lgers bugs qui apparaissent au fur
et mesure de nos tests. . . Il faut avancer petit petit partir du moment o a se
complique un peu et lorsque lapplication commence grandir.
Il ny a pas une unique solution pour ce TP. Toute solution fonctionnelle est bonne.
Je vais vous prsenter la mienne. Jai port une attention quasi nulle la prsentation
histoire que cela soit assez simple. Jespre que de votre ct, vous en avez profit pour
faire quelque chose de joli !
Jai donc commenc par crer une nouvelle application, nomme TpFluxRss. tant
donn que nous avons besoin de manipuler des flux RSS, il faut rfrencer lassembly
System.ServiceModel.Syndication.dll. Voici la page de menu :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8
9

10
11

<! - - TitlePanel contient le nom de l ' application et le titre


de la page - - >
< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " TP Lecteur
de flux RSS " Style = " { StaticResource
P honeTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Menu " Margin = "9 , -7 ,
0 , 0 " Style = " { StaticResource PhoneTextTitle1Style } " / >
</ StackPanel >

12
13
14

<! - - ContentPanel - placez tout contenu suppl mentaire ici


-->
< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>

323

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE


< StackPanel >
< Button Content = " Voir les flux RSS " Tap = "
VoirFluxTap " / >
< Button Content = " Ajouter un flux RSS " Tap = "
AjouterFluxTap " / >
</ StackPanel >

15
16
17
18
19

</ Grid >


</ Grid >

20
21

Avec le code-behind suivant :


1
2
3
4
5
6

public partial class MainPage : PhoneApplicationPage


{
// Constructeur
public MainPage ()
{
I nitializeComponent () ;

7
8
9
10

11

12

if ( I s ol at ed S to ra ge S et ti ng s . ApplicationSettings .
Contains ( " ListeUrl " ) )
{
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ " ListeUrl " ] =
I so la te d St or ag e Se tt in g s . ApplicationSettings [ "
ListeUrl " ];
}

13

private void VoirFluxTap ( object sender , System . Windows .


Input . GestureEventArgs e )
{
NavigationService . Navigate ( new Uri ( " / VoirFlux . xaml " ,
UriKind . Relative ) ) ;
}

14
15
16
17
18
19
20
21
22
23

private void AjouterFluxTap ( object sender , System . Windows .


Input . GestureEventArgs e )
{
NavigationService . Navigate ( new Uri ( " / AjouterFlux . xaml " ,
UriKind . Relative ) ) ;
}

On observe dans le constructeur quon commence par charger un objet que lon met
directement dans le dictionnaire dtat. Le nom de la cl nous fait pressentir quil sagit
de la liste des URL de flux RSS que notre utilisateur a saisi dans notre application. Ce
qui nous permet de les faire persister dun lancement lautre. Pour le reste, il sagit
dafficher deux boutons qui nous renvoient sur dautres pages, les pages VoirFlux.xaml
et AjouterFlux.xaml (voir la figure 22.1).
Commenons par la page AjouterFlux.xaml qui, sans surprises, affiche une page per324

CORRECTION

Figure 22.1 La page de menu du lecteur de flux RSS


mettant de saisir des URL de flux RSS. Le XAML de la page est encore trs simple :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

10

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " TP Lecteur
de flux RSS " Style = " { StaticResource
P honeTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Ajouter un flux "
Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
P honeTextTitle1Style } " / >
</ StackPanel >

11
12
13
14
15
16
17

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< TextBlock Text = " Saisir une url " / >
< TextBox x : Name = " Url " InputScope = " Url " / >
< Button Content = " Ajouter " Tap = " AjouterTap " / >
</ StackPanel >

325

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE


</ Grid >
</ Grid >

18
19

Il permet dafficher une zone de texte, o le clavier sera adapt pour la saisie dURL
(InputScope="Url") et possdant un bouton pour faire lajout de lURL, comme vous
pouvez le voir sur la figure 22.2.

Figure 22.2 La page dajout des flux du lecteur de flux RSS


Cest dans le code-behind que tout se passe :
1
2
3

public partial class AjouterFlux : PhoneApplicationPage


{
private List < Uri > listeUrl ;

4
5
6
7
8

public AjouterFlux ()
{
I nitializeComponent () ;
}

9
10
11
12
13
14

326

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
Url . Text = " http :// " ;
if ( P h on eA pp l ic at io n Se rv ic e . Current . State . ContainsKey ( "
ListeUrl " ) )
listeUrl = ( List < Uri >) P ho ne A pp li ca t io nS er v ic e .
Current . State [ " ListeUrl " ];

CORRECTION
else

15
16
17

18

listeUrl = new List < Uri >() ;


base . OnNavigatedTo ( e ) ;

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

private void AjouterTap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
Uri uri ;
if (! Uri . TryCreate ( Url . Text , UriKind . Absolute , out uri )
)
MessageBox . Show ( " Le format de l ' url est incorrect " )
;
else
{
listeUrl . Add ( uri ) ;
I so la te d St or a ge Se tt i ng s . ApplicationSettings [ "
ListeUrl " ] = listeUrl ;
P ho ne Ap p li ca t io nS er v ic e . Current . State [ " ListeUrl " ] =
listeUrl ;
MessageBox . Show ( "L ' url a t correctement ajout e " )
;
}
}

On commence par obtenir la liste des URL potentiellement dj charge, ensuite nous
lajoutons la liste si elle nexiste pas dj, puis nous mettons jour cette liste dans
le rpertoire local ainsi que dans le dictionnaire dtat.
Remarque : il pourrait tre judicieux de vrifier ici si lURL correspond vraiment un flux RSS.
Maintenant, il sagit de raliser la page VoirFlux.xaml, qui contient la liste des titres
des diffrents flux. Pour lexemple, je men suis ajout quelques uns, comme vous pouvez
le voir sur la figure 22.3.
Ici le XAML intressant se situe dans la deuxime grille :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " TP Lecteur
de flux RSS " Style = " { StaticResource

327

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE

Figure 22.3 La liste de tous les flux du lecteur de flux RSS

10

PhoneTextNormalStyle } " / >


< TextBlock x : Name = " PageTitle " Text = " Voir les flux "
Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle1Style } " / >
</ StackPanel >

11
12
13
14
15
16
17

18

19
20
21
22
23
24

328

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< TextBlock x : Name = " Chargement " HorizontalAlignment = "
Center " Foreground = " Red " FontSize = " 20 " FontWeight = "
Bold " / >
< ListBox x : Name = " ListBoxFlux " ItemsSource = " { Binding
ListeFlux } " Grid . Row = " 1 " SelectionChanged = "
L is t B ox _ S el e c ti o n C ha n g ed " >
< ListBox . ItemTemplate >
< DataTemplate >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 50 " / >
< ColumnDefinition Width = " * " / >

CORRECTION
25
26
27
28
29
30
31
32

33

34

35
36
37
38
39
40

</ Grid . ColumnDefinitions >


< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< Image Source = " { Binding UrlImage } "
Height = " 50 " Width = " 50 " / >
< TextBlock Grid . Column = " 1 " Text = " {
Binding Titre } " TextWrapping = " Wrap "
Style = " { StaticResource
PhoneTextTitle3Style } " / >
< TextBlock Grid . ColumnSpan = " 2 " Grid . Row
= " 1 " Text = " { Binding Description } "
TextWrapping = " Wrap " Margin = " 0 0 0 20
" FontStyle = " Italic " / >
< Line X1 = " 0 " X2 = " 480 " Y1 = " 0 " Y2 = " 0 "
StrokeThickness = " 5 " Stroke = " Blue "
Grid . Row = " 2 " Grid . ColumnSpan = " 2 " / >
</ Grid >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
</ Grid >
</ Grid >

On y remarque un TextBlock qui servira dindicateur de chargement des flux, puis une
ListBox qui est lie la proprit ListeFlux. Le modle des lments de la ListBox
affiche limage du flux, via la liaison la proprit UrlImage, le titre via la liaison
la proprit Titre ainsi que la description. . . Notons un unique effet desthtique
permettant de sparer deux flux, via une magnifique ligne bleue. Passons au code
behind :
1
2
3

public partial class VoirFlux : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
private WebClient client ;

4
5
6
7
8
9
10

private ObservableCollection < Flux > listeFlux ;


public ObservableCollection < Flux > ListeFlux
{
get { return listeFlux ; }
set { Not ifyPro perty Change d ( ref listeFlux , value ) ; }
}

11
12

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

13
14
15
16

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )

329

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE


17
18

PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (


nomPropriete ) ) ;

19
20
21
22

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

23
24
25
26
27

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

28
29
30
31
32
33

public VoirFlux ()
{
I nitializeComponent () ;
DataContext = this ;
}

34
35
36
37
38
39
40
41

protected override async void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
List < Uri > listeUrl ;
if ( P h on eA pp l ic at io n Se rv ic e . Current . State . ContainsKey ( "
ListeUrl " ) )
listeUrl = ( List < Uri >) P ho ne A pp li ca t io nS er v ic e .
Current . State [ " ListeUrl " ];
else
listeUrl = new List < Uri >() ;

42
43
44
45
46
47
48
49
50
51
52
53
54

if ( listeUrl . Count == 0 )
{
MessageBox . Show ( " Vous devez d ' abord ajouter des
flux " ) ;
if ( NavigationService . CanGoBack )
NavigationService . GoBack () ;
}
else
{
Chargement . Text = " Chargement en cours ... " ;
Chargement . Visibility = Visibility . Visible ;
ListeFlux = new ObservableCollection < Flux >() ;
client = new WebClient () ;

55
56
57
58
59
60

330

queue = new Queue < Uri >() ;


foreach ( Uri uri in listeUrl )
{
try
{

CORRECTION
string rss = await client .
D ow nl o ad St ri n gT as kA s yn c ( uri ) ;
AjouteFlux ( rss ) ;

61
62

}
catch ( Exception )
{
MessageBox . Show ( " Impossible de lire le flux
l ' adresse : " + uri + " \ nV rifiez
votre connexion internet " ) ;
}

63
64
65
66

67

}
Chargement . Text = string . Empty ;
Chargement . Visibility = Visibility . Collapsed ;

68
69
70
71
72
73

}
base . OnNavigatedTo ( e ) ;

74
75
76
77
78
79
80
81
82

private void AjouteFlux ( string flux )


{
StringReader stringReader = new StringReader ( flux ) ;
XmlReader xmlReader = XmlReader . Create ( stringReader ) ;
SyndicationFeed feed = SyndicationFeed . Load ( xmlReader ) ;
listeFlux . Add ( ConstruitFlux ( feed ) ) ;
P h o n e Ap p li ca t io nS er v ic e . Current . State [ " ListeFlux " ] =
ListeFlux . ToList () ;
}

83
84
85
86

87
88
89
90
91
92

93
94
95
96

private Flux ConstruitFlux ( SyndicationFeed feed )


{
Flux flux = new Flux { Titre = feed . Title . Text ,
UrlImage = feed . ImageUrl , Description = feed .
Description == null ? string . Empty : feed .
Description . Text , ListeBillets = new List < Billet >()
};
foreach ( SyndicationItem item in feed . Items .
OrderByDescending ( e = > e . LastUpdatedTime . DateTime ) )
{
Uri url = item . Links . Select ( e = > e . Uri ) .
FirstOrDefault () ;
if ( url != null )
{
Billet billet = new Billet { Id = url .
AbsolutePath , DatePublication = item .
LastUpdatedTime . DateTime , Titre = item . Title
. Text , EstDejaLu = EstDejaLu ( url .
AbsolutePath ) , Url = url };
flux . ListeBillets . Add ( billet ) ;
}
}
return flux ;

331

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE


}

97
98

private bool EstDejaLu ( string id )


{
if ( I s ol at ed S to ra ge S et ti ng s . ApplicationSettings .
Contains ( " ListeDejaLus " ) )
{
List < string > dejaLus = ( List < string >)
I so la te d St or a ge Se tt i ng s . ApplicationSettings [ "
ListeDejaLus " ];
bool any = dejaLus . Any ( e = > e == id ) ;
return any ;
}
return false ;
}

99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

private void L i st B o x_ S e le c t io n C ha n g ed ( object sender ,


S e l e c ti o n C h a n g e d E ve n t A r g s e )
{
if ( ListBoxFlux . SelectedItem != null )
{
Flux flux = ( Flux ) ListBoxFlux . SelectedItem ;
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ " FluxCourant "
] = flux ;
NavigationService . Navigate ( new Uri ( " / VoirFluxPivot .
xaml " , UriKind . Relative ) ) ;
ListBoxFlux . SelectedItem = null ;
}
}

Cest le code-behind le plus long de lapplication. Nous voyons limplmentation classique de INotifyPropertyChanged ainsi quune proprit ListeFlux qui est une ObservableColl
de Flux. Lobjet Flux contiendra toutes les proprits dun Flux que nous souhaitons
afficher sur la prochaine page :
1
2
3
4
5
6
7

public class Flux


{
public string Titre { get ; set ; }
public Uri UrlImage { get ; set ; }
public string Description { get ; set ; }
public List < Billet > ListeBillets { get ; set ; }
}

Un titre, lURL de limage, la description ainsi quune liste de billets, sachant que la
classe Billet sera :
1
2
3
4

public class Billet


{
public string Id { get ; set ; }
public DateTime DatePublication { get ; set ; }

332

CORRECTION
5
6
7
8

public string Titre { get ; set ; }


public Uri Url { get ; set ; }
public bool EstDejaLu { get ; set ; }

Ensuite, il y a le chargement des flux. Voyez comme cest facile grce await, une
simple boucle et on nen parle plus.
La mthode AjouteFlux cre un objet SyndicationFeed comme on la dj vu puis
assemble un objet Flux partir du SyndicationFeed. La liste dobjets Flux est ensuite mise jour dans le dictionnaire dtat. Notons que lors de lassemblage, je dois
dterminer si le billet a dj t lu ou non. Pour cela, je tente de lire dans le rpertoire
local la liste de tous les billets dj lus, identifis par leur id. Si je le trouve, cest que
le billet est dj lu. Cette liste est alimente lorsquon visionne rellement le billet.
Il serait judicieux de ne lire quune unique fois la liste des billets dj lus
depuis le rpertoire local, par exemple dans la mthode OnNavigatedTo, afin
damliorer les performances. Je ne lai pas fait ici pour que cela soit plus
clair, mais nhsitez pas le faire.
Enfin, lvnement de slection dun flux soccupe de rcuprer le flux slectionn, de le
mettre dans le dictionnaire dtat et de naviguer sur la page VoirFluxPivot.xaml. Finalement, ce nest pas trs compliqu. Il faut juste enchaner tranquillement les actions
sans rien oublier.
Passons prsent la page VoirFluxPivot.xaml o le but est dafficher dans un pivot
la liste des flux, en se positionnant sur celui choisi. Ce qui nous permettra de naviguer
de flux en flux en faisant un glissement de doigt. Chaque vue du pivot contiendra une
ListBox avec tous les billets du flux en cours. Notons au passage que jai choisi de
marquer les billets dj lus en italique (voir la figure 22.4).
Ici cest le XAML qui est sans doute le plus compliqu. Nous avons besoin du contrle
Pivot. Notre contrle Pivot est li la proprit ListeFlux :
1
2
3
4
5
6
7
8
9
10
11
12

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< phone : Pivot x : Name = " PivotFlux " ItemsSource = " { Binding
ListeFlux } " Loaded = " PivotFlux_Loaded " >
< phone : Pivot . HeaderTemplate >
< DataTemplate >
< TextBlock Text = " { Binding Titre } " / >
</ DataTemplate >
</ controls : Pivot . HeaderTemplate >
< phone : Pivot . ItemTemplate >
< DataTemplate >
< StackPanel >
< TextBlock Text = " { Binding Titre } "
FontWeight = " Bold " FontSize = " 20 " / >
< ListBox ItemsSource = " { Binding ListeBillets
} " SelectionChanged = "
ListeBoxBillets_SelectionChanged ">

333

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE

Figure 22.4 La liste des billets dun flux du lecteur de flux RSS
13
14
15

16
17
18
19
20
21
22
23

< ListBox . ItemTemplate >


< DataTemplate >
< TextBlock Text = " { Binding Titre
} " Margin = " 0 0 0 40 "
FontStyle = " { Binding
EstDejaLu , Converter ={
StaticResource
FontFamilyConverter }} " / >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
</ StackPanel >
</ DataTemplate >
</ controls : Pivot . ItemTemplate >
</ controls : Pivot >
</ Grid >

Nous nous abonnons galement lvnement de chargement de Pivot. Lentte du


Pivot est lie au titre du flux, via le modle dentte. Quant au corps du Pivot, il
possde un modle o il y a notamment une ListBox, lie la proprit ListeBillets.
Notons que nous nous sommes abonns lvnement de changement de slection de
la ListBox. Le corps de la ListBox consiste lier le titre du billet avec une subtilit
o jutilise un converter pour positionner le style de la police en fonction de si le billet
a dj t lu ou pas. Ce converter devra donc tre dclar dans les ressources :
334

CORRECTION
1
2
3

< phone : P h on e A pplicationPage . Resources >


< converter : FontFamilyConverter x : Key = " FontFamilyConverter "
/>
</ phone : P ho n e ApplicationPage . Resources >

Avec lespace de nom qui va bien :


1

xmlns : converter = " clr - namespace : TpFluxRss "

La classe de conversion devra donc convertir un boolen en FontStyle :


1
2
3
4
5
6

public class FontFamilyConverter : IValueConverter


{
public object Convert ( object value , Type targetType , object
parameter , CultureInfo culture )
{
return ( bool ) value ? FontStyles . Italic : FontStyles .
Normal ;
}

7
8
9
10
11
12

public object ConvertBack ( object value , Type targetType ,


object parameter , CultureInfo culture )
{
throw new N o tI mp le m en te dE x ce pt i on () ;
}

Ici, nul besoin dimplmenter la mthode de conversion de FontStyle vers boolen,


elle ne sera pas utilise. La conversion consiste avoir le style italique si le boolen est
vrai, normal sinon. Reste le code-behind :
1
2
3
4
5
6
7
8

public partial class VoirFluxPivot : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
private ObservableCollection < Flux > listeFlux ;
public ObservableCollection < Flux > ListeFlux
{
get { return listeFlux ; }
set { Not ifyPro perty Change d ( ref listeFlux , value ) ; }
}

9
10

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

11
12
13
14
15
16

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

17
18

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )

335

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE


19

20

if ( object . Equals ( variable , valeur ) ) return false ;

21
22
23
24
25

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

26
27
28
29
30
31

public VoirFluxPivot ()
{
I nitializeComponent () ;
DataContext = this ;
}

32
33
34
35

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
ListeFlux = new ObservableCollection < Flux >(( List < Flux >)
P ho ne Ap p li ca t io nS er v ic e . Current . State [ " ListeFlux " ]) ;

36
37
38

base . OnNavigatedTo ( e ) ;

39
40
41
42
43
44

private void PivotFlux_Loaded ( object sender ,


RoutedEventArgs e )
{
PivotFlux . SelectedItem = ( Flux ) P h on eA pp l ic at io n Se rv i ce .
Current . State [ " FluxCourant " ];
PivotFlux . SelectionChanged +=
PivotFlux_SelectionChanged ;
}

45
46
47
48
49
50
51
52
53
54
55
56

private void L i s t e B o x B i l l e t s _ S e l e c t i o n C h a n g e d ( object sender


, S e l e c t i o nC h a n g e d E v e nt A r g s e )
{
ListBox listBox = ( ListBox ) sender ;
if ( listBox . SelectedItem != null )
{
Billet billet = ( Billet ) listBox . SelectedItem ;
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ "
BilletCourrant " ] = billet ;
NavigationService . Navigate ( new Uri ( " / VoirBillet .
xaml " , UriKind . Relative ) ) ;
listBox . SelectedItem = null ;
}
}

57
58
59

336

private void P i v o t F l u x _ S e l e c t i o n C h a n g e d ( object sender ,


S e l e c ti o n C h a n g e d E ve n t A r g s e )
{

CORRECTION
60
61
62

P h o n e Ap p li ca t io nS er v ic e . Current . State [ " FluxCourant " ] =


PivotFlux . SelectedItem ;

On retrouve notre proprit ListeFlux, ainsi que son implmentation classique. On


peut voir que lors de larrive sur la page, je rcupre dans le dictionnaire dtat la
liste des flux et que je construis une ObservableCollection avec, pour la mettre
dans la proprit ListeFlux. Dans lvnement de chargement du Pivot, jen profite
pour rcuprer le flux courant et ainsi positionner le pivot sur le flux prcdemment
slectionn dans la ListBox. Enfin, lors de la slection dun billet dans la ListBox, il
ne me reste plus qu positionner le billet dans le dictionnaire dtat et de naviguer sur
la page VoirBillet.xaml. Sachant que la ListBox est dans un contrle qui possde
plusieurs vues, il nest pas possible dobtenir la ListBox en cours par son nom. En
effet, en fait il y a autant de ListBox que de flux. On utilisera donc le paramtre
sender de lvnement de slection qui nous donne la ListBox concerne. Remarquons
que nous devons modifier le flux courant lorsque nous changeons dlment dans le
Pivot. On utilise lvnement de changement de slection auquel on sabonne la fin
du chargement du Pivot. On fait ceci une fois que llment en cours est positionn,
afin que lvnement ne soit pas lev.
Il ne reste plus que la page VoirBillet.xaml, qui est plutt simple. En tous cas, le
XAML ne contient que le contrle WebBrowser :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " TP Lecteur
de flux RSS " Style = " { StaticResource
P honeTextNormalStyle } " / >
</ StackPanel >

10
11
12
13
14

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< phone : WebBrowser x : Name = " PageBillet " NavigationFailed =
" PageBillet_NavigationFailed " />
</ Grid >
</ Grid >

Nous nous sommes quand mme abonns lvnement derreur de navigation pour
afficher un petit message, au cas o. . . Dans le code-behind, nous aurons juste besoin
de dmarrer la navigation et de marquer le billet comme lu :
1
2

public partial class VoirBillet : PhoneApplicationPage


{

337

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE


private Billet billet ;

3
4

public VoirBillet ()
{
I nitializeComponent () ;
}

5
6
7
8
9

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
billet = ( Billet ) Ph on eA p pl ic at i on Se r vi ce . Current . State [
" BilletCourrant " ];
billet . EstDejaLu = true ;

10
11
12
13
14

List < string > dejaLus ;


if ( I s ol at ed S to ra ge S et ti ng s . ApplicationSettings .
Contains ( " ListeDejaLus " ) )
dejaLus = ( List < string >) I s ol at ed S to ra ge S et ti n gs .
ApplicationSettings [ " ListeDejaLus " ];
else
dejaLus = new List < string >() ;
if (! dejaLus . Any ( elt = > elt == billet . Id ) )
dejaLus . Add ( billet . Id ) ;

15
16
17
18
19
20
21
22
23
24
25

26

I so la te d St or ag e Se tt i ng s . ApplicationSettings [ "
ListeDejaLus " ] = dejaLus ;
PageBillet . Navigate ( billet . Url ) ;
base . OnNavigatedTo ( e ) ;

27
28
29
30
31
32

private void P a g e B i l l e t _ N a v i g a t i o n F a i l e d ( object sender ,


System . Windows . Navigation . N a vi g a t i o n F a i le d E v e n t A r g s e )
{
MessageBox . Show ( " Impossible de charger la page , v
rifiez votre connexion internet " ) ;
}

Noublions pas de faire persister la liste des billets lus dans le rpertoire local. . . Et
voici le rsultat la figure 22.5.
Et voil, notre application commence tre fonctionnelle. Jespre que vous aurez
russi matriser notamment les histoires de binding et de slection dlments. Cest
un travail trs classique dans la cration dapplication Windows Phone avec le XAML.
Ce sont des lments que vous devez matriser. Nhsitez pas refaire ce TP si vous ne
lavez pas compltement russi. Vous pouvez aussi complter cette application qui est
loin dtre parfaite, vous entraner faire des jolies interfaces dans le style Modern UI,
rajouter des transitions, des animations, etc.
338

ALLER PLUS LOIN

Figure 22.5 Affichage dun billet dans le lecteur de flux RSS

Aller plus loin


Vous avez remarqu que dans la correction, je ne stocke dans le dictionnaire dtat que
les objets que jai moi-mme crs, savoir les objets Flux et Billet. Vous me direz,
pourquoi ne pas mettre directement les objets SyndicationFeed et SyndicationItem ?
Eh bien pour une bonne raison, ceci introduit un bug pas forcment visible du premier coup. Ce bug survient lorsque notre application est dsactive, par exemple si
lon reoit un coup de fil ou si on affiche le menu. Cela vient du fait que les objets
SyndicationFeed et SyndicationItem ne sont pas srialisables, ce qui fait que lorsquon passe en dsactiv, la srialisation choue et fait planter lapplication. En effet,
le dictionnaire dtat est un emplacement qui est disponible tant que notre application
est dsactive. Il est plus rapide daccs que le rpertoire local, mais ceci implique que
les objets quon y stocke soient srialisables. Pour rsoudre ce point, il suffit de ne pas
mettre ces objets dans le dictionnaire dtat et par exemple de mettre plutt directement nos objets Flux et Billet, comme ce que jai propos en correction. Ceci est
dailleurs plus logique.
Lautre solution est de vider le dictionnaire dtat dans lvnement de dsactivation
de lapplication, avec par exemple :
1
2
3

private void Ap p li ca t io n_ De a ct iv at e d ( object sender ,


D e a c t i v a t edEventArgs e )
{
P h o n e A p p l i ca t io nS er v ic e . Current . State [ " ListeFlux " ] = null ;

339

CHAPITRE 22. TP : CRATION DUN LECTEUR DE FLUX RSS SIMPLE


4

videmment, il faut re-remplir le dictionnaire dtat dans la mthode dactivation de


lapplication :
1
2
3
4

private void Applic ation _Activ ated ( object sender ,


Ac ti vatedEventArgs e )
{
// code faire , utilisation du WebClient pour recharger
tous les flux
}

Ou alors renvoyer lutilisateur sur la page o seffectue ce chargement, en utilisant le


service de navigation.
Vous serez aussi daccord avec moi, lajout de lURL dun flux nest pas des plus
commodes. Si vous voulez approfondir lapplication, vous pouvez galement essayer
dimporter des flux partir dune autre source. . . depuis un autre syndicateur de flux,
partir dun fichier OPML - http://fr.wikipedia.org/wiki/Outline_Processor_
Markup_Language.

340

Chapitre

23

Grer lorientation
Difficult :
Jusqu prsent, nous avons toujours utilis notre tlphone (enfin, surtout lmulateur)
dans sa position portrait, o les boutons sont orients vers le bas, le tlphone ayant son
ct le plus long dans le sens vertical, droit devant nous. Il peut se tenir aussi horizontalement, en mode paysage. Suivant les cas, les applications sont figes et ne peuvent se
tenir que dans un sens. Dans dautres cas, elles offrent la possibilit de tenir son tlphone
dans plusieurs modes, rvlant au passage une interface lgrement diffrente. Si on tient le
tlphone en mode paysage, il y a plus de place en largeur, il peut tre pertinent dafficher
plus dinformations. . .

341

CHAPITRE 23. GRER LORIENTATION

Les diffrentes orientations


Lorientation portrait est particulirement adapte la visualisation des listes droulantes pouvant contenir beaucoup dlments. Il devient galement naturel de faire
dfiler les lments vers le bas. Par contre, en mode paysage, il y a plus de place en
largeur. Si on garde le mme type daffichage, il risque dy avoir un gros trou sur la
droite, et la liste dlments deviendra sans doute un peu moins visible. De plus, dans
ce mode-l, il semble plus naturel de faire dfiler les lments de gauche droite. Et
quel mode portrait adopter quand on tourne son tlphone vers la droite, pour avoir
les boutons gauche ou quand on tourne le tlphone vers la gauche et ainsi avoir les
boutons droite ?
Un tlphone Windows Phone sait dtecter quand il change dorientation. Il est capable
de lever un vnement nous permettant de ragir ce changement dorientation. . . mais
nanticipons pas et revenons la base, la page. Si nous regardons en haut dans les
proprits de la page, nous pouvons voir que nous avons rgulirement cr des pages
qui possdent ces proprits :
S u p p o r t e d Orien tation s = " Portrait " Orientation = " Portrait "

Cela indique que lapplication supporte le mode portrait et que la page dmarre en
mode portrait. Il est possible de changer les valeurs de ces proprits. On peut par
exemple affecter les valeurs suivantes la proprit SupportedOrientation :
Portrait
Landscape
PortraitOrLandscape
qui signifient respectivement portrait, paysage et portrait-ou-paysage. Ainsi, si lon positionne cette dernire valeur, lapplication va automatiquement ragir au changement
dorientation. Modifions donc cette proprit pour utiliser le mode portrait et paysage :
S u p p o r t e d Orien tation s = " PortraitOrLandscape " Orientation = "
Portrait "

et utilisons par exemple le code XAML suivant :


1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

342

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " MON
APPLICATION " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Hello World " Margin
= "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle1Style } " / >

LES DIFFRENTES ORIENTATIONS


10

</ StackPanel >

11
12
13
14
15
16
17
18

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< TextBlock Text = " Bonjour tous " / >
< Button Content = " Cliquez - moi " / >
</ StackPanel >
</ Grid >
</ Grid >

Si nous dmarrons lapplication nous obtenons la figure 23.1.

Figure 23.1 Emulateur en mode portrait avec les boutons de lmulateur pour le
faire pivoter
Il est possible de simuler un changement dorientation avec lmulateur, il suffit de
cliquer sur les boutons disponibles dans la barre en haut droite de lmulateur, comme
indiqu sur limage du dessus. Lcran est retourn et linterface est automatiquement
adapte la nouvelle orientation, comme vous pouvez le voir la figure 23.2.
Ceci est possible grce la proprit que nous avons ajoute. Les contrles se redessinent automatiquement lors du changement dorientation. Remarquez que lorsquon
retourne le tlphone dans lautre mode paysage, le principe reste le mme.
Ici, le changement dorientation reste globalement lgant. Mais si nous avions utilis
un contrle en positionnement absolu grce un Canvas par exemple, il est fort possible
343

CHAPITRE 23. GRER LORIENTATION

Figure 23.2 Lmulateur en mode paysage


quil se retrouve un emplacement non voulu lors du changement dorientation. Cest
la mme chose avec un StackPanel, peut tre que tous les lments tiennent dans
la hauteur en mode portrait, mais il faudra srement rajouter un ScrollViewer en
mode paysage. . . Par exemple, le XAML suivant en mode portrait semble avoir ses
composants centrs :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

10

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " MON
APPLICATION " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Hello World " Margin
= "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle1Style } " / >
</ StackPanel >

11
12
13
14
15
16
17
18

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< Canvas >
< TextBlock x : Name = " MonTextBlock " Text = " Bonjour
tous " Canvas . Left = " 160 " / >
< Button x : Name = " MonBouton " Content = " Cliquez - moi "
Canvas . Left = " 140 " Canvas . Top = " 40 " / >
</ Canvas >
</ Grid >
</ Grid >

Alors que si on le retourne, on peut voir un beau trou droite (voir la figure 23.3).
344

DTECTER LES CHANGEMENTS DORIENTATION

Figure 23.3 Positionnement dcal suivant lorientation

Dtecter les changements dorientation


Il est possible de dtecter le type dorientation dun tlphone en consultant la proprit
Orientation dune page.
1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I ni t i alizeComponent () ;

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

switch ( Orientation )
{
case PageOrientation . Landscape :
case PageOrientation . LandscapeLeft :
case PageOrientation . LandscapeRight :
MessageBox . Show ( " Mode paysage " ) ;
break ;
case PageOrientation . Portrait :
case PageOrientation . PortraitDown :
case PageOrientation . PortraitUp :
MessageBox . Show ( " Mode portrait " ) ;
break ;
}

345

CHAPITRE 23. GRER LORIENTATION


Que lon peut galement simplifier de cette faon :
1
2
3
4
5
6
7
8

if (( Orientation & PageOrientation . Landscape ) ==


PageOrientation . Landscape )
{
MessageBox . Show ( " Mode paysage " ) ;
}
else
{
MessageBox . Show ( " Mode portrait " ) ;
}

Et, bien souvent plus utile, il est possible dtre notifi des changements dorientation.
Cela pourra permettre par exemple de raliser des ajustements. Pour tre prvenu, il
suffit de substituer la mthode OnOrientationChanged :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)
{
switch ( e . Orientation )
{
case PageOrientation . Landscape :
case PageOrientation . LandscapeLeft :
case PageOrientation . LandscapeRight :
// faire des choses pour le mode paysage
break ;
case PageOrientation . Portrait :
case PageOrientation . PortraitDown :
case PageOrientation . PortraitUp :
// faire des choses pour le mode portrait
break ;
}
base . OnOrientationChanged ( e ) ;
}

que lon peut nouveau simplifier en :


1
2
3
4
5
6
7
8
9
10
11
12

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)
{
if (( e . Orientation & PageOrientation . Landscape ) ==
PageOrientation . Landscape )
{
// faire des choses pour le mode paysage
}
else
{
// faire des choses pour le mode portrait
}
base . OnOrientationChanged ( e ) ;
}

346

STRATGIES DE GESTION DORIENTATION


Prenons notre prcdent exemple. Nous pouvons modifier les proprits de dpendance
Canvas.Left pour avoir :
1
2
3
4
5
6
7
8
9
10
11
12
13
14

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)
{
if (( e . Orientation & PageOrientation . Landscape ) ==
PageOrientation . Landscape )
{
MonTextBlock . SetValue ( Canvas . LeftProperty , 320 . 0 ) ;
MonBouton . SetValue ( Canvas . LeftProperty , 300 . 0 ) ;
}
else
{
MonTextBlock . SetValue ( Canvas . LeftProperty , 160 . 0 ) ;
MonBouton . SetValue ( Canvas . LeftProperty , 140 . 0 ) ;
}
base . O n O r ientationChanged ( e ) ;
}

Ainsi, nous recentrons les contrles chaque changement dorientation.

Stratgies de gestion dorientation


Bien sr, lexemple prcdent est un mauvais exemple, vous laurez compris. La bonne
solution est dutiliser correctement le systme de placement du XAML pour centrer
nos composants, avec uniquement du XAML :
1
2
3
4
5
6

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< StackPanel >
< TextBlock x : Name = " MonTextBlock " Text = " Bonjour tous "
H orizontalAlignment = " Center " / >
< Button x : Name = " MonBouton " Content = " Cliquez - moi "
H orizontalAlignment = " Center " / >
</ StackPanel >
</ Grid >

Mais modifier des proprits de contrles permet quand mme de pouvoir faire des
choses intressantes. Prenez lexemple suivant o nous positionnons des contrles dans
une grille :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >

347

CHAPITRE 23. GRER LORIENTATION


< TextBlock x : Name = " ApplicationTitle " Text = " MON
APPLICATION " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Hello World " Margin
= "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle1Style } " / >
</ StackPanel >

10
11

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Button x : Name = " Accueil " Content = " Accueil " / >
< Button Grid . Column = " 1 " x : Name = " Cours " Content = " Cours "
/>
< Button Grid . Column = " 2 " x : Name = " MesMPs " Content = " Mes
MPs " / >
< Image x : Name = " ImageSdz " Grid . Row = " 1 " Grid . RowSpan = " 2 "
Grid . ColumnSpan = " 3 " Source = " http :// open -e - education 2013 . openclassrooms . com / img / logos / logo openclassrooms . png " / >
</ Grid >
</ Grid >

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

27
28

Observez la figure 23.4 pour le rendu.


Nous pouvons utiliser la technique prcdente pour changer la disposition des contrles
dans la grille afin de produire un autre rendu en mode paysage. Pour cela, voyez le
code-behind suivant :
1
2
3
4
5
6
7
8

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)
{
if (( e . Orientation & PageOrientation . Landscape ) ==
PageOrientation . Landscape )
{
Grid . SetRow ( ImageSdz , 0 ) ;
Grid . SetRow ( Accueil , 0 ) ;
Grid . SetRow ( Cours , 1 ) ;
Grid . SetRow ( MesMPs , 2 ) ;

9
10
11

348

Grid . SetColumn ( ImageSdz , 0 ) ;


Grid . SetColumn ( Accueil , 2 ) ;

STRATGIES DE GESTION DORIENTATION

Figure 23.4 La grille en mode portrait


Grid . SetColumn ( Cours , 2 ) ;
Grid . SetColumn ( MesMPs , 2 ) ;

12
13
14
15
16

}
else
{

17
18
19
20
21
22
23

Grid . SetRowSpan ( ImageSdz , 3 ) ;


Grid . SetColumnSpan ( ImageSdz , 2 ) ;

Grid . SetRow ( ImageSdz , 1 ) ;


Grid . SetRow ( Accueil , 0 ) ;
Grid . SetRow ( Cours , 0 ) ;
Grid . SetRow ( MesMPs , 0 ) ;

24

Grid . SetColumn ( ImageSdz , 0 ) ;


Grid . SetColumn ( Accueil , 0 ) ;
Grid . SetColumn ( Cours , 1 ) ;
Grid . SetColumn ( MesMPs , 2 ) ;

25
26
27
28
29

Grid . SetRowSpan ( ImageSdz , 2 ) ;


Grid . SetColumnSpan ( ImageSdz , 3 ) ;

30
31
32
33
34

}
base . O n O r ientationChanged ( e ) ;

349

CHAPITRE 23. GRER LORIENTATION


Ainsi, on dplace les contrles dune colonne lautre, on arrange la fusion de certaines
lignes, etc. Regardez la figure 23.5 pour le rendu.

Figure 23.5 La grille rordonne en mode paysage


Et voil un positionnement compltement diffrent, plutt sympathique. Noubliez bien
sr pas de repositionner les contrles en mode portrait galement, sinon ils garderont
la disposition prcdente.
Cependant, il est parfois judicieux de ne pas grer le changement dorientation et de
forcer lapplication une unique orientation. Beaucoup de jeux sont bloqus en mode
paysage et beaucoup dapplications, notamment ds quil y a un panorama ou un pivot
forcent lorientation en portrait. Cest un choix qui peut se justifier et il vaut parfois
mieux proposer une exprience utilisateur optimale dans un mode, quune exprience
bancale dans les deux modes. Nous avons vu quil suffisait de positionner la proprit
SupportedOrientations Portrait ou Landscape. Sachant que ceci peut galement
se faire via le code-behind, si par exemple toute lapplication gre la double orientation,
mais quun cran en particulier peut uniquement tre utilis en mode portrait.
Si vous le pouvez, cest galement trs pratique que vos applications grent diffrentes
orientations. La solution la plus pratique est dutiliser la mise en page automatique
du XAML, comme ce que nous avons fait en centrant le bouton et la zone de texte
grce la proprit HorizontalAlignement. La premire chose faire est de toujours
utiliser des conteneurs qui peuvent se redimensionner automatiquement, dviter de
fixer des largeurs ou des hauteurs et de penser des contrles pouvant faire dfiler
leur contenu (ListBox, ScrollViewer, etc.). Cela peut par contre parfois donner des
rendus pas toujours esthtiques, avec un bouton qui prend toute la longueur de lcran
par exemple. Pensez-bien au design de vos crans et noubliez pas de tester dans toutes
les orientations possibles. Comme on la dj vu, vous pouvez galement faire vos
ajustements lors de la dtection du changement dorientation. Vous pouvez modifier
la hauteur/largeur dun contrle, changer son positionnement, en ajouter un nouveau
ou au contraire, supprimer un contrle qui ne rentre plus. Linconvnient de cette
technique est quelle requiert plus de code et quelle est plus complique mettre en
place avec une approche MVVM.
350

STRATGIES DE GESTION DORIENTATION


Vous pouvez galement rediriger lutilisateur sur une page adapte un mode prcis
lorsquil y a un changement dorientation. Par exemple sur la PagePortrait.xaml :
1
2
3
4
5
6
7
8

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)
{
if (( e . Orientation & PageOrientation . Landscape ) ==
PageOrientation . Landscape )
{
Navig ationService . Navigate ( new Uri ( " / PagePaysage . xaml " ,
UriKind . Relative ) ) ;
}
base . O n O r ientationChanged ( e ) ;
}

et sur la PagePaysage.xaml :
1
2
3
4
5
6
7
8

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)
{
if (!(( e . Orientation & PageOrientation . Landscape ) ==
PageOrientation . Landscape ) )
{
Navig ationService . Navigate ( new Uri ( " / PagePortrait . xaml "
, UriKind . Relative ) ) ;
}
base . O n O r ientationChanged ( e ) ;
}

Linconvnient est que cela fait empiler beaucoup de pages dans la navigation. Il faut
alors retirer la page prcdente de la pile de navigation. Il suffit de redfinir la mthode
OnNavigatedTo et dutiliser la mthode RemoveBackEntry :
1
2
3
4
5

protected override void OnNavigatedTo ( NavigationEventArgs e )


{
Navig ationService . RemoveBackEntry () ;
base . OnNavigatedTo ( e ) ;
}

Dans le mme style, nous pouvons crer des contrles utilisateurs ddis une orientation, que nous affichons et masquons en fonction de lorientation. Un contrle utilisateur
est un bout de page que nous pouvons rutiliser dans nimporte quelle page. Pour crer
un nouveau contrle utilisateur, on utilise le modle Contrle utilisateur Windows
Phone lors dun clic droit sur le projet, ajouter, nouvel lment (voir la figure 23.6).
Le contrle utilisateur portrait pourra contenir le XAML suivant :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " >


< StackPanel >
< TextBlock Text = " Je suis en mode portrait "
H orizontalAlignment = " Center " / >
</ StackPanel >
</ Grid >

351

CHAPITRE 23. GRER LORIENTATION

Figure 23.6 Ajout dun contrle utilisateur


Alors que le contrle utilisateur paysage contiendra :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " >


< StackPanel >
< TextBlock Text = " Je suis en mode paysage "
HorizontalAlignment = " Center " / >
</ StackPanel >
</ Grid >

Nous pourrons alors avoir le code-behind suivant dans la page principale :


1
2
3
4

public partial class MainPage : PhoneApplicationPage


{
private ControlePortrait controlePortrait ;
private ControlePaysage controlePaysage ;

5
6
7
8
9
10

public MainPage ()
{
I nitializeComponent () ;
controlePortrait = new ControlePortrait () ;
controlePaysage = new ControlePaysage () ;

11
12
13

ContentPanel . Children . Add ( controlePortrait ) ;

14
15

352

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)

STRATGIES DE GESTION DORIENTATION


{

16
17
18
19
20
21
22
23
24
25
26
27
28
29

if (( e . Orientation & PageOrientation . Landscape ) ==


PageOrientation . Landscape )
{
ContentPanel . Children . Remove ( controlePortrait ) ;
ContentPanel . Children . Add ( controlePaysage ) ;
}
else
{
ContentPanel . Children . Remove ( controlePaysage ) ;
ContentPanel . Children . Add ( controlePortrait ) ;
}
base . OnOrientationChanged ( e ) ;

Le principe est dinstancier les deux contrles et de les ajouter ou de les retirer la
grille en fonction de lorientation. Linconvnient, comme pour la prcdente stratgie,
est quil faut potentiellement dupliquer pas mal de code dans chacun des contrles.
Enfin, nous pouvons utiliser le gestionnaire dtat. De la mme faon que nous lavons
vu dans la partie prcdente pour les contrles, une page peut avoir plusieurs tats.
Il suffit de considrer que ltat normal est celui en mode portrait et que nous allons
crer un tat pour le mode paysage. Pour cela, le plus simple est dutiliser Blend.
Dmarrons-le et commenons crer notre page (voir la figure 23.7).

Figure 23.7 Page portrait dans Blend


353

CHAPITRE 23. GRER LORIENTATION


qui correspond au XAML suivant :
1
2
3
4

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< TextBlock Text = " Mode Portrait " HorizontalAlignment = " Center
" />
< Button Content = " Cliquez - moi " Width = " 200 " VerticalAlignment
= " Bottom " HorizontalAlignment = " Right " / >
</ Grid >

Maintenant, nous allons crer un nouvel tat. Slectionnez la page dans larbre visuel
(en bas gauche) puis dans longlet tat, cliquez sur le petit bouton droite permettant
dajouter un groupe dtat, comme indiqu sur la figure 23.8.

Figure 23.8 Ajout dun groupe dtat dans Blend


Nous pouvons le nommer par exemple EtatsOrientations.
Avant de pouvoir crer un tat propre au mode paysage, nous allons positionner le
designer en mode paysage. Pour cela il faut aller dans longlet Appareil qui est un peu
plus droite que longlet tats et basculer en landscape (voir la figure 23.9).
Revenez dans longlet tat, et cliquez prsent droite sur ajouter un tat , comme
indiqu la figure 23.10.
Nous pouvons appeler ce nouvel tat ModePaysage (voir la figure 23.11).
Nous pouvons voir que ltat est en mode enregistrement, cest--dire que nous allons pouvoir maintenant modifier la position des contrles pour crer notre cran en
mode paysage. Changez la disposition et changez la proprit Text du TextBlock pour
afficher Mode paysage, comme indiqu la figure 23.12.
354

STRATGIES DE GESTION DORIENTATION

Figure 23.9 Passer en mode paysage dans Blend

Figure 23.10 Ajout dun tat dans Blend

355

CHAPITRE 23. GRER LORIENTATION

Figure 23.11 Ajout dun tat dans Blend

Figure 23.12 Modification de la disposition des contrles en mode paysage

356

STRATGIES DE GESTION DORIENTATION


Vous pouvez maintenant sauvegarder vos modifications, fermer Blend et revenir dans
Visual Studio. Remarquons que Blend nous a modifi notre XAML, notamment pour
rajouter de quoi faire une animation des contrles :
1

2
3
4
5
6

7
8
9
10

< TextBlock x : Name = " textBlock " Text = " Mode Portrait "
H or i zo n t alAlignment = " Center " Rend erTra nsform Origi n = " 0 .5 , 0 . 5 "
>
< TextBlock . RenderTransform >
< C om positeTransform / >
</ TextBlock . RenderTransform >
</ TextBlock >
< Button x : Name = " button " Content = " Cliquez - moi " Width = " 200 "
Verti calAlignment = " Bottom " HorizontalAlignment = " Right "
R e n d e r T r a nsfor mOrigi n = " 0 .5 , 0 . 5 " >
< Button . RenderTransform >
< C om positeTransform / >
</ Button . RenderTransform >
</ Button >

De mme, il a rajout de quoi grer les tats :


1
2
3
4
5

8
9
10

< V is ua lSt at eM anager . VisualStateGroups >


< VisualStateGroup x : Name = " EtatsOrientations " >
< VisualState x : Name = " ModePaysage " >
< Storyboard >
< DoubleAnimation Duration = " 0 " To = " - 254 "
Storyboard . TargetProperty = " (
UIElement . RenderTransform ) .(
CompositeTransform . TranslateX ) "
Storyboard . TargetName = " textBlock " d :
IsOptimized = " True " / >
< DoubleAnimation Duration = " 0 " To = " 182 "
Storyboard . TargetProperty = " (
UIElement . RenderTransform ) .(
CompositeTransform . TranslateY ) "
Storyboard . TargetName = " textBlock " d :
IsOptimized = " True " / >
< ObjectAnimationUsingKeyFrames
Storyboard . TargetProperty = " (
TextBlock . Text ) " Storyboard .
TargetName = " textBlock " >
< D isc re teO bj ect Ke yFr ame KeyTime
= " 0 " Value = " Mode Paysage " / >
</ ObjectAnimationUsingKeyFrames >
< DoubleAnimation Duration = " 0 " To = " - 300 "
Storyboard . TargetProperty = " (
UIElement . RenderTransform ) .(
CompositeTransform . TranslateX ) "
Storyboard . TargetName = " button " d :
IsOptimized = " True " / >

357

CHAPITRE 23. GRER LORIENTATION


< DoubleAnimation Duration = " 0 " To = " - 208 "
Storyboard . TargetProperty = " (
UIElement . RenderTransform ) .(
CompositeTransform . TranslateY ) "
Storyboard . TargetName = " button " d :
IsOptimized = " True " / >
</ Storyboard >
</ VisualState >
</ VisualStateGroup >
</ V is ua lStateManager . VisualStateGroups >

11

12
13
14
15

Nous pouvons constater quil sopre des changements de coordonnes via translations, ainsi que la modification du texte du TextBlock. cet tat, ajoutons un tat
ModePortrait vide pour signifier ltat de dpart, le plus simple ici est de le faire dans
le XAML, avec :
1
2
3
4
5
6
7
8

< VisualStateGroup x : Name = " EtatsOrientations " >


< VisualState x : Name = " ModePortrait " / >
< VisualState x : Name = " ModePaysage " >
< Storyboard >
...
</ Storyboard >
</ VisualState >
</ VisualStateGroup >

Enfin, vous allez avoir besoin dindiquer que vous grez le multi-orientation et que vous
dmarrez en mode portrait avec :
S u p p o r t e d Orien tation s = " PortraitOrLandscape " Orientation = "
Portrait "

Puis maintenant, il faudra ragir un changement dorientation et modifier ltat de


la page grce au VisualStateManager :
1
2
3
4
5
6
7
8
9
10

protected override void OnOrientationChanged (


OrientationChangedEventArgs e)
{
if (( e . Orientation & PageOrientation . Landscape ) ==
PageOrientation . Landscape )
{
VisualStateManager . GoToState ( this , " ModePaysage " , true )
;
}
else
{
VisualStateManager . GoToState ( this , " ModePortrait " , true
);
}

11
12
13

358

base . OnOrientationChanged ( e ) ;

STRATGIES DE GESTION DORIENTATION


Maintenant, vous pouvez changer lorientation de lmulateur ; vous pouvez voir le
rendu la figure 23.13.

Figure 23.13 Changement dtat de la page lors du changement dorientation


Et voil !
Si vous dcidez de grer les deux orientations dans votre application, cest
une bonne ide de prvoir une configuration permettant de figer lorientation
dans un unique sens. Par exemple, il est trs dsagrable de voir son cran
se tourner quand on utilise son tlphone en position allong. Pouvoir figer
lorientation la demande de lutilisateur savre tre un plus pour les pauvres
utilisateurs fatigus ou alits.

En rsum
Un tlphone sait dtecter les changements dorientation, ainsi une application
peut sexcuter en mode portrait ou en mode paysage.
Pour tre averti dun changement dorientation, il suffit de redfinir la mthode
OnOrientationChanged.
Il faut toujours tester fond notre application avec diffrentes orientations si
lon souhaite grer le changement dorientation.

359

CHAPITRE 23. GRER LORIENTATION

360

Chapitre

24

Grer les multiples rsolutions


Difficult :
Alors que Windows Phone 7.X ne supportait que des tlphones ayant la rsolution 480x800,
Windows Phone 8 permet maintenant de supporter 3 rsolutions. Ceci est un avantage pour
les utilisateurs qui peuvent ainsi avoir des tlphones avec des rsolutions diffrentes, mais
cela devient un inconvnient pour le dveloppeur qui doit maintenant faire en sorte que son
application fonctionne pour toutes les rsolutions des appareils.
Voyons maintenant ce quil faut savoir.

361

CHAPITRE 24. GRER LES MULTIPLES RSOLUTIONS

Les diffrentes rsolutions


Windows Phone 8 supporte trois rsolutions :
Rsolution
WVGA
WXGA
720p

Taille
480x800
768x1280
720x1280

Format
15/9
15/9
16/9

Cest aussi le cas de lmulateur, que nous pouvons dmarrer suivant plusieurs rsolutions, en la changeant dans la liste droulante, comme indiqu sur la figure 24.1.

Figure 24.1 Les diffrentes rsolutions de lmulateur


Voici prsent le mme cran prsent dans les 3 diffrentes rsolutions, avec de gauche
droite WVGA, WXGA et 720P (voir la figure 24.2).
Pour raliser cet exemple, je me suis servi dun contrle du Windows Phone
Toolkit que nous allons dcouvrir dans quelques chapitres : le WrapPanel.
Jai positionn une srie de boutons qui font tous 90 pixels de longueur et de
largeur.
La premire chose remarquer est que le rendu est le mme entre la rsolution WVGA
et WXGA, nonobstant la diffrence de rsolution. En effet, le format tant le mme,
savoir du 15/9 ime, Windows Phone prend automatiquement en charge la mise
lchelle de la page et ramne finalement la rsolution 768x1280 une taille de 480x800.
Ce nest par contre pas le cas pour la rsolution 720p, qui est du 16/9 ime, et o la
mise lchelle automatique donne du 480x853. On peut voir en effet sur la copie
dcran que nous avons un peu plus de choses qui sont affichs en bas du 720p.

Grer plusieurs rsolutions


Alors, comment grer toutes ces belles rsolutions ?
Vous aurez srement devin que cest grosso modo le mme principe que pour les
diffrentes orientations. Il y a plusieurs techniques pour grer les rsolutions :
362

GRER PLUSIEURS RSOLUTIONS

Figure 24.2 Le mme cran dans les trois rsolutions

Utiliser le redimensionnement automatique des contrles, nhsitez pas utiliser


ltoile (*) dans les grilles ou la valeur auto, les alignements centrs, etc.
Faire un ajustement avec le code-behind.
Rediriger vers les bonnes pages ou utiliser les bons contrles utilisateurs en
fonction de la rsolution.
Modifier ltat de la page en fonction de la rsolution.
...
La seule diffrence est quil ne faut pas le faire en rception un vnement de changement de taille, car en effet il est trs rare quun tlphone puisse changer de taille
dcran en cours dutilisation. Par contre, il est possible de dtecter la rsolution au
lancement de lapplication afin de faire les ajustements adquats. Il suffit dutiliser les
proprits prsentes dans Application.Current.Host.Content, comme :
ActualHeight qui donne la hauteur de lcran
ActualWidth qui donne la largeur de lcran
ScaleFactor qui donne le facteur dchelle
Et nous aurons pour chaque rsolution, les valeurs suivantes :
Ces valeurs vont donc nous permettre de dtecter la rsolution du tlphone.
363

CHAPITRE 24. GRER LES MULTIPLES RSOLUTIONS


Rsolution
WVGA
WXGA
720p

Hauteur
800
800
853

Largeur
480
480
480

Facteur dchelle
100
160
150

Les images
tant donn que nous avons diffrentes rsolutions, il se pose la question des images.
Mon image va-t-elle tre jolie dans toutes les rsolutions ? La premire ide tentante
serait dinclure 3 images diffrentes, chacune optimise pour une rsolution dcran.
Nous pourrions alors crire un petit helper qui nous dtecterait la rsolution et nous
permettrait de mettre la bonne image au bon moment :
1
2
3
4
5

public static class ResolutionHelper


{
public static bool EstWvga
{
get { return Application . Current . Host . Content .
ActualHeight == 800 && Application . Current . Host .
Content . ScaleFactor == 100 ; }
}

public static bool EstWxga


{
get { return Application . Current . Host . Content .
ScaleFactor == 160 ; }
}

8
9
10
11
12
13
14
15
16
17

public static bool Est720p


{
get { return Application . Current . Host . Content .
ScaleFactor == 150 ; }
}

Il serait ainsi facile de charger telle ou telle image en fonction de la rsolution :


1
2
3
4
5
6

if ( ResolutionHelper . EstWvga )
MonImage . Source = new BitmapImage ( new Uri ( " / Assets / Images /
Wvga / fond . png " , UriKind . Relative ) ) ;
if ( ResolutionHelper . EstWxga )
MonImage . Source = new BitmapImage ( new Uri ( " / Assets / Images /
Wxga / fond . png " , UriKind . Relative ) ) ;
if ( ResolutionHelper . Est720p )
MonImage . Source = new BitmapImage ( new Uri ( " / Assets / Images /
720p / fond . png " , UriKind . Relative ) ) ;

Cest une bonne solution sauf que cela augmentera considrablement la taille de notre
.xap et la consommation mmoire de notre application. tant donn que le facteur est
364

LES IMAGES
le mme entre WVGA et WXGA, il est possible de ninclure que les images optimises
pour la rsolution WXGA et de laisser le systme redimensionner automatiquement
les images pour la rsolution WVGA. De mme, si on peut faire en sorte de ne pas
inclure dimages en 720p et que ce soit la mise en page qui soit lgrement diffrente
pour cette rsolution, cest toujours une image en moins de gagne dans le .xap final
et en mmoire dans lapplication. Cependant, il peut parfois tre justifi dinclure les
images en diffrentes rsolutions et dutiliser notre petit helper.
noter que nous pouvons utiliser notre helper pour choisir la bonne image dans le
XAML galement. Crons une nouvelle classe :
1
2
3
4
5
6
7
8
9
10
11
12
13

public class ChoisisseurImage


{
public Uri MonImageDeFond
{
get
{
if ( ResolutionHelper . EstWxga )
return new Uri ( " / Assets / Images / Wxga / fond . png " ,
UriKind . Relative ) ;
if ( ResolutionHelper . Est720p )
return new Uri ( " / Assets / Images / 720p / fond . png " ,
UriKind . Relative ) ;
return new Uri ( " / Assets / Images / Wvga / fond . png " ,
UriKind . Relative ) ;
}
}

14
15
16
17
18
19
20
21
22
23
24
25
26

public Uri ImageRond


{
get
{
if ( ResolutionHelper . EstWxga )
return new Uri ( " / Assets / Images / Wxga / rond . png " ,
UriKind . Relative ) ;
if ( ResolutionHelper . Est720p )
return new Uri ( " / Assets / Images / 720p / rond . png " ,
UriKind . Relative ) ;
return new Uri ( " / Assets / Images / Wvga / rond . png " ,
UriKind . Relative ) ;
}
}

Que nous allons dclarer en ressources de notre application :


1
2
3
4

< Application
x : Class = " DemoResolution . App "
...
xmlns : local = " clr - namespace : DemoResolution " >

365

CHAPITRE 24. GRER LES MULTIPLES RSOLUTIONS


< Application . Resources >
< local : ChoisisseurImage x : Key = " C h oi s i ss e u rI m a ge R e s ou r c e
"/>
</ Application . Resources >

6
7
8
9

[...]
</ Application >

10
11

Ainsi, nous pourrons utiliser cette ressource dans le XAML :


1
2

< Image Source = " { Binding MonImageDeFond , Source ={ StaticResource


C h o i s i s s e ur I m ag e R e so u r ce }} " / >
< Image Source = " { Binding ImageRond , Source ={ StaticResource
C h o i s i s s e ur I m ag e R e so u r ce }} " / >

Limage de lcran daccueil


Vous vous rappelez que nous avons vu comment ajouter une image pour notre cran
daccueil, le fameux splash screen ? Vous pouvez galement fournir des images de splash
screen dans diffrentes rsolutions, il suffit dutiliser trois images dans les bonnes rsolutions portant ces noms :
SplashScreenImage.Screen-WVGA.jpg
SplashScreenImage.Screen-WXGA.jpg
SplashScreenImage.Screen-720p.jpg
Vous devez quand mme garder limage SplashScreenImage.jpg qui est limage par
dfaut.

En rsum
Windows Phone 8 apporte 3 rsolutions diffrentes que nous devons grer en
tant que dveloppeur pour offrir la meilleure qualit dapplication possible.
Les diffrentes stratgies pour grer les rsolutions multiples sont les mmes que
pour grer les diffrentes orientations.
Il est possible dutiliser des images de diffrentes rsolutions dans son application
mais gardez lesprit quelles occuperont de la mmoire et augmenteront la taille
du .xap.

366

Chapitre

25

Lapplication Bar
Difficult :
Nous ne lavons pas encore vue, mais vous la connaissez srement si vous possdez un
tlphone quip de Windows Phone. La barre dapplication, si elle est prsente, est situe
en bas de lcran et possde des boutons que nous pouvons cliquer. Elle fait office plus ou
moins de menu, accessible tout le temps, un peu comme le menu en haut des fentres qui
existaient sur nos vieilles applications Windows.
Elle peut savrer trs pratique et est plutt simple daccs, deux bonnes raisons pour
pousser un peu plus loin sa dcouverte.

367

CHAPITRE 25. LAPPLICATION BAR

Prsentation et utilisation
Si vous utilisez le SDK pour Windows Phone 7, vous lavez srement dj vue dans le
code XAML sans vraiment y faire attention. Il y a un exemple dutilisation comment
dans chaque nouvelle page cre :
1
2
3
4
5
6
7
8
9
10
11

<! - - Exemple de code illustrant l ' utilisation d ' ApplicationBar


-->
< phone : P h oneApplicationPage . ApplicationBar >
< shell : ApplicationBar IsVisible = " True " IsMenuEnabled = " True "
>
< shell : A p p li c a ti o n B ar I c on B u tt o n IconUri = " / Images /
appbar_button1 . png " Text = " Bouton 1 " / >
< shell : A p p li c a ti o n B ar I c on B u tt o n IconUri = " / Images /
appbar_button2 . png " Text = " Bouton 2 " / >
< shell : ApplicationBar . MenuItems >
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 1 "
/>
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 2 "
/>
</ shell : ApplicationBar . MenuItems >
</ shell : ApplicationBar >
</ phone : P honeApplicationPage . ApplicationBar >

Si vous d-commentez ces lignes ou que vous les recopiez dans votre page Windows
Phone 8, que vous dmarrez lmulateur, vous aurez la figure 25.1.
La barre dapplication est bien sr ce que jai encadr en rouge. Ce XAML doit tre
au mme niveau que la page, cest--dire hors du conteneur racine :
1
2
3

< phone : P h oneApplicationPage


x : Class = " DemoBarreApplication . MainPage "
... >

4
5
6
7

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
...
</ Grid >

8
9
10
11
12

< phone : PhoneApplicationPage . ApplicationBar >


...
</ phone : PhoneApplicationPage . ApplicationBar >
</ phone : PhoneApplicationPage >

Le XAML est plutt facile comprendre. On constate que la barre est visible, que le
menu est activ et quelle possde deux boutons, bouton 1 et bouton 2 , ainsi
que deux lments de menu, lmentMenu 1 et lmentMenu 2 . Pour afficher
les lments de menu, il faut cliquer sur les trois petits points en bas droite. Cela
permet galement de voir le texte associ un bouton, comme vous pouvez le voir sur
la figure 25.2.
368

PRSENTATION ET UTILISATION

Figure 25.1 Affichage de la barre dapplication

Figure 25.2 Les lments de menu de la barre


369

CHAPITRE 25. LAPPLICATION BAR


Par contre, nos images ne sont pas trs jolies. En effet, le code XAML fait rfrence
des images qui nexistent pas dans notre solution. La croix affiche reprsente donc
labsence dimage. Ces images sont des icnes. Elles sont toujours visibles sur la barre
dapplication alors que ce nest pas forcment le cas des lments de menu. Il est
donc primordial que ces icnes soient les plus reprsentatives possibles, car pour voir la
lgende de licne, il faut cliquer sur les trois petits points permettant dafficher la suite
de la barre. Les icnes doivent avoir la taille de 48x48, sachant que le cercle est ajout
automatiquement en surimpression par Windows Phone. Cela implique que la zone
visible de votre icne doit tre de 26x26 au centre de licne. Enfin, licne doit tre de
couleur blanche, sur fond transparent. Cest un look trs Modern UI que nous impose
Microsoft afin de privilgier la smantique du dessin plutt que sa beaut. Pour nous
aider, Microsoft propose un pack dicne Modern UI que nous pouvons librement
utiliser dans nos applications. Ce pack a t install avec le SDK de Windows Phone,
vous pouvez le retrouver cet emplacement : C:\Program Files (x86)\Microsoft
SDKs\Windows Phone\v8.0\Icons.
Vous pouvez voir quil y a plusieurs rpertoires. Vous navez qu vous soucier
du rpertoire dark qui correspond au thme fonc. Windows Phone utilise
automatiquement la bonne image en fonction du thme slectionn.
Maintenant, ajoutons deux images notre application, disons delete.png et edit.png.
Pour ce faire, crez par exemple un rpertoire Icones sous le rpertoire Assets et
ajoutez les icnes dedans en ajoutant un lment existant. Ensuite, dans la fentre de
proprits, modifiez laction de gnration en Contenu ainsi que la copie dans le
rpertoire de sortie en copier si plus rcent . Modifiez ensuite le XAML pour utiliser
nos nouvelles icnes :
1
2
3
4
5
6
7
8
9
10

< phone : P h oneApplicationPage . ApplicationBar >


< shell : ApplicationBar IsVisible = " True " IsMenuEnabled = " True "
>
< shell : A p p li c a ti o n B ar I c on B u tt o n IconUri = " / Assets / Icones
/ delete . png " Text = " Supprimer " / >
< shell : A p p li c a ti o n B ar I c on B u tt o n IconUri = " / Assets / Icones
/ edit . png " Text = " Modifier " / >
< shell : ApplicationBar . MenuItems >
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 1 "
/>
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 2 "
/>
</ shell : ApplicationBar . MenuItems >
</ shell : ApplicationBar >
</ phone : P honeApplicationPage . ApplicationBar >

Et voil la figure 25.3 le beau rsultat !


La barre dapplication est positionne en bas de lcran, juste au-dessus des trois boutons physiques. On ne peut pas la changer de place, mais elle sait cependant sorienter
diffremment suivant lorientation du tlphone. Par exemple dans la figure 25.4, en
mode paysage, nous pouvons voir la rotation des images et du texte.
370

PRSENTATION ET UTILISATION

Figure 25.3 Utilisation des icnes de la barre dapplication

Figure 25.4 La barre dapplication subit une rotation lors du changement dorientation du tlphone

371

CHAPITRE 25. LAPPLICATION BAR


Il faudra bien sr au pralable faire en sorte que la page supporte les deux orientations
comme nous lavons vu dans un chapitre prcdent.
Remarquez quon ne peut avoir que 4 boutons dans la barre dapplication. Si
vous tentez den mettre plus, vous aurez une exception.
Bon, une barre avec des boutons cest bien, mais si on pouvait cliquer dessus pour
faire des choses, a serait pas plus mal non ? Rien de plus simple, vous pouvez ajouter
lvnement de clic sur un bouton ou sur un lment de menu :
1
2
3

4
5
6
7
8
9
10

< phone : P h oneApplicationPage . ApplicationBar >


< shell : ApplicationBar IsVisible = " True " IsMenuEnabled = " True "
>
< shell : A p p li c a ti o n B ar I c on B u tt o n IconUri = " / Assets / Icones
/ delete . png " Text = " Supprimer " Click = "
ApplicationBarIconButton_Click_1 "/>
< shell : A p p li c a ti o n B ar I c on B u tt o n IconUri = " / Assets / Icones
/ edit . png " Text = " Modifier " / >
< shell : ApplicationBar . MenuItems >
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 1 "
Click = " A p p l i c a t i o n B a r M e n u I t e m _ C l i c k _ 1 " / >
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 2 "
/>
</ shell : ApplicationBar . MenuItems >
</ shell : ApplicationBar >
</ phone : P honeApplicationPage . ApplicationBar >

Ici, nous utilisons lvnement Click, il nexiste pas dvnement Tap pour
la barre dapplication.
Avec :
1
2
3
4

private void A p p l i c a t i o n B a r I c o n B u t t o n _ C l i c k _ 1 ( object sender ,


EventArgs e )
{
MessageBox . Show ( " Voulez - vous vraiment supprimer ? " , "
Suppression " , MessageBoxButton . OKCancel ) ;
}

5
6
7
8
9

private void A p p l i c a t i o n B a r M e n u I t e m _ C l i c k _ 1 ( object sender ,


EventArgs e )
{
MessageBox . Show ( " Menu ! " ) ;
}

Observez la figure 25.5 pour le rendu.


372

PRSENTATION ET UTILISATION

Figure 25.5 Le clic sur un bouton de menu dclenche laffichage du message


On peut jouer sur les proprits de la barre dapplication. Citons par exemple sa proprit IsVisible qui permet de la rendre visible ou invisible. Citons encore la proprit
Opacity qui permet dafficher des contrles sous la barre dapplication, par exemple
avec le XAML suivant :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

6
7
8

9
10

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock Text = " MON APPLICATION " Style = " {
StaticResource PhoneTextNormalStyle } " Margin = " 12 , 0 "
/>
< TextBlock Text = " nom de la page " Margin = "9 , -7 ,0 , 0 "
Style = " { StaticResource PhoneTextTitle1Style } " / >
</ StackPanel >

11
12
13
14

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< Canvas >
< TextBlock Text = " Je suis cach " Foreground = " Red "
FontSize = " 50 " Canvas . Top = " 500 " / >

373

CHAPITRE 25. LAPPLICATION BAR


</ Canvas >
</ Grid >
</ Grid >

15
16
17
18
19
20
21

22
23
24
25
26
27
28

< phone : P h oneApplicationPage . ApplicationBar >


< shell : ApplicationBar IsVisible = " True " IsMenuEnabled = " True "
>
< shell : A p p li c a t io n B ar I c on B u tt o n IconUri = " / Assets / Icones
/ delete . png " Text = " Supprimer " Click = "
ApplicationBarIconButton_Click_1 "/>
< shell : A p p li c a t io n B ar I c on B u tt o n IconUri = " / Assets / Icones
/ edit . png " Text = " Modifier " / >
< shell : ApplicationBar . MenuItems >
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 1 "
Click = " A p p l i c a t i o n B a r M e n u I t e m _ C l i c k _ 1 " / >
< shell : A ppl ica ti onB ar Men uIt em Text = " l mentMenu 2 "
/>
</ shell : ApplicationBar . MenuItems >
</ shell : ApplicationBar >
</ phone : P honeApplicationPage . ApplicationBar >

Si vous dmarrez lapplication, le texte rouge est invisible car il est derrire la barre
dapplication. Si vous changez lopacit en mettant par exemple 0.5 :
1

< shell : ApplicationBar IsVisible = " True " IsMenuEnabled = " True "
Opacity = " 0 . 5 " >

le texte apparatra sous la barre dapplication (voir la figure 25.6).


Pour les boutons, on peut remarquer en plus la proprit IsEnabled qui permet de
dsactiver ou dactiver un bouton. Il est bien sr possible de manipuler la barre dapplication par le code-behind, via la proprit ApplicationBar. Par exemple, je peux
rendre invisible la barre dapplication de cette faon :
ApplicationBar . IsVisible = false ;

Ou modifier le texte du premier bouton :


1

(( A p p l i c a t i on B a rI c o nB u t to n ) ApplicationBar . Buttons [ 0 ]) . Text = "


Changement dynamqiue " ;

Pour les lments de menu, le principe est le mme que pour les boutons.
Notons juste quil est possible de sabonner un vnement qui permet de savoir si ltat
de la barre change, cest--dire quand les lments de menu sont affichs ou non. Cela
permet par exemple de faire en sorte que certaines infos soient affiches diffremment
si jamais le menu les cache. Il sagit de lvnement StateChanged :
1
2
3
4

private void A p p l i c a t i o n B a r _ S t a t e C h a n g e d ( object sender ,


ApplicationBarStateChangedEventArgs e)
{
if ( e . IsMenuVisible )
{

374

PRSENTATION ET UTILISATION

Figure 25.6 Utilisation de lopacit sur la barre dapplication

5
6
7

// ...

Enfin, remarquons une dernire petite chose qui peut-tre vous a intrigu. Il sagit de
lendroit dans le XAML o est dfinie la barre dapplication :
1
2

< phone : P h on e A pplicationPage


... >

3
4
5
6

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
...
</ Grid >

7
8
9
10
11

< phone : P honeApplicationPage . ApplicationBar >


...
</ phone : PhoneApplicationPage . ApplicationBar >
</ phone : PhoneApplicationPage >

Jen ai parl un peu en haut mais vous avez vu ? Elle est au mme niveau que la grille !
Alors que nous avons dit quun seul conteneur racine tait autoris. Cest parce que
lobjet ApplicationBar ne drive pas de FrameworkElement, pour notre plus grand
malheur dailleurs. La proprit ApplicationBar de la PhoneApplicationPage est une
proprit de dpendance : http://msdn.microsoft.com/fr-fr/library/microsoft.
375

CHAPITRE 25. LAPPLICATION BAR


phone.controls.phoneapplicationpage.applicationbarproperty(v=vs.92). Cest
pour cela que nous pouvons (et dailleurs devons !) la mettre au niveau de la page.
Attention : il est recommand de ne pas avoir de trop long textes dans les
lments de menu, entre 14 et 20 caractres.

Appliquer le Databinding
tant donn que notre barre dapplication nest pas un FrameworkElement, elle ne
sait pas grer la liaison de donnes. Il nest donc pas possible dutiliser les extensions
de balisage {Binding} ni les commandes sur cet lment. Ce qui pose un problme
lorsque lon fait du MVVM ou mme quand il sagit de traduire notre application. Il
y a cependant une petite astuce pour rendre notre barre application fonctionnelle avec
la liaison de donnes ainsi quavec le systme de commandes. Il suffit de dvelopper
une nouvelle classe qui encapsule les fonctionnalits de la classe ApplicationBar. On
appelle communment ce genre de classe un wrapper.
Nous nallons pas faire cet exercice ici vu que dautres personnes lont dj fait pour
nous. Cest le cas par exemple de la bibliothque BindableApplicationBar que nous
pouvons trouver cet emplacement : http://bindableapplicationb.codeplex.com/.
Vous pouvez tlcharger cette bibliothque ou tout simplement utiliser NuGet (voir la
figure 25.7).
Il ne reste plus qu importer lespace de nom suivant :
xmlns : barre = " clr - namespace : Bin da ble Ap pli cat io nBa r ; assembly =
B i n d a bl eA ppl ica ti onB ar "

et modifier notre barre comme suit :


1
2
3

4
5

6
7
8

< barre : Bindable . ApplicationBar >


< barre : B ind abl eA ppl ic ati onB ar IsVisible = " { Binding
EstBarreVisible } " >
< barre : B i n d a b l e A p p l i c a t i o n B a r B u t t o n Command = " { Binding
SupprimerCommand } " Text = " { Binding SupprimerText } "
IconUri = " / Assets / Icones / delete . png " / >
< barre : B ind abl eA ppl ic ati onB ar . MenuItems >
< barre : B i n d a b l e A p p l i c a t i o n B a r M e n u I t e m Text = " {
Binding ElementText } " Command = " { Binding
ElementCommand } " / >
</ barre : Bi nd abl eA ppl ica ti onB ar . MenuItems >
</ barre : BindableApplicationBar >
</ barre : Bindable . ApplicationBar >

Notons la liaison de donnes sur la proprit IsVisible de la barre dapplication, de


la proprit Text du bouton ainsi que lutilisation dune commande lors du clic sur le
bouton. Nous aurons donc un contexte qui pourra tre :
376

APPLIQUER LE DATABINDING

Figure 25.7 Utilisation de NuGet pour tlcharger la barre dapplication bindable

1
2
3

public partial class MainPage : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

4
5
6
7
8
9

public void N otify Proper tyChan ged ( string nomPropriete )


{
if ( PropertyChanged != null )
PropertyChanged ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
nomPropriete ) ) ;
}

10
11
12
13

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

14
15
16
17
18

variable = valeur ;
N o t i f yPro pertyC hange d ( nomPropriete ) ;
return true ;

19
20
21

private bool estBarreVisible ;


public bool EstBarreVisible

377

CHAPITRE 25. LAPPLICATION BAR


{

22
23
24

25

get { return estBarreVisible ; }


set { Noti fyPro pertyC hange d ( ref estBarreVisible , value )
; }

26

public string SupprimerText


{
get { return " Supprimer " ; }
}

27
28
29
30
31

public string ElementText


{
get { return " El ment " ; }
}

32
33
34
35
36

public ICommand SupprimerCommand { get ; set ; }


public ICommand ElementCommand { get ; set ; }

37
38
39

public MainPage ()
{
I nitializeComponent () ;
SupprimerCommand = new RelayCommand ( OnSupprimer ) ;
ElementCommand = new RelayCommand ( OnElement ) ;
EstBarreVisible = true ;

40
41
42
43
44
45
46
47

48

DataContext = this ;

49

private void OnSupprimer ()


{
MessageBox . Show ( " Voulez - vous vraiment supprimer ? " , "
Suppression " , MessageBoxButton . OKCancel ) ;
}

50
51
52
53
54
55
56
57
58
59

private void OnElement ()


{
EstBarreVisible = false ;
}

Notez que vous aurez besoin dune classe RelayCommand, venant de votre framework
MVVM prfr ou bien lexemple simple que nous avons cr prcdemment. Grce
cette classe, nous pouvons dsormais respecter correctement le pattern MVVM, chose
qui tait plus difficile sans.
Le framework Ultralight MVVM que nous avons cit dans un chapitre prcdent propose une solution pour grer la liaison de donnes avec la barre
dapplication.
378

MODE PLEIN CRAN

Mode plein cran


Vous avez d remarquer quil y a tout en haut de lcran, un petit bout qui est rserv
laffichage de la batterie, de la qualit de la connexion, etc. Il sagit de la barre systme.
Ce nest pas vraiment la barre dapplication, mais je trouve quil est pertinent den
parler ici. Il nest pas possible de modifier le contenu de la barre systme, mais il est
par contre possible de la masquer afin davoir un mode plein cran , grce une
proprit. Cette proprit est positionne vrai par dfaut lorsque lon cre un nouvelle
page XAML, elle rend la barre systme visible :
1
2
3

< phone : P h on e A pplicationPage


...
shell : SystemTray . IsVisible = " True "

Nous pouvons modifier ici sa valeur en mettant False. Ou bien par code, en utilisant la classe statique SystemTray - http://msdn.microsoft.com/en-us/library/
microsoft.phone.shell.systemtray(v=vs.92).aspx :
1

SystemTray . IsVisible = false ;

Cela permet de rcuprer 16 pixels en hauteur. Vous aurez besoin dinclure lespace de
nom suivant :
1

using Microsoft . Phone . Shell ;

Attention cependant, ce nest pas toujours pertinent de masquer cette barre


systme car elle fournit des informations intressantes. Personnellement,
lorsque je dois tre connect internet, jaime bien vrifier la force du signal histoire de savoir sil vaut mieux relancer la requte, ou tout simplement
pour avoir lheure. . . utiliser judicieusement.

En rsum
La barre dapplication est un contrle utile lorsquil y a besoin de faire des
actions rcurrentes dans une application.
Elle fonctionne un peu comme un menu, accessible de partout.
Ne supportant pas la liaison de donnes par dfaut, il est possible dutiliser des
wrappers pour la rendre fonctionnelle avec le binding.
Il est recommand de ne pas masquer la barre systme, mais cest cependant
possible pour passer en mode plein cran.

379

CHAPITRE 25. LAPPLICATION BAR

380

Chapitre

26

Le toolkit Windows Phone


Difficult :
Bien que dj bien fournis, les contrles Windows Phone ne font pas tout. Nous avons
dj vu des bibliothques tierces, comme le MVVM Light toolkit qui permet de faire en
sorte que son application respecte plus facilement le patron de conception MVVM. Nous
avons galement utilis une bibliothque qui permet dutiliser le binding avec la barre
dapplication.
Il existe dautres bibliothques bien pratiques contenant des contrles volus qui vont
nous permettre denrichir nos applications et doffrir avec le moindre effort une exprience
utilisateur des plus sympathiques.
En plus, il y en a qui sont gratuites, alors pourquoi sen priver ?

381

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE

Prsentation et installation du toolkit Windows Phone


La bibliothque la plus connue est sans nul doute le Toolkit pour Windows Phone. Il
sagit dun projet trs suivi qui contient beaucoup de contrles pour Windows Phone.
Historiquement, ce toolkit proposait dj toute une gamme de contrles supplmentaires pour Silverlight. Depuis la sortie de Silverlight pour Windows Phone 7.X, cest
tout naturellement quune bibliothque parallle, ddie Windows Phone, a vu le
jour. Et aujourdhui, avec larrive de Windows Phone 8, il en existe une version pour
lui.
Comme beaucoup de bibliothques, elle est open-source, utilisable dans beaucoup de
situations mais pas toujours exempte de bug. Il y a tout une communaut qui travaille
sur ces projets et qui continue rgulirement lamliorer. Cette bibliothque est tlchargeable sur http://phone.codeplex.com/. lheure o jcris ces lignes, nous
pouvons tlcharger la version de novembre 2012. Vous pouvez tlcharger les binaires
sur le site ou encore une fois utiliser NuGet qui facilite la rcupration de bibliothques
tierces (voir la figure 26.1).

Figure 26.1 Utilisation de NuGet pour tlcharger le Windows Phone Toolkit


NuGet nous a donc ajout la rfrence lassembly Microsoft.Phone.Controls.Toolkit.dll.
Je ne vais pas vous prsenter tous les contrles tellement il y en a. Je vous encourage
tlcharger les sources ainsi que lapplication exemple et de la tester pour voir ce
quelle renferme. Mais voyons-en quand mme quelques uns.
382

PERFORMANCEPROGRESSBAR

PerformanceProgressBar
Je souhaitais vous parler de la barre de progression PerformanceProgressBar car
cest un trs bon exemple de la puissance du toolkit. La barre de progression qui tait
originellement prsente avec le SDK pour Windows Phone 7.X posait des problmes
de performances. Celle du toolkit profite de toutes les optimisations possibles et gre
galement beaucoup mieux les threads. Nous parlerons des Threads dans un prochain
chapitre. Toujours est-il que pour Windows Phone 7, ctait cette barre de progression
qui tait recommande un peu partout sur le net et par Microsoft (pour la petite histoire, cest un dveloppeur de Microsoft qui a cr cette nouvelle barre de progression).
Elle a depuis t intgre dans le SDK de Windows Phone 8 afin de tirer parti de
ces amliorations. Je vais vous montrer ici comment sen servir dans une application
pour Windows Phone 7 et aprs je rebasculerai sur Windows Phone 8. Crez donc une
application qui cible le SDK 7.1. Ensuite, pour utiliser la barre, rien de plus simple, il
vous suffit dimporter lespace de nom du toolkit :
1

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


Microsoft . Phone . Controls . Toolkit "

et de la dclarer o vous souhaitez quelle saffiche :


1

< toolkit : P e r f orm anc eP rog re ssB ar IsIndeterminate = " True " / >

La barre de progression nen est en fait pas vraiment une, du moins pas dans le sens o
on les connait : en loccurrence, elle naffiche pas un pourcentage de progression sur une
tche dont on connait la dure totale. Il sagit plutt dune petite animation qui montre
quil se passe un chargement, sans que lon sache vraiment o nous en sommes. Vous
pouvez voir son fonctionnement dans le designer de Visual Studio, la barre sanime et
affiche des points, comme indiqu sur la figure 26.2.

La barre est visible dans un tat o la progression est indtermine. Cest pour cela
que la proprit sappelle IsIndeterminate et permet dindiquer si elle est visible ou
non. Passez la False pour la voir se masquer. En gnral, ce que vous ferez, cest
dans un premier temps mettre la proprit vrai, dmarrer quelque chose de long de
manire asynchrone, par exemple un tlchargement, et une fois termin vous passerez
la proprit faux. Nous pouvons simuler ce fonctionnement avec un DispatcherTimer http://msdn.microsoft.com/fr-fr/library/system.windows.threading.dispatchertimer(v=
vs.95).aspx, il sagit dune classe qui va nous permettre dappeler une mthode une
frquence dtermine. Nous en reparlerons plus tard, mais par exemple ici, mon Timer
va me permettre de masquer ma barre de progression au bout de 7 secondes :
1
2
3

public partial class MainPage : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
private DispatcherTimer timer ;

4
5

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

6
7
8

private void Notify Proper tyCha nged ( String propertyName )


{

383

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE

Figure 26.2 Animation de la barre dans le designer

9
10
11
12
13
14

P r o p e r t y C h a n g e d E v e n t H a n d l e r handler = PropertyChanged ;
if ( null != handler )
{
handler ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
propertyName ) ) ;
}

15
16
17
18
19
20
21
22
23
24
25

private bool chargementEnCours ;


public bool ChargementEnCours
{
get { return chargementEnCours ; }
set
{
chargementEnCours = value ;
Not ifyPro perty Change d ( " ChargementEnCours " ) ;
}
}

26
27
28
29
30

public MainPage ()
{
I nitializeComponent () ;
DataContext = this ;

31
32

384

ChargementEnCours = true ;

LISTPICKER
33
34
35

36

timer = new DispatcherTimer { Interval = TimeSpan .


FromSeconds ( 7 ) };
timer . Tick += timer_Tick ;
timer . Start () ;

37
38
39
40
41
42
43

private void timer_Tick ( object sender , EventArgs e )


{
Charg ementEnCours = false ;
timer . Stop () ;
}

Noubliez pas dinclure lespace de noms suivant pour pouvoir utiliser le DispatcherTimer :
1

using System . Windows . Threading ;

Et dans le XAML, nous aurons le binding suivant :


1

< toolkit : P e r f orm anc eP rog re ssB ar IsIndeterminate = " { Binding


Charg ementEnCours } " / >

Remarquez quil est possible de changer facilement la couleur des points grce la
proprit Foreground. noter que son utilisation est globalement la mme avec le SDK
pour Windows Phone 8, on utilisera cependant le contrle ProgressBar directement.
Sachant que pour reproduire exactement le mme fonctionnement, il faudra masquer
la barre de progression une fois son statut indtermin activ :
1

< ProgressBar IsIndeterminate = " { Binding ChargementEnCours } "


Visibility = " { Binding ChargementEnCours , Converter ={
StaticResource VisibilityConverter }} " / >

Usez et abusez de cette barre ds quil faut avertir lutilisateur que quelque chose de
potentiellement long se passe.

ListPicker
Le contrle ListPicker est une espce de ListBox simplifie. Il est lquivalent dun
contrle connu mais qui manquait dans les contrles Windows Phone, la ComboBox.
Ce ListPicker permet de choisir un lment parmi une liste dlments relativement
restreinte. Puissant grce son systme de modle, il permet de prsenter linformation
de manire volue. Voyons prsent comment il fonctionne.
La manire la plus simple de lutiliser est de le lier une liste de chanes de caractres :
1

< toolkit : ListPicker ItemsSource = " { Binding Langues } " / >

Avec dans le code-behind :


1

public partial class MainPage : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed

385

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE


2

public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

private void Notify Prope rtyCha nged ( String propertyName )


{
P r o p e r t y C h a n g e d E v e n t H a n d l e r handler = PropertyChanged ;
if ( null != handler )
{
handler ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
propertyName ) ) ;
}
}

5
6
7
8
9
10
11
12
13

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

14
15
16
17
18
19
20

21

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

22

private List < string > langues ;


public List < string > Langues
{
get { return langues ; }
set { Noti fyPro pertyC hange d ( ref langues , value ) ; }
}

23
24
25
26
27
28
29

public MainPage ()
{
I nitializeComponent () ;
DataContext = this ;

30
31
32
33
34
35
36
37

Langues = new List < string > { " Fran ais " , " English " , "
Deutsch " , " Espa ol " };

Nous obtenons la figure 26.3.


Et lorsque nous cliquons dessus, il se dplie pour nous permettre de slectionner un
lment, comme vous pouvez le voir la figure 26.4.
Pour la slection, ce contrle fonctionne comme la ListBox. Nous pouvons prslectionner un lment et tre informs de quel lment est slectionn. Donnons un nom
notre ListPicker et abonnons-nous lvnement SelectionChanged :
1

< toolkit : ListPicker ItemsSource = " { Binding Langues } " x : Name = "
Liste " SelectionChanged = " Lis te _Se le cti onC ha nge d " / >

386

LISTPICKER

Figure 26.3 Le ListPicker pli

Figure 26.4 Le ListPicker dpli nous permet de slectionner un lment


387

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE


Et dans le code-behind :
1
2
3
4

public MainPage ()
{
I ni t i alizeComponent () ;
DataContext = this ;

5
6
7
8

Langues = new List < string > { " Fran ais " , " English " , "
Deutsch " , " Espa ol " };
Liste . SelectedIndex = 2 ;

9
10
11
12
13
14
15
16

private void Li ste _Se le cti on Cha ng ed ( object sender ,


S e l e c t io n C h a n g e d E ve n t A r g s e )
{
if ( Liste . SelectedItem != null )
{
MessageBox . Show ( Liste . SelectedItem . ToString () ) ;
}
}

Nous obtenons la figure 26.5.

Figure 26.5 Slection dun lement du ListPicker


Dans cet exemple, lorsquon clique dans le ListPicker, la liste se droule pour nous
montrer tous les lments de la liste. Il existe un autre mode de slection : le mode
plein cran. Il suffit de changer dans le XAML :
388

LISTPICKER
1

< toolkit : ListPicker ItemsSource = " { Binding Langues } " Header = "
Langue : " FullModeHeader = " Choisir une langue : "
ExpansionMode = " FullScreenOnly " / >

Remarquons la proprit ExpansionMode qui permet de forcer la slection dans un


mode plein cran. Nous pouvons galement donner un titre notre ListPicker grce
la proprit Header, titre qui peut tre diffrent si nous sommes en mode plein cran
(voir la figure 26.6).

Figure 26.6 Slection en mode plein cran


Comme je lai dj dit, il est possible de fournir un modle pour chaque lment et ceci
grce aux proprits ItemTemplate et FullModeItemTemplate. Par exemple, changeons le type de notre liste dlments :
1
2
3
4
5
6

private List < Element > langues ;


public List < Element > Langues
{
get { return langues ; }
set { N o t ifyPro perty Change d ( ref langues , value ) ; }
}

Avec la classe Element suivante :


1
2
3
4

public class Element


{
public string Langue { get ; set ; }
public string Code { get ; set ; }

389

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE


5
6

public Uri Url { get ; set ; }

Que nous alimentons ainsi :


1
2
3
4
5
6
7

Langues = new List < Element >


{
new Element { Code = " FR " , Langue = " Fran ais " , Url = new
Uri ( " / Assets / Images / france . png " , UriKind . Relative ) } ,
new Element { Code = " US " , Langue = " English " , Url = new
Uri ( " / Assets / Images / usa . png " , UriKind . Relative ) } ,
new Element { Code = " DE " , Langue = " Deutsch " , Url = new
Uri ( " / Assets / Images / allemagne . png " , UriKind . Relative ) } ,
new Element { Code = " ES " , Langue = " Espa ol " , Url = new
Uri ( " / Assets / Images / espagne . png " , UriKind . Relative ) } ,
};

sachant que les images correspondant aux drapeaux de chaque langue sont inclues dans
le rpertoire Images, sous le rpertoire Assets, dans notre solution (en ayant chang
laction de gnration Contenu ). Je peux alors modifier le XAML pour avoir :
1

2
3
4
5
6
7
8
9
10
11
12
13
14
15

16
17
18
19
20

< toolkit : ListPicker ItemsSource = " { Binding Langues } " Header = "
Langue : " FullModeHeader = " Choisir une langue : "
ExpansionMode = " FullScreenOnly " >
< toolkit : ListPicker . ItemTemplate >
< DataTemplate >
< StackPanel Orientation = " Horizontal " >
< Border BorderThickness = " 2 " BorderBrush = " Beige "
Background = " Azure " >
< TextBlock Text = " { Binding Code } " Foreground
= " Blue " / >
</ Border >
< TextBlock Margin = " 20 0 0 0 " Text = " { Binding
Langue } " / >
</ StackPanel >
</ DataTemplate >
</ toolkit : ListPicker . ItemTemplate >
< toolkit : ListPicker . FullModeItemTemplate >
< DataTemplate >
< StackPanel Orientation = " Horizontal " >
< TextBlock Margin = " 20 0 0 0 " Text = " { Binding
Langue } " Style = " { StaticResource
PhoneTextTitle1Style } " / >
< Image Source = " { Binding Url } " / >
</ StackPanel >
</ DataTemplate >
</ toolkit : ListPicker . FullModeItemTemplate >
</ toolkit : ListPicker >

Ce qui donnera la page prsente dans la figure 26.7.


Ainsi que dans le mode de slection plein cran (voir la figure 26.8).
390

LISTPICKER

Figure 26.7 Utilisation des templates du ListPicker

Figure 26.8 Le template plein cran


391

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE


Ce ListPicker est dfinitivement bien pratique lorsquil sagit de permettre de choisir
entre plusieurs lments, dans une liste relativement courte. De plus, ses diffrents
modes de slection offrent une touche supplmentaire vos applications.

WrapPanel
Au mme titre que le StackPanel, le WrapPanel du toolkit est un conteneur de contrles
qui a une fonctionnalit intressante. Il est capable de passer automatiquement la ligne
ou la colonne suivante si les contrles prsents lintrieur dpassent du conteneur.
Prenons par exemple le XAML suivant et rutilisons les images de lexemple prcdent :
1
2
3
4
5
6

< StackPanel Orientation = " Horizontal " >


< Image Source = " / Assets / Images / france . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / usa . png " Width = " 150 " Height = "
150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / allemagne . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / espagne . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
</ StackPanel >

Vous pouvez voir le rsultat la figure 26.9.

Figure 26.9 Lcran nest pas assez large pour voir les 4 images avec un StackPanel
392

WRAPPANEL
Oh diantre, cest plutt embtant, on ne voit que 3 images sur les 4 promises. . . ! Eh
oui, il ny a pas assez de place sur lcran et la troisime image dpasse du conteneur.
Changeons maintenant juste le conteneur et remplaons-le par un WrapPanel :
1
2
3
4
5
6

< toolkit : WrapPanel >


< Image Source = " / Assets / Images / france . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / usa . png " Width = " 150 " Height = "
150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / allemagne . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / espagne . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
</ toolkit : WrapPanel >

Nous aurons la figure 26.10.

Figure 26.10 Le WrapPanel passe automatiquement la ligne sil ny a pas assez de


place
Et miracle, toutes les images sont dsormais prsentes. Vous avez compris que cest
le WrapPanel qui a fait automatiquement passer les images la ligne afin quelles
apparaissent lcran. Son but : faire en sorte que tout tienne lcran !
Cela fonctionne galement en orientation verticale :
1
2

< toolkit : WrapPanel Orientation = " Vertical " >


< Image Source = " / Assets / Images / france . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >

393

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE


3
4
5
6

< Image Source = " / Assets / Images / usa . png " Width = " 150 " Height = "
150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / allemagne . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
< Image Source = " / Assets / Images / espagne . png " Width = " 150 "
Height = " 150 " Margin = " 10 " / >
</ toolkit : WrapPanel >

Ce qui donnera la figure 26.11.

Figure 26.11 Le WrapPanel en orientation verticale

Le WrapPanel est trs pratique combin avec une ListBox ou mme un ListPicker.
Dans lexemple fourni avec le toolkit, on peut le voir utilis lintrieur dun Pivot.
Nhsitez pas vous servir de ce conteneur ds que vous aurez besoin dafficher une
liste dlments dont vous ne maitrisez pas forcment le nombre.
Et je ne sais pas si vous vous souvenez, mais je vous ai fait une capture dcran prcdemment lorsque je parlais des diffrentes rsolutions des Windows Phone o javais
ajout plein de boutons les uns la suite des autres. Ils taient dans un WrapPanel et
salignaient alors automatiquement, les uns la suite des autres.
394

LONGLISTSELECTOR

LongListSelector
Avant de sarrter sur lexploration des contrles de ce toolkit, regardons encore le
contrle LongListSelector. Bien que jaie choisi de prsenter ce contrle dans le chapitre sur le toolkit Windows Phone, le LongListSelector existe dans le SDK de Windows Phone 8. Eh oui, cest comme pour le ProgressBar, voici un contrle qui nexistait
pas avec le SDK pour Windows Phone 7, qui a t cr et approuv par la communaut, et qui trouve naturellement sa place dans le SDK pour Windows Phone 8. Il
permet damliorer encore la ListBox, notamment quand celle-ci contient normment
de valeurs. Grce lui, nous pouvons regrouper des valeurs afin que chacune soit plus
facilement accessible. Ce type de contrle est utilis par exemple avec les contacts dans
notre tlphone, lorsque nous commenons en avoir beaucoup.
La figure 26.12 montre le rsultat que nous allons obtenir la fin de ce chapitre, les
prnoms sont regroups suivant leur premire lettre.

Figure 26.12 Les prnoms sont regroups avec le LongListSelector


Et lors dun clic sur un groupe, nous obtenons la liste des groupes o nous pouvons
slectionner un lment pour latteindre plus rapidement (voir la figure 26.13).
Pour dmontrer ce fonctionnement, prenons par exemple la liste des 50 prnoms fminins de bb les plus donns en 2012 :
1

List < string > liste = new List < string > { " Emma " , " Manon " , " Chlo
" , " Camille " , " L a " , " Lola " , " Louise " , " Zo " , " Jade " , " Clara
" , " Lena " , " Eva " , " Ana s " , " Cl mence " , " Lilou " , " In s " , "

395

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE

Figure 26.13 La liste des groupes du LongListSelector


Juliette " , " Ma lys " , " Lucie " , " Alice " , " Sarah " , " Romane " , "
Jeanne " , " Margaux " , " Mathilde " , " Elise " , " L onie " , " Louna " ,
" Lisa " , " Ambre " , " Anna " , " Justine " , " Lily " , " Pauline " , "
Laura " , " Charlotte " , " Rose " , " Julia " , " Noemie " , " Elo se " , "
Lou " , " Alicia " , " El na " , " Margot " , " Elsa " , " Louane " , " Elisa "
, " Nina " , " Marie " , " Agathe " };

Le principe est de faire une liste de liste groupe dlments grce loprateur Linq
join :
IEnumerable < List < string > > prenoms = from prenom in liste
group prenom by prenom [ 0 ]. ToString () into p
orderby p . Key
select new List < string >( p ) ;

1
2
3
4
5

ListePrenoms = prenoms . ToList () ;

Ici, jai choisi de regrouper les prnoms en fonction de leur premire lettre, mais on pourrait trs bien imaginer une liste de villes regroupes en fonction de leur pays dappartenance. . . ou quoi que ce soit dautre. . . Pour utiliser le binding avec ce LongListSelector,
nous devons avoir la proprit suivante :
1
2
3

private List < List < string > > listePrenoms ;


public List < List < string > > ListePrenoms
{

396

LONGLISTSELECTOR
4
5
6

get { return listePrenoms ; }


set { N o t ifyPro perty Change d ( ref listePrenoms , value ) ; }

Cot XAML cest un peu plus compliqu. Nous avons premirement besoin de lier la
proprit ItemsSource du LongListSelector notre proprit ListePrenoms :
1
2

< phone : LongListSelector ItemsSource = " { Binding ListePrenoms } " >


</ phone : LongListSelector >

Noubliez pas quil sagit dun contrle Windows Phone 8,


et que son espace de nom est inclus dans la page avec :
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=
Microsoft.Phone". Si vous utilisez le SDK pour Windows Phone 7, il vous
suffira dutiliser le contrle du toolkit, mais il y aura quand mme quelques
adaptations faire sur les templates.
Il faut maintenant indiquer plusieurs choses, premirement que le contrle est en mode
liste, quil accepte de grouper les lments et quil a la possibilit dobtenir la liste des
groupes. Cela se fait grce ces proprits :
1

< phone : LongListSelector ItemsSource = " { Binding ListePrenoms } "


LayoutMode = " List " IsGroupingEnabled = " True " JumpListStyle = " {
StaticResource L o n g L i s t S e l e c t o r J u m p L i s t S t y l e } " >
</ phone : LongListSelector >

Notez la prsence du style LongListSelectorJumpListStyle qui est dfinir.


Ensuite, il y a plusieurs modles crer. Premirement le modle des lments, cest-dire des prnoms. Il sagit du modle ItemTemplate. Ici, un simple TextBlock suffit :
1
2
3
4
5
6
7

< phone : LongListSelector ItemsSource = " { Binding ListePrenoms } " >


< phone : LongListSelector . ItemTemplate >
< DataTemplate >
< TextBlock Text = " { Binding } " FontSize = " 26 " Margin = "
12 , - 12 , 12 , 6 " / >
</ DataTemplate >
</ phone : LongListSelector . ItemTemplate >
</ phone : LongListSelector >

Ensuite, nous rajoutons un modle pour les groupes visibles dans la liste. Il sagit du
modle GroupHeaderTemplate. Ici nous devons afficher la premire lettre du groupe,
entoure dans un rectangle dont le bord correspond la couleur daccentuation :
1
2
3
4
5

< phone : LongListSelector ItemsSource = " { Binding ListePrenoms } " >


...
< phone : LongListSelector . GroupHeaderTemplate >
< DataTemplate >
< Border BorderBrush = " { Binding Converter ={
StaticResource BackgroundConverter }} "

397

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE


BorderThickness = " 2 " Width = " 60 " Margin = " 10 "
HorizontalAlignment = " Left " >
< TextBlock Text = " { Binding Converter ={
StaticResource ListConverter }} " FontSize = " 40
" Foreground = " { Binding Converter ={
StaticResource BackgroundConverter }} "
HorizontalAlignment = " Center "
VerticalAlignment = " Center " / >
</ Border >
</ DataTemplate >
</ phone : LongListSelector . GroupHeaderTemplate >
</ phone : LongListSelector >

7
8
9
10

Remarquez qutant donn que la liaison se fait sur la List<string>, jutilise un converter pour nafficher que la premire lettre. Ce converter est dfini classiquement en
ressource :
1

< converter : ListConverter x : Key = " ListConverter " / >

Avec limport despace de nom adquat :


xmlns : converter = " clr - namespace : DemoToolkit "

Le converter quant lui est trs simple, il prend simplement la premire lettre du
premier lment de la liste :
1
2
3
4
5
6
7
8
9

public class ListConverter : IValueConverter


{
public object Convert ( object value , Type targetType , object
parameter , CultureInfo culture )
{
List < string > liste = ( List < string >) value ;
if ( liste == null || liste . Count == 0 )
return string . Empty ;
return liste [ 0 ][ 0 ];
}

10
11
12
13
14
15

public object ConvertBack ( object value , Type targetType ,


object parameter , CultureInfo culture )
{
throw new N o tI mp le m en te d Ex ce pt i on () ;
}

Notez lutilisation du converter BackgroundConverter qui permet dobtenir la couleur


daccentuation. Ce converter est dfini dans les ressources et est un converter systme.
Dclarons-le ainsi que son petit frre, le ForegroundConverter, qui permet davoir la
couleur du thme :
1

< phone : J u m p L i s t I t e m B a c k g r o u n d C o n v e r t e r x : Key = "


B ac k g roundConverter " / >

398

LONGLISTSELECTOR
2

< phone : J u m p L i s t I t e m F o r e g r o u n d C o n v e r t e r x : Key = "


F or e gr o u ndConverter " / >

Enfin, il reste le style de la liste des groupes, qui apparat lorsquon clique sur un groupe.
Il sagit du style LongListSelectorJumpListStyle que nous avons prcdemment vu,
qui est galement mettre en ressources :
1
2
3
4
5
6
7
8
9
10

11
12

13
14
15
16
17
18

< Style x : Key = " L o n g L i s t S e l e c t o r J u m p L i s t S t y l e " 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 Converter ={
StaticResource ListConverter }} "
FontFamily = " {
StaticResource
P ho ne Fo n tF am i ly Se mi B ol d
}"
FontSize = " 48 " Padding = "
6"
Foreground = " { Binding
Converter ={
StaticResource
ForegroundConverter
}} "
VerticalAlignment = "
Center " / >
</ Border >
</ DataTemplate >
</ Setter . Value >
</ Setter >
</ Style >

Ici, le principe est le mme, on utilise le converter pour afficher la premire lettre. Elle
sera dans un cadre au fond du thme daccentuation. Ce qui nous donne le code XAML
complet suivant :
1

2
3
4
5

< phone : LongListSelector ItemsSource = " { Binding ListePrenoms } "


LayoutMode = " List " IsGroupingEnabled = " True " JumpListStyle = " {
StaticResource L o n g L i s t S e l e c t o r J u m p L i s t S t y l e } " >
< phone : LongListSelector . ItemTemplate >
< DataTemplate >
< TextBlock Text = " { Binding } " FontSize = " 26 " Margin = "
12 , - 12 , 12 , 6 " / >
</ DataTemplate >

399

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE


</ phone : LongListSelector . ItemTemplate >
< phone : LongListSelector . GroupHeaderTemplate >
< DataTemplate >
< Border BorderBrush = " { Binding Converter ={
StaticResource BackgroundConverter }} "
BorderThickness = " 2 " Width = " 60 " Margin = " 10 "
HorizontalAlignment = " Left " >
< TextBlock Text = " { Binding Converter ={
StaticResource ListConverter }} " FontSize = " 40
" Foreground = " { Binding Converter ={
StaticResource BackgroundConverter }} "
HorizontalAlignment = " Center "
VerticalAlignment = " Center " / >
</ Border >
</ DataTemplate >
</ phone : LongListSelector . GroupHeaderTemplate >
</ phone : LongListSelector >

6
7
8
9

10

11
12
13
14

Cest vrai, je le reconnais, il fait un peu peur ! Nous allons le simplifier en mettant nos
DataTemplate en ressource. Il suffit de les dclarer ainsi :
1
2

< phone : P h oneApplicationPage . Resources >


...

< DataTemplate x : Key = " ModeleElement " >


< TextBlock Text = " { Binding } " FontSize = " 26 " Margin = " 12 , 12 , 12 , 6 " / >
</ DataTemplate >
< DataTemplate x : Key = " ModeleGroupe " >
< Border BorderBrush = " { Binding Converter ={ StaticResource
BackgroundConverter }} " BorderThickness = " 2 " Width = "
60 " Margin = " 10 " HorizontalAlignment = " Left " >
< TextBlock Text = " { Binding Converter ={ StaticResource
ListConverter }} " FontSize = " 40 " Foreground = " {
Binding Converter ={ StaticResource
BackgroundConverter }} " HorizontalAlignment = "
Center " VerticalAlignment = " Center " / >
</ Border >
</ DataTemplate >

4
5
6
7
8

10
11
12

...
</ phone : P honeApplicationPage . Resources >

13
14

Noubliez pas quil faut imprativement une cl un lment prsent dans les ressources.
Nous pourrons alors simplifier lcriture du contrle ainsi :
1

< phone : LongListSelector ItemsSource = " { Binding ListePrenoms } "


LayoutMode = " List " IsGroupingEnabled = " True " JumpListStyle = " {
StaticResource L o n g L i s t S e l e c t o r J u m p L i s t S t y l e } " ItemTemplate =
" { StaticResource ModeleElement } " GroupHeaderTemplate = " {
StaticResource ModeleGroupe } " / >

400

AVANTAGES & LIMITES DU TOOLKIT


Ce qui est beaucoup plus court et qui permet galement de potentiellement rutiliser
les modles. . . Je nai pas dcrit toutes les possibilits de ce contrle. Sachez quil est
possible de dfinir un modle pour raliser un entte de liste et un pied de liste. Elle
peut galement se consulter plat , en utilisant la proprit :
1

IsFlatList = " True "

Ce contrle est plutt pratique. Jai choisi dutiliser un converter pour afficher le titre
dun groupe en utilisant la premire lettre des prnoms. Ce qui se fait aussi cest
de construire un objet directement liable possdant une proprit Titre et une liste
dlments, en gnral quelque chose comme a :
1
2
3
4
5
6
7

public class Group <T > : List <T >


{
public Group ( string nom , IEnumerable <T > elements )
: base ( elements )
{
this . Titre = nom ;
}

8
9
10

public string Titre { get ;

set ;

Avantages & limites du toolkit


Bon, je marrte l pour cette petite prsentation limite du toolkit. Nous aurons loccasion de revoir des choses du toolkit de temps en temps dans la suite du cours. Il
rajoute des fonctionnalits trs intressantes qui manquaient aux dveloppeurs souhaitant raliser des applications denvergure avec Windows Phone. Il ne sagit bien sr pas
de contrles officiels, mais ils sont largement reconnus par la communaut. Ce toolkit,
notamment dans sa version XAML, sert parfois galement de bac sable pour les dveloppeurs Microsoft afin de fournir des contrles sans quils ne fassent partie intgrante
de la bibliothque de contrles officielle.
Il y a tout une communaut active et dynamique qui pousse afin que ces contrles aient
le moins de bug possible, cependant nous ne sommes pas labri dun comportement
indsirable. . .

Les autres toolkits


Le toolkit pour Windows Phone nest pas la seule bibliothque de contrles du march. Il en existe dautres. Citons par lexemple la bibliothque Coding4Fun - http:
//coding4fun.codeplex.com/ qui est gratuite et qui fournit des contrles plutt sympathiques. Notons par exemple un contrle qui permet de slectionner une date ou une
heure (voir la figure 26.14).
401

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE

Figure 26.14 Le choix dune dure grce au toolkit Coding4Fun

402

LES AUTRES TOOLKITS


Dautres sont payants et dvelopps par des socits tierces, citons par exemple les
contrles de la socit Telerik ou encore de Syncfusion. Ils fournissent des contrles qui
nous simplifient la tche et qui permettent de gagner du temps dans nos dveloppements. Nhsitez pas y jeter un coup dil, cest souvent plus intressant dacheter
un contrle dj tout fait que de passer du temps (beaucoup de temps !) le raliser.

En rsum
Le toolkit pour Windows Phone est une bibliothque de contrles fournie gratuitement qui permet denrichir vos applications avec des contrles trs pratiques.
Il y a beaucoup de bibliothques qui existent et qui proposent des contrles
complets, sadaptant beaucoup de situation. Certaines sont gratuites, dautres
payantes. Nhsitez pas les consulter, il y a souvent de bonnes surprises.

403

CHAPITRE 26. LE TOOLKIT WINDOWS PHONE

404

Chapitre

27

Le contrle de cartes (Map)


Difficult :
Avant de commencer regarder le contrle Map, il faut savoir quil est diffrent entre
la version du SDK pour dvelopper pour Windows Phone 7 et celui pour dvelopper pour
Windows Phone 8. Le premier est le contrle de cartes de Microsoft alors que pour Windows
Phone 8, cest celui ralis par Nokia qui est utilis. Ils se ressemblent cependant fortement
dans leurs utilisations, mais celui de Nokia a une particularit intressante lui permettant de
faire des choses hors connexion que ne permet pas celui de Microsoft. Mais rassurez-vous,
nul besoin de possder un tlphone Nokia pour pouvoir sen servir, il fonctionne pour tous
les tlphones Windows Phone 8.

405

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


Je vais prsenter ici le contrle de Windows Phone 8, mais sachez quil
est galement possible dutiliser lancien contrle avec des applications Windows Phone 8. Il sagit dun contrle trs complet et bien pratique :
le contrle Map - http://msdn.microsoft.com/en-us/library/windowsphone/
develop/microsoft.phone.maps.controls.map(v=vs.105).aspx. Il permet dembarquer une carte dans notre application. Nous allons pouvoir afficher la carte de
France, la carte dEurope, etc . . . dfinir des positions, calculer des itinraires . . . Bref,
tout plein de choses qui peuvent servir nos tlphones quips de GPS.
Dcouvrons prsent ce fameux contrle.

Prsentation et utilisation
Pour utiliser le contrle Map, le plus simple est de le faire glisser depuis la boite outils
pour le mettre par exemple lintrieur de la grille, comme indiqu la figure 27.1.

Figure 27.1 Le contrle de carte dans la boite outils


Visual Studio nous ajoute le contrle et nous pouvons voir dans la fentre de design
une mini carte du monde (voir la figure 27.2).
Aprs lajout du contrle, si nous regardons le XAML, Visual Studio nous a ajout
lespace de nom suivant :
xmlns : Controls = " clr - namespace : Microsoft . Phone . Maps . Controls ;
assembly = Microsoft . Phone . Maps "

ainsi que le XAML positionnant le contrle :


1

< Controls : Map / >

Personnellement, je change le Controls en carte et je donne le nom Carte


mon contrle :
406

PRSENTATION ET UTILISATION

Figure 27.2 Le contrle map dans le designer

< carte : Map Name = " Carte " / >

Si vous dmarrez lmulateur tout de suite, vous allez avoir un problme. Visual Studio
nous lve une exception de type :
1

Une exception de premi re chance de type ' System . Windows . Markup


. XamlParseException ' s ' est produite dans System . Windows . ni .
dll

Mais si on fouille un peu dans les InnerException, on trouve plutt :


1

Access to Maps requires ID_CAP_MAP to be defined in the


manifest

En fait, pour utiliser le contrle de cartes, nous devons dclarer notre application comme
utilisatrice du contrle de cartes. Cela permettra notamment aux personnes qui veulent
tlcharger notre application de savoir quelle utilise le contrle de carte. On appelle
cela les capacits. Pour dclarer une capacit, nous allons avoir besoin de double-cliquer
sur le fichier WMAppManifest.xml (sous Properties dans lexplorateur de solutions) et
daller dans longlet Capacits. Ensuite, il faut cocher la capacit ID_CAP_MAP, comme
lindique la figure 27.3.
Et voil, maintenant en dmarrant lmulateur, nous allons avoir une jolie carte du
monde (si vous tes connects Internet) - voir la figure 27.4.
407

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)

Figure 27.3 Activer la capacit dutilisation de carte

Figure 27.4 La carte saffiche dans lmulateur


408

INTERACTIONS AVEC LE CONTRLE

Interactions avec le contrle


Et si nous prenions le contrle de la carte ? On peut tout faire avec cette carte, comme
se positionner un emplacement prcis grce des coordonnes GPS, ajouter des
marques pour pingler des lieux, zoomer, d-zoomer, etc. Pour illustrer tout cela, nous
allons manipuler cette fameuse carte. Premire chose faire, nous allons afficher des
boutons pour zoomer et d-zoomer. Utilisons par exemple une barre dapplication pour
ce faire et intgrons-y les deux icnes suivantes, prsentes dans le rpertoire du SDK :
add.png
minus.png
Nhsitez pas aller faire un tour dans le chapitre de la barre dapplication si vous
avez un doute sur la marche suivre. Voici donc le XAML de la barre dapplication
utilise :
1
2
3
4
5
6

< phone : P h on e A pplicationPage . ApplicationBar >


< shell : ApplicationBar IsVisible = " True " IsMenuEnabled = " True "
>
< shell : A p p li c a ti o n Ba r I co n B ut t o n IconUri = " / Assets / Icones
/ add . png " Text = " Zoom " Click = " Zoom_Click " / >
< shell : A p p li c a ti o n Ba r I co n B ut t o n IconUri = " / Assets / Icones
/ minus . png " Text = " D - zoom " Click = " Dezoom_Click " / >
</ shell : ApplicationBar >
</ phone : P ho n e ApplicationPage . ApplicationBar >

Comme dhabitude, prenez garde au chemin des icnes. Et le code-behind associ :


1
2
3
4
5
6

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I ni t i alizeComponent () ;
}

private void Zoom_Click ( object sender , EventArgs e )


{
Carte . ZoomLevel ++;
}

8
9
10
11
12
13
14
15
16
17

private void Dezoom_Click ( object sender , EventArgs e )


{
Carte . ZoomLevel - -;
}

Vous pouvez voir que nous pouvons facilement zoomer ou d-zoomer en utilisant les
boutons de la barre dapplication, et tout a grce la proprit ZoomLevel. De plus,
nous pouvons nous dplacer sur la carte en faisant glisser notre doigt (ou notre souris !),
comme indiqu la figure 27.5.
409

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)

Figure 27.5 Dplacement et zoom pour afficher la carte de Paris

Tout au long de lutilisation, sur un tlphone, nous pouvons galement zoomer et dzoomer grce au geste qui consiste ramener ses deux doigts vers le centre de lcran
ou au contraire les carter pour d-zoomer (geste que lon appelle le Pinch-to-zoom
ou le Stretch-to-zoom). Cest par contre un peu plus difficile faire la souris dans
lmulateur, sachant que nous avons quand mme la possibilit de zoomer en doublecliquant. . . Mais cela sera quand mme bien plus simple dans lmulateur avec la barre
dapplication.
Nous pouvons aussi centrer la carte un emplacement prcis. Pour cela, on utilise la proprit Center qui est du type GeoCoordinate - http://msdn.microsoft.
com/fr-fr/library/system.device.location.geocoordinate.aspx. Il sagit dune
classe contenant des coordonnes GPS, avec notamment la latitude et la longitude.
Nous pouvons utiliser des coordonnes pour centrer la carte un emplacement dsir.
Par exemple :
1
2
3
4
5
6

public MainPage ()
{
I ni t i alizeComponent () ;
Carte . ZoomLevel = 17 ;
Carte . Center = new GeoCoordinate { Latitude = 48 . 858115 ,
Longitude = 2 . 294710 };
}

410

INTERACTIONS AVEC LE CONTRLE


qui centre la carte sur la tour Eiffel. On noubliera pas dinclure lespace de nom
permettant dutiliser le type GeoCoordinate :
1

using System . Device . Location ;

La carte est galement disponible en mode satellite (ou arien), il suffit de changer la
proprit CartographicMode du contrle pour passer en mode arien avec :
1

Carte . CartographicMode = MapCartographicMode . Aerial ;

Disponible avec limport despace de nom suivant :


1

using Microsoft . Phone . Maps . Controls ;

Vous pouvez voir le rsultat la figure 27.6.

Figure 27.6 La carte en mode arien


Le mode route, par dfaut, correspond la valeur dnumration MapCartographicMode.Road,
sachant quil existe galement la valeur Terrain et Hybrid. Nous pouvons inclure des
points de repres dans la carte grce la proprit LandmarksEnabled en la passant
True. la figure 27.7, la mme carte sans et avec points de repres.
Vous noterez la diffrence ! Vous pouvez galement indiquer un degr dinclinaison
grce la proprit Pitch. Voici par exemple la figure 27.8 la carte avec un degr
dinclinaison de 0 et de 45.
Nous pouvons galement inclure les informations pitonnes, comme les escaliers grce
la proprit PedestrianFeaturesEnabled en la passant True (voir la figure 27.9).
411

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)

Figure 27.7 La carte peut afficher des points de repres

Figure 27.8 La carte sans et avec un degr dinclinaison


412

EPINGLER DES POINTS DINTRT

Figure 27.9 La carte avec les informations pitonnes


Le zoom doit tre suprieur 7 et le pitch suprieur 25 pour avoir accs
cette fonctionnalit.

Epingler des points dintrt


Une des grandes forces du contrle Map est quon peut dessiner nimporte quoi par
dessus, pour par exemple pingler des points dintrts sur la carte. Cela permet de
mettre en valeur certains lieux et pourquoi pas afficher une information contextuelle
complmentaire. Le principe est simple, il suffit dajouter des coordonnes GPS et une
punaise apparat automatiquement cet emplacement. Cela ne se fait par contre pas
tout seul, mais grce au Windows Phone Toolkit que nous venons de voir. Ajoutez une
rfrence celui-ci, comme nous venons de le faire, et utilisons la classe PushPin qui
reprsente une telle pingle. Il faut dans un premier temps importer lespace de nom :
1

xmlns : toolkitcarte = " clr - namespace : Microsoft . Phone . Maps . Toolkit ;


assembly = Microsoft . Phone . Controls . Toolkit "

puis ajouter des lments PushPin :


1
2

< carte : Map Name = " Carte " >


< toolkitcarte : MapExtensions . Children >

413

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


< toolkitcarte : Pushpin GeoCoordinate = " 48 . 842276 , 2 .
321747 " / >
< toolkitcarte : Pushpin GeoCoordinate = " 48 . 858115 , 2 .
294710 " / >
< toolkitcarte : Pushpin GeoCoordinate = " 48 . 873783 , 2 .
294930 " / >
</ toolkitcarte : MapExtensions . Children >
</ carte : Map >

3
4
5
6
7

Et nous aurons ce rsultat (voir la figure 27.10).

Figure 27.10 Les punaises pour pingler des points dintrts


Pour avoir le mme rendu avec le code behind, il suffit davoir le XAML suivant :
1

< carte : Map Name = " Carte " / >

Et le code suivant :
1
2
3
4
5

public MainPage ()
{
I ni t i alizeComponent () ;
Carte . ZoomLevel = 12 ;
Carte . Center = new GeoCoordinate { Latitude = 48 . 863134 ,
Longitude = 2 . 320518 };

6
7
8

414

var elts = MapExtensions . GetChildren ( Carte ) ;


MapExtensions . Add ( elts , new Pushpin () , new GeoCoordinate {
Longitude = 2 . 321747 , Latitude = 48 . 842276 }) ;

EPINGLER DES POINTS DINTRT


9
10
11

MapExtensions . Add ( elts , new Pushpin () , new GeoCoordinate {


Longitude = 2 . 294710 , Latitude = 48 . 858115 }) ;
MapExtensions . Add ( elts , new Pushpin () , new GeoCoordinate {
Longitude = 2 . 294930 , Latitude = 48 . 873783 }) ;

La punaise est bien sr stylisable souhait, car celle-l est un peu triste. Prenez par
exemple celle-ci, permettant de remplacer la grosse punaise par un petit point bleu :
1
2
3
4
5
6
7
8
9
10

< phone : P h on e A pplicationPage . Resources >


< ControlTemplate x : Key = " P ush pin Co ntr ol Tem pla te " TargetType =
" toolkitcarte : Pushpin " >
< Ellipse Fill = " { TemplateBinding Background } "
HorizontalAlignment = " Center "
VerticalAlignment = " Center "
Width = " 20 "
Height = " 20 "
Stroke = " { TemplateBinding Foreground } "
StrokeThickness = " 3 " / >
</ ControlTemplate >

11
12
13
14
15
16
17

< Style TargetType = " toolkitcarte : Pushpin " x : Key = "


PushpinControlTemplateEllipse ">
< Setter Property = " Template " Value = " { StaticResource
P u sh pin Con tr olT em pla te } " / >
< Setter Property = " Background " Value = " Blue " / >
< Setter Property = " Foreground " Value = " White " / >
</ Style >
</ phone : P ho n e ApplicationPage . Resources >

Que nous pourrons utiliser ainsi :


1
2
3

6
7

< carte : Map Name = " Carte " >


< toolkitcarte : MapExtensions . Children >
< toolkitcarte : Pushpin GeoCoordinate = " 48 . 842276 , 2 .
321747 " Style = " { StaticResource
PushpinControlTemplateEllipse }" />
< toolkitcarte : Pushpin GeoCoordinate = " 48 . 858115 , 2 .
294710 " Style = " { StaticResource
PushpinControlTemplateEllipse }" />
< toolkitcarte : Pushpin GeoCoordinate = " 48 . 874956 , 2 .
350690 " Style = " { StaticResource
PushpinControlTemplateEllipse }" />
</ toolkitcarte : MapExtensions . Children >
</ carte : Map >

ou la mme chose avec le code behind :


1
2

var elts = MapExtensions . GetChildren ( Carte ) ;


MapExtensions . Add ( elts , new Pushpin { Style = Resources [ "
P u s h p i n C o n t r o l T e m p l a t e E l l i p s e " ] as Style } , new

415

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


GeoCoordinate { Longitude = 2 . 321747 , Latitude = 48 . 842276
}) ;
MapExtensions . Add ( elts , new Pushpin { Style = Resources [ "
P u s h p i n C o n t r o l T e m p l a t e E l l i p s e " ] as Style } , new
GeoCoordinate { Longitude = 2 . 294710 , Latitude = 48 . 858115
}) ;
MapExtensions . Add ( elts , new Pushpin { Style = Resources [ "
P u s h p i n C o n t r o l T e m p l a t e E l l i p s e " ] as Style } , new
GeoCoordinate { Longitude = 2 . 350690 , Latitude = 48 . 874956
}) ;

Ce qui donne le rsultat affich la figure 27.11.

Figure 27.11 Les punaises ont du style !


Do vient cette punaise OpenClassrooms ? Simplement de mon autre style utilisant
une image comme punaise :
1
2

< ControlTemplate x : Key = " P u s h p i n C o n t r o l T e m p l a t e I m a g e " TargetType


= " toolkitcarte : Pushpin " >
< Image Source = " http :// open -e - education - 2013 . openclassrooms .
com / img / logos / logo - openclassrooms . png " Width = " 60 " Height
= " 60 " / >
</ ControlTemplate >

4
5

< Style TargetType = " toolkitcarte : Pushpin " x : Key = "


PushpinControlTemplateImageStyle ">

416

EPINGLER DES POINTS DINTRT


6
7

< Setter Property = " Template " Value = " { StaticResource


PushpinControlTemplateImage }" />
</ Style >

Le logo dOpenClassrooms pour indiquer lemplacement des locaux dOpenClassrooms ?


Cest pas la classe a ? Ajouter des punaises en spcifiant des coordonnes dans le
XAML, cest bien. Mais nous pouvons galement le faire par binding ! Voyez par
exemple avec ce XAML :
1
2
3

4
5

< carte : Map Name = " Carte " >


< toolkitcarte : MapExtensions . Children >
< toolkitcarte : Pushpin GeoCoordinate = " { Binding
PositionTourEiffel } " Style = " { StaticResource
PushpinControlTemplateEllipse }" />
</ toolkitcarte : MapExtensions . Children >
</ carte : Map >

La proprit GeoCoordinate de lobjet Pushpin est lie la proprit PositionTourEiffel,


qui sera du genre :
1
2
3
4
5
6

private GeoCoordinate positionTourEiffel ;


public GeoCoordinate PositionTourEiffel
{
get { return positionTourEiffel ; }
set { N o t ifyPro perty Change d ( ref positionTourEiffel , value ) ;
}
}

Que lon pourra alimenter avec :


1
2
3
4
5
6
7
8

public MainPage ()
{
I ni t i al i zeComponent () ;
Carte . ZoomLevel = 12 ;
Carte . Center = new GeoCoordinate { Latitude = 48 . 863134 ,
Longitude = 2 . 320518 };
Po si ti onT ourEiffel = new GeoCoordinate { Longitude = 2 .
321747 , Latitude = 48 . 842276 };
DataContext = this ;
}

Noubliez pas dimplmenter correctement INotifyPropertyChanged, mais bon, vous


savez faire maintenant. On peut mme leur rajouter un petit texte grce la proprit
Content :
1

< toolkitcarte : Pushpin GeoCoordinate = " { Binding


Po si ti onT ourEiffel } " Content = " { Binding Texte } " / >

Avec :
1
2

private string texte ;


public string Texte

417

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


3

4
5
6

get { return texte ; }


set { Noti fyPro pertyC hange d ( ref texte , value ) ; }

Et :
Texte = " La tour Eiffel " ;

Ce qui donne la figure 27.12.

Figure 27.12 Lgende sur les punaises


Notez que jai retir le style ellipse bleue car ce style ne prenait pas en charge la
proprit Content. Pour ce faire, il faudrait modifier le style pour avoir, par exemple :
1
2
3
4
5
6
7
8
9
10

< ControlTemplate x : Key = " P ush pi nCo ntr ol Tem pl ate " TargetType = "
toolkitcarte : Pushpin " >
< StackPanel >
< ContentPresenter Content = " { TemplateBinding Content } "
/>
< Ellipse Fill = " { TemplateBinding Background } "
HorizontalAlignment = " Center "
VerticalAlignment = " Center "
Width = " 20 "
Height = " 20 "
Stroke = " { TemplateBinding Foreground } "
StrokeThickness = " 3 " / >

418

EPINGLER DES POINTS DINTRT


11
12

</ StackPanel >


</ ControlTemplate >

Et le contrle serait :
1
2
3

4
5

< carte : Map Name = " Carte " >


< toolkitcarte : MapExtensions . Children >
< toolkitcarte : Pushpin GeoCoordinate = " { Binding
PositionTourEiffel } " Style = " { StaticResource
P u s h p i n C o n t r o l T e m p l a t e E l l i p s e } " Content = " { Binding
Texte } " Foreground = " Black " / >
</ toolkitcarte : MapExtensions . Children >
</ carte : Map >

Pour le rsultat, observez la figure 27.13.

Figure 27.13 Le style avec une lgende


Et si on a plusieurs punaises lier ? On pourrait tre tents dutiliser le binding,
mais ce jour ce nest pas fonctionnel. Peut-tre un bug corriger ? On peut quand
mme sen sortir grce au code-behind. La premire chose faire est de dfinir un
MapItemsControl possdant un template :
1
2
3
4
5

< carte : Map Name = " Carte " >


< toolkitcarte : MapExtensions . Children >
< toolkitcarte : MapItemsControl >
< toolkitcarte : MapItemsControl . ItemTemplate >
< DataTemplate >

419

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


< toolkitcarte : Pushpin GeoCoordinate = " {
Binding } " Style = " { StaticResource
PushpinControlTemplateEllipse }" />
</ DataTemplate >
</ toolkitcarte : MapItemsControl . ItemTemplate >
</ toolkitcarte : MapItemsControl >
</ toolkitcarte : MapExtensions . Children >
</ carte : Map >

7
8
9
10
11

Il faudra ensuite lier par exemple une liste de positions ce MapItemsControl via
code-behind :
1
2
3
4
5
6

List < GeoCoordinate > maListeDePositions = new List < GeoCoordinate


>
{
new GeoCoordinate { Longitude = 2 . 321747 , Latitude = 48 .
842276 } ,
new GeoCoordinate { Longitude = 2 . 294710 , Latitude = 48 .
858115 } ,
new GeoCoordinate { Longitude = 2 . 294930 , Latitude = 48 .
873783 }
};

MapItemsControl mapItemsControl = MapExtensions . GetChildren (


Carte ) . OfType < MapItemsControl >() . FirstOrDefault () ;
mapItemsControl . ItemsSource = maListeDePositions ;

8
9

Comme ceci, cela fonctionne et donne le rsultat que vous voyez sur la figure 27.14.
Personnellement, jadore ces punaises !
Elles peuvent galement ragir un clic, ce qui nous laisse lopportunit dafficher par
exemple des informations complmentaires sur cette position. On utilisera lvnement
Tap du contrle Pushpin. Sauf quen gnral, si on utilise une liste de positions comme
on la fait juste au-dessus, nous ne disposons pas dinformations suffisantes pour savoir quelle punaise a t clique. Ce quon peut faire ce moment-l, cest utiliser
la proprit Tag du contrle, qui est une espce dobjet fourre-tout pour passer des
informations.
La proprit Tag est une proprit hrite de la classe FrameworkElement.
Tous les contrles possdent donc cette proprit fourre-tout.
Illustrons ce point en crant une nouvelle classe :
1
2
3
4
5

public class MaPosition


{
public GeoCoordinate Position { get ; set ; }
public string Informations { get ; set ; }
}

420

EPINGLER DES POINTS DINTRT

Figure 27.14 Plusieurs punaises lies par code-behind


Puis, changeons notre proprit MaListeDePositions pour avoir une liste dobjets
MaPosition :
1
2
3
4
5
6

private IEnumerable < MaPosition > maListeDePositions ;


public IEnumerable < MaPosition > MaListeDePositions
{
get { return maListeDePositions ; }
set { N o t ifyPro perty Change d ( ref maListeDePositions , value ) ;
}
}

Qui sera alimente de cette faon :


1
2
3

Ma Li st eDe Po si tions = new List < MaPosition >


{
new MaPosition { Position = new GeoCoordinate { Longitude =
2 . 321747 , Latitude = 48 . 842276 } , Informations = " Tour
Eiffel " } ,
new MaPosition { Position = new GeoCoordinate { Longitude =
2 . 294710 , Latitude = 48 . 858115 } , Informations = " Tour
Montparnasse " } ,
new MaPosition { Position = new GeoCoordinate { Longitude =
2 . 294930 , Latitude = 48 . 873783 } , Informations = " Arc
de triomphe " }
};

421

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


Puis changeons le XAML pour avoir :
1
2
3
4
5
6

7
8
9
10
11

< carte : Map Name = " Carte " >


< toolkitcarte : MapExtensions . Children >
< toolkitcarte : MapItemsControl ItemsSource = " { Binding
PositionList } " >
< toolkitcarte : MapItemsControl . ItemTemplate >
< DataTemplate >
< toolkitcarte : Pushpin GeoCoordinate = " {
Binding Position } " Style = " {
StaticResource
P u s h p i n C o n t r o l T e m p l a t e E l l i p s e } " Tag = " {
Binding Informations } " Tap = " Pushpin_Tap "
/>
</ DataTemplate >
</ toolkitcarte : MapItemsControl . ItemTemplate >
</ toolkitcarte : MapItemsControl >
</ toolkitcarte : MapExtensions . Children >
</ carte : Map >

Notons que la proprit GeoCoordinate de la punaise est lie la proprit Position


de notre classe et que la proprit Tag est lie la proprit Informations. Ce qui
nous permet, dans lvnement associ, de faire :
1
2
3
4

private void Pushpin_Tap ( object sender , GestureEventArgs e )


{
MessageBox . Show ((( FrameworkElement ) sender ) . Tag . ToString () ) ;
}

Et nous aurons ce rsultat (voir la figure 27.15).


Pratique.

Afficher un itinraire
Bonne nouvelle, avec Windows Phone 8, il est trs facile de calculer et dafficher un
itinraire entre deux points. Prenons une carte classique :
1

< maps : Map x : Name = " Carte " / >

Et calculons dans le code-behind un itinraire entre des coordonnes de dpart, disons


la Tour Montparnasse, jusquaux Champs-lyses. Il suffit dutiliser un objet GeocodeQuery - http://msdn.microsoft.com/en-us/library/windowsphone/develop/
jj208581(v=vs.105).aspx et de faire :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private List < GeoCoordinate > coordonnesTrajet ;

4
5

422

public MainPage ()

AFFICHER UN ITINRAIRE

Figure 27.15 Le clic sur une punaise nous dclenche laffichage dun message

I ni t i alizeComponent () ;

Carte . ZoomLevel = 13 ;
Carte . Center = new GeoCoordinate { Latitude = 48 . 863134
, Longitude = 2 . 320518 };
coordonnesTrajet = new List < GeoCoordinate >() ;

9
10
11
12
13
14

Ca lc ulerItineraire () ;

15
16
17
18
19

private void CalculerItineraire ()


{
GeoCoordinate positionDepart = new GeoCoordinate ( 48 .
858115 , 2 . 294710 ) ;
coordonnesTrajet . Add ( positionDepart ) ;

20
21
22
23
24
25

GeocodeQuery geocodeQuery = new GeocodeQuery


{
SearchTerm = " avenue des champs - lys es , Paris "
,
GeoCoordinate = positionDepart
};

26

423

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


27
28

29

geocodeQuery . QueryCompleted +=
geocodeQuery_QueryCompleted ;
geocodeQuery . QueryAsync () ;

30

private void g e o c o d e Q u e r y _ Q u e r y C o m p l e t e d ( object sender ,


QueryCompletedEventArgs < IList < MapLocation > > e )
{
if ( e . Error == null )
{
RouteQuery routeQuery = new RouteQuery () ;
coordonnesTrajet . Add ( e . Result [ 0 ]. GeoCoordinate ) ;
routeQuery . Waypoints = coordonnesTrajet ;
routeQuery . QueryCompleted +=
r o u t e Qu e r y _ Q u e r y C om p l e t e d ;
routeQuery . QueryAsync () ;
}
}

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

void r ou t e Q u e r y _ Q ue r y C o m p l e t e d ( object sender ,


QueryCompletedEventArgs < Route > e )
{
if ( e . Error == null )
{
Carte . AddRoute ( new MapRoute ( e . Result ) ) ;
}
}

Dans lobjet GeocodeQuery il suffit de renseigner la destination souhaite et la mthode


QueryAsync() calcule automatiquement le trajet et nous fournit de quoi construire une
route ajouter la carte grce la mthode AddRoute. Remarquez que la destination
souhaite peut galement tre des coordonnes GPS.
Et nous aurons la figure 27.16.
Il est bien sr possible dappliquer la mme technique que pour le WebClient afin de
pouvoir utiliser les mots-cls await et async. Il suffit de rajouter ces mthodes dans
une classe dextensions :
1
2
3
4
5

6
7

public static class Extensions


{
public static Task < IList < MapLocation > > QueryLocationAsync (
this GeocodeQuery geocodeQuery )
{
TaskCompletionSource < IList < MapLocation > >
taskCompletionSource = new TaskCompletionSource <
IList < MapLocation > >() ;
EventHandler < QueryCompletedEventArgs < IList < MapLocation
> > > que ry Com ple te ddH an dle r = null ;
q u er yCo mpl et edd Ha ndl er = (s , e ) = >

424

AFFICHER UN ITINRAIRE

Figure 27.16 Calcul ditinraire entre deux points

8
9
10
11
12
13

};

14

geocodeQuery . QueryCompleted -=
qu er yCo mpl et edd Ha ndl er ;
if ( e . Error != null )
taskCompletionSource . TrySetException ( e . Error ) ;
else
taskCompletionSource . TrySetResult ( e . Result ) ;

15

geocodeQuery . QueryCompleted += qu ery Co mpl ete dd Han dl er ;


geocodeQuery . QueryAsync () ;

16
17
18
19
20

return taskCompletionSource . Task ;

21
22
23
24
25
26
27

public static Task < Route > QueryRouteAsync ( this RouteQuery


routeQuery )
{
TaskCompletionSource < Route > taskCompletionSource = new
TaskCompletionSource < Route >() ;
EventHandler < QueryCompletedEventArgs < Route > >
q u er yCo mpl et edd Ha ndl er = null ;
q u e r y Co mpl et edd Ha ndl er = (s , e ) = >
{

425

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


28
29
30
31
32

};

33

routeQuery . QueryCompleted -= q ue ryC om ple ted dH and le r


;
if ( e . Error != null )
taskCompletionSource . TrySetException ( e . Error ) ;
else
taskCompletionSource . TrySetResult ( e . Result ) ;

34

routeQuery . QueryCompleted += q ue ryC om ple ted dH and le r ;


routeQuery . QueryAsync () ;

35
36
37
38
39
40

return taskCompletionSource . Task ;

Et ensuite de faire :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private List < GeoCoordinate > coordonnesTrajet ;

4
5
6
7

public MainPage ()
{
I nitializeComponent () ;

Carte . ZoomLevel = 13 ;
Carte . Center = new GeoCoordinate { Latitude = 48 . 863134
, Longitude = 2 . 320518 };
coordonnesTrajet = new List < GeoCoordinate >() ;

9
10
11
12
13
14

CalculerItineraire () ;

15
16
17
18
19

private async void CalculerItineraire ()


{
GeoCoordinate positionDepart = new GeoCoordinate ( 48 .
858115 , 2 . 294710 ) ;
coordonnesTrajet . Add ( positionDepart ) ;

20
21
22
23
24
25

GeocodeQuery geocodeQuery = new GeocodeQuery


{
SearchTerm = " avenue des champs - lys es , Paris " ,
GeoCoordinate = positionDepart
};

26
27
28
29

try
{

var resultat = await geocodeQuery .


QueryLocationAsync () ;

30
31

426

RouteQuery routeQuery = new RouteQuery () ;

AFFICHER UN ITINRAIRE
coordonnesTrajet . Add ( resultat [ 0 ]. GeoCoordinate ) ;
routeQuery . Waypoints = coordonnesTrajet ;
var route = await routeQuery . QueryRouteAsync () ;
Carte . AddRoute ( new MapRoute ( route ) ) ;

32
33
34
35
36
37
38
39
40
41

}
catch ( Exception )
{
}

Nous pouvons mme avoir les instructions de dplacement. Rajoutons une ListBox dans
le XAML :
1
2
3
4
5
6
7
8
9
10
11
12
13
14

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< maps : Map x : Name = " Carte " / >
< ListBox x : Name = " ListeDirections " Grid . Row = " 1 " >
< ListBox . ItemTemplate >
< DataTemplate >
< TextBlock Text = " { Binding } " / >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >
</ Grid >

Que nous pouvons lier la liste dinstruction, obtenues ainsi :


1
2
3

try
{

var resultat = await geocodeQuery . QueryLocationAsync () ;

4
5
6
7
8
9

RouteQuery routeQuery = new RouteQuery () ;


coordonnesTrajet . Add ( resultat [ 0 ]. GeoCoordinate ) ;
routeQuery . Waypoints = coordonnesTrajet ;
var route = await routeQuery . QueryRouteAsync () ;
Carte . AddRoute ( new MapRoute ( route ) ) ;

10
11
12
13
14
15
16
17
18

List < string > routeList = new List < string >() ;
foreach ( RouteLeg leg in route . Legs )
{
foreach ( RouteManeuver maneuver in leg . Maneuvers )
{
routeList . Add ( maneuver . InstructionText ) ;
}
}

19
20

ListeDirections . ItemsSource = routeList ;

427

CHAPITRE 27. LE CONTRLE DE CARTES (MAP)


21
22
23
24

}
catch ( Exception )
{
}

Ce qui donne la rsultat affich la figure 27.17.

Figure 27.17 Les instructions de litinraire


noter que si vous souhaitez calculer un itinraire piton, vous pouvez changer le
mode de calcul en modifiant lobjet RouteQuery :
routeQuery . TravelMode = TravelMode . Walking ;

En rsum
Le contrle Map est un contrle trs puissant qui nous permet dexploiter les
cartes du monde entier dans nos applications.
Il est possible dpingler des points dintrts grce aux objets PushPin du toolkit Windows Phone.
Le contrle de carte fournit de quoi calculer un itinraire, que lon peut afficher
sur la carte.

428

Chapitre

28

TP : Une application mto


Difficult :
Bienvenue dans ce nouveau TP. Nous allons mettre en pratique les derniers lments que
nous avons appris, mais aussi des lments dj vus. Eh oui ! tant donn quils vont faire
partie intgrante de beaucoup de vos futures applications, vous vous devez de les matriser.
Bref, le but de ce TP sera de raliser une petite application mto tout fait fonctionnelle,
que vous pourrez exhiber devant vos amis : regardez, cest moi qui lai fait ! Allez, passons
sans plus attendre lnonc du TP.

429

CHAPITRE 28. TP : UNE APPLICATION MTO

Instructions pour raliser le TP


Il sagit donc de raliser une application mto. Cette application fournira les prvisions
dune ville grce au service web de mto de worldweatheronline. Pourquoi celui-l ?
Parce que je le trouve trs facile utiliser, vous le verrez par vous-mme et que cela
vous forcera manipuler du JSON, ce qui ne fait jamais de mal. Il ncessite cependant
une petite inscription pralable afin de disposer dune cl dAPI, mais rassurez-vous,
tout est gratuit.
Notez que je nai pas dactions chez eux, vous ntes bien sr pas obligs de
vous inscrire (ou utilisez un email poubelle). Au tout dbut de la rdaction de
ce cours, jutilisais le service mto de Google, mais ils ont dcid de larrter,
me forant en trouver un autre.
Allez sur le lien - http://www.worldweatheronline.com/register.aspx et remplissez le formulaire avec votre nom et votre email, ainsi que le captcha (voir la figure
28.1).
Aprs linscription, vous recevez un mail pour vrifier votre compte, ainsi quune cl
dAPI une fois le mail vrifi. Avec cette cl dAPI, vous pourrez ensuite construire
votre requte. Par exemple pour obtenir la mto de Bordeaux 5 jours, jappellerai
lURL suivante :
1

http : // free . worldweatheronline . com / feed / weather . ashx ? q = Bordeaux


& format = json & num_of_days = 5 & key = MA_CLE_API

On peut donc prciser le nom de la ville dans le paramtre q, le type de format souhait
et le nombre de jours dinformations mtos souhaits (maxi 5). Jobtiens un JSON en
retour, du genre :
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18

{ " data " : { " current_condition " : [ ... abr g ... ] ,


" request " : [ { " query " : " Bordeaux , France " ,
" type " : " City "
} ],
" weather " : [ { " date " : " 2012 - 11 - 14 " ,
" tempMaxC " : " 17 " ,
" tempMinC " : " 8 " ,
" weatherDesc " : [ { " value " : " Sunny " } ] ,
" weatherIconUrl " : [ { " value " : " http :// www .
worldweatheronline . com / images / wsymbols01_png_64 /
wsymbol_0001_sunny . png " } ] ,
[... pur ...]
},
{ " date " : " 2012 - 11 - 15 " ,
[... abr g ...]
},
{ " date " : " 2012 - 11 - 16 " ,
[... abr g ...]
},
{ " date " : " 2012 - 11 - 17 " ,

430

INSTRUCTIONS POUR RALISER LE TP

Figure 28.1 Le formulaire de cration de compte

431

CHAPITRE 28. TP : UNE APPLICATION MTO


19
20
21
22
23
24

} }

25

[... abr g ...]


},
{ " date " : " 2012 - 11 - 18 " ,
[... abr g ...]
}

Nous pouvons voir quelques informations intressantes, comme la temprature mini,


la temprature maxi, une image qui illustre le temps prvu, la description du temps,
etc. Ah oui tiens, la description du temps est en anglais, il pourra tre judicieux de
se faire une petite matrice de traduction grce la liste que lon trouve ici : http:
//www.worldweatheronline.com/feed/wwoConditionCodes.xml.
Vous avez lhabitude maintenant du format JSON. Nous allons donc devoir exploiter
ces informations. Lapplication sera compose de 3 pages. La premire page prsentera
les diffrentes conditions mtos dans un Pivot qui nous permettra de consulter les
conditions mto du jour et des jours suivants. Vous afficherez le nom de la ville, et
dans le pivot toutes les informations que nous possdons, de la faon que vous le souhaitez. Pendant le chargement des informations de mto, vous mettrez une barre de
progression indtermine afin que lutilisateur ne soit pas perturb et ne croie lapplication inactive. Cette page contiendra une barre dapplication contenant deux icnes
qui renverront vers une page permettant dajouter une ville et vers une autre page
permettant de slectionner une ville avec le ListPicker parmi la liste de toutes les villes
prcdemment ajoutes. Bien sr, lapplication retiendra la liste de toutes les villes
ajoutes ainsi que la dernire ville slectionne afin dafficher directement les conditions mto de cette ville lors de la prochaine ouverture de lapplication.
Vous vous sentez prt ? Vous avez tout ce quil faut ? Alors, allez-y et crez une belle
application mto. Bon courage

Correction
Ah voil une petite application quelle est sympathique. Et utile en plus ! Cest toujours
pratique de pouvoir savoir sil vaut mieux prendre son parapluie ou ses tongues. Pour
raliser cette correction, nous allons commencer par crer la page qui permet dajouter
une ville, je lappelle Ajouter.xaml. Voici le XAML :
1
2
3

< phone : P h oneApplicationPage


x : Class = " TpApplicationMeteo1 . Ajouter "
...

4
5
6
7
8
9
10

432

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

CORRECTION
11
12

13

14

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12
, 17 ,0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " M t o en
direct " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Ajouter une
ville " Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle2Style } " / >
</ StackPanel >

15
16
17
18
19
20
21
22
23
24

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 ,
12 , 0 " >
< StackPanel >
< TextBlock Text = " Nom de la ville " / >
< TextBox x : Name = " NomVille " / >
< Button Content = " Ajouter " Tap = " Button_Tap " / >
</ StackPanel >
</ Grid >
</ Grid >
</ phone : PhoneApplicationPage >

Elle nest pas trs complique, nous avons une zone de saisie permettant dindiquer
une ville et un bouton permettant dajouter la ville. Le code-behind est :
1
2
3
4
5
6

public partial class Ajouter : PhoneApplicationPage


{
public Ajouter ()
{
I ni t i alizeComponent () ;
}

7
8
9
10
11
12
13
14
15
16
17
18
19

20
21
22

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
if ( string . IsNullOrEmpty ( NomVille . Text ) )
{
MessageBox . Show ( " Veuillez saisir un nom de ville " ) ;
}
else
{
I so la te d St or a ge Se tt i ng s . ApplicationSettings [ "
DerniereVille " ] = NomVille . Text ;
List < string > nomVilles ;
if ( I so l at ed St o ra ge S et ti ng s . ApplicationSettings .
Contains ( " ListeVilles " ) )
nomVilles = ( List < string >)
I so la t ed St or a ge Se tt i ng s . ApplicationSettings [
" ListeVilles " ];
else
nomVilles = new List < string >() ;
nomVilles . Add ( NomVille . Text ) ;

433

CHAPITRE 28. TP : UNE APPLICATION MTO


I so la te d St or ag e Se tt i ng s . ApplicationSettings [ "
ListeVilles " ] = nomVilles ;

23
24
25
26
27
28
29

if ( NavigationService . CanGoBack )
NavigationService . GoBack () ;

Aprs un test pour vrifier quil y a bien un lment dans la zone de saisie, jenregistre
la ville dans le rpertoire local, en tant que dernire ville consulte et dans la liste
totale des villes dj enregistres. Bien sr, si cette liste nexiste pas, je la cre. Je
mautorise mme une petite navigation arrire aprs lenregistrement, fainant comme
je suis, pour viter davoir appuyer sur le bouton de retour arrire (voir la figure
28.2).

Figure 28.2 Lcran dajout de ville


Bon, cette page est plutt simple faire. Passons la page qui permet de choisir une
ville dj enregistre, je lappelle ChoisirVille.xaml. Le XAML sera :
1
2
3
4

< phone : P h oneApplicationPage


x : Class = " TpApplicationMeteo1 . ChoisirVille "
...
xmlns : toolkit = " clr - namespace : Microsoft . Phone . Controls ;
assembly = Microsoft . Phone . Controls . Toolkit " >

434

CORRECTION
6
7
8
9
10

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

11
12
13

14

15

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12
, 17 ,0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " M t o en
direct " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< TextBlock x : Name = " PageTitle " Text = " Choisir une
ville " Margin = "9 , -7 ,0 , 0 " Style = " { StaticResource
PhoneTextTitle2Style } " / >
</ StackPanel >

16
17
18
19
20
21
22
23
24

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 ,
12 , 0 " >
< toolkit : ListPicker x : Name = " Liste " ItemsSource = " {
Binding ListeVilles } "
Header = " Ville choisie : "
CacheMode = " BitmapCache " >
</ toolkit : ListPicker >
</ Grid >
</ Grid >
</ phone : PhoneApplicationPage >

Nous notons lutilisation du ListPicker et sa liaison la proprit ListeVilles. Il


a donc fallu importer lespace de nom du toolkit ainsi que rfrencer lassembly du
toolkit. Le code-behind sera :
1
2
3

public partial class ChoisirVille : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

4
5
6
7
8
9
10
11
12

private void Notify Proper tyCha nged ( String propertyName )


{
P r o p e r t y C h a n g e d E v e n t H a n d l e r handler = PropertyChanged ;
if ( null != handler )
{
handler ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
propertyName ) ) ;
}
}

13
14
15
16

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

17

435

CHAPITRE 28. TP : UNE APPLICATION MTO


18
19
20
21

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

22
23
24
25
26
27
28

private List < string > listeVilles ;


public List < string > ListeVilles
{
get { return listeVilles ; }
set { Noti fyPro pertyC hange d ( ref listeVilles , value ) ; }
}

29
30
31
32
33
34

public ChoisirVille ()
{
I nitializeComponent () ;
DataContext = this ;
}

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
if (! I so l at ed S to ra ge S et ti ng s . ApplicationSettings .
Contains ( " ListeVilles " ) )
{
MessageBox . Show ( " Vous devez ajouter des villes " ) ;
if ( NavigationService . CanGoBack )
NavigationService . GoBack () ;
}
else
{
ListeVilles = ( List < string >) Is ol a te dS t or ag eS e tt in gs
. ApplicationSettings [ " ListeVilles " ];
if ( ListeVilles . Count == 0 )
{
MessageBox . Show ( " Vous devez ajouter des villes "
);
if ( NavigationService . CanGoBack )
NavigationService . GoBack () ;
}

53
54
55
56
57
58
59
60
61

436

if ( I s ol at ed S to ra ge S et ti ng s . ApplicationSettings .
Contains ( " DerniereVille " ) )
{
string ville = ( string ) I so la t ed St o ra ge Se t ti ng s .
ApplicationSettings [ " DerniereVille " ];
int index = ListeVilles . IndexOf ( ville ) ;
if ( index >= 0 )
Liste . SelectedIndex = index ;
}
Liste . SelectionChanged += L is te_ Se lec ti onC han ge d ;

CORRECTION
62
63

64

}
base . OnNavigatedTo ( e ) ;

65

protected override void OnNavigatedFrom ( System . Windows .


Navigation . NavigationEventArgs e )
{
Liste . SelectionChanged -= L is te_ Se lec tio nC han ge d ;
base . OnNavigatedFrom ( e ) ;
}

66
67
68
69
70
71
72
73
74
75
76
77
78
79

private void Lis te _Se le cti on Cha nge d ( object sender ,


S e l e c ti o n C h a n g e d Ev e n t A r g s e )
{
if ( Liste . SelectedItem != null )
{
I so la te d St or a ge Se tt i ng s . ApplicationSettings [ "
DerniereVille " ] = ( string ) Liste . SelectedItem ;
}
}

On commence par un petit test, sil ny a pas de ville choisir alors, nous navons rien
faire ici. Ensuite nous associons la proprit ListeVilles au contenu du rpertoire
local et nous rcuprons la dernire ville afin de prslectionner le ListPicker avec la
ville dj choisie. Attention, lorsque nous slectionnons un lment ainsi, lvnement
de changement de slection est lev. Ce qui ne mintresse pas. Cest pour cela que
je me suis abonn cet vnement aprs avoir modifi la proprit SelectedIndex.
Pour la propret du code, ceci implique que je me dsabonne de ce mme vnement
lorsque je quitte la page. Enfin, en cas de changement de slection, jenregistre la ville
slectionne dans le rpertoire local. Pas trs compliqu non plus, part peut-tre la
petite astuce pour viter que lvnement de slection ne soit lev. De toute faon, ce
genre de chose se voit trs rapidement lorsque nous testons notre application, comme
vous pouvez le constater sur la figure 28.3.
Enfin, il reste la page affichant les conditions mto. Voici ma classe Meteo utilise,
ainsi que les classes gnres pour le mapping des donnes JSON :
1
2
3
4
5
6
7
8

public class Meteo


{
public string Date { get ; set ; }
public string TemperatureMin { get ; set ; }
public string TemperatureMax { get ; set ; }
public Uri Url { get ; set ; }
public string Temps { get ; set ; }
}

9
10
11
12

public class WeatherDesc


{
public string value { get ; set ; }

437

CHAPITRE 28. TP : UNE APPLICATION MTO

Figure 28.3 Lcran de choix dune ville

13

14
15
16
17
18

public class WeatherIconUrl


{
public string value { get ; set ; }
}

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

public class CurrentCondition


{
public string cloudcover { get ; set ; }
public string humidity { get ; set ; }
public string observation_time { get ; set ; }
public string precipMM { get ; set ; }
public string pressure { get ; set ; }
public string temp_C { get ; set ; }
public string temp_F { get ; set ; }
public string visibility { get ; set ; }
public string weatherCode { get ; set ; }
public List < WeatherDesc > weatherDesc { get ; set ; }
public List < WeatherIconUrl > weatherIconUrl { get ; set ; }
public string winddir16Point { get ; set ; }
public string winddirDegree { get ; set ; }
public string windspeedKmph { get ; set ; }
public string windspeedMiles { get ; set ; }

438

CORRECTION
37

38
39
40
41
42
43

public class Request


{
public string query { get ; set ; }
public string type { get ; set ; }
}

44
45
46
47
48

public class WeatherDesc2


{
public string value { get ; set ; }
}

49
50
51
52
53

public class WeatherIconUrl2


{
public string value { get ; set ; }
}

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

public class Weather


{
public string date { get ; set ; }
public string precipMM { get ; set ; }
public string tempMaxC { get ; set ; }
public string tempMaxF { get ; set ; }
public string tempMinC { get ; set ; }
public string tempMinF { get ; set ; }
public string weatherCode { get ; set ; }
public List < WeatherDesc2 > weatherDesc { get ; set ; }
public List < WeatherIconUrl2 > weatherIconUrl { get ; set ; }
public string winddir16Point { get ; set ; }
public string winddirDegree { get ; set ; }
public string winddirection { get ; set ; }
public string windspeedKmph { get ; set ; }
public string windspeedMiles { get ; set ; }
}

72
73
74
75
76
77
78

public class Data


{
public List < CurrentCondition > current_condition { get ; set ;
}
public List < Request > request { get ; set ; }
public List < Weather > weather { get ; set ; }
}

79
80
81
82
83

public class RootObject


{
public Data data { get ; set ; }
}

Voyons prsent le XAML de la page, qui sera donc MainPage.xaml :


439

CHAPITRE 28. TP : UNE APPLICATION MTO


1
2
3

< phone : P h oneApplicationPage


x : Class = " TpApplicationMeteo1 . MainPage "
... >

4
5
6
7

< phone : PhoneApplicationPage . Resources >


< converter : VisibilityConverter x : Key = "
VisibilityConverter " / >
</ phone : PhoneApplicationPage . Resources >

8
9
10
11
12
13

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

14
15
16

17

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12
, 17 ,0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " M t o en
direct " Style = " { StaticResource
PhoneTextNormalStyle } " / >
</ StackPanel >

18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36
37

440

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 ,
12 , 0 " >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< ProgressBar IsIndeterminate = " { Binding
ChargementEnCours } " Visibility = " { Binding
ChargementEnCours , Converter ={ StaticResource
VisibilityConverter }} " / >
< TextBlock Text = " { Binding NomVille } " Style = " {
StaticResource PhoneTextTitle2Style } " / >
< phone : Pivot Grid . Row = " 1 " ItemsSource = " { Binding
ListeMeteo } " >
< phone : Pivot . HeaderTemplate >
< DataTemplate >
< TextBlock Text = " { Binding Date } " / >
</ DataTemplate >
</ phone : Pivot . HeaderTemplate >
< phone : Pivot . ItemTemplate >
< DataTemplate >
< StackPanel >
< TextBlock Text = " { Binding
TemperatureMin } " / >
< TextBlock Text = " { Binding
TemperatureMax } " / >
< TextBlock Text = " { Binding Temps } "
/>

CORRECTION
38

39
40
41
42
43

44
45
46
47
48

49

50
51

< Image Source = " { Binding Url } " Width


= " 200 " Height = " 200 " Margin = " 0 50
0 0 " HorizontalAlignment = "
Center " / >
</ StackPanel >
</ DataTemplate >
</ phone : Pivot . ItemTemplate >
</ phone : Pivot >
< TextBlock Text = " Ajoutez une ville avec les boutons
en bas " Visibility = " Collapsed " x : Name = "
Information " / >
</ Grid >
</ Grid >
< phone : P honeApplicationPage . ApplicationBar >
< shell : ApplicationBar IsVisible = " True " >
< shell : A p p li c a ti o n Ba r I co n B ut t o n IconUri = " / Assets /
Icones / add . png " Text = " Ajouter " Click = "
Ajouter_Click " / >
< shell : A p p li c a ti o n Ba r I co n B ut t o n IconUri = " / Assets /
Icones / feature . settings . png " Text = " Choisir "
Click = " Choisir_Click " / >
</ shell : ApplicationBar >
</ phone : PhoneApplicationPage . ApplicationBar >

52
53

</ phone : PhoneApplicationPage >

Tout dabord, regardons la barre dapplication tout en bas. Elle possde deux boutons avec deux icnes. Il faudra bien sr rajouter ces icnes dans notre application,
en action de gnration gale contenu et en copie si plus rcent. Les deux boutons
permettent de naviguer vers nos deux pages, cres prcdemment. Ensuite, nous avons
une barre de progression, lie la proprit ChargementEnCours, que ce soit sa proprit IsIndeterminate ou sa proprit Visibility. Nous avons galement le Pivot,
li la proprit ListeMeteo. Lentte du Pivot sera le nom du jour et les lments du
corps du pivot sont les diverses proprits de lobjet Meteo. Accompagnant le XAML,
nous aurons le code-behind suivant :
1
2
3

public partial class MainPage : PhoneApplicationPage ,


I N o t i f y P r o pe rty Ch ang ed
{
public event P r o p e r t y C h a n g e d E v e n t H a n d l e r PropertyChanged ;

4
5
6
7
8
9
10
11
12

private void Notify Proper tyCha nged ( String propertyName )


{
P r o p e r t y C h a n g e d E v e n t H a n d l e r handler = PropertyChanged ;
if ( null != handler )
{
handler ( this , new P ro p e rt y C ha n g ed E v en t A rg s (
propertyName ) ) ;
}
}

441

CHAPITRE 28. TP : UNE APPLICATION MTO


13
14
15
16

private bool NotifyPropertyChanged <T >( ref T variable , T


valeur , [ CallerMemberName ] string nomPropriete = null )
{
if ( object . Equals ( variable , valeur ) ) return false ;

17
18
19
20
21

variable = valeur ;
N ot ifyPro perty Change d ( nomPropriete ) ;
return true ;

22
23
24
25
26
27
28

private List < Meteo > listeMeteo ;


public List < Meteo > ListeMeteo
{
get { return listeMeteo ; }
set { Noti fyPro pertyC hange d ( ref listeMeteo , value ) ; }
}

29
30
31
32
33
34
35

private bool chargementEnCours ;


public bool ChargementEnCours
{
get { return chargementEnCours ; }
set { Noti fyPro pertyC hange d ( ref chargementEnCours ,
value ) ; }
}

36
37
38
39
40
41
42

private string nomVille ;


public string NomVille
{
get { return nomVille ; }
set { Noti fyPro pertyC hange d ( ref nomVille , value ) ; }
}

43
44
45
46
47
48

public MainPage ()
{
I nitializeComponent () ;
DataContext = this ;
}

49
50
51
52
53
54
55
56
57

442

protected async override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
if ( I s ol at ed S to ra ge S et ti ng s . ApplicationSettings .
Contains ( " DerniereVille " ) )
{
Information . Visibility = Visibility . Collapsed ;
ChargementEnCours = true ;
NomVille = ( string ) I so l at ed St o ra ge Se t ti ng s .
ApplicationSettings [ " DerniereVille " ];
WebClient client = new WebClient () ;

CORRECTION
try
{

58
59
60
61

ChargementEnCours = false ;
string resultatMeteo = await client .
D ow nl o ad St ri n gT as kA s yn c ( new Uri ( string .
Format ( " http :// free . worldweatheronline . com /
feed / weather . ashx ? q ={ 0 }& format = json &
num_of_days = 5 & key = MA_CLE_API " , NomVille .
Replace ( ' ', '+ ') ) , UriKind . Absolute ) ) ;

62

RootObject resultat = JsonConvert .


DeserializeObject < RootObject >( resultatMeteo )
;
List < Meteo > liste = new List < Meteo >() ;
foreach ( Weather temps in resultat . data . weather
. OrderBy ( w = > w . date ) )
{
Meteo meteo = new Meteo { TemperatureMax =
temps . tempMaxC + " C " , TemperatureMin =
temps . tempMinC + " C " };
DateTime date ;
if ( DateTime . TryParse ( temps . date , out date )
)
{
meteo . Date = date . ToString ( " dddd dd
MMMM " ) ;
meteo . Temps = GetTemps ( temps .
weatherCode ) ;
WeatherIconUrl2 url = temps .
weatherIconUrl . FirstOrDefault () ;
if ( url != null )
{
meteo . Url = new Uri ( url . value ,
UriKind . Absolute ) ;
}
}
liste . Add ( meteo ) ;
}
ListeMeteo = liste ;

63

64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

86
87
88
89

}
else

}
catch ( Exception )
{
MessageBox . Show ( " Impossible de r cup rer les
informations de m t o , v rifiez votre
connexion internet " ) ;
}
Information . Visibility = Visibility . Visible ;

90

443

CHAPITRE 28. TP : UNE APPLICATION MTO


91

92

base . OnNavigatedTo ( e ) ;

93

private string GetTemps ( string code )


{
// compl ter ...
switch ( code )
{
case " 113 " :
return " Clair / Ensoleill " ;
case " 116 " :
return " Partiellement nuageux " ;
case " 119 " :
return " Nuageux " ;
case " 296 " :
return " Faible pluie " ;
case " 353 " :
return " Pluie " ;
default :
return " " ;
}
}

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

private void Ajouter_Click ( object sender , EventArgs e )


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

114
115
116
117
118
119
120
121
122
123

private void Choisir_Click ( object sender , EventArgs e )


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

On commence par tester si la dernire ville consulte existe bien. Si ce nest pas le
cas, nous affichons un petit message pour dire quoi faire. Puis nous dmarrons le tlchargement des conditions de mto, sans oublier danimer la barre de progression.
Aprs avoir attendu la fin du tlchargement (mot cl await), nous pouvons utiliser
JSON.NET pour extraire les conditions mto et les mettre dans la proprit lie au
Pivot. Nous remarquons au passage ma technique hautement labore pour obtenir une
traduction de la description du temps. . . Quoi il manque des traductions ? Nhsitez
pas complter avec les vtres . . . Enfin, les deux mthodes de clic sur les boutons
de la barre dapplication appellent simplement le service de navigation (voir la figure
28.4).
Et si vous griez lorientation multiple maintenant ?

444

CORRECTION

Figure 28.4 Affichage de la mto

445

CHAPITRE 28. TP : UNE APPLICATION MTO

446

Quatrime partie

Un tlphone ouvert vers


lextrieur

447

Chapitre

29

La gestuelle
Difficult :
Au contraire de la gnration prcdente, Windows Mobile, les Windows Phone sont utilisables exclusivement avec les doigts. Cela peut paraitre vident, mais un doigt est beaucoup
plus large que le pointeur dune souris. Pour les dveloppeurs qui sont habitus crer des
applications ou des sites web utilisables avec une souris, il faut prendre conscience que les
zones qui sont touchables par un doigt doivent tre tailles en consquence. De plus, les
crans des Windows Phone sont multipoints, cest--dire que nous pouvons exercer plusieurs points de pressions simultans, avec notamment plusieurs doigts. Ce qui offre tout
une gamme de nouvelles faons dapprhender linteraction avec lutilisateur. Malheureusement, il est difficile de faire du multipoint avec une souris dans lmulateur. Il est prfrable
dans ce cas dutiliser directement un tlphone.

449

CHAPITRE 29. LA GESTUELLE

Le simple toucher
Nous lavons vu plusieurs reprises, cest le mode dinteraction le plus pratique et
le plus naturel pour lutilisateur. Il utilise un doigt pour toucher lcran. Geste trs
classique qui ressemble trs fortement un clic dune souris. Le doigt est utilis pour
slectionner un lment. En gnral, les contrles qui ont besoin dtre slectionns par
une pression exposent un vnement Tap. Cest le cas par exemple des boutons que
nous avons vu :
1

< Button x : Name = " MonBouton " Content = " Cliquez - moi " Tap = "
MonBouton_Tap " / >

Ils exposent galement un vnement Click :


1

< Button x : Name = " MonBouton " Content = " Cliquez - moi " Click = "
MonBo uton_Click_1 " / >

Nous lavons vu par exemple sur la barre dapplication, mais il est prsent un peu
partout. Cet vnement est hrit de Silverlight pour PC, il reste cependant utilisable
mais il est dconseill pour des raisons notamment de performance. Nous lui prfrerons
lvnement Tap, comme on la dj vu. Il existe dautres vnements, toujours hrits
de Silverlight, comme lvnement MouseLeftButtonDown :
1

< Rectangle Width = " 200 " Height = " 200 " Fill = " Aqua "
M ou s e LeftButtonDown = " R e c t a n g l e _ M o u s e L e f t B u t t o n D o w n " / >

Il correspond lvnement qui est lev lorsque lon touche un contrle. Lvnement
MouseLeftButtonUp est quant lui lev quand le doigt est relev. Mais le toucher
simple ne sert pas qu cliquer , il permet galement darrter un dfilement. Voyez
par exemple lorsque vous faites dfiler une ListBox bien remplie, si vous touchez la
ListBox et que vous lancez le doigt vers le bas, la liste dfile vers le bas en fonction de la
vitesse laquelle vous avez lanc le doigt (ce mouvement sappelle le Flick ). Si vous
retouchez la ListBox, le dfilement sarrtera. Tous ces toucher sont grs nativement
par beaucoup de contrles XAML. Il est alors trs simple de ragir un toucher.

Les diffrents touchers


Dautres touchers sont utilisables, notamment le double-toucher, que lon peut rapprocher du double-clic bien connu des utilisateurs de Windows. Il correspond lvnement
DoubleTap. Gnralement peu utilis, il peut servir effectuer un zoom, comme dans
Internet Explorer.
Nous connaissons un autre toucher, que lon utilise pour faire dfiler une ListBox par
exemple. Il sappelle le pan et consiste toucher lcran et maintenir le toucher
tout en bougeant le doigt dans nimporte quelle direction. Cette gestuelle ressemble au
drag & drop que lon connait sous Windows. Dans le mme genre, il existe le flick
qui correspond un toucher puis un mouvement rapide dans une direction. Cest
ce mouvement que lon utilise dans le contrle Pivot par exemple, et qui ressemble
450

GESTUELLE AVANCE
un tourner de page. Ce flick est galement utilis dans la ListBox pour effectuer un
fort dfilement. Notons encore un autre toucher, le touch and hold qui correspond
un clic long . On maintient le doigt appuy pendant un certain temps. En gnral,
cela fait apparatre un menu contextuel. Enfin, nous avons le pinch et le stretch qui
consistent, avec deux doigts rapprocher ou carter ses doigts. Cest ce mouvement
qui est utilis pour zoomer et d-zoomer.
Il ny a pas de support pour ces gestuelles volues dans les contrles Windows Phone,
aussi si nous souhaitons les utiliser nous allons devoir implmenter par nous-mme ces
gestuelles. Certaines sont plus ou moins faciles. Pour ceux par exemple qui ont dj
implment un drag & drop dans des applications Windows, il devient assez facile dimplmenter le Pan grce aux vnements MouseLeftButtonDown, MouseLeftButtonUp
et MouseMove (qui correspond la souris qui bouge).

Gestuelle avance
Dautres vnements un peu plus complexes sont galement disponibles sur nos
contrles, il sagit des vnements ManipulationStarted, ManipulationDelta, et
ManipulationCompleted. Ce qui est intressant dans ces vnements cest quils
fournissent un paramtre avec beaucoup dinformations sur le toucher. Lvnement
ManipulationStarted est lev au dmarrage de la manipulation fournissant la position dun ou plusieurs points de pression. Au fur et mesure de la gestuelle, cest
lvnement ManipulationDelta qui est lev fournissant des informations par exemple
sur les translations opres. Enfin, la fin de la manipulation, cest lvnement
ManipulationCompleted qui est lev. Par exemple, on pourrait se servir de ces vnements pour dterminer la gestuelle du flick car cet vnement fourni la vlocit
finale du mouvement ainsi que la translation totale. La translation nous renseigne sur
la direction du mouvement et la vlocit nous permet de savoir sil sagit rellement
dun flick et sa puissance .
Bref, ces vnements peuvent fournir beaucoup dinformations sur la gestuelle en cours,
mais cest nous de fournir du code pour interprter ces mouvements, ce qui nest pas
toujours simple. . .
Je pourrais vous faire une petite dmonstration, mais . . . il y a mieux !

Le toolkit la rescousse
Heureusement, dautres personnes ont raliss ces calculs pour nous. Ouf ! Mme si cela
pourrait tre trs intressant de prendre en compte des considrations de moteur physique pour dterminer la puissance dun flick, il savre que cest une tche fastidieuse.
Cest l quintervient nouveau le toolkit et ses contrles de gestuelle.
Prenez justement le Flick, il suffit de faire dans le XAML :
1
2

< Rectangle Width = " 300 " Height = " 300 " Fill = " Aqua " >
< toolkit : GestureService . GestureListener >

451

CHAPITRE 29. LA GESTUELLE


< toolkit : GestureListener
Flick = " G esture Liste ner_Fl ick " / >
</ toolkit : GestureService . GestureListener >
</ Rectangle >

3
4
5
6

On utilise la proprit attache GestureListener et on sabonne lvnement Flick.


Ainsi, dans le code-behind on pourra avoir :
1
2
3
4
5
6
7
8
9
10
11
12

private void Gestur eList ener_F lick ( object sender ,


F l i c k Gest ureEve ntArg s e )
{
switch ( e . Direction )
{
case System . Windows . Controls . Orientation . Horizontal :
MessageBox . Show ( " Flick horizontal , angle : " + e .
Angle ) ;
break ;
case System . Windows . Controls . Orientation . Vertical :
MessageBox . Show ( " Flick vertical , angle : " + e .
Angle ) ;
break ;
}
}

Ce qui est quand mme super simple pour interprter un flick.


Nhsitez pas tester ce code, la gestuelle est difficile faire passer avec des
copies dcrans.
Dautres gestuelles sont gres avec les vnements suivants :
vnement
Tap
DoubleTap
Flick
DragStarted, DragDelta,
DragCompleted
Hold
PinchStarted, PinchDelta,
PinchCompleted
GestureBegin, GestureCompleted

Description
Simple toucher
Deux touchers rapprochs
Mouvement rapide dans une direction
Utiliss pour le mouvement du Pan
Reprsente le toucher long
Pour le mouvement du zoom
Classe de base pour toutes les gestuelles

Chaque vnement possde un paramtre qui fournit des informations complmentaires


sur la gestuelle. Par exemple, le PinchDelta fournit des informations sur langle de
rotation ou sur la distance parcourue lors du mouvement.
Pour finir, nous allons illustrer lvnement DragDelta pour dplacer notre rectangle
grce une transformation :
452

LE TOOLKIT LA RESCOUSSE
1
2
3
4
5
6
7
8

< Rectangle Width = " 300 " Height = " 300 " Fill = " Aqua " >
< Rectangle . RenderTransform >
< C om positeTransform x : Name = " Transformation " / >
</ Rectangle . RenderTransform >
< toolkit : GestureService . GestureListener >
< toolkit : GestureListener DragDelta = "
G e s t u re L i s t e n e r _ Dr a g D e l t a " / >
</ toolkit : GestureService . GestureListener >
</ Rectangle >

Et dans le code behind :


1
2
3
4
5

private void G e s t u r e L is t e n e r _ D r a g De l t a ( object sender ,


D r a g D e l t a G e s t u r e Ev e n t A r g s e )
{
Transformation . TranslateX += e . HorizontalChange ;
Transformation . TranslateY += e . VerticalChange ;
}

Difficile dillustrer ce mouvement avec une copie dcran, mais nhsitez pas tester
cet exemple. Vous verrez que le rectangle bouge en fonction de votre mouvement de
type Pan.
Merci le Windows Phone Toolkit !

En rsum
Les Windows Phone tant utilisables exclusivement avec les doigts, il est important de bien grer les gestuelles en utilisant les vnements des contrles.
Le Windows Phone Toolkit nous aide fortement dans limplmentation de ces
gestuelles.

453

CHAPITRE 29. LA GESTUELLE

454

Chapitre

30

Lacclromtre
Difficult :
Chaque Windows Phone est quip dun acclromtre. Il sagit dun capteur qui permet
de mesurer lacclration linaire suivant les trois axes dans lespace. Ainsi, tout moment,
on peut connaitre la direction et lacclration de la pesanteur qui sexerce sur le tlphone.
Ceci peut nous permettre de raliser des petites applications sympathiques en nous servant
de lorientation du tlphone comme dune interface avec lutilisateur. Dautres capteurs
facultatifs existent sur un Windows Phone, comme le gyroscope ou le compas.
Voyons comment nous en servir.

455

CHAPITRE 30. LACCLROMTRE

Utiliser lacclromtre
Si le tlphone est pos sur la table et que la table est bien horizontale, nous allons
pouvoir dtecter une acclration d1g sur laxe des Z, qui correspond la force de
lapesanteur. Si lcran est tourn vers le haut, alors lacclration sera de (0, 0, -1) voir la figure 30.1.

Figure 30.1 Acclration sur laxe des Z


Bien sr, lacclromtre nest pas fig dans cette position, la valeur oscille sans arrt
et vous aurez plus vraisemblablement une valeur comme (0,02519062, -0,05639198, 0,994348). . .
Pour utiliser lacclromtre, vous allez devoir importer lespace de nom :
using Microsoft . Devices . Sensors ;

tant donn que lacclromtre fourni un objet de Type Vector3, qui fait partie
de la bibliothque XNA, nous aurons besoin dajouter une rfrence lassembly
Microsoft.Xna.Framework.dll (uniquement dans les versions 7.X car pour le SDK
8, la rfrence est dj ajoute).
Il suffit ensuite de sabonner lvnement de changement de valeur et de dmarrer
lacclromtre :
1
2
3
4

public partial class MainPage : PhoneApplicationPage


{
private Accelerometer accelerometre ;
public MainPage ()

456

UTILISER LACCLROMTRE AVEC LMULATEUR


{

5
6
7
8
9

10

I ni t i alizeComponent () ;
accelerometre = new Accelerometer () ;
accelerometre . CurrentValueChanged +=
accelerometre_CurrentValueChanged ;
accelerometre . Start () ;

11
12
13
14

15
16

private void a c c e l e r o m e t r e _ C u r r e n t V a l u e C h a n g e d ( object


sender , SensorReadingEventArgs < AccelerometerReading > e )
{
Dispatcher . BeginInvoke (() = > Valeurs . Text = e .
SensorReading . Acceleration . X + " , " + e .
SensorReading . Acceleration . Y + " , " + e .
SensorReading . Acceleration . Z ) ;
}

On utilisera le Dispatcher pour pouvoir mettre jour notre contrle dans le thread
ddi linterface :
1

< TextBlock x : Name = " Valeurs " / >

Cet vnement nous fournit les 3 valeurs des diffrents axes. Par contre, partir du
moment o nous dmarrons lacclromtre, nous recevrons un paquet de positions
trs rgulirement. Il est important de pouvoir faire du tri l-dedans. Ne vous inquitez pas si vos valeurs oscillent dans une petite fourchette, cest normal. Mme
si vous tes le plus immobile possible. Suivant vos besoins, vous aurez peut-tre besoin de lisser les informations obtenus, par exemple en faisant une moyenne sur les
X dernires valeurs. Une autre solution est dutiliser une formule un peu plus compliqu, issue du traitement du signal. Je ne rentrerai pas dans ces dtails car nous
nen aurons pas besoin mais vous pouvez retrouver quelques informations en anglais
cette adresse - http://windowsteamblog.com/windows_phone/b/wpdev/archive/
2010/09/08/using-the-accelerometer-on-windows-phone-7.aspx.

Utiliser lacclromtre avec lmulateur


Alors, lacclromtre, cest trs bien avec un tlphone, mais comment faire lorsque
nous navons pas encore appris dployer une application sur notre tlphone ? Cela
semble difficile dorienter notre PC pour faire croire lmulateur que nous sommes en
train de bouger. . . Heureusement, il existe une autre solution : les outils de lmulateur.
On peut les dmarrer en cliquant sur le dernier bouton de sa barre droite, comme
indiqu sur la figure 30.2.
Dans le premier onglet des outils, nous avons de quoi simuler lacclromtre (voir la
figure 30.3).
Il suffit de slectionner le petit point orange pour simuler une acclration du tlphone.
Vous pouvez utiliser la liste droulante en bas gauche pour changer lorientation du
457

CHAPITRE 30. LACCLROMTRE

Figure 30.2 Accder aux outils de lmulateur

Figure 30.3 En dplaant le rond orange, nous simulons une acclration du tlphone

458

EXPLOITER LACCLROMTRE
tlphone afin de faciliter lutilisation de lacclromtre. De mme, la liste en bas
droite permet de simuler un secouage de tlphone. . .

Exploiter lacclromtre
Maintenant que nous savons titiller lacclromtre de lmulateur, utilisons ds prsent les valeurs brutes de lacclromtre pour raliser une petite application o nous
allons faire bouger une balle en bougeant notre tlphone. Nous avons dit que lorsquon
tient le tlphone plat, cran vers le haut, nous avons une acclration de (0,0,-1).
Lorsquon incline le tlphone vers la gauche, on tend vers lacclration suivante (1,0,0). Lorsquon incline vers la droite, on tend vers lacclration (1,0,0). De la mme
faon, lorsquon incline le tlphone vers lavant, on tend vers (0,1,0) et lorsquon incline
vers nous, on tend vers (0,-1,0).
On peut donc utiliser la force des composantes du vecteur pour faire bouger notre balle.
Utilisons un Canvas et ajoutons un cercle dedans :
1
2
3

< phone : P h on e A pplicationPage


x : Class = " DemoAccelerometre . MainPage "
... >

4
5
6
7

< Canvas x : Name = " LayoutRoot " Background = " Transparent " Width =
" 480 " Height = " 800 " >
< Ellipse x : Name = " Balle " Fill = " Blue " Width = " 50 " Height = "
50 " / >
</ Canvas >

8
9

</ phone : PhoneApplicationPage >

Dans le code-behind, nous prendrons gare dmarrer et arrter lacclromtre, puis


il suffira de rcuprer les coordonnes de la balle et de les modifier en fonction des
composantes du vecteur :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private Accelerometer accelerometre ;

4
5
6
7

public MainPage ()
{
I ni t i alizeComponent () ;

8
9
10

Balle . SetValue ( Canvas . LeftProperty , LayoutRoot . Width /


2);
Balle . SetValue ( Canvas . TopProperty , LayoutRoot . Height /
2);

11
12
13

accelerometre = new Accelerometer () ;


accelerometre . CurrentValueChanged +=
accelerometre_CurrentValueChanged ;

459

CHAPITRE 30. LACCLROMTRE


}

14
15

private void a c c e l e r o m e t r e _ C u r r e n t V a l u e C h a n g e d ( object


sender , SensorReadingEventArgs < AccelerometerReading > e )
{
Dispatcher . BeginInvoke (() = >
{
double x = ( double ) Balle . GetValue ( Canvas .
LeftProperty ) + e . SensorReading . Acceleration
.X;
double y = ( double ) Balle . GetValue ( Canvas .
TopProperty ) - e . SensorReading . Acceleration .
Y;

16
17
18
19
20

21

22

if ( x
x
if ( y
y
if ( x
x
if ( y
y

23
24
25
26
27
28
29
30

<= 0 )
= 0;
<= 0 )
= 0;
>= LayoutRoot . Width - Balle . Width )
= LayoutRoot . Width - Balle . Width ;
>= LayoutRoot . Height - Balle . Height )
= LayoutRoot . Height - Balle . Height ;

31
32
33
34

35

}) ;

Balle . SetValue ( Canvas . LeftProperty , x ) ;


Balle . SetValue ( Canvas . TopProperty , y ) ;

36

protected override void OnNavigatedFrom ( System . Windows .


Navigation . NavigationEventArgs e )
{
accelerometre . Stop () ;
base . OnNavigatedFrom ( e ) ;
}

37
38
39
40
41
42
43
44
45
46
47
48

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
accelerometre . Start () ;
base . OnNavigatedTo ( e ) ;
}

Et voil, notre cercle bouge en fonction de lorientation du tlphone. Remarquez que


pour avoir un mouvement naturel, nous avons invers laxe des Y en ralisant une
soustraction. Bon, cest bien mais la balle navance pas trs vitre. Il pourrait tre
judicieux dappliquer un facteur de vitesse, par exemple :
1
2

460

double vitesse = 3 . 5 ;
double x = ( double ) Balle . GetValue ( Canvas . LeftProperty ) + e .
SensorReading . Acceleration . X * vitesse ;

LES AUTRES CAPTEURS FACULTATIFS


3

double y = ( double ) Balle . GetValue ( Canvas . TopProperty ) - e .


SensorReading . Acceleration . Y * vitesse ;

La balle va cette fois-ci un peu plus vite. Mais elle pourrait avoir un peu plus dinertie, et
notamment freiner lorsquon redresse le tlphone. Noubliez pas que plus le tlphone
est plat et plus Z tend vers -1. On peut donc trouver une formule o le fait que le
tlphone soit plat ralentisse la balle, en divisant par exemple par la valeur absolue de
Z:
1
2
3
4

double facteur = e . SensorReading . Acceleration . Z == 0 ? 0 . 00001


: Math . Abs ( e . SensorReading . Acceleration . Z ) ;
double vitesse = 3 . 5 ;
double x = ( double ) Balle . GetValue ( Canvas . LeftProperty ) + e .
SensorReading . Acceleration . X * vitesse / facteur ;
double y = ( double ) Balle . GetValue ( Canvas . TopProperty ) - e .
SensorReading . Acceleration . Y * vitesse / facteur ;

Bon, on est loin dun vrai moteur physique simulant lacclration, mais cest dj un
peu mieux.
Attention faire en sorte que Z soit toujours diffrent de zro, sinon cest la
mga honte. Je crois que diviser par zro cest vraiment la pire des exceptions
que lon peut avoir.
Attention, noubliez pas darrter lacclromtre quand vous avez fini avec lui grce
la mthode Stop() afin dconomiser la batterie. Cest ce que je fais dans la mthode
OnNavigatedFrom().

Les autres capteurs facultatifs


Lacclromtre est le seul capteur obligatoire dans les spcifications dun Windows
Phone. Mais il y a dautres capteurs facultatifs qui peuvent faire partie dun Windows
Phone. Il y a notamment le compas (ou le magntomtre). Il permet de connaitre
lemplacement du ple nord magntique et peut servir faire une boussole par exemple.
tant facultatif, il faudra penser vrifier sil est prsent avec :
1
2
3
4

if (! Compass . IsSupported )
{
MessageBox . Show ( " Votre t l phone ne poss de pas de compas " )
;
}

De mme, le gyroscope est facultatif sur les Windows Phone. Il sert mesurer la
vitesse de rotation suivant les trois axes. Il est diffrent de lacclromtre. Pour voir la
diffrence entre les deux, imaginez-vous debout avec le tlphone en position portrait
devant vous, face au nord. Si vous faites un quart de tour vers la droite, vous vous
retrouvez face lest, le tlphone na pas chang de position dans vos mains, mais il
461

CHAPITRE 30. LACCLROMTRE


a subi une rotation suivant un axe. Cette rotation, cest le gyroscope qui va tre en
mesure de vous la fournir. Pour lacclromtre, pensez notre petite application de
balle ralise plus haut. Pour tester sa prsence, vous pourrez faire :
1
2
3
4

if (! Gyroscope . IsSupported )
{
MessageBox . Show ( " Votre t l phone ne poss de pas de
gyroscope " ) ;
}

Il vous faudra peut-tre adapter le comportement de votre application en consquence,


ou prvenir lutilisateur quelle est inutilisable sans gyroscope.

La motion API
Travailler avec tous ces capteurs en mme temps est plutt complexe. De plus, si jamais
il manque au tlphone un capteur que vous souhaitez utiliser, il vous faut revoir vos
calculs pour adapter votre application. Cest l quintervient la motion API, que lon
peut traduire en API de mouvement. Elle permet de grer toute la logique mathmatique associe ces trois capteurs afin de fournir des valeurs facilement exploitables.
Au final, on arrive trs facilement dterminer comment le tlphone est orient dans
lespace. Cela permet par exemple de transposer le tlphone dans un monde 3D virtuel, permettant les applications de ralit augmente, les jeux et pourquoi pas des
applications auxquelles nous navons pas encore pens. . .
La motion API sutilise grosso modo comme lacclromtre. Elle nous fournit plusieurs
valeurs avec notamment une proprit Attitude que lon peut traduire en assiette ,
dans le langage de laviation, qui permet dobtenir lorientation de lavion dans lespace. Cette proprit nous fournit plusieurs valeurs intressantes qui, toujours dans le
domaine de laviation, sont :
Yaw : qui indique la direction par rapport au nez de lavion, afin de savoir si
lavion tourne droite ou gauche ;
Pitch : qui indique si le nez de lavion monte ou descend ;
Roll : qui permet de savoir si lavion bascule sur la droite ou la gauche.
Grosso modo, imaginons que vous ayez le tlphone pos sur la table devant vous, avec
les boutons proches de vous :
Si vous lui faite faire une rotation sur la table, tout en le conservant pos sur la
table, alors vous changez le Yaw.
Si vous levez le tlphone pour lavoir en position verticale, alors vous changez
le pitch.
Si vous basculez le tlphone pour le mettre sur sa tranche, alors vous changez
le roll.
On peut le reprsenter ainsi (voir la figure 30.4).
Mme si je ne doute pas de la qualit de mon dessin ( :- ), le mieux pour comprendre
est de lexprimenter sur un vrai tlphone. Pour obtenir ces valeurs, il vous suffit
462

LA MOTION API

Figure 30.4 Assiette dun tlphone


de dclarer un objet Motion et de vous abonner lvnement CurrentValueChanged.
Notez que vous pouvez utiliser un petit Helper venant du framework XNA afin dobtenir
un angle partir de cette valeur.
Utilisez donc le XAML suivant :
1
2
3

< TextBlock x : Name = " Yaw " / >


< TextBlock x : Name = " Pitch " / >
< TextBlock x : Name = " Roll " / >

Avec le code-behind :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
Motion motion ;

4
5
6
7
8

public MainPage ()
{
I ni t i alizeComponent () ;
}

9
10
11
12
13
14

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
if (! Motion . IsSupported )
{
MessageBox . Show ( "L ' API Motion n ' est pas support e
sur ce t l phone . " ) ;

463

CHAPITRE 30. LACCLROMTRE


15

16

return ;

17

if ( motion == null )
{
motion = new Motion { TimeBetweenUpdates = TimeSpan
. FromMilliseconds ( 20 ) };
motion . CurrentValueChanged +=
motion_CurrentValueChanged ;
}

18
19
20
21
22
23

try
{

24
25
26
27
28
29
30
31

32

motion . Start () ;
}
catch ( Exception ex )
{
MessageBox . Show ( " Impossible de d marrer l ' API . " ) ;
}

33

private void m o t i o n _ C u r r e n t V a l u e C h a n g e d ( object sender ,


SensorReadingEventArgs < MotionReading > e )
{
Dispatcher . BeginInvoke (() = >
{
if ( motion . IsDataValid )
{
float yaw = MathHelper . ToDegrees ( e .
SensorReading . Attitude . Yaw ) ;
float pitch = MathHelper . ToDegrees ( e .
SensorReading . Attitude . Pitch ) ;
float roll = MathHelper . ToDegrees ( e .
SensorReading . Attitude . Roll ) ;

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

}) ;

Yaw . Text = " Yaw : " + yaw + " " ;


Pitch . Text = " Pitch : " + pitch + " " ;
Roll . Text = " Roll : " + roll + " " ;

Pour utiliser le MathHelper, il faudra inclure lespace de nom suivant :


1

using Microsoft . Xna . Framework ;

Lobjet Motion est quant lui disponible avec :


1

464

using Microsoft . Devices . Sensors ;

LA MOTION API
Vous ne pourrez malheureusement pas tester lAPI motion dans lmulateur,
celle-ci nest pas supporte. Et vous ne savez pas encore comment dployer
une application sur votre tlphone, sauf si vous tes alls vers la fin du cours
en avance !
En plus de lassiette, lAPI motion fourni dautres valeurs :
DeviceAcceleration, qui est la mme chose que ce que lon obtient avec lacclromtre.
DeviceRotationRate, qui est la mme chose que ce que lon obtient avec le
gyroscope.
Gravity, qui renvoie un vecteur en direction du centre de la terre, pour reprsenter la gravit, comme avec lacclromtre.
Vous avez compris que ces valeurs peuvent tre obtenues avec les mthodes propres
aux capteurs et que ce qui est vraiment intressant, ce sont les valeurs calcules de
lassiette.

En rsum
Lacclromtre est un capteur obligatoire sur tout tlphone Windows Phone
qui nous offre de nouvelles faons dinteragir avec notre utilisateur.
Il est facile muler grce aux outils de lmulateur Windows Phone.
Dautres capteurs facultatifs existent, comme le gyroscope ou le compas.
La motion API nous simplifie grandement la dtection de lorientation du tlphone dans lespace et nous ouvre les portes de la ralit augmente.

465

CHAPITRE 30. LACCLROMTRE

466

Chapitre

31

TP : Jeux de hasard (Grattage et


secouage)
Difficult :
Ahhh, a commence devenir sympa ce quon peut faire ! Lcran tactile et la gestuelle
associe, ainsi que lacclromtre sont des outils vraiment intressant utiliser. Et puis cela
nous oblige sortir de la classique souris et imaginer des nouveaux types dinteractions
avec lutilisateur. Nous allons donc mettre en pratique ces derniers lments dans ce nouveau
TP o nous allons crer une petite application de jeu de hasard, dcoupe en deux petits
jeux qui vont utiliser la gestuelle et lacclromtre.

467

CHAPITRE 31. TP : JEUX DE HASARD (GRATTAGE ET SECOUAGE)

Instructions pour raliser le TP


Crez dans un premier temps une page de menu qui renverra vers une page o nous
aurons un jeu de grattage et une autre page o nous aurons un jeu de secouage. . . Le
jeu en lui-mme ne sera pas super volu et peu esthtique car je souhaite que vous
vous concentriez sur les techniques tudies prcdemment, mais rien ne vous empche
de laisser courir votre imagination et de raliser la prochaine killer-app.
Donc, le grattage, je propose quil sagisse dafficher 3 rectangles que lon peut gratter.
Une fois ces rectangles gratts, ils dcouvrent si nous avons gagn ou pas. Un tirage
alatoire est fait pour dterminer le rectangle gagnant et une fois que nous avons
commenc gratter un rectangle, il nest plus possible den gratter un autre. Pas
besoin de gratter tout le rectangle, vous pourrez afficher la victoire ou la dfaite de
lutilisateur une fois un certain pourcentage du rectangle gratt. Noubliez pas doffrir
lutilisateur la possibilit de rejouer et de voir le nombre de parties gagnes. Voici
la figure 31.1 le rsultat que je vous propose datteindre.

Figure 31.1 Le grattage


Passons maintenant au secouage. Le principe est de dtecter via lacclromtre lorsque
lutilisateur secoue son tlphone. ce moment-l, nous pourrons gnrer un nombre
alatoire avec une chance sur 3 de gagner. Pourquoi ne pas faire mariner un peu lutilisateur en lui affichant une barre de progression indtermine et en attendant deux
secondes pour afficher le rsultat. Voici la figure 31.2 le rsultat que je vous propose
datteindre.
468

INSTRUCTIONS POUR RALISER LE TP

Figure 31.2 Le secouage


Alors, si vous vous le sentez, nhsitez pas vous lancer directement. Sinon, je vais
vous proposer quelques pistes de rflexion pour dmarrer sereinement le TP.

Tout dabord, au niveau du grattage. Il y a plusieurs solutions envisageables. Celle que


je vous propose est de ne pas avoir rellement un unique rectangle gratter, mais plutt
plein de petits rectangles qui recouvrent un TextBlock contenant un texte affichant si
cest gagn ou perdu. Chaque TextBlock sera lcoute dun vnement de manipulation, jai choisi pour ma part la gestuelle du drap & drop du toolkit. Il faut ensuite
arriver dterminer quel lment est concern lorsque nous touchons lcran. Pour cela,
jutilise une mthode du framework .NET : FindElementsInHostCoordinates - http://
msdn.microsoft.com/fr-fr/library/system.windows.media.visualtreehelper.findelements
vs.95).aspx. Par exemple, pour rcuprer le TextBlock choisi lors du premier contact,
je pourrais faire :
1
2
3
4

private void G e s t u r e L i s t e n e r _ D r a g S t a r t e d ( object sender ,


DragStartedGestureEventArgs e)
{
Point position = e . GetPosition ( Application . Current .
RootVisual ) ;
IEnumerable < UIElement > elements = VisualTreeHelper .
F i n d E l e m e n t s I n H o s t C o o r d i n a t e s ( new Point ( position .X ,
position . Y ) , Application . Current . RootVisual ) ;
TextBlock textBlockChoisi = elements . OfType < TextBlock >() .
FirstOrDefault () ;

469

CHAPITRE 31. TP : JEUX DE HASARD (GRATTAGE ET SECOUAGE)


6

La mthode GetPosition nous fournit la position du doigt par rapport la page courante, que nous pouvons obtenir grce la proprit RootVisual de lapplication - http:
//msdn.microsoft.com/fr-fr/library/system.windows.application.rootvisual(v=
vs.95).aspx. Ainsi, il sera possible de dterminer les lments qui sont slectionns et
les supprimer de devant le TextBlock.
Passons maintenant au secouage. Comment dtecter que lutilisateur secoue son tlphone ? Il y a plusieurs solutions. Celle que jai choisi consister dtecter un cart
significatif entre les deux dernires acclrations du tlphone. Si cet cart se reproduit
plusieurs fois, alors je peux considrer quil sagit dun secouage. Par contre, si lcart
passe sous un certain seuil, alors je dois arrter dimaginer un potentiel secouage.
Allez, cest vous de jouer.

Correction
Alors, vous avez trouv comment ? Facile ? Difficile ? Ce nest pas toujours facile de
se confronter directement ce genre de situations, surtout lorsquon a lhabitude de
raliser des applications clientes lourdes ou web, ou mme lorsquon a pas du tout
lhabitude de raliser des applications.
Voici la correction que je propose. Tout dabord le menu, vous savez faire, il sagit de
ma page MainPage.xaml qui renvoie vers deux autres pages :
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " TP Jeux de
hasard " Style = " { StaticResource PhoneTextNormalStyle }
"/>
< TextBlock x : Name = " PageTitle " Text = " Menu " Margin = "9 , -7 ,
0 , 0 " Style = " { StaticResource PhoneTextTitle1Style } " / >
</ StackPanel >
< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< Button Content = " Grattage ... " Tap = " Button_Tap " / >
< Button Content = " Secouage ... " Tap = " Button_Tap_1 "
/>
</ StackPanel >
</ Grid >
</ Grid >

Cest trs pur, le code-behind sera :


470

CORRECTION
1
2
3
4
5
6

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I ni t i alizeComponent () ;
}

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
Navig ationService . Navigate ( new Uri ( " / Grattage . xaml " ,
UriKind . Relative ) ) ;
}

8
9
10
11
12
13
14
15
16
17

private void Button_Tap_1 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
Navig ationService . Navigate ( new Uri ( " / Secouage . xaml " ,
UriKind . Relative ) ) ;
}

Une utilisation trs classique du service de navigation. Passons maintenant la page


Grattage.xaml :
1
2
3

< phone : P h on e A pplicationPage


...
xmlns : toolkit = " clr - namespace : Microsoft . Phone . Controls ;
assembly = Microsoft . Phone . Controls . Toolkit " >

4
5
6
7
8
9
10
11

12
13
14

15
16

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12
, 17 ,0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " Grattage
" Style = " { StaticResource PhoneTextNormalStyle } "
/>
</ StackPanel >
< Canvas x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0
, 12 , 0 " >
< TextBlock x : Name = " TextBlock1 " Width = " 100 " Height = "
100 " Canvas . Top = " 10 " Canvas . Left = " 40 " Margin = " 20
30 0 0 " >
< toolkit : GestureService . GestureListener >
< toolkit : GestureListener DragStarted = "
G e s t u r e L i s t e n e r _ D r a g S t a r t e d " DragDelta = "
G e s t u r eL i s t e n e r _ D ra g D e l t a " DragCompleted
=" GestureListener_DragCompleted " />

471

CHAPITRE 31. TP : JEUX DE HASARD (GRATTAGE ET SECOUAGE)


</ toolkit : GestureService . GestureListener >
</ TextBlock >
< TextBlock x : Name = " TextBlock2 " Width = " 100 " Height = "
100 " Canvas . Top = " 10 " Canvas . Left = " 170 " Margin = "
20 30 0 0 " >
< toolkit : GestureService . GestureListener >
< toolkit : GestureListener DragStarted = "
G e s t u r e L i s t e n e r _ D r a g S t a r t e d " DragDelta = "
G e s t u re L i s t e n e r _ D ra g D e l t a " DragCompleted
=" GestureListener_DragCompleted " />
</ toolkit : GestureService . GestureListener >
</ TextBlock >
< TextBlock x : Name = " TextBlock3 " Width = " 100 " Height = "
100 " Canvas . Top = " 10 " Canvas . Left = " 300 " Margin = "
20 30 0 0 " >
< toolkit : GestureService . GestureListener >
< toolkit : GestureListener DragStarted = "
G e s t u r e L i s t e n e r _ D r a g S t a r t e d " DragDelta = "
G e s t u re L i s t e n e r _ D ra g D e l t a " DragCompleted
=" GestureListener_DragCompleted " />
</ toolkit : GestureService . GestureListener >
</ TextBlock >
< StackPanel Canvas . Top = " 250 " Width = " 480 " >
< Button Content = " Rejouer " HorizontalAlignment = "
Center " Tap = " Button_Tap " / >
< TextBlock x : Name = " Resultat " / >
</ StackPanel >
</ Canvas >
</ Grid >
</ phone : PhoneApplicationPage >

17
18
19

20
21

22
23
24

25
26

27
28
29
30
31
32
33
34
35

Comme je lai propos, jai simplement ajout trois TextBlock dans un Canvas. Remarquez que jai spcifi exhaustivement les largeurs et les hauteurs de chacun. Sur
chaque TextBlock, je suis galement lcoute des trois vnements de drag & drop.
Rien de bien compliqu. Cest cot code-behind que cela se complique.
1
2
3
4
5
6
7
8
9

public partial class Grattage : PhoneApplicationPage


{
private const int largeurRectangle = 15 ;
private int nbRects ;
private TextBlock textBlockChoisi ;
private Random random ;
private bool aGagne ;
private int nbParties ;
private int nbPartiesGagnees ;

10
11
12
13
14
15

472

public Grattage ()
{
I nitializeComponent () ;
random = new Random () ;
}

CORRECTION
16

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
Init () ;

17
18
19
20
21
22
23

base . OnNavigatedTo ( e ) ;

Nous dclarons dans un premier temps plusieurs variables qui vont nous servir dans la
classe. Il y a notamment un gnrateur de nombre alatoires, initialis dans le constructeur. Ensuite, cela se passe dans la mthode Init(). Il faut dans un premier temps
crer plein de petits rectangles afficher par-dessus chaque TextBlock :
1
2
3
4

private void Init ()


{
textBlockChoisi = null ;
TextBlock [] textBlocks = new [] { TextBlock1 , TextBlock2 ,
TextBlock3 };

5
6
7
8
9
10
11
12
13
14

int gagnant = random . Next (0 , 3 ) ;


for ( int cpt = 0 ; cpt < textBlocks . Length ; cpt ++)
{
TextBlock tb = textBlocks [ cpt ];
if ( cpt == gagnant )
tb . Text = " Gagn ! " ;
else
tb . Text = " Perdu ! " ;
}

15
16
17
18
19

foreach ( TextBlock textBlock in textBlocks )


{
double x = ( double ) textBlock . GetValue ( Canvas .
LeftProperty ) ;
double y = ( double ) textBlock . GetValue ( Canvas .
TopProperty ) ;

20
21
22
23
24
25
26
27
28
29
30
31

nbRects = 0 ;
for ( double j = 0 ; j < textBlock . Height ; j +=
largeurRectangle )
{
for ( double i = 0 ; i < textBlock . Width ; i +=
largeurRectangle )
{
double width = largeurRectangle ;
double height = largeurRectangle ;
if ( i + width > textBlock . Width )
width = textBlock . Width - i ;
if ( j + height > textBlock . Height )
height = textBlock . Height - j ;

473

CHAPITRE 31. TP : JEUX DE HASARD (GRATTAGE ET SECOUAGE)


32

33
34
35
36
37
38
39
40

Rectangle r = new Rectangle { Fill = new


SolidColorBrush ( Colors . Gray ) , Width = width ,
Height = height , Tag = textBlock };
r . SetValue ( Canvas . LeftProperty , i + x ) ;
r . SetValue ( Canvas . TopProperty , j + y ) ;
ContentPanel . Children . Add ( r ) ;
nbRects ++;

Le principe est de rcuprer la position de chaque TextBlock dans le Canvas puis de gnrer plein de petits rectangles qui couvrent la surface du TextBlock. Chaque rectangle
est positionn dans le Canvas et ajout celui-ci. Notez que jen profite pour rattacher
chaque rectangle son TextBlock grce la proprit Tag, cela sera plus simple par
la suite pour dterminer quel rectangle on peut supprimer. Remarquons au passage
que nous utilisons le gnrateur de nombre alatoire pour dterminer quel TextBlock
est le gagnant. Viens ensuite le grattage en lui-mme. Cest lors du dmarrage de la
gestuelle drag & drop que nous allons pouvoir dterminer le TextBlock choisi. Comme
je lavais indiqu, jutilise la mthode FindElementsInHostCoordinates partir des
coordonnes du doigt. Puis, je peux utiliser le mme principe lors de lexcution de la
gestuelle, rcuprer le Rectangle cette position et lenlever du Canvas.
1
2
3
4
5
6
7
8
9
10

11
12
13

public partial class Grattage : PhoneApplicationPage


{
[...]
private void G e s t u r e L i s t e n e r _ D r a g S t a r t e d ( object sender ,
DragStartedGestureEventArgs e)
{
if ( textBlockChoisi == null )
{
aGagne = false ;
Point position = e . GetPosition ( Application . Current .
RootVisual ) ;
IEnumerable < UIElement > elements = VisualTreeHelper .
F i n d E l e m e n t s I n H o s t C o o r d i n a t e s ( new Point ( position
.X , position . Y ) , Application . Current . RootVisual )
;
textBlockChoisi = elements . OfType < TextBlock >() .
FirstOrDefault () ;
}
}

14
15
16
17
18
19

474

private void G e s t u r e L i st e n e r _ D r a g De l t a ( object sender ,


D r a g D el t a G e s t u r e E ve n t A r g s e )
{
if ( textBlockChoisi != null )
{
Point position = e . GetPosition ( Application . Current .

CORRECTION
RootVisual ) ;
20
21

22

23
24
25
26

27

IEnumerable < UIElement > elements = VisualTreeHelper .


F i n d E l e m e n t s I n H o s t C o o r d i n a t e s ( new Point ( position
.X , position . Y ) , Application . Current . RootVisual )
;
foreach ( Rectangle element in elements . OfType <
Rectangle >() . Where ( r = > r . Tag == textBlockChoisi
))
{
ContentPanel . Children . Remove ( element ) ;
}

28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47

private void G e s t u r e L i s t e n e r _ D r a g C o m p l e t e d ( object sender ,


DragCompletedGestureEventArgs e)
{
if ( textBlockChoisi != null )
{
double taux = nbRects / 3 . 0 ;
double nbRectangles = ContentPanel . Children . OfType <
Rectangle >() . Count ( r = > r . Tag == textBlockChoisi
);
if ( nbRectangles <= taux )
{
if ( textBlockChoisi . Text == " Perdu ! " )
MessageBox . Show ( " Vous avez perdu " ) ;
else
{
aGagne = true ;
MessageBox . Show ( " F licitations " ) ;
}
}
}
}

Enfin, quand la gestuelle est termine, je peux dterminer combien il reste de rectangles
qui recouvrent le TextBlock choisi, et au-dessous dun certain taux, je peux afficher si
cest gagn ou pas. Reste plus qu rinitialiser tout pour offrir la possibilit de rejouer :
1
2
3
4
5
6
7
8

public partial class Grattage : PhoneApplicationPage


{
[...]
private void Button_Tap ( object sender , System . Windows . Input
. GestureEventArgs e )
{
if ( aGagne )
nbPartiesGagnees ++;
nbParties ++;

475

CHAPITRE 31. TP : JEUX DE HASARD (GRATTAGE ET SECOUAGE)


9
10
11
12

Resultat . Text = nbPartiesGagnees + " parties gagn es


sur " + nbParties ;
Init () ;

Et voil, le grattage est termin. Passons maintenant la partie secouage. Cot XAML
cest plutt simple :
1
2
3
4
5

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " Secouage "
Style = " { StaticResource PhoneTextNormalStyle } " / >
</ StackPanel >

7
8
9
10

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< Grid . RowDefinitions >
< RowDefinition Height = " 100 " / >
< RowDefinition Height = " 100 " / >
< RowDefinition Height = " 100 " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< TextBlock Text = " Secouez le t l phone ... " / >
< ProgressBar x : Name = " Barre " Grid . Row = " 1 " Visibility = "
Collapsed " / >
< TextBlock x : Name = " Resultat " Grid . Row = " 2 "
HorizontalAlignment = " Center " / >
< TextBlock x : Name = " Total " Grid . Row = " 3 " / >
</ Grid >
</ Grid >

11
12
13
14
15
16
17
18
19
20
21
22
23

Tout se passe dans le code-behind, la premire chose est dinitialiser lacclromtre


et le gnrateur de nombres alatoires. Jutilise galement un DispatcherTimer pour
faire mariner un peu lutilisateur avant de lui fournir le rsultat :
1
2
3
4
5
6
7
8
9

public partial class Secouage : PhoneApplicationPage


{
private Accelerometer accelerometre ;
private const double ShakeThreshold = 0 . 7 ;
private DispatcherTimer timer ;
private Random random ;
private int nbParties ;
private int nbPartiesGagnees ;
private Vector3 derniereAcceleration ;

476

CORRECTION
private int cpt ;

10
11

public Secouage ()
{
I ni t i alizeComponent () ;
accelerometre = new Accelerometer () ;
accelerometre . CurrentValueChanged +=
accelerometre_CurrentValueChanged ;
timer = new DispatcherTimer { Interval = TimeSpan .
FromSeconds ( 2 ) };
random = new Random () ;
}

12
13
14
15
16
17
18
19
20

protected override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
accelerometre . Start () ;
timer . Tick += timer_Tick ;
base . OnNavigatedTo ( e ) ;
}

21
22
23
24
25
26
27
28
29
30
31
32
33
34

protected override void OnNavigatedFrom ( System . Windows .


Navigation . NavigationEventArgs e )
{
accelerometre . Stop () ;
timer . Tick -= timer_Tick ;
base . OnNavigatedFrom ( e ) ;
}

Attention, noubliez pas darrter lacclromtre lorsque nous nen avons plus
besoin afin dconomiser la batterie.
Il ne reste plus qu grer la dtection du secouage. Comme je vous lai indiqu, il suffit
de comparer la diffrence dacclration suivant un axe entre deux mesures. Si celle-ci
dpasse un certain seuil, alors il y a un premier secouage, si par contre elle repasse
sous un autre seuil, la dtection est interrompue. Jai fix arbitrairement ces valeurs
0.8 et 0.1 car je trouve quelles simulent des valeurs acceptables. Pour mesurer la
diffrence dacclration, je prends la valeur absolue de la diffrence entre la dernire
valeur dacclration sur un axe et la prcdente. Si un changement brutal est dtect,
alors on incrmente le compteur. Sil dpasse 4 alors nous considrons quil sagit dun
vrai secouage de tlphone.
1
2
3
4

public partial class Secouage : PhoneApplicationPage


{
[...]
private void a c c e l e r o m e t r e _ C u r r e n t V a l u e C h a n g e d ( object
sender , SensorReadingEventArgs < AccelerometerReading > e )

477

CHAPITRE 31. TP : JEUX DE HASARD (GRATTAGE ET SECOUAGE)


{

5
6
7
8

Dispatcher . BeginInvoke (() = >


{
Vector3 accelerationEnCours = e . SensorReading .
Acceleration ;

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

}) ;

if ( derniereAcceleration == null )
{
derniereAcceleration = accelerationEnCours ;
return ;
}
double seuilMax = 0 . 8 ;
double seuilMin = 0 . 1 ;
int maxSecouage = 3 ;
if ( cpt <= maxSecouage && (
Math . Abs ( accelerationEnCours . X derniereAcceleration . X ) >= seuilMax ||
Math . Abs ( accelerationEnCours . Y derniereAcceleration . Y ) >= seuilMax ||
Math . Abs ( accelerationEnCours . Z derniereAcceleration . Z ) >= seuilMax ) )
{
Resultat . Text = string . Empty ;
cpt ++;
if ( cpt > maxSecouage )
{
cpt = 0 ;
Barre . Visibility = Visibility . Visible ;
Barre . IsIndeterminate = true ;
timer . Start () ;
}
}
else
{
if ( Math . Abs ( accelerationEnCours . X derniereAcceleration . X ) >= seuilMin ||
Math . Abs ( accelerationEnCours . Y derniereAcceleration . Y ) >= seuilMin ||
Math . Abs ( accelerationEnCours . Z derniereAcceleration . Z ) >= seuilMin )
{
cpt = 0 ;
}
}
derniereAcceleration = accelerationEnCours ;

ce moment-l, je dmarre le timer ainsi que lanimation de la barre de progression.


478

ALLER PLUS LOIN


Il ne reste plus qu grer la suite du jeu :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

public partial class Secouage : PhoneApplicationPage


{
[...]
private void timer_Tick ( object sender , EventArgs e )
{
timer . Stop () ;
Barre . Visibility = Visibility . Collapsed ;
Barre . IsIndeterminate = false ;
int nombre = random . Next (0 , 3 ) ;
if ( nombre == 1 )
{
// nombre gagnant
Resultat . Text = " Gagn ! " ;
nbPartiesGagnees ++;
}
else
{
Resultat . Text = " Perdu ! " ;
}
nbParties ++;
Total . Text = nbPartiesGagnees + " parties gagn es sur "
+ nbParties ;
}
}

Et voil. Un petit jeu qui nous a permis de plonger doucement dans la gestuelle et dans
lutilisation de lacclromtre !

Aller plus loin


Ici, je suis rest trs sobre sur linterface (qui a dit trs moche ?). Mais il serait tout
fait pertinent damliorer cet aspect-l de lapplication. Pourquoi ne pas utiliser des
images de ds et des transformations pour raliser un lancer de ds majestueux ?
De mme, plutt que dutiliser des rectangles lors du grattage, on pourrait utiliser des
images ainsi que la classe WriteableBitmap - http://msdn.microsoft.com/fr-fr/
library/system.windows.media.imaging.writeablebitmap(v=vs.95).aspx pour effacer des lments de limage. . . Je nen parle pas ici car cela sort de la porte de ce
cours, mais cest une ide creuser. En ce qui concerne le secouage, jai propos un
algorithme ultra simpliste permettant de dtecter ce genre de mouvement. Il existe une
bibliothque dveloppe par des personnes Microsoft qui permet de dtecter les secouages. Il sagit de la Shake Gesture Library, que vous pouvez tlcharger ici - http://
www.codeproject.com/Articles/176021/Shake-Gestures-Library-A-Windows-Phone-Recipe.
Nous aurons tout fait intrt lutiliser ici. Lalgorithme de dtection est plus pertinent et pourquoi rinventer la roue alors que dautres personnes lont dj fait pour
nous.
479

CHAPITRE 31. TP : JEUX DE HASARD (GRATTAGE ET SECOUAGE)

480

Chapitre

32

La golocalisation
Difficult :
De plus en plus de matriels technologiques sont quips de systmes de localisation,
notamment grce aux GPS. Cest galement le cas des tlphones quips de Windows
Phone dont les spcifications imposent la prsence dun GPS. Mais le systme de localisation
est un peu plus pouss que le simple GPS, qui a ses limites. En effet, on se trouve souvent
lintrieur dun immeuble ou entours par des gros murs qui peuvent bloquer le signal
du GPS. . . heureusement, la triangulation base des rseaux WIFI peut prendre la relve
pour indiquer la position dun tlphone. Nous allons pouvoir exploiter la position de notre
utilisateur et raliser ainsi des applications golocalises, nous allons voir dans ce chapitre
comment faire.

481

CHAPITRE 32. LA GOLOCALISATION


Avec Windows Phone 7, on utilisait une premire version du service de localisation grce
au GeoCoordinateWatcher - http://msdn.microsoft.com/fr-fr/library/system.
device.location.geocoordinatewatcher.aspx. Windows 8 a propos une refonte du
service de localisation et cest tout naturellement que Windows Phone 8 en a profit.
Les deux fonctionnent globalement de la mme faon, mais celui de Windows Phone
8 supporte par dfaut lasynchronisme avanc avec await et async. Cest celui-ci que
nous allons tudier. Sachez quand mme que si vous projetez de faire en sorte que votre
application fonctionne avec Windows Phone 7.X et 8, vous aurez intrt utiliser la
premire version du service de localisation.
Remarquez que vous devrez indiquer dans votre application un descriptif de votre
politique dutilisation des donnes golocalises, soit directement dans une page, soit
en donnant un lien vers cette politique. De mme, votre application devra fournir un
moyen de dsactiver explicitement lutilisation du service de localisation.

Dterminer sa position
Il est trs facile de rcuprer sa position, on parle de golocalisation. Pour ce faire, on
pourra utiliser la classe Geolocator - http://msdn.microsoft.com/en-us/library/
windows/apps/windows.devices.geolocation.geolocator qui est le point daccs
au service de localisation. Nous aurons besoin dans un premier temps dindiquer que
notre application utilise le service de localisation en rajoutant la capacit ID_CAP_LOCATION.
Il suffit de la slectionner en double-cliquant sur le fichier WMAppManifest.xml, comme
indiqu sur la figure 32.1.

Figure 32.1 Activer la capacit de golocalisation


482

DTERMINER SA POSITION
Ensuite, il faut avoir une instance de la classe Geolocator et potentiellement indiquer
des paramtres comme la prcision. Utilisons dans un premier temps ce XAML :
1
2
3
4
5

< StackPanel >


< Button Click = " Button_Click_1 " Content = " Obtenir la
position du t l phone " / >
< TextBlock x : Name = " Latitude " / >
< TextBlock x : Name = " Longitude " / >
</ StackPanel >

Et dans le code-behind, nous pouvons obtenir notre position avec :


1
2
3
4

private async void Button_Click_1 ( object sender ,


RoutedEventArgs e )
{
Geolocator geolocator = new Geolocator () ;
geolocator . De si r ed Ac cu r ac yI nM e te rs = 50 ;

try
{

6
7
8

Geoposition geoposition = await geolocator .


G etGeopositionAsync ( TimeSpan . FromMinutes ( 5 ) ,
TimeSpan . FromSeconds ( 10 ) ) ;

Dispatcher . BeginInvoke (() = >


{
Latitude . Text = geoposition . Coordinate . Latitude .
ToString () ;
Longitude . Text = geoposition . Coordinate . Longitude .
ToString () ;
}) ;

10
11
12
13
14
15
16
17
18
19
20
21
22
23

}
catch ( U n a u t h o r i z e d A c c e s s E x c e p t i o n )
{
MessageBox . Show ( " Le service de location est d sactiv
dans les param tres du t l phone " ) ;
}
catch ( Exception ex )
{
}

Grce la mthode GetGeopositionAsync nous obtenons la position du tlphone, et


ce, de manire asynchrone (notez lemploi des mots-cls await et async), ce qui vite
de bloquer linterface utilisateur. Nous pouvons ensuite exploiter lobjet Geoposition
obtenu en retour et notamment ses proprits Latitude et Longitude. Dans cet objet,
nous obtenons galement dautres informations, comme laltitude (en mtres au-dessus
du niveau de la mer), lorientation (en degrs par rapport au nord), la vitesse (en mtres
par secondes) ; cette dernire donne ayant peu de sens lorsque la golocalisation est
demande une unique fois.
483

CHAPITRE 32. LA GOLOCALISATION


Sauf quune position, a change au fur et mesure ! Eh oui, en voiture, dans le train, etc.
On peut avoir besoin dtre notifi dun changement de position ; ce qui peut tre trs
pratique dans une application de navigation ou pour enregistrer un parcours de course
pied. Dans ce cas, il faut sabonner lvnement PositionChanged qui nous fournira
une nouvelle position chaque fois. Nous aurons galement besoin de nous abonner
lvnement StatusChanged qui nous permettra de connatre les changements de statut
du matriel :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private Geolocator geolocator ;

4
5
6
7
8

public MainPage ()
{
I nitializeComponent () ;
}

9
10
11
12
13
14

protected override void OnNavigatedTo ( NavigationEventArgs e


)
{
geolocator = new Geolocator { DesiredAccuracy =
PositionAccuracy . High , MovementThreshold = 20 };
geolocator . StatusChanged += g eo l o ca t o r_ S t at u s Ch a n ge d ;
geolocator . PositionChanged +=
geolocator_PositionChanged ;

15
16
17

base . OnNavigatedTo ( e ) ;

18
19
20
21
22
23

protected override void OnNavigatedFrom ( NavigationEventArgs


e)
{
geolocator . PositionChanged -=
geolocator_PositionChanged ;
geolocator . StatusChanged -= g eo l o ca t o r_ S t at u s Ch a n ge d ;
geolocator = null ;

24
25
26

base . OnNavigatedFrom ( e ) ;

27
28
29
30

private void g e ol o c at o r _S t a tu s C ha n g ed ( Geolocator sender ,


S t at usC ha nge dEv en tAr gs args )
{
string status = " " ;

31
32
33
34
35

484

switch ( args . Status )


{
case PositionStatus . Disabled :
status = " Le service de localisation est d
sactiv dans les param tres " ;

DTERMINER SA POSITION
36
37
38
39
40
41
42
43
44
45
46

47

break ;
case PositionStatus . Initializing :
status = " En cours d ' initialisation " ;
break ;
case PositionStatus . Ready :
status = " Service de localisation pr t " ;
break ;
case PositionStatus . NotAvailable :
case PositionStatus . NotInitialized :
case PositionStatus . NoData :
break ;

48
49
50
51
52

53

Dispatcher . BeginInvoke (() = >


{
Statut . Text = status ;
}) ;

54
55
56
57
58
59
60
61
62
63

private void g e o l o c a t o r _ P o s i t i o n C h a n g e d ( Geolocator sender ,


P o s i t i on C h an g e dE v e nt A r gs args )
{
Dispatcher . BeginInvoke (() = >
{
Latitude . Text = args . Position . Coordinate . Latitude .
ToString () ;
Longitude . Text = args . Position . Coordinate . Longitude
. ToString () ;
}) ;
}

Il faut toujours vrifier la bonne disponibilit du GPS, car il peut arriver que lutilisateur veuille explicitement le dsactiver. Cest le cas par exemple lorsque le statut est
gal Disabled. Pour afficher le statut, jai lgrement modifi mon XAML :
1
2
3

< TextBlock x : Name = " Statut " / >


< TextBlock x : Name = " Latitude " / >
< TextBlock x : Name = " Longitude " / >

Remarque : il ny a pas de mthode pour dmarrer le service de localisation comme il


pouvait y avoir la mthode Start() avec le service de localisation de Windows Phone
7. Le dmarrage seffectue lorsquon sabonne lvnement PositionChanged. Ce qui
implique que lorsquon nen aura plus besoin, il sera possible darrter lutilisation
du service de localisation en se dsabonnant de ce mme vnement. Cela permet
dconomiser la batterie. Cest ce que jai fait dans mon exemple en dmarrant le
service de navigation lorsque jarrive sur la page et en le dsactivant lorsque je quitte
la page.
Et voil, dans la mthode geolocator_PositionChanged, nous recevons un objet nous
fournissant les coordonnes de la position du tlphone, comme vu plus haut, et nous
485

CHAPITRE 32. LA GOLOCALISATION


avons des relevs successifs de la position du tlphone.
Remarquons quil est possible de dfinir leffort que doit faire le service de localisation
pour dterminer notre position. Il suffit de crer le Geolocator en affectant sa proprit
DesiredAccuracy. Il existe deux modes : prcision forte ou prcision par dfaut. Vous
aurez compris que la prcision forte sobtient avec :
1

DesiredAccuracy = PositionAccuracy . High

Et que la prcision par dfaut sobtient avec


1

DesiredAccuracy = PositionAccuracy . Default

En mode par dfaut, le tlphone utilise essentiellement les bornes wifi et les antennes
radios pour faire une triangularisation, ce qui est relativement peu consommateur de
batterie. En mode de prcision forte, le tlphone va utiliser lensemble des moyens
dont il dispose pour nous golocaliser en ngligeant la consommation de batterie. Il
utilisera notamment la puce GPS pour un reprage par satellite.
noter que lon peut indiquer partir de quelle distance (en mtres par rapport la
dernire position trouve) le service de localisation envoie un vnement pour nous indiquer un changement de position. Cela se fait via la proprit MovementThreshold qui
est 0 mtre par dfaut, ce qui implique quun vnement de changement de position
est envoy chaque changement de position. Nous avons galement notre disposition
la proprit DesiredAccuracyInMeters qui permet dindiquer le niveau de prcision
souhait (en mtres) pour les donnes renvoyes depuis le service de localisation.

Utiliser la golocalisation dans lmulateur


Oui mais un GPS, cest trs bien sur un tlphone. . . mais sur un mulateur ?
Comment on fait ? Il peut quand mme nous fournir une position ?
Pour le vrifier, dmarrons lmulateur et rcuprons la position comme nous lavons
vu. Nous obtenons une latitude de 47,669444 et une longitude de -122,123889, qui
correspond la position. . . des btiments de Microsoft. Hasard ?
En fait, ceci se passe dans les outils supplmentaires de lmulateur, le dernier bouton
en bas de la barre situe droite de lmulateur. Il est possible de changer les valeurs
et mme de dfinir des itinraires pour simuler des dplacements, cela se passe dans
longlet Localisation, comme indiqu la figure 32.2.
En quelques clics, je dfinis ma position et mon tour de France. Il sera alors trs facile
de faire changer lmulateur de position pendant son excution, soit manuellement, soit
en jouant un itinraire pralablement enregistr. . . Pratique pour simuler des positions
ou des changements de position tout en restant confortablement install sur son PC
avec son dbogueur.
486

UTILISER LA GOLOCALISATION DANS LMULATEUR

Figure 32.2 Simuler la golocalisation dans les outils de lmulateur

487

CHAPITRE 32. LA GOLOCALISATION

Utiliser la golocalisation avec le contrle Map


La golocalisation prend tout son intrt utilise concomitamment avec le contrle Map
vu dans la partie prcdente. tant donn que le service de localisation fourni un objet
avec une latitude et une longitude (de type Geocoordinate - http://msdn.microsoft.
com/fr-fr/library/windows/apps/windows.devices.geolocation.geocoordinate),
il est trs facile dutiliser les coordonnes GPS de notre utilisateur sur la carte, par
exemple ici je centre la carte sur la position de lutilisateur :
1
2
3
4
5
6

private void g e o l o c a t o r _ P o s i t i o n C h a n g e d ( Geolocator sender ,


P o s i t i o n C ha n g ed E v e nt A r gs args )
{
Dispatcher . BeginInvoke (() = >
{
Latitude . Text = args . Position . Coordinate . Latitude .
ToString () ;
Longitude . Text = args . Position . Coordinate . Longitude .
ToString () ;

7
8

9
10

}) ;

Carte . Center = new GeoCoordinate ( args . Position .


Coordinate . Latitude , args . Position . Coordinate .
Longitude ) ;

Attention, il y a une diffrence entre la classe Geocoordinate, issue du


service de localisation, et la classe GeoCoordinate que nous utilisons avec
le contrle de carte. Notez la subtile diffrence de casse et les espaces de
noms diffrents, savoir respectivement Windows.Devices.Geolocation
et System.Device.Location.
Remarquons quil faut utiliser le dispatcher pour revenir sur le thread dinterface afin
de mettre jour cette dernire.

En rsum
Le service de localisation est accessible partir du moment o il a t dclar
et accept par lutilisateur.
La classe Geolocator permet dobtenir les informations issues du service de
localisation.
On peut utiliser le contrle de carte pour afficher sa position, en utilisant la
classe GeoCoordinate.

488

Chapitre

33

Les Tasks du tlphone


Difficult :
Utiliser les capteurs du tlphone nest pas le seul moyen daccder aux fonctionnalits
internes du tlphone. Les Task, que lon peut traduire en tches, sont une solution permettant daccder dautres applications du tlphone. Il sagit par exemple de la possibilit de slectionner un contact dans le rpertoire de lutilisateur, denvoyer un SMS,
de prendre une photo, etc. Nous allons dcouvrir dans ce chapitre quil y a deux sortes
de tches, les choosers et les launchers, elles sont situes dans lespace de nom : using
Microsoft.Phone.Tasks;

489

CHAPITRE 33. LES TASKS DU TLPHONE

Les choosers
Les choosers, comme le nom le suggre aux anglophones, permettent de choisir une
information. Plus prcisment, il sagira de dmarrer une fonctionnalit qui va nous
renvoyer quelque chose dexploitable, par exemple un contact dans le rpertoire. Voici
une liste des choosers des Windows Phone 8 :
Chooser
AddressChooserTask
AddWalletItemTask

CameraCaptureTask
EmailAddressChooserTask
GameInviteTask
PhoneNumberChooserTask
PhotoChooserTask
SaveContactTask
SaveEmailAddressTask
SavePhoneNumberTask
SaveRingtoneTask

Description
Dmarre lapplication Contacts pour
choisir une adresse physique
Dmarrer lapplication Wallet
(portefeuille) afin de permettre dy
ajouter un produit
Dmarre lappareil photo pour pouvoir
prendre une photo
Dmarre lapplication Contacts pour
choisir une adresse email
Permet dinviter des autres joueurs
participer un jeu en ligne
Dmarre lapplication Contacts pour
choisir un numro de tlphone
Permet de slectionner une photo du
tlphone
Dmarre lapplication Contacts pour
enregistrer un contact
Dmarre lapplication Contacts pour
enregistrer un email
Dmarre lapplication Contacts pour
enregistrer un numro de tlphone
Dmarre lapplication Sonneries

Je ne vais pas tous vous les prsenter en dtail, car ce serait un peu fastidieux, dautant
plus que la description est globalement assez explicite. Sachez cependant que lmulateur ne permet pas de simuler toutes les tches et que vous aurez parfois besoin
dun tlphone pour vous rendre vraiment compte du rsultat. Limportant est que les
choosers renvoient un lment exploiter. Donc globalement, il faudra instancier la
tche, sabonner lvnement de fin de tche et exploiter le rsultat. Par exemple,
pour slectionner une photo disponible dans le tlphone, nous pourrons utiliser le code
suivant :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private PhotoChooserTask photoChooserTask ;

4
5
6
7

490

public MainPage ()
{
I nitializeComponent () ;

LES CHOOSERS
8
9

10

photoChooserTask = new PhotoChooserTask () ;


photoChooserTask . Completed +=
photoChooserTask_Completed ;

11

private void p h o t o C h o o s e r T a s k _ C o m p l e t e d ( object sender ,


PhotoResult e )
{
if ( e . TaskResult == TaskResult . OK )
{
BitmapImage image = new BitmapImage () ;
image . SetSource ( e . ChosenPhoto ) ;
Photo . Source = image ;
}
}

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
photoChooserTask . Show () ;
}

Comme vous pouvez le constater, la photo slectionne est dans le paramtre de lvnement. Celui-ci possde galement un rsultat de tche, par exemple, nous naurons
une valeur dans la proprit ChosenPhoto que si lutilisateur est all au bout de la
tche, et qugalement le rsultat soit OK. Cot XAML, nous pourrons avoir :
1
2
3
4

< StackPanel >


< Button Content = " Choisir la photo " Tap = " Button_Tap " / >
< Image x : Name = " Photo " / >
</ StackPanel >

Dans lmulateur, nous avons quelques valeurs bouchonnes pour cette tche, voyez
plutt sur la figure 33.1.
Illustrons encore les choosers avec la tche CameraCaptureTask qui permet de prendre
une photo depuis votre application :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private CameraCaptureTask cameraCaptureTask ;

4
5
6
7
8
9
10

public MainPage ()
{
I ni t i alizeComponent () ;
camer aCaptureTask = new CameraCaptureTask () ;
camer aCaptureTask . Completed +=
cameraCaptureTask_Completed ;
}

11

491

CHAPITRE 33. LES TASKS DU TLPHONE

Figure 33.1 Choix dune image depuis lmulateur

private void c a m e r a C a p t u r e T a s k _ C o m p l e t e d ( object sender ,


PhotoResult e )
{
if ( e . TaskResult == TaskResult . OK )
{
BitmapImage image = new BitmapImage () ;
image . SetSource ( e . ChosenPhoto ) ;
Photo . Source = image ;
}
}

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
cameraCaptureTask . Show () ;
}

Ici aussi, lmulateur fonctionne en mode bouchon et nous propose une image factice
pour simuler la prise dune photo, comme on peut le voir sur la figure 33.2.
Vous pourrez prendre la photo depuis lmulateur en cliquant avec le bouton droit.
492

LES LAUNCHERS

Figure 33.2 Simulation de prise de photo

Attention, vous ne pourrez pas utiliser la tche CameraCaptureTask sur votre


tlphone lorsque celui-ci est branch votre PC.

Les launchers
Les launchers permettent de dmarrer une application sur le tlphone, par exemple
envoyer un mail ou un SMS. Ils fonctionnent comme les choosers mais nattendent pas
dinformations en retour.
Ici aussi, les descriptions parlent delles-mmes. Dans notre application de lecteur de
flux RSS, il aurait par exemple t possible dutiliser le WebBrowserTask au lieu dembarquer le contrle WebBrowser dans nos pages. Il sutilise ainsi :
1

WebBrowserTask webBrowserTask = new WebBrowserTask { Uri = new


Uri ( " http :// fr . openclassrooms . com / informatique / cours /
apprenez -a - developper - en - c " , UriKind . Absolute ) };
webBrowserTask . Show () ;

qui dmarre donc Internet Explorer avec la page demande (voir la figure 33.3).
493

CHAPITRE 33. LES TASKS DU TLPHONE

Launcher
BingMapsDirectionsTask

BingMapsTask

ConnectionSettingsTask
EmailComposeTask
MapDownloaderTask
MapsDirectionsTask

MapsTask
MapUpdaterTask
MarketplaceDetailTask
MarketplaceHubTask
MarketplaceReviewTask
MarketplaceSearchTask
MediaPlayerLauncher
PhoneCallTask
SaveAppointmentTask
SearchTask
ShareLinkTask
ShareMediaTask

ShareStatusTask
SmsComposeTask
WebBrowserTask

494

Description
Permet de dmarrer lapplication carte
de Windows Phone 7 pour afficher un
itinraire
Permet de dmarrer la carte de
Windows Phone 7, centre sur une
position
Permet dafficher la page de
configuration du rseau
Permet de composer un nouvel email
via lapplication de messagerie
Permet de tlcharger les cartes pour
une utilisation hors ligne
Permet de dmarrer lapplication de
carte pour afficher un itinraire entre
deux points
Permet de dmarrer lapplication de
carte centre sur un emplacement
Permet de tlcharger les mises jour
de cartes
Permet dafficher le dtail dune
application dans le Marketplace
Dmarre lapplication Marketplace
Permet datteindre la fiche des avis
dune application
Affiche les rsultats dune recherche
Marketplace
Dmarre le lecteur de mdia
Permet de passer des appels
Permet denregistrer un rendez-vous
Dmarre une recherche sur le web
Partage un lien sur un rseau social
Permet de partager une photo ou
vido avec les applications qui se sont
enregistres pour les exploiter
Partage un message de statut sur un
rseau social
Permet de composer un SMS
Dmarre le navigateur web

LES LAUNCHERS

Figure 33.3 Le launcher WebBrowserTask affiche une page web


Il est possible dutiliser le WebBrowserTask pour demander Internet Explorer de nous afficher un document Word par exemple.
Tous les launchers fonctionnent de la mme faon, par exemple pour pr-remplir un
SMS avant envoi :
1
2

SmsComposeTask smsComposeTask = new SmsComposeTask { To = " +


33123456789 " , Body = " Bon anniversaire ! " };
smsComposeTask . Show () ;

la figure 33.4, vous pouvez observer le rendu dans lmulateur.


Voyons enfin un autre launcher bien pratique qui nous permet dafficher un itinraire
sans passer par les API de la carte que nous avons prcdemment vues :
1
2
3
4

Ma ps Di rec ti on sTask mapsDirectionsTask = new MapsDirectionsTask


() ;
La be le dMa pL oc ation emplacement = new LabeledMapLocation ( " Tour
Eiffel " , null ) ;
ma ps Di rec ti on sTask . End = emplacement ;
ma ps Di rec ti on sTask . Show () ;

Regardez la figure 33.5 pour le rendu.


noter que dans ce cas, il prend la position de lutilisateur comme point de dpart pour
495

CHAPITRE 33. LES TASKS DU TLPHONE

Figure 33.4 Composition dun SMS pr-rempli

Figure 33.5 Calcul ditinraire grce au launcher


496

ETAT DE LAPPLICATION
calculer litinraire. Vous pouvez affecter la proprit mapsDirectionsTask.Start afin
de prciser un point de dpart diffrent de la position courante de lutilisateur. Vous
pouvez galement spcifier des coordonnes GPS comme point darrive, et dans ce
cas-l, la chane passe est utilise comme tiquette :
1
2

GeoCoordinate tourEiffel = new GeoCoordinate { Latitude = 48 .


858115 , Longitude = 2 . 294710 };
La be le dMa pL oc ation emplacement = new LabeledMapLocation ( " Tour
Eiffel " , tourEiffel ) ;

Il est de bon ton dencadrer la mthode Show() dun launcher ou dun chooser
par un try/catch, surtout si lappel la mthode Show() est fait dans
un bouton. Si vous dmarrez deux tches en mme temps, par exemple en
appuyant deux fois rapidement sur le bouton, vous aurez une exception qui
fera planter lapplication.

Etat de lapplication
Comme nous lavons dj appris, lorsquest dmarre une nouvelle application, lapplication en cours passe en mode suspendu et peut potentiellement tre termine. Cela
veut dire que lorsque nous dmarrons un chooser ou un launcher, il est possible que
votre application soit arrte. Il faut donc que vous veilliez enregistrer ltat de votre
application au cas o celle-ci serait arrte. Mais en plus, dans le cas du chooser, il
pourrait arriver que votre application soit termine avant davoir rcupr la rponse
du chooser, ce qui pose un problme. Pour viter cela, en instanciant un chooser, Windows Phone est capable de vrifier la prsence dune information dans celui-ci. Cela
implique que lvnement de fin de choix soit dfini sur une variable dinstance de la
classe et non dans une mthode, comme ce que jai montr dans les exemples de chooser. On ne doit pas dclarer un chooser par exemple dans le constructeur de notre page.
Linstanciation correcte du chooser doit donc tre :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private E ma i lA dd re s sC ho os e rT as k e ma il Ad d re ss Ch o os er T as k ;

4
5
6
7
8
9
10

public MainPage ()
{
I ni t i alizeComponent () ;
e m a i l Ad d re ss C ho os er T as k = new E ma i lA dd re s sC ho o se rT as k ()
;
e m a i l Ad d re ss C ho os er T as k . Completed +=
emailAddressChooserTask_Completed ;
}

11
12

private void e m a i l A d d r e s s C h o o s e r T a s k _ C o m p l e t e d ( object


sender , EmailResult e )

497

CHAPITRE 33. LES TASKS DU TLPHONE


{

13
14
15

16

if ( e . TaskResult == TaskResult . OK )
MessageBox . Show ( " Adresse choisie : " + e . Email ) ;

17
18
19
20
21
22

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
e ma il Ad d re ss Ch o os er T as k . Show () ;
}

afin davoir une chance de rcuprer la slection.

En rsum
Les launchers et les choosers offrent une solution pour interagir avec dautres
applications du tlphone trs facilement.
Ils permettent davoir une exprience utilisateur propre et cohrente pour utiliser
les applications internes du tlphone.
Un chooser doit tre dclar dans la porte dune page.

498

Chapitre

34

Les tuiles
Difficult :
Les tuiles, ce sont les icnes reprsentant nos applications prsentes sur la page daccueil
dun Windows Phone. Elles sont un raccourci pour dmarrer les applications prsentes dans
notre tlphone, un peu comme les icnes prsentes sur le bureau de nos vieux Windows.
Pour linstant, rien dextraordinaire vous me direz !
Mais elles sont un peu plus quune simple icne permettant de dmarrer une application.
Elles peuvent avoir une icne mais galement du texte fournissant des informations sur
lapplication ou son contexte. Elles peuvent galement tre animes et sont une des grandes
forces de Windows Phone. Dailleurs, Windows 8 sest empress de se les rcuprer.
Prsentes en une seule taille avec Windows Phone 7.5, elles se dclinent en trois tailles
partir de Windows Phone 7.8, pour le plus grand plaisir des utilisateurs qui les ont adoptes
avec intrt.
Dcouvrons prsent ces fameuses tuiles.

499

CHAPITRE 34. LES TUILES

Que sont les tuiles ?


Les tuiles, ce sont les icnes reprsentant nos applications prsentes sur la page daccueil
dun Windows Phone (ou sur la page daccueil de lmulateur, comme la figure 34.1) :

Figure 34.1 Les tuiles de la page daccueil dans lmulateur


Lorsque lon clique dessus, notre application est lance. Lorsque lon tlcharge (ou
dveloppe) une application, la tuile nest pas tout de suite prsente sur laccueil. Cest
lutilisateur de choisir explicitement de ly mettre en lpinglant sur la page daccueil.
Cela se fait en allant dans la liste des applications et en appuyant longtemps sur lapplication. Cela fait apparatre un menu contextuel qui propose de supprimer lapplication,
de lpingler sur la page daccueil ou daller la noter sur le Windows Phone Store. Il
est galement possible dpingler une application depuis lmulateur suivant le mme
principe, en cliquant longtemps sur lapplication. Par exemple dans la figure 34.2, pour
pingler lapplication DemoChooser, je clique dessus et un menu droulant apparat.
Note : Pour accder la liste des applications de lmulateur, le plus simple
est de cliquer sur le bouton de menu et de faire bouger lcran sur la droite.
Dans lmulateur, on ne peut bien sr pas noter les applications et dautant plus celles
que nous sommes en train de dvelopper !
Une fois lapplication pingle, nous pouvons aller sur lcran daccueil et nous aurons
une magnifique tuile (voir figure 34.3).
500

QUE SONT LES TUILES ?

Figure 34.2 pingler une application

Figure 34.3 Notre application est pingle et nous voyons sa tuile sur la page daccueil
501

CHAPITRE 34. LES TUILES

Des tuiles pour tous les gots


Les tuiles existent en diffrentes tailles. Il y a les petites, les moyennes et les larges.
Si les applications lont prvu, il est possible de redimensionner ces tuiles sur notre
cran daccueil. Voici par exemple dans la figure 34.4 la tuile permettant daccder aux
images en taille large.

Figure 34.4 Une tuile en taille large


Pour redimensionner la tuile, il faut faire un toucher long sur celle-ci. Deux icnes
apparaissent alors droite de la tuile, comme le montre la figure 34.5.
La premire icne, en haut droite de la tuile, permet de dspingler la tuile. Mais ici,
cest la seconde, en bas droite de la tuile, qui nous intresse et qui permet de passer
dans la taille infrieure, ici la taille moyenne, puis la petite taille dans la figure 34.6.
On peut constater que chaque taille offre une exprience diffrente fournissant plus
ou moins dinformations, voire une information diffrente en fonction des souhaits de
lutilisateur. Si vous essayez de redimensionner dautres tuiles, vous verrez quelles ne
supportent pas toutes les trois tailles, cest une question de choix du crateur de lapplication. De mme, les tuiles peuvent tre de plusieurs styles diffrents. Nous venons
de voir la tuile de lapplication images qui, dans son format large et moyen, affiche des
images qui dfilent sur la tuile. On appelle ce format le cycle tile template , que
lon peut traduire en modle de tuile cyclique. Elle permet de faire dfiler entre 1 et 9
images. Il existe trois modles en tout que nous allons dcouvrir dans ce chapitre :
Le modle de tuile cyclique (cycle tile template)
502

DES TUILES POUR TOUS LES GOTS

Figure 34.5 Redimensionnement des tuiles

Figure 34.6 Tuiles en tailles moyenne et petite


503

CHAPITRE 34. LES TUILES


Le modle de tuile qui se retourne (flip tile template)
Le modle de tuile icne (iconic tile template)
Les tuiles ont les tailles suivantes :
Petite
Moyenne
Large

Flip et cycle
159x159
336x336
691x336

Iconic
110x110
202x202
-

Nul besoin de vous soucier des diffrentes rsolutions, Windows Phone soccupe de
redimensionner tout pour nous, vous navez besoin que dajouter les images pour la
rsolution XWGA.

Personnaliser les tuiles par dfaut


Nous venons de voir comment pingler notre application sur la page daccueil. Celleci prend par dfaut lapparence de limage se trouvant dans le rpertoire du projet
Assets\Tiles\FlipCycleTileMedium.png, que lon peut retrouver dans lexplorateur de
solutions illustr dans la figure 34.7.

Figure 34.7 Les images par dfaut des tuiles


Cette tuile senrichit du titre de lapplication (voir figure 34.8).

Figure 34.8 Image de la tuile par dfaut


504

PERSONNALISER LES TUILES PAR DFAUT


Plus prcisment, elle est construite grce aux paramtres prsents dans le fichier
WMAppManifest.xml, que lon obtient en dpliant le rpertoire properties du projet
et en double-cliquant dessus (voir figure 34.9).

Figure 34.9 Paramtrage de la tuile par dfaut, dans WMAppManifest.xml


Nous pouvons voir que le modle de la vignette est Flip et que lapplication ne supporte
que les tuiles de taille petite et moyenne. Dailleurs, si vous essayez de redimensionner
la tuile sur la page daccueil, vous ne pourrez pas lavoir en large. Pour cela, vous
pouvez cocher la case prise en charge des grandes vignettes et choisir une image
pour la grande taille, par exemple FlipCycleTileLarge.png qui est (comme par hasard)
la bonne taille et qui se trouve dans le rpertoire Assets/Tiles avec les autres images
de tuiles.
Note : pour voir les changements dans lmulateur, vous allez devoir dspingler la tuile et la r-pingler.
Mais lditeur du fichier WMAppManifest.xml nest pas complet. Enfin, disons quon
peut faire plus de choses directement en modifiant le contenu du fichier XML. Si vous
louvrez, vous verrez notamment la section Tokens contenant la dfinition des tuiles :
1
2
3
4

5
6

7
8
9
10

< Tokens >


< PrimaryToken TokenID = " DemoTuilesToken " TaskName = " _default " >
< TemplateFlip >
< SmallImageURI IsRelative = " true " IsResource = " false " >
Assets \ Tiles \ FlipCycleTileSmall . png </ SmallImageURI
>
< Count >0 </ Count >
< B ackgroundImageURI IsRelative = " true " IsResource = "
false " > Assets \ Tiles \ FlipCycleTileMedium . png </
BackgroundImageURI >
< Title > DemoTuiles </ Title >
< BackContent >
</ BackContent >
< BackBackgroundImageURI >

505

CHAPITRE 34. LES TUILES


</ BackBackgroundImageURI >
< BackTitle >
</ BackTitle >
< La rg eB a ck gr o un dI ma g eU RI IsRelative = " true " IsResource
= " false " > Assets \ Tiles \ FlipCycleTileLarge . png </
LargeBackgroundImageURI >
< LargeBackContent / >
< L a r g e B a c k B a c k g r o u n d I m a g e U R I IsRelative = " true "
IsResource = " false " >
</ LargeBackBackgroundImageURI >
< DeviceLockImageURI >
</ DeviceLockImageURI >
< HasLarge > True </ HasLarge >
</ TemplateFlip >
</ PrimaryToken >
</ Tokens >

11
12
13
14

15
16
17
18
19
20
21
22
23

On y retrouve les diverses urls relatives vers nos images de tuiles, mais aussi dautres
choses. Modifions par exemple la balise count pour mettre la valeur 3 la place de 0 :
1

< Count >3 </ Count >

Modifions galement les balises BackContent et BackTitle :


1
2

< BackContent > Ma super appli </ BackContent >


< BackTitle > Vive les tuiles </ BackTitle >

Nous obtenons le rsultat dans la figure 34.10.


Sur la partie gauche de limage, nous pouvons voir un petit 3, qui correspond la balise
Count. Cela permet, pourquoi pas, dindiquer quil y a 3 lments nouveaux venir
consulter dans notre application. La partie droite de limage correspond au dos de la
tuile. Rappelez-vous, notre application utilise le modle de tuile Flip qui se retourne
pour fournir dautres informations. Ici, nous avons affich du texte, mais il est galement
possible dafficher une image grce la balise BackBackgroundImageURI. Notez que
si vous changez la taille de la tuile, vous verrez que le dos de la tuile ne saffiche quen
grande ou moyenne taille et quil ne saffiche pas en petite taille. Mais tout ceci est bien
statique . . . notamment par rapport au chiffre en haut droite. On ne peut pas envisager
de dterminer quil y aura 3 nouveaux lments tout le temps, ds la cration de
lapplication. Heureusement, il est galement possible de modifier la tuile pour mettre
jour quelques informations lorsque lapplication se ferme, et rappeler lutilisateur
o il en tait lorsquil la rouvre. Pour effectuer ces mises jour, il faut accder la
tuile concerne via la classe statique ShellTile - http://msdn.microsoft.com/fr-fr/
library/windowsphone/develop/hh220861(v=vs.105).aspx, avec le code suivant :
1
2
3

public MainPage ()
{
I ni t i alizeComponent () ;

4
5
6

506

ShellTile tuileParDefaut = ShellTile . ActiveTiles . First () ;

PERSONNALISER LES TUILES PAR DFAUT

Figure 34.10 La tuile, de face et de dos


if ( tuileParDefaut != null )
{
FlipTileData flipTileData = new FlipTileData
{
Title = " Ma tuile " ,
Count = 4 ,
BackTitle = " Nouveau titre " ,
BackContent = " Ouvrez vite ! " ,
};

7
8
9
10
11
12
13
14
15
16
17
18
19

tuileParDefaut . Update ( flipTileData ) ;

Une fois la mise jour de la tuile faite, nous pourrons avoir un nouveau rendu, illustr
dans la figure 34.11.
Du coup, je vous montre la tuile en grande taille. Notez la prsence du chiffre 4 en
haut droite qui correspond la proprit Count. Mettre 0 ou null dans la proprit
effacera le nombre qui apparat.
Attention : le fait de mettre jour la tuile par dfaut par code npingle pas
pour autant lapplication sur lcran daccueil. Il faut que ce soit une action
volontaire de lutilisateur. Cela changera juste son apparence.
507

CHAPITRE 34. LES TUILES

Figure 34.11 La tuile cre par code, en grande taille

Et les autres modles de tuiles alors ?


Jy arrive. Car effectivement, nous navons vu que les tuiles qui se retournent ( flip ),
il reste encore les tuiles cycliques ( cycle ) et les tuiles icnes ( iconic ). Sachez ds
prsent que le principe de cration est globalement le mme. Pour les tuiles cycliques
( cycle ), vous pouvez faire dfiler de 1 9 images. Changez le modle dans lditeur
et vous pourrez voir que vous allez pouvoir remplir jusqu 9 images (voir figure 34.12).
Jen ai profit pour mettre quelques images de notre mascotte prfre dans la figure
34.13. Une fois la mascotte pingle, nous pourrons voir les images dfiler.
Pour mettre jour limage qui saffiche par dfaut sur la tuile, on utilisera la classe CycleTileData - http://msdn.microsoft.com/fr-fr/library/windowsphone/develop/
microsoft.phone.shell.cycletiledata(v=vs.105).aspx et notamment la proprit
CycleImages qui est un tableau dUri pointant sur des images afficher. Notez que la
proprit Count est galement disponible pour ce modle de tuile.
Reste les tuiles icnes ( iconic , voir figure 34.14).
Le rsultat des tuiles icnes en grande taille est illustr dans la figure 34.15.
Remarque, ici jai modifi la balise Count et la balise Message :
1

< Count >1 </ Count >

508

PERSONNALISER LES TUILES PAR DFAUT

Figure 34.12 Modification du modle de tuile cyclique

Figure 34.13 Les images de Zozor dfilent dans la tuile


509

CHAPITRE 34. LES TUILES

Figure 34.14 Modification du modle de tuiles icnes

Figure 34.15 Tuile icne en grande taille

510

CRER DES TUILES SECONDAIRES


2

< Message > Venez vite d couvrir Zozor ! </ Message >

noter quon utilisera la classe IconicTileData - http://msdn.microsoft.com/fr-fr/


library/windowsphone/develop/microsoft.phone.shell.iconictiledata(v=vs.105)
.aspx pour crer la tuile icne par code.
Attention : il nest pas possible de changer dynamiquement (en utilisant du
code) le modle de tuile par dfaut que nous venons de changer par linterface.
Si vous souhaitez le faire une fois votre application termine et dploye sur
un tlphone, il vous faudra mettre jour votre application et la dployer
nouveau.

Crer des tuiles secondaires


La tuile principale permet de lancer lapplication. Il ny a quune faon pour les obtenir :
pingler lapplication depuis le menu, comme nous lavons vu. Les tuiles secondaires
permettent de dmarrer lapplication dune faon particulire. Elles servent en gnral accder des fonctionnalits de lapplication, comme un raccourci. Ainsi, il est
possible de crer des tuiles qui vont naviguer directement sur une page prcise de lapplication, avec pourquoi pas des paramtres. Reprenez par exemple notre dernier TP,
qui contenait un jeu de grattage et un jeu de secouage . . . Quand jai ralis ces jeux,
ctait un peu insupportable de devoir passer chaque fois par le menu pour lancer
une application et tester une petite modification. Que je suis triste de ne pas avoir
eu une telle fonctionnalit de tuiles secondaires ! Avec elles, jaurai pu facilement crer
des raccourcis vers les pages de mon choix . . . Alors, rparons tout de suite cette erreur et rajoutons sur la page principale deux boutons permettant de crer nos tuiles
secondaires :
1
2
3
4
5
6
7
8
9
10
11
12
13
14

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< StackPanel >
< Button Content = " Grattage ... " Tap = " Button_Tap " / >
< Button Content = " Secouage ... " Tap = " Button_Tap_1 " / >
</ StackPanel >
< StackPanel Grid . Row = " 1 " VerticalAlignment = " Bottom " >
< Button Content = " Cr er tuile grattage " Tap = "
Button_Tap_2 " / >
< Button Content = " Cr er tuile secouage " Tap = "
Button_Tap_3 " / >
</ StackPanel >
</ Grid >

Nous utiliserons grosso modo le mme code et le mme principe pour crer une tuile
secondaire :
511

CHAPITRE 34. LES TUILES


1
2
3

4
5
6
7
8
9
10
11
12

private void Button_Tap_2 ( object sender , System . Windows . Input .


GestureEventArgs e )
{
ShellTile tuileGrattage = ShellTile . ActiveTiles .
FirstOrDefault ( elt = > elt . NavigationUri . ToString () .
Contains ( " Grattage . xaml " ) ) ;
if ( tuileGrattage == null )
{
FlipTileData tuile = new FlipTileData
{
SmallBackgroundImage = new Uri ( " / Assets / Tiles /
gratter - petit . png " , UriKind . Relative ) ,
BackgroundImage = new Uri ( " / Assets / Tiles / gratter moyen . png " , UriKind . Relative ) ,
Title = " Grattage " ,
BackContent = " Acc s direct au gratage " ,
};

13
14
15
16

ShellTile . Create ( new Uri ( " / Grattage . xaml " , UriKind .


Relative ) , tuile , false ) ;

17
18
19
20

21
22
23
24
25
26
27
28
29

private void Button_Tap_3 ( object sender , System . Windows . Input .


GestureEventArgs e )
{
ShellTile tuileSecouage = ShellTile . ActiveTiles .
FirstOrDefault ( elt = > elt . NavigationUri . ToString () .
Contains ( " Secouage . xaml " ) ) ;
if ( tuileSecouage == null )
{
FlipTileData tuile = new FlipTileData
{
SmallBackgroundImage = new Uri ( " / Assets / Tiles /
secouer - petit . png " , UriKind . Relative ) ,
BackgroundImage = new Uri ( " / Assets / Tiles / secouer moyen . png " , UriKind . Relative ) ,
Title = " Secouage " ,
BackContent = " Acc s direct au secouage " ,
};

30
31
32
33

ShellTile . Create ( new Uri ( " / Secouage . xaml " , UriKind .


Relative ) , tuile , false ) ;

Pour viter dajouter plusieurs fois la mme tuile, on pourra se baser sur la proprit
NavigationUri des tuiles existantes, afin de vrifier si elles ont dj t ajoutes. Il ne
reste plus qu cliquer sur nos boutons. Vous verrez que lorsque la tuile secondaire
est cre, nous sommes renvoys sur la page daccueil pour voir le rsultat (voir figure
512

CRER DES TUILES SECONDAIRES


34.16), ce qui implique que lapplication passe dans un mode dsactiv. Pour la ractiver, il faudra soit faire un retour arrire, soit pourquoi pas utiliser nos nouvelles
tuiles.

Figure 34.16 Les tuiles secondaires de notre application de jeu


Et ainsi, nous accdons directement la page XAML indique lors de la cration de
la tuile secondaire, qui permet de naviguer directement sur la page. Pratique non ?
Remarquez que vous pouvez galement passer des paramtres la page, comme ce
que nous avions fait dans le chapitre sur la navigation. Ici, ce nest pas utile car nous
avons deux pages distinctes, mais nous pourrions avoir la mme page qui affiche une
fiche produit dune application de vente que lon souhaiterait charger avec des produits
diffrents . . . Par exemple :
1
2

3
4
5
6
7
8
9

string idProduit = " telehd " ;


ShellTile tuileProduit = ShellTile . ActiveTiles . FirstOrDefault (
elt = > elt . NavigationUri . ToString () . Contains ( " idproduit = " +
idProduit ) ) ;
if ( tuileProduit == null )
{
FlipTileData tuile = new FlipTileData
{
Title = " T l HD " ,
BackContent = " Acc s direct la t l de vos r ves ... "
,
};

10

513

CHAPITRE 34. LES TUILES


11
12

ShellTile . Create ( new Uri ( " / VoirProduit . xaml ? idproduit = " +


idProduit , UriKind . Relative ) , tuile , false ) ;

Vous pouvez voir ce que donne ce bout de code sur la figure 34.17.

Figure 34.17 La tuile secondaire


Je vous renvoie au chapitre sur la navigation pour exploiter la query string, avec :
Navig ationContext . QueryString . TryGetValue ( " idproduit " , out
valeur ) ;

Bien sr, vous pouvez crer des tuiles secondaires dans tous les modles que vous
souhaitez. Illustrons par exemple le modle cyclique :
1

2
3
4
5
6
7
8
9
10

ShellTile secondeTuile = ShellTile . ActiveTiles . FirstOrDefault (


elt = > elt . NavigationUri . ToString () . Contains ( " VieZozor . xaml "
));
if ( secondeTuile == null )
{
CycleTileData tuile = new CycleTileData
{
CycleImages = new Uri []
{
new Uri ( " / zozor_01 . png " , UriKind . Relative ) ,
new Uri ( " / zozor_02 . png " , UriKind . Relative ) ,
new Uri ( " / zozor_03 . png " , UriKind . Relative ) ,

514

MODIFIER ET SUPPRIMER UNE TUILE


11
12
13

};

14

new Uri ( " / zozor_04 . png " , UriKind . Relative ) ,


},
Title = " Acc dez la vie de zozor "

15
16
17

ShellTile . Create ( new Uri ( " / VieZozor . xaml " , UriKind . Relative
) , tuile , false ) ;

Modifier et supprimer une tuile


Nous avons vu quil tait possible de modifier des tuiles via la mthode Update. Ceci est
valable pour la tuile principale, mais galement pour les tuiles secondaires. Par exemple,
le code ci-dessous montre comment mettre jour une tuile secondaire dune application
de vente en ligne en affichant le changement de stock de lun de ses produits :
1

2
3
4
5
6
7
8

ShellTile tuileProduit = ShellTile . ActiveTiles . FirstOrDefault (


elt = > elt . NavigationUri . ToString () . Contains ( " idproduit = " +
idProduit ) ) ;
if ( tuileProduit != null )
{
FlipTileData tuile = new FlipTileData
{
Title = " T l HD " ,
BackContent = " Plus que 2 produits en stock !!! " ,
};

9
10
11

tuileProduit . Update ( tuile ) ;

Il nest par contre pas possible de modifier lURL de la page afficher. Dans ce cas,
il faudra la supprimer puis la recrer. Supprimer une tuile secondaire est trs facile, il
suffit dutiliser la mthode Delete. Par exemple, si nous souhaitons supprimer la tuile
du secouage, nous pourrons crire :
1

2
3

ShellTile tuileSecouage = ShellTile . ActiveTiles . FirstOrDefault (


elt = > elt . NavigationUri . ToString () . Contains ( " Secouage . xaml "
));
if ( tuileSecouage == null )
tuileSecouage . Delete () ;

Lutilisateur pourra bien sr faire la mme chose tout seul depuis lcran daccueil.
Attention, on peut supprimer une tuile secondaire, par contre il est impossible
de supprimer la tuile principale par code.
515

CHAPITRE 34. LES TUILES


Remarque, il est trs courant de mettre jour une tuile via un agent qui
tourne en tche de fond. Cela offre la possibilit dinformer lutilisateur que
quelque chose de nouveau sest pass dans son application et quil serait bien
de lancer lapplication pour le dcouvrir, comme lapplication de mail qui
affiche le nombre de mails non-lus. Nous dcouvrirons les agents tournant en
tche de fond dans un chapitre ultrieur.

En rsum
Les tuiles se trouvent sur la page daccueil dun Windows Phone et sont un
raccourci volu permettant de dmarrer une application.
Elles sont disponibles en plusieurs tailles : petite, moyenne ou grande.
Il existe plusieurs modles de tuiles : tuile icne ( iconic ), tuile cyclique ( cyclic ), tuile qui se retourne ( flip ).
Il est possible de crer des tuiles secondaires, qui permettent daccder une
application laide de raccourcis supplmentaires.

516

Chapitre

35

Les notifications
Difficult :
Le principe des notifications nest pas vraiment nouveau mais est globalement plutt simple.
Imaginons que jinstalle une application qui me permette de consulter les cours de la plateforme OpenClassrooms. Ca y est, jai lu tous les cours qui mintressent et jai hte que de
nouveaux cours voient le jour pour que je puisse assouvir ma soif dapprendre. Sauf que je
ne vais pas dmarrer lapplication toutes les 5 minutes histoire de voir si un nouveau cours
est en ligne . . . a serait bien si quelquun me prvenait ds quun nouveau cours est mis
en ligne. Et tout a, mme si lapplication nest pas dmarre ou pingle . . .

517

CHAPITRE 35. LES NOTIFICATIONS


Voil, le principe de la notification. Cela consiste en la rception dun petit message sur
notre tlphone avec une information. La rception du message peut tre signale par
un bruit ou une vibration, puis le message reste affich quelques secondes pour nous
donner le temps de le lire. Mieux, si nous cliquons sur le message, notre application
est automatiquement dmarre. En revanche, un des inconvnients est justement que
la notification ne saffiche que quelques secondes et si on est loin du tlphone, il ny
a aucun moyen pour voir le contenu si on la rat (en tout cas lheure o jcris ces
lignes, car il y aurait des rumeurs de cration de centre de notification . . .).
Mais les notifications sont tout de mme bien pratiques, donc plongeons nous tout
de suite dans le vif du sujet pour crer nos propres alertes et rester au courant des
nouveauts de nos applications prfres !

Le principe darchitecture des notifications


Pour recevoir une notification, il faut que quelquun lenvoie. Forcment ! Et pour que
ce quelquun lenvoie, il faut quon lui dise que a nous intresse de recevoir des notifications. Donc, le principe global peut se rsumer dans les tapes suivantes :
Lapplication Windows Phone dclare un canal pour recevoir des notifications.
Lapplication Windows Phone indique lmetteur (le crateur de lapplication)
quelle souhaite recevoir des notifications et lui donne lidentifiant unique du
tlphone sur lequel elle tourne ainsi que le canal.
Lmetteur enregistre lidentifiant et le canal et lajoute la liste des toutes les
applications souhaitant recevoir des notifications.
Lmetteur souhaite envoyer un message : pour chaque tlphone souhaitant
recevoir des notifications, le message est envoy au service de notification de
Microsoft (le Microsoft Push Notification Service) grce au canal.
Le service de notification envoi le message chaque tlphone.
Le message saffiche sur le tlphone de lutilisateur.
Il y a donc trois intervenants dans ce mcanisme :
Le tlphone, qui reoit les notifications, ainsi que lapplication installe dessus
(votre application !),
Le service de notification, cr par le dveloppeur qui souhaite envoyer des notifications (VOUS !),
Le service de notification de Microsoft, qui soccupe denvoyer vraiment le message au tlphone.
Sachez que le message peut tre de plusieurs types.
Cela peut tre un message systme qui apparat mme si lapplication na pas
dmarre. Il apparat en haut du tlphone et si lon clique dessus alors lapplication dmarre. Il sagit des notifications Toast.
Cela peut tre un message qui va modifier lapparence dune tuile. Ce message
a peu dintrt depuis Mango car les tuiles peuvent tre pilotes par code facilement, comme nous lavons vu prcdemment, mais il peut quand mme trouver
son intrt. Il sagit des notifications Tile.
518

LE PRINCIPE DE CRATION DU SERVEUR DE NOTIFICATION


Enfin, cela peut tre un message qui apparat alors que lapplication est en cours
dutilisation. Si lapplication nest pas lance, alors le message est ignor. Il sagit
des notifications Raw.
On peut rsumer larchitecture avec le schma de la figure 35.1, qui comprend 4 grandes
tapes :

1.
2.
3.
4.

La connexion stablit et une URL est cre pour la communication.


Communication de lURL.
Envoi du message.
Envoi du message sur le tlphone.

Figure 35.1 Architecture des notifications

Le principe de cration du serveur de notification


Je vais dcrire ici le principe de cration du serveur de notification sachant que nous
nallons pas le raliser car il sort de la porte de ce cours. Mais ne vous inquitez pas,
dans ce chapitre nous ferons comme si le serveur avait t cr et nous nous occuperons
de ce qui se passe juste aprs un envoi de notification. Ce serveur de notification
consiste en gnral en un site web ou des services web, car ceux-ci doivent tre toujours
disponibles en ligne, nimporte quel moment. Celui-ci peut-tre bien sr un serveur
base de produits Microsoft (avec un logiciel de serveur IIS et des composants WCF),
mais il peut tout aussi bien tre en PHP, Ruby ou ce que vous matrisez. Le principe est
de fournir une URL permettant denregistrer un identifiant unique de tlphone ainsi
519

CHAPITRE 35. LES NOTIFICATIONS


que le canal (en fait, seul le canal est vraiment indispensable). Un tlphone souhaitant
recevoir des notifications va senregistrer auprs du service de notification de Microsoft
et recevra une URL identifiant le canal en retour. LURL est de la forme :
http://db3.notify.live.net/throttledthirdparty/01.00/
AAE17kNJdrSwXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXQTg1QkZDMkUxREQ
Le tlphone appelle ensuite le serveur de notification pour lui fournir cette URL ainsi
que son identifiant unique. Le serveur devra tre capable de stocker cet identifiant
ainsi que lURL et devra galement permettre de modifier lURL si lidentifiant est
dj stock. Le serveur va donc servir stocker une liste didentifiants associs des
URL et la maintenir jour. Puis, lorsque nous voudrons envoyer une notification
tous les utilisateurs de notre application, il suffira de parcourir cette liste et denvoyer
un message au serveur de Microsoft, via lURL du canal, contenant le message. Cest
ensuite le serveur de Microsoft qui soccupe denvoyer la notification chaque tlphone,
grce ce canal.
Et voil, en fait cest trs simple.
Dans lidal, le serveur devra galement permettre de dsabonner un tlphone ne souhaitant plus recevoir de notifications, car il est indispensable que votre application
propose son utilisateur un moyen darrter de recevoir des notifications. De mme,
lorsque le serveur de notification envoie la requte Microsoft permettant lenvoi du
message, ce dernier lui rpond en lui indiquant notamment si le tlphone est injoignable. Cela pourra correspondre au fait que lapplication est dsinstalle par exemple.
ce moment-l, il faudra mettre jour la liste des identifiants et des URL afin de supprimer le couple identifiant-canal, pour ne plus tenter de lui envoyer de notifications.
Nhsitez pas crer votre propre serveur de notification, avec le langage de votre
choix, et pourquoi pas proposer les sources dun tel serveur afin que chacun puisse
sen servir pour hberger son serveur de notification.

Les diffrents messages de notifications


Nous avons vu les diffrents types de notification :
Toast,
Tile,
Raw.
Pour dclencher une telle notification, il faut envoyer un message au serveur de Microsoft contenant du XML, ou du texte simple, en fonction du type de notification.
Ce message devra tre envoy avec la mthode POST, dans un format XML (contenttype= text/xml ) et contenir des en-ttes HTTP personnalises. Il y a plusieurs
lments passer dans les en-ttes HTTP, plus ou moins obligatoires. Par exemple,
il est possible et facultatif de fournir un identifiant de message via len-tte HTTP
MessageID. En revanche, nous devons obligatoirement fournir dans len-tte le type
de notification. Cela se fait avec lattribut X-Windows-Phone-Target. Par exemple, on
ajoute le code ci-dessous dans len-tte pour une notification de type toast :
520

LES DIFFRENTS MESSAGES DE NOTIFICATIONS


1

X - WindowsPhone - Target : type

Le message de notification envoy au serveur de Microsoft peut tre de trois types


diffrents :
Message de type toast, pour les notifications toast.
Message de type token, pour les notifications tile.
Message de type raw, pour les notifications raw.
Remarque : si le type de notification nest pas prcis, alors la notification
sera de type raw.
Nous devons galement indiquer le dlai denvoi, qui varie selon le type de notification
comme le montre le tableau suivant.
-

Envoi immdiat

Tile
Toast
Raw

1
2
3

Dlai de 450
secondes (7
minutes et demie)
11
12
13

Dlai de 15
minutes
21
22
23

Par exemple, pour envoyer une notification Toast immdiatement, je devrais avoir
len-tte HTTP suivante :
1
2

X - WindowsPhone - Target : toast


X - Noti ficationClass : 2

Ensuite, reste le corps du message.


Pour envoyer un message de type raw, cest trs simple. Il suffit denvoyer le
message tel quel, pas besoin dXML.
Pour envoyer un message de type toast, nous aurons besoin de dmarrer lapplication dans un contexte particulier. Pour cela, il nous faudra excuter une
requte query string dans une page XAML de lapplication. Il faudra envoyer le
XML suivant avec cette page XAML comme Paramtre :
<?xml version="1.0" encoding="utf-8"?>
<wp:Notification xmlns:wp="WPNotification">
<wp:Toast>
<wp:Text1>Titre</wp:Text1>
<wp:Text2>Sous titre</wp:Text2>
<wp:Param>paramtre</wp:Param>
</wp:Toast>
</wp:Notification>

Enfin, pour envoyer un message de type tile, nous aurons le XML suivant :
<?xml version="1.0" encoding="utf-8"?>
<wp:Notification xmlns:wp="WPNotification">

521

CHAPITRE 35. LES NOTIFICATIONS

<wp:Tile>
<wp:BackgroundImage>Url image de fond</wp:BackgroundImage>
<wp:Count>Numro en haut droite</wp:Count>
<wp:Title>Titre</wp:Title>
<wp:BackBackgroundImage>Url image de fond du dos de la tuile</wp:BackBackgroundImage
<wp:BackTitle>Titre du dos de la tuile</wp:BackTitle>
<wp:BackContent>Contenu du dos de la tuile</wp:BackContent>
</wp:Tile>
</wp:Notification>

Nous allons voir dans le chapitre suivant comment envoyer ces beaux messages et
surtout ce quil faut faire ct Windows Phone pour crer un canal et ragir aux
notifications.
Note : Si vous devez mettre des caractres spciaux dans vos messages, alors
vous devez les encoder en XML, par exemple < devient &lt ;

Cration du client Windows Phone recevant la notification


Avant toute chose, vous devez dclarer que votre application utilise des notifications.
Cela se fait en utilisant la capacit ID_CAP_PUSH_NOTIFICATION que nous allons
voir luvre dans cette section. Il faut ensuite crer ou utiliser un canal depuis notre
application Windows Phone. Un canal est identifi par son nom. La premire chose est
de tenter de le rcuprer sil existe dj, sinon il faut le crer. Voici comment utiliser
un canal pour recevoir une notification Toast :
1
2
3

public MainPage ()
{
I ni t i alizeComponent () ;

4
5
6

string nomCanal = " T e s t C a n a l O p e n C l a s s r o o m s N i c o " ;


H t t p N ot i fi ca ti o nC ha n ne l canal = H tt pN o ti fi ca t io nC h an ne l .
Find ( nomCanal ) ;

7
8
9
10
11

if ( canal == null )
{
// si le canal n ' est pas trouv , on le cr e
canal = new H tt p No ti fi c at io nC h an ne l ( nomCanal ) ;

12
13
14

canal . ChannelUriUpdated += ca na l _C ha nn e lU ri Up d at ed ;
canal . ErrorOccurred += canal_ErrorOccurred ;

15
16

522

// On s ' abonne cet v nement si on veut tre averti


de la reception de la notification toast lorsque l '
application

CRATION DU CLIENT WINDOWS PHONE RECEVANT LA NOTIFICATION


// est ouverte , sinon on ne la recoit pas
canal . S h e l l T o a s t N o t i f i c a t i o n R e c e i v e d +=
canal_ShellToastNotificationReceived ;

17
18
19

canal . Open () ;

20
21
22
23

}
else
{

24
25
26
27
28
29
30

// Permet de d clarer le canal pour recevoir les toasts


canal . BindToShellToast () ;

// le canal est d j cr , on l ' utilise


canal . ChannelUriUpdated += ca na l _C ha nn e lU ri U pd at ed ;
canal . ErrorOccurred += canal_ErrorOccurred ;
canal . S h e l l T o a s t N o t i f i c a t i o n R e c e i v e d +=
canal_ShellToastNotificationReceived ;

31
32

33
34
35
36

// On affiche le canal dans la console de sortie pour


pouvoir l ' exploiter ensuite dans notre appli de test
d ' envoi
// normalement , on l ' aurait envoy notre serveur de
notification , avec l ' identifiant unique du t l phone
Debug . WriteLine ( canal . ChannelUri . ToString () ) ;

Si le canal existe dj, alors on obtient son URL qui normalement devra tre envoye
notre serveur de notififcation. Ici, je vais simplement lafficher dans la console de sortie
pour viter de devoir crer un serveur de notification. Je vais ainsi pouvoir rcuprer
lURL et lutiliser dans une application tierce qui enverra la requte au serveur de
notification de Microsoft. Pour linstant, implmentez les vnements ainsi :
1
2
3
4

private void c a n a l _ S h e l l T o a s t N o t i f i c a t i o n R e c e i v e d ( object sender


, N o t i f i c atio nEvent Args e )
{
// optionnel , cet v nement est lev quand l ' appli est
ouverte et qu ' on recoit la notif
}

5
6
7
8

private void canal_ErrorOccurred ( object sender ,


NotificationChannelErrorEventArgs e)
{
Dispatcher . BeginInvoke (() = > MessageBox . Show ( string . Format (
" Une erreur est survenue { 0 } , { 1 } ({ 2 }) { 3 } " , e .
ErrorType , e . Message , e . ErrorCode , e . ErrorAdditionalData
)) );
}

10
11

private void ca n al _C h an ne lU r iU pd at e d ( object sender ,


NotificationChannelUriEventArgs e)

523

CHAPITRE 35. LES NOTIFICATIONS


12

13

14
15
16

// On affiche le canal dans la console de sortie pour


pouvoir l ' exploiter ensuite dans notre appli de test d '
envoi
// normalement , on l ' aurait envoy notre serveur de
notification , avec l ' identifiant unique du t l phone
Debug . WriteLine ( e . ChannelUri . ToString () ) ;

Vous aurez besoin de :


1

using Microsoft . Phone . Notification ;

Puis dmarrez lapplication en debug, lURL saffiche dans la fentre de sortie de Visual
Studio comme dans la figure 35.2.

Figure 35.2 Url du canal dans la fentre de sortie


Je lai un peu masque ici, mais elle a la forme :
http://db3.notify.live.net/throttledthirdparty/01.00/
AAHmJDXpe47LTI0ct6NNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXg1QTg1QkZDMkUxREQ
Sauvegardez cette URL dans un coin, nous allons en avoir besoin. Nous pouvons arrter
lapplication mais sans arrter lmulateur. Le canal est cr. Remarquez que si vous
redmarrez lapplication, nous rcuprrons le canal dj cr et obtenons nouveau la
mme URL.
Si vous vous souvenez de ce dont nous avons parl juste avant, cest cette URL qui
devra tre utilise pour envoyer un message au serveur de notification de Microsoft.
Par souci de simplicit, ici je vais utiliser directement lURL pour envoyer ma requte
en POST, mais vous aurez compris que le rle de notre serveur est de stocker ces
URL dans une liste et denvoyer le message toutes les URL que nous avons stockes,
correspondant aux utilisateurs qui souhaitent recevoir nos notifications.
524

CRATION DU CLIENT WINDOWS PHONE RECEVANT LA NOTIFICATION


Nous devons donc envoyer dsormais une requte en POST cette url, pour cela nous
allons crer une petite application console grce Visual Studio Express 2012 pour
Windows Desktop, que vous pouvez tlcharger gratuitement cet emplacement http://www.microsoft.com/fr-fr/download/details.aspx?id=34673 (je vous fait
grce de linstallation qui ne devrait pas poser de problme).
Note : Vous pouvez bien sr implmenter un vrai serveur de notification si
vous le souhaitez, mais pour la dmonstration ce sera plus simple denvoyer
un simple message en POST ainsi.
Ici, vu que je me suis abonn aux notifications toast (grce canal.BindToShellToast()),
il faut que jenvoie un message toast, par exemple :
1
2
3
4
5
6
7
8

<? xml version = " 1 . 0 " encoding = " utf - 8 " ? >
< wp : Notification xmlns : wp = " WPNotification " >
< wp : Toast >
< wp : Text1 > Texte 1 youpi </ wp : Text1 >
< wp : Text2 > Texte 2 joie </ wp : Text2 >
< wp : Param >/ MainPage . xaml ? cle = parametre </ wp : Param >
</ wp : Toast >
</ wp : Notification >

Le principe est donc de faire une requte POST, avec les bons headers, contenant le
message de type toast, et de le poster lurl rcupre. Voici le code C# permettant
de raliser ceci dans une application console :
1
2
3
4
5
6

try
{

// url o envoyer le message


// normalement , il faudrait r cup rer la liste des urls
stock es sur le serveur de notification
// et envoyer le message toutes ces urls
string u r lOuEnvoyerMessage = " http :// db3 . notify . live . net /
t hr o ttledthirdparty / 01 . 00 /
AAHmJDXpe47LTI0ct6NNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXg1QTg1QkZDMkUxREQ
";

7
8
9
10
11
12
13
14
15
16
17

HttpWebRequest requete = ( HttpWebRequest ) WebRequest . Create (


u rl O uEnvoyerMessage ) ;
requete . Method = " POST " ; // envoi en POST
string m essageToastAEnvoyer = @ " <? xml version = " " 1 . 0 " "
encoding = " " utf - 8 " " ? >
< wp : Notification xmlns : wp = " " WPNotification " " >
< wp : Toast >
< wp : Text1 > Texte 1 youpi </ wp : Text1 >
< wp : Text2 > Texte 2 joie </ wp : Text2 >
< wp : Param >/ MainPage . xaml ? cle = parametre </ wp :
Param >
</ wp : Toast >
</ wp : Notification > " ;

525

CHAPITRE 35. LES NOTIFICATIONS


18

byte [] message = Encoding . Default . GetBytes (


m e ssageToastAEnvoyer ) ;
requete . ContentLength = message . Length ;
requete . ContentType = " text / xml " ;
requete . Headers . Add ( "X - WindowsPhone - Target " , " toast " ) ; //
notification de type toast
requete . Headers . Add ( "X - NotificationClass " , " 2 " ) ; // envoi
imm diat

19
20
21
22
23
24

using ( Stream requestStream = requete . GetRequestStream () )


{
requestStream . Write ( message , 0 , message . Length ) ;
}

25
26
27
28
29

// envoi de la notification , et r cup ration du retour


HttpWebResponse response = ( HttpWebResponse ) requete .
GetResponse () ;
string statutNotification = response . Headers [ "X No tificationStatus " ];
string statutCanal = response . Headers [ "X - SubscriptionStatus
" ];
string statutMateriel = response . Headers [ "X D e vi ceC on nec tio nS tat us " ];

30
31
32
33
34
35

// affiche le r sultat de la requ te


Console . WriteLine ( statutNotification + " | " +
statutMateriel + " | " + statutCanal ) ;

36
37
38

// gestion d ' erreur ultra simplifi e


if ( string . Compare ( statutNotification , " Dropped " ,
StringComparison . C u rr e n tC u l tu r e Ig n o re C a se ) == 0 )
{
// il faut arr ter de tenter d ' envoyer des messages
ce t l phone
}

39
40
41
42
43
44
45
46
47
48

}
catch ( Exception ex )
{
Console . WriteLine ( " Erreur d ' envoi : " + ex ) ;
}

Et nous verrons la notification Toast safficher sur notre Windows Phone (voir figure
35.3).
Et ce qui est intressant, cest que si lon clique sur la notification toast, alors notre
application se lance et navigue sur la page passe en paramtre. Ceci nous permet galement de rcuprer les paramtres de la query string (voir code et figure qui suivent).
1
2

protected override void OnNavigatedTo ( NavigationEventArgs e )


{

526

CRATION DU CLIENT WINDOWS PHONE RECEVANT LA NOTIFICATION

Figure 35.3 La notification Toast saffiche dans lmulateur


3
4
5
6
7
8
9

string valeur ;
if ( NavigationContext . QueryString . TryGetValue ( " cle " , out
valeur ) )
{
MessageBox . Show ( valeur ) ;
}
base . OnNavigatedTo ( e ) ;

Pour avoir le rsultat prsent dans la figure 35.4.


Attention, il est important de traiter le retour du message en fonction des
codes derreurs que nous obtenons en rponse de lenvoi de la notification.
Les erreurs peuvent tre de diverses sortes. On peut par exemple dtecter
si lutilisateur a dsinstall lapplication, cela voudra dire quil nest plus ncessaire de lui envoyer de message et quil va falloir enlever cette url de la
liste de notre serveur de notification. Cest ce que jai fait dans lexemple de
code prcdent, de manire minimale je le reconnais. Vous pouvez consulter les codes derreurs cet emplacement - http://msdn.microsoft.com/
fr-fr/library/windowsphone/develop/ff941100(v=vs.105).aspx.
noter que si lapplication est ouverte, alors le message toast ne saffiche pas en haut de
lcran. Lapplication peut cependant tre notifie de la rception dun message toast
527

CHAPITRE 35. LES NOTIFICATIONS

Figure 35.4 Utilisation de la query string


en sabonnant lvnement ShellToastNotificationReceived et ainsi faire ce quelle
dsire, par exemple afficher le message :
1
2
3
4
5
6

private void c a n a l _ S h e l l T o a s t N o t i f i c a t i o n R e c e i v e d ( object sender


, N o t ifi cation Event Args e )
{
// optionnel , cet v nement est lev quand l ' appli est
ouverte et qu ' on recoit la notif
string message = e . Collection [ " wp : Text1 " ] + e . Collection [ "
wp : Text2 " ];
Dispatcher . BeginInvoke (() = > MessageBox . Show ( message ) ) ;
}

Ici, nous utilisons simplement la bote de message MessageBox.Show pour afficher le


message de la notification reue. Vous pouvez en faire ce que vous voulez, lafficher
dans un TextBlock, ou juste traiter le message.
Pour les autres types de notifications, le principe est le mme. Veuillez toujours
faire attention au type de notifications que vous envoyez dans les en-ttes HTTP, ainsi
quau dlai denvoi. De mme, pour quun tlphone puisse recevoir les notifications
dun certain type, pensez dclarer le canal comme tel. Par exemple, nous avons vu
que pour recevoir des notifications toasts, nous utilisions :
1

canal . BindToShellToast () ;

Pour les notifications tiles, il faudra utiliser :


528

CRATION DU CLIENT WINDOWS PHONE RECEVANT LA NOTIFICATION


1

canal . BindToShellTile () ;

Pour les notifications raw, il ny a rien de spcial faire sur le canal. Il faudra par
contre sabonner lvnement HttpNotificationReceived :
1
2
3
4
5
6
7
8
9
10
11

[...]
canal . H t t p N o t i f ic a t io n R e ce i v ed +=
canal_HttpNotificationReceived ;
[...]
private void c a n a l _ H t t p N o t i f i c a t i o n R e c e i v e d ( object sender ,
H t t p N o t i f i c a t i o n Ev e n t A r g s e )
{
using ( System . IO . StreamReader reader = new System . IO .
StreamReader ( e . Notification . Body ) )
{
string message = reader . ReadToEnd () ;
Dispatcher . BeginInvoke (() = > MessageBox . Show ( "
Notification raw re ue : " + message ) ) ;
}
}

Remarque : Il est demand de pouvoir dsactiver les notifications depuis lapplication. Pensez rajouter une interface qui permet lutilisateur de se
dsabonner.

En rsum
Les notifications permettent denvoyer un message ou une demande de mise
jour de tuile.
La notification de type toast peut-tre reue mme si lapplication nest pas
dmarre.
Le dveloppeur aura besoin de crer un serveur de notification, accessible
tout moment, afin de maintenir une liste de canaux de communication avec les
tlphones intresss pour recevoir des notifications.

529

CHAPITRE 35. LES NOTIFICATIONS

530

Chapitre

36

TP : Amliorer lapplication mto


avec golocalisation et tuiles
Difficult :
Que de nouvelles fonctionnalits dcouvertes. Cest le moment rv pour les mettre en
pratique et amliorer notre superbe application mto de la partie prcdente.
Nous allons jouer avec les tuiles et la golocalisation. Vous tes prt ? Alors cest parti !

531

CHAPITRE 36. TP : AMLIORER LAPPLICATION MTO AVEC


GOLOCALISATION ET TUILES

Instructions pour raliser le TP


Franchement, une application mto o il faut absolument saisir le nom de la ville o
nous nous trouvons, vous conviendrez avec moi que ce nest pas terrible ! Alors que
nos tlphones possdent un GPS intgr. . . Nous allons donc proposer lutilisateur
dafficher la mto correspondant sa position. Donc premire chose faire, rajouter
un bouton dans la barre dapplication qui affichera la mto votre position. Ensuite,
imaginons que je veuille rapidement connatre la mto de Paris et de Toulouse, histoire
de savoir o il vaut mieux que je passe le week-end. Comme cest quelque chose que
je fais rgulirement, jai besoin de la possibilit de rajouter un raccourci vers ces
villes. Mon application va donc permettre dpingler des villes depuis la page de choix
de villes. Si la ville a dj t choisie, il faut que le bouton soit gris. Bien sr, le
dmarrage de lapplication via des tuiles secondaires affichera directement la mto de
la ville choisie. La tuile contiendra forcment le nom de la ville. Je vous laisse libre de
choisir le modle de tuile qui vous fait plaisir.
Voici un programme intressant.
Attention avant de vous lancer, il va vous manquer quelque chose. Le GPS fournit une
position avec une longitude et une latitude alors que nous interrogions le service web
de mto avec un nom de ville en entre. Donc, soit il vous faut donc une solution pour
transformer une position GPS en un nom de ville (on appelle cela du gocodage invers),
soit il faudrait que le service web accepte dutiliser des coordonnes GPS. Et vous savez
quoi ? Il laccepte. Cest la classe ! Cest le mme principe, il suffit de remplacer le nom
de la ville par les coordonnes sous la forme ci-dessous, avec la premire coordonne,
la latitude, et la seconde, la longitude.
1

http : // free . worldweatheronline . com / feed / weather . ashx ? q = 44 .


839073 , - 0 . 579113 & format = json & num_of_days = 5 & key = MA_CLE_API

En rsum, je vous propose de raliser les lments suivants : 1. Utiliser le GPS pour
permettre de vous golocaliser, 2. Rajouter un bouton dans la barre dapplication pour
afficher la mto votre position, 3. Rajouter deux tuiles de raccourci affichant la
mto Paris et Toulouse.
Voil, vous avez tout. vous de jouer.

Correction
Je suis certain que vous vous en tes trs bien sorti avec ce petit exercice sympathique.
Voici ma correction, je ne vais pas tout re-dtailler mais simplement les choses qui ont
chang par rapport au TP prcdent dapplication mto.
1. Utiliser le GPS pour vous golocaliser Nous avons premirement besoin dutiliser le GPS afin de nous golocaliser. Vous devez utiliser la classe Geolocator, via par
exemple une variable prive :
1

532

private Geolocator geolocator ;

CORRECTION
Vous initialisez cette variable geolocator dans le constructeur :
1
2
3
4
5
6

public MainPage ()
{
I ni t i al i zeComponent () ;
geolocator = new Geolocator () ;
DataContext = this ;
}

Ici, nul besoin davoir une prcision importante, la localisation approximative


suffit.
2. Rajouter un bouton dapplication pour afficher la mto votre position
On rajoute galement un bouton dans la barre dapplication. Il possde une image
(gps.png) et un texte (Ma position), et appelle une mthode (Position_Click) lorsquon
clique dessus :
1
2
3

4
5

6
7

< phone : P h on e A pplicationPage . ApplicationBar >


< shell : ApplicationBar IsVisible = " True " >
< shell : A p p li c a ti o n Ba r I co n B ut t o n IconUri = " / Assets / Icones
/ gps . png " Text = " Ma position " Click = " Position_Click "
/>
< shell : A p p li c a ti o n Ba r I co n B ut t o n IconUri = " / Assets / Icones
/ add . png " Text = " Ajouter " Click = " Ajouter_Click " / >
< shell : A p p li c a ti o n Ba r I co n B ut t o n IconUri = " / Assets / Icones
/ feature . settings . png " Text = " Choisir " Click = "
Choisir_Click " / >
</ shell : ApplicationBar >
</ phone : P ho n e ApplicationPage . ApplicationBar >

Pour la peine, je me suis ralis une petite image pour symboliser le GPS. Vous ne
manquerez pas dadmirer ma matrise de Paint (image dans le chapitre suivant) ! Noubliez pas de changer les proprits de limage pour mettre laction de gnration
Contenu , et la proprit Copier dans le rpertoire Copier si plus rcent .
Lorsque lon clique sur le bouton, une mthode est appele pour dmarrer le service de
localisation :
1
2
3
4
5
6

private async void Position_Click ( object sender , EventArgs e )


{
Charg ementEnCours = true ;
try
{
Geoposition geoposition = await geolocator .
G etGeopositionAsync ( TimeSpan . FromMinutes ( 5 ) ,
TimeSpan . FromSeconds ( 10 ) ) ;
string position = geoposition . Coordinate . Latitude .
ToString ( NumberFormatInfo . InvariantInfo ) + " ," +
geoposition . Coordinate . Longitude . ToString (
NumberFormatInfo . InvariantInfo ) ;

533

CHAPITRE 36. TP : AMLIORER LAPPLICATION MTO AVEC


GOLOCALISATION ET TUILES
WebClient client = new WebClient () ;
string resultatMeteo = await client .
D ow nl oa d St ri n gT as kA s yn c ( new Uri ( string . Format ( " http
:// free . worldweatheronline . com / feed / weather . ashx ? q ={
0 }& format = json & num_of_days = 5 & key = MA_CLE_API " ,
position , UriKind . Absolute ) ) ) ;
TraiteResultats ( resultatMeteo ) ;
NomVille = position ;

8
9

10
11
12
13
14
15
16
17
18

}
catch ( Exception )
{
MessageBox . Show ( " Vous devez activer le GPS pour pouvoir
utiliser cette fonctionnalit " ) ;
ChargementEnCours = false ;
}

Noubliez pas dactiver la capacit ID_CAP_LOCATION, qui sert autoriser


lutilisation du GPS.
Notez la prsence du mot cl async dans la signature de la mthode. Il y a galement
un petit try/catch pour vrifier que le GPS est utilisable. Et puis jappelle le service de
localisation pour obtenir la position de lutilisateur, que je fournis au service web. La
mthode TraiteResultats contient la logique daffichage qui a t factorise, car utilise
deux endroits :
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16

private void TraiteResultats ( string resultats )


{
RootObject resultat = JsonConvert . DeserializeObject <
RootObject >( resultats ) ;
List < Meteo > liste = new List < Meteo >() ;
foreach ( Weather temps in resultat . data . weather . OrderBy ( w
= > w . date ) )
{
Meteo meteo = new Meteo { TemperatureMax = temps .
tempMaxC + " C " , TemperatureMin = temps . tempMinC +
" C " };
DateTime date ;
if ( DateTime . TryParse ( temps . date , out date ) )
{
meteo . Date = date . ToString ( " dddd dd MMMM " ) ;
meteo . Temps = GetTemps ( temps . weatherCode ) ;
WeatherIconUrl2 url = temps . weatherIconUrl .
FirstOrDefault () ;
if ( url != null )
{
meteo . Url = new Uri ( url . value , UriKind . Absolute
);

534

CORRECTION
}
}
liste . Add ( meteo ) ;

17
18
19
20
21
22
23

}
ListeMeteo = liste ;
Charg ementEnCours = false ;

3. Rajouter deux tuiles secondaires affichant la mto Paris et Toulouse


Reste maintenant grer lpinglage des villes. Pour ajouter un bouton, modifions la
page XAML ChoisirVille que nous avons construite dans le TP prcdent :
1
2
3
4
5
6
7
8
9
10

11

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< toolkit : ListPicker x : Name = " Liste " ItemsSource = " { Binding
ListeVilles } "
Header = " Ville choisie : "
CacheMode = " BitmapCache " >
</ toolkit : ListPicker >
< Button Grid . Row = " 1 " VerticalAlignment = " Bottom " Content = "
Epingler " x : Name = " BoutonEpingle " Tap = " BoutonEpingle_Tap "
/>
</ Grid >

Le clic sur le bouton ajoutera une tuile secondaire :


1
2
3
4

5
6
7
8
9
10

private void BoutonEpingle_Tap ( object sender , System . Windows .


Input . GestureEventArgs e )
{
string ville = ( string ) I so l at ed St o ra ge Se t ti ng s .
A pp l icationSettings [ " DerniereVille " ];
ShellTile tuileVille = ShellTile . ActiveTiles . FirstOrDefault
( elt = > elt . NavigationUri . ToString () . Contains ( " ville = " +
ville ) ) ;
if ( tuileVille == null )
{
FlipTileData tuile = new FlipTileData
{
Title = " M t o " + ville ,
};

11
12
13
14
15

ShellTile . Create ( new Uri ( " / MainPage . xaml ? ville = " +


ville , UriKind . Relative ) , tuile , false ) ;
BoutonEpingle . IsEnabled = false ;

Bien sr, dans la query string, on indique le nom de la ville afin de pouvoir lexploiter
535

CHAPITRE 36. TP : AMLIORER LAPPLICATION MTO AVEC


GOLOCALISATION ET TUILES
au dmarrage de lapplication. Dans lvnement permettant de choisir sa ville, on en
profite pour dsactiver les boutons des villes qui ont dj une tuile de raccourci :
1
2
3
4
5
6

7
8
9

private void Li ste _Se le cti on Cha ng ed ( object sender ,


S e l e c t io n C h a n g e d E ve n t A r g s e )
{
if ( Liste . SelectedItem != null )
{
I so la te d St or ag e Se tt i ng s . ApplicationSettings [ "
DerniereVille " ] = ( string ) Liste . SelectedItem ;
ShellTile tuileVille = ShellTile . ActiveTiles .
FirstOrDefault ( elt = > elt . NavigationUri . ToString () .
Contains ( " ville = " + ( string ) Liste . SelectedItem ) ) ;
BoutonEpingle . IsEnabled = tuileVille == null ;
}
}

Il ne reste plus qu exploiter linformation passe en query string au dmarrage de


lapplication afin de charger la mto de la bonne ville. On peut le faire avec la mthode
OnNavigatedTo par exemple :
1
2
3
4
5
6
7
8

protected async override void OnNavigatedTo ( System . Windows .


Navigation . NavigationEventArgs e )
{
string ville ;
if ( NavigationContext . QueryString . TryGetValue ( " ville " , out
ville ) )
{
NavigationContext . QueryString . Remove ( " ville " ) ;
I so la te d St or ag e Se tt i ng s . ApplicationSettings [ "
DerniereVille " ] = ville ;
}

9
10
11
12
13
14
15
16
17
18
19

20

536

if ( I s ol at ed S to ra ge S et ti ng s . ApplicationSettings . Contains ( "


DerniereVille " ) )
{
Information . Visibility = Visibility . Collapsed ;
ChargementEnCours = true ;
NomVille = ( string ) I so l at ed St o ra ge Se t ti ng s .
ApplicationSettings [ " DerniereVille " ];
WebClient client = new WebClient () ;
try
{
ChargementEnCours = false ;
string resultatMeteo = await client .
D ow nl oa d St ri n gT as kA s yn c ( new Uri ( string . Format ( "
http :// free . worldweatheronline . com / feed / weather .
ashx ? q ={ 0 }& format = json & num_of_days = 5 & key =
MA_CLE_API " , NomVille . Replace ( ' ', '+ ') ) ,
UriKind . Absolute ) ) ;

ALLER PLUS LOIN


21
22
23
24
25

26

}
else

27
28
29

TraiteResultats ( resultatMeteo ) ;
}
catch ( Exception )
{
MessageBox . Show ( " Impossible de r cup rer les
informations de m t o , v rifiez votre connexion
internet " ) ;
}
Information . Visibility = Visibility . Visible ;

30
31
32

base . OnNavigatedTo ( e ) ;

Remarquez quune fois que jai extrait lventuelle ville passe en paramtre
dans la query string, je la supprime afin que lapplication ne soit pas bloque
sur cette ville.
Et le tour est jou. Voici une belle application mto qui exploite les infos de golocalisation et qui nous permet mme davoir des raccourcis vers nos villes favorites. Pas
mal comme TP, non ?

Aller plus loin


Vous aurez remarqu que lorsquon consulte la mto par rapport ses coordonnes
GPS, on ne dispose plus du nom de la ville. En gnral, ce nest pas trop grave
car nous savons o nous sommes . . . mais on pourrait amliorer notre application
pour afficher le nom de la ville o nous nous trouvons, et pas les coordonnes GPS.
Il suffit dutiliser le gocodage invers jen ai parl en introduction du TP
et il se trouve quil existe des services gratuits de gocodage invers. Prenons par
exemple celui de Google qui est assez facile utiliser : si vous naviguez sur le lien suivant - http://maps.googleapis.com/maps/api/geocode/json?latlng=44.839073,
-0.579113&sensor=true vous pourrez obtenir du code JSON qui nous indique notamment dans quelle ville nous nous trouvons. Il ny a plus qu modifier notre code pour
faire lappel ce service web et nous pourrons obtenir la ville o nous sommes, ce qui
nous permettra dailleurs de la stocker dans le dossier local :
1
2
3
4
5
6
7
8

private async void Position_Click ( object sender , EventArgs e )


{
Charg ementEnCours = true ;
WebClient client = new WebClient () ;
string position ;
try
{
Geoposition geoposition = await geolocator .
G etGeopositionAsync ( TimeSpan . FromMinutes ( 5 ) ,

537

CHAPITRE 36. TP : AMLIORER LAPPLICATION MTO AVEC


GOLOCALISATION ET TUILES
9

10

11
12
13
14
15
16
17
18
19
20
21

TimeSpan . FromSeconds ( 10 ) ) ;
position = geoposition . Coordinate . Latitude . ToString (
NumberFormatInfo . InvariantInfo ) + " ," + geoposition .
Coordinate . Longitude . ToString ( NumberFormatInfo .
InvariantInfo ) ;
string resultatMeteo = await client .
D ow nl oa d St ri n gT as kA s yn c ( new Uri ( string . Format ( " http
:// free . worldweatheronline . com / feed / weather . ashx ? q ={
0 }& format = json & num_of_days = 5 & key = MA_CLE_API " ,
position , UriKind . Absolute ) ) ) ;
TraiteResultats ( resultatMeteo ) ;

}
catch ( Exception )
{
MessageBox . Show ( " Vous devez activer le GPS pour pouvoir
utiliser cette fonctionnalit " ) ;
ChargementEnCours = false ;
return ;
}
try
{
string json = await client . Do w nl oa dS t ri ng T as kA sy n c ( new
Uri ( string . Format ( " http :// maps . googleapis . com / maps /
api / geocode / json ? latlng ={ 0 }& sensor = true " , position ) ,
UriKind . Absolute ) ) ;

22
23
24
25
26
27

28

29
30
31
32
33
34
35
36
37

538

GeocodageInverse geocodageInverse = JsonConvert .


DeserializeObject < GeocodageInverse >( json ) ;
if ( geocodageInverse . status == " OK " )
{
AddressComponent adresse = ( from result in
geocodageInverse . results
from addressComponent
in result .
address_components
from type in
addressComponent .
types
where type == " locality
"
select addressComponent
) . FirstOrDefault () ;
if ( adresse == null )
{
MessageBox . Show ( " Impossible de d terminer le g
ocodage invers " ) ;
ChargementEnCours = false ;
}
else
{

ALLER PLUS LOIN


38
39
40

41
42
43
44
45
46
47

NomVille = adresse . long_name ;


I so la te d St or a ge Se tt i ng s . ApplicationSettings [ "
DerniereVille " ] = adresse . long_name ;

}
catch ( Exception )
{
MessageBox . Show ( " Impossible de d terminer le g ocodage
invers " ) ;
}

Vous commencez avoir lhabitude du langage JSON maintenant. Bien sr,


vous allez devoir gnrer les classes correspondantes mais faites attention, car
vous avez dj un objet RootObject qui a t gnr juste avant. . . Il faudra
donc le renommer.
Le rsultat est montr dans la figure 36.1.

Figure 36.1 Gocodage invers sur lapplication mto


Moi, je suis fan . . . et vous ?

539

CHAPITRE 36. TP : AMLIORER LAPPLICATION MTO AVEC


GOLOCALISATION ET TUILES

540

Chapitre

37

Une application fluide = une


application propre !
Difficult :
La performance est un point crucial prendre en compte lors du dveloppement dapplications pour Windows Phone. Les ressources du tlphone sont beaucoup moins importantes
que nos PC de dveloppement. Aussi, il est important dy faire attention afin de faire en
sorte que son application soit ractive et ne paraisse pas bloque. Vous devez bien sr
veiller ce que vos algorithmes soient un minimum optimiss et ne pas vous dire oh, ce
nest pas grave, le processeur du tlphone va moptimiser tout a. . . . De mme, vous
devez comprendre le mcanisme des threads pour pouvoir tirer le meilleur de votre Windows
Phone et obtenir lapplication la plus fluide possible.

541

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !

Un thread, cest quoi ?


On peut traduire Thread par fil dexcution ou tche . Il sagit dun processus
qui peut excuter du code dans notre application, en loccurrence le thread principal
est celui que nous utilisons pour excuter notre code C#. Il y aussi le thread principal dinterface, que lon nomme UI Thread. Windows Phone possde un autre thread
en relai du principal, cest le thread de composition, appel Composition Thread (ou
Compositor Thread ).
Dautres threads sont notre disposition, ce sont des threads qui tournent en arrireplan, de manire asynchrone. Nous nous en sommes dj servis sans le savoir en utilisant
la programmation asynchrone, par exemple lorsque nous tlchargeons des donnes avec
les classes WebClient ou HttpWebRequest. Ces oprations asynchrones se font sur un
thread secondaire.

Le thread dinterface
Le thread dinterface (UI Thread) va servir mettre jour linterface ; ce quon voit
lcran. En loccurrence, il va servir crer les objets depuis le XAML et dessiner
tous les contrles. Il gre galement toutes les interactions avec lutilisateur, notamment tous les touchers. Il est donc trs important que ce thread soit le moins charg
possible afin que lapplication reste ractive, notamment aux actions de lutilisateur. Si
ce thread contient une longue srie de codes excuter, alors linterface sera bloque
et lutilisateur ne pourra plus rien faire, ce qui est fortement dplaisant et risque de le
faire trs vite dsinstaller votre application. . . Essayez plutt de rpartir les tches, en
imaginant que vous avez deux boutons dans votre page : lun qui fait une action longue
et lautre qui affiche simplement un message dans une bote. Voici le code de la bote
contenant les deux boutons :
1
2
3
4

< StackPanel >


< Button Content = " Lancer le calcul " Tap = " Button_Tap " / >
< Button Content = " Cliquez - moi " Tap = " Button_Tap_1 " / >
</ StackPanel >

Voici le code-behind des deux boutons :


1
2
3
4
5
6
7
8
9

private void Button_Tap ( object sender , RoutedEventArgs e )


{
List < int > nombrePremiers = new List < int >() ;
for ( int i = 0 ; i < 2000000 ; i ++)
{
if ( EstNombrePremier ( i ) )
nombrePremiers . Add ( i ) ;
}
}

10
11
12

private void Button_Tap_1 ( object sender , RoutedEventArgs e )


{

542

LE THREAD DINTERFACE
13
14

MessageBox . Show ( " Clic " ) ;

15
16
17
18
19
20
21
22
23
24
25
26
27

private bool EstNombrePremier ( int nombre )


{
if (( nombre % 2 ) == 0 )
return nombre == 2 ;
int racine = ( int ) Math . Sqrt ( nombre ) ;
for ( int i = 3 ; i <= racine ; i += 2 )
{
if ( nombre % i == 0 )
return false ;
}
return nombre != 1 ;
}

Le premier bouton permettra de dterminer les nombres premiers de 0 jusqu 2000000,


le deuxime affichera un simple message, dans une bote de dialogue. Si vous dmarrez lapplication et cliquez sur le premier bouton, vous ne pourrez pas cliquer sur le
deuxime bouton tant que le premier calcul nest pas termin. De plus, on voit ltat
du bouton que celui-ci reste cliqu tant que le traitement long nest pas termin. Nous
avons donc bloqu le thread UI en effectuant un calcul trop long. De ce fait, lapplication nest plus capable de traiter correctement les entres utilisateurs, comme le clic
sur le deuxime bouton, tant donn que le thread UI est surcharg par le long calcul.
Afin que le code soit plus court, nous allons remplacer le long calcul par une mise
en veille du thread courant grce la mthode Thread.Sleep(), que nous retrouverons
dans lespace de noms :
1

using System . Threading ;

Ceci nous permet de simuler un traitement long tout en conomisant des lignes de
codes, do le code-behind devient :
1
2
3
4

private void Button_Tap ( object sender , RoutedEventArgs e )


{
Thread . Sleep ( TimeSpan . FromSeconds ( 4 ) ) ;
}

5
6
7
8
9

private void Button_Tap_1 ( object sender , RoutedEventArgs e )


{
MessageBox . Show ( " Clic " ) ;
}

Ceci me permet de simuler un traitement qui dure 4 secondes.


Ok, cest bien beau, mais notre interface semble toujours bloque et incapable de traiter
le clic sur le deuxime bouton.
543

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !

Utiliser un thread darrire-plan


Une solution pour rsoudre ce problme serait dutiliser un thread darrire-plan. Ce ne
sera donc plus le thread UI qui va grer le calcul mais un thread qui tourne en arrireplan. Cest un peu le mme principe quavec une opration asynchrone, comme lorsque
nous effectuions un tlchargement, notre code qui sexcute utilisera une partie de la
mmoire pour fonctionner de manire plus ou moins parallle au thread UI, ce qui lui
permettra de continuer pouvoir traiter les actions de lutilisateur. On pourra utiliser
pour cela la classe Thread - http://msdn.microsoft.com/fr-fr/library/system.
threading.thread(v=vs.95).aspx. Il suffit dinclure une mthode dans le thread (ici
je passe une expression lambda, qui le met en pause pendant 4 secondes), dappeler
la mthode Start, et Windows Phone soccupera dexcuter notre mthode dans un
thread darrire-plan. Cela donnera :
1
2
3
4
5
6

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;
}

private void Button_Tap ( object sender , RoutedEventArgs e )


{
Thread thread = new Thread (() = > Thread . Sleep ( TimeSpan .
FromSeconds ( 4 ) ) ) ;
thread . Start () ;
}

8
9
10
11
12
13
14
15
16
17
18

private void Button_Tap_1 ( object sender , RoutedEventArgs e )


{
MessageBox . Show ( " Clic " ) ;
}

Note : Cette faon de crer des threads nest pas la plus optimale, nous
verrons dautres solutions pour crer des threads.
Maintenant, si vous dmarrez votre application, vous pourrez voir que lexcution du
code long ne bloque plus le traitement du clic sur lautre bouton. joie, merci les
threads ! En fait, notre code est un peu bte ! On fait des calculs mais ils ne nous
servent rien ici . . . Je suis sr que, comme moi, vous seriez trs curieux de connatre
le plus grand nombre premier infrieur 10 millions, nest-ce pas ? Ok, ressortons notre
mthode, utilisons notre thread et affichons le rsultat dans un TextBlock :
1
2
3

< StackPanel >


< Button Content = " Lancer le calcul " Tap = " Button_Tap " / >
< Button Content = " Cliquez - moi " Tap = " Button_Tap_1 " / >

544

UTILISER UN THREAD DARRIRE-PLAN


4
5

< TextBlock x : Name = " Resultat " / >


</ StackPanel >

Le calcul sera fait ainsi :


1
2
3
4
5
6
7
8
9
10
11
12
13
14

private void Button_Tap ( object sender , RoutedEventArgs e )


{
Thread thread = new Thread (() = >
{
int max = 0 ;
for ( int i = 0 ; i < 10000000 ; i ++)
{
if ( EstNombrePremier ( i ) )
max = i ;
}
Resultat . Text = " R sultat : " + max ;
}) ;
thread . Start () ;
}

Sauf que, si vous dmarrez lapplication, que vous lancez le calcul, votre application
va se mettre planter avec une belle erreur du nom de UnauthorizedAccessException
(que lon peut apercevoir dans la figure 37.1).

Figure 37.1 Leve dexception lors de laccs au TextBlock

Pourquoi cette erreur ?

Pour une simple et bonne raison et je crois quon peut la mettre en avertissement :
Seul le thread UI a le droit de mettre jour linterface. Si nous tentons de
mettre jour un lment de linterface depuis un autre thread, comme un
thread darrire-plan, nous aurons une erreur.
545

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !

Utiliser le Dispatcher
Nous avons dj rapidement vu comment rsoudre ce problme lorsque nous avons utilis des oprations asynchrones (HttpWebRequest et WebClient) et que nous avons d
mettre jour linterface. Nous avons rsolu le problme grce au Dispatcher - http://
msdn.microsoft.com/fr-fr/library/system.windows.threading.dispatcher(v=vs.
95).aspx.
Ce dispatcher permet dexcuter des actions sur le thread auquel il est associ, grce sa
mthode BeginInvoke. En loccurrence, chaque DependyObject possde un dispatcher
et donc, la PhoneApplicationPage possde galement un dispatcher par hritage, qui a
t cr depuis le thread UI. Ainsi, lappel BeginInvoke depuis un thread darrireplan sur le dispatcher de la page excutera automatiquement laction sur le thread UI.
Voyons ceci dans le code et le rsultat illustr suivants.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

private void Button_Tap ( object sender , RoutedEventArgs e )


{
Thread thread = new Thread (() = >
{
int max = 0 ;
for ( int i = 0 ; i < 10000000 ; i ++)
{
if ( EstNombrePremier ( i ) )
max = i ;
}
Dispatcher . BeginInvoke (() = > Resultat . Text = " R
sultat : " + max ) ;
}) ;
thread . Start () ;
}

Ce qui donne le rsultat prsent dans la figure 37.2.


Et voil, plus de problmes daccs interdit entre les threads.
Note : cest une bonne ide dafficher une barre de progression pendant les
traitements longs. Nous avons dj utilis la ProgressBar, nhsitez pas
lemployer chaque fois quil y a un traitement potentiellement long, afin de
toujours montrer lutilisateur quil se passe quelque chose.

Utiliser un BackgroundWorker
Je vous ai dit quutiliser directement la classe Thread ntait pas la solution la plus
lgante pour crer des threads. Cest vrai (je ne mens jamais) ! Il existe dautres
classes particulirement adaptes pour raliser des traitements longs sur un thread
darrire-plan, cest le cas par exemple de la classe BackgroundWorker - http://msdn.
microsoft.com/fr-fr/library/system.componentmodel.backgroundworker(v=vs.
546

UTILISER UN BACKGROUNDWORKER

Figure 37.2 Affichage correct grce au Dispatcher


95).aspx. Elle permet notamment de connatre ltat davancement du thread, et de
lannuler si besoin. Pour lutiliser, vous aurez besoin dinclure lespace de nom suivant :
1

using System . ComponentModel ;

Vous aurez galement besoin davoir une variable reprsentant le BackgroundWorker.


En gnral, on utilise une variable membre de la classe :
1

private BackgroundWorker bw ;

Au moment dinstancier le BackgroundWorker, il faudra lui passer les paramtres dont


il a besoin.
1
2
3

public MainPage ()
{
I ni t i al i zeComponent () ;

4
5
6
7
8
9

bw = new BackgroundWorker { W o r k e r S u p p o r t s C a n c e l l a t i o n =
true , Work erRepo rtsPr ogress = true };
bw . DoWork += bw_DoWork ;
bw . ProgressChanged += bw_ProgressChanged ;
bw . R un Wor kerCompleted += bw_R unWork erComp leted ;

Ici, nous lui indiquons quil est autoris dannuler le thread et que celui-ci peut tmoigner de son avancement. Ensuite nous dclarons des vnements. DoWork contiendra le
547

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !


long traitement effectuer. ProgressChanged sera un vnement lev lorsque le thread
tmoigne de son avancement. Enfin, RunWorkerCompleted sera lev lorsque le thread
aura termin son travail. Voyons la mthode de travail :
1
2
3

private void bw_DoWork ( object sender , DoWorkEventArgs e )


{
BackgroundWorker worker = ( BackgroundWorker ) sender ;

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

int max = 0 ;
int derniereValeur = 0 ;
for ( int i = 0 ; i < 10000000 ; i ++)
{
if ( worker . CancellationPending )
{
e . Cancel = true ;
break ;
}
int pourcentage = i * 100 / 10000000 ;
if ( pourcentage != derniereValeur )
{
derniereValeur = pourcentage ;
worker . ReportProgress ( pourcentage ) ;
}
if ( EstNombrePremier ( i ) )
max = i ;
}
e . Result = max ;

On peut retrouver notre long calcul des nombres premiers. On y trouve galement
plusieurs choses, comme de vrifier sil naurait pas t demand notre thread de se
terminer prmaturment. Cela se fait en testant la proprit CancellationPending. Si
elle vaut vrai, alors on peut arrter le calcul et marquer le BackgroundWorker comme
tant annul, grce largument de lvnement. Ensuite, la mthode ReportProgress
nous offre lopportunit dindiquer lavancement du calcul. Ici, je lui indique le pourcentage davancement par rapport au max calculer. Remarquez que je nappelle la
mthode ReportProgress que si le pourcentage davancement a chang car sinon je
lappellerai normment de fois inutilement, ce qui ralentirait considrablement mon
calcul. Enfin, je peux utiliser la proprit Result pour stocker le rsultat de mon calcul.
Vous avez donc compris que lvnement ProgressChanged tait lev lorsque nous appelions la mthode ReportProgress. Cela nous permet de mettre jour un affichage
par exemple pour montrer lavancement du thread :
1
2
3
4

private void bw_ProgressChanged ( object sender ,


P r o g r e s s C ha n g ed E v e nt A r gs e )
{
Resultat . Text = e . ProgressPercentage + " % " ;
}

548

UTILISER UN BACKGROUNDWORKER
Notez quici, il ny a pas besoin de Dispatcher pour mettre jour linterface, malgr
lutilisation dun thread darrire-plan. Tout cela est gr par le BackgroundWorker
Enfin, il reste la mthode qui est appele lorsque le calcul est termin, RunWorkerCompleted :
1
2
3
4
5
6
7
8
9

private void bw_Run Worker Compl eted ( object sender ,


RunWorkerCompletedEventArgs e)
{
if ( e . Cancelled )
Resultat . Text = " Vous avez annul le calcul ! " ;
else if ( e . Error != null )
Resultat . Text = " Erreur : " + e . Error . Message ;
else
Resultat . Text = " Fini " + e . Result ;
}

Ici, nous avons plusieurs cas de figure :


Le thread a t annul. Dans ce cas, la proprit Cancelled de largument est
Vraie (True).
Il y a une erreur. Dans ce cas, la proprit Error est diffrente de null.
Ou alors tout sest bien pass. Dans ce cas, on peut rcuprer le rsultat dans
la proprit Result.
Comment annuler le thread ? Modifions notre XAML pour ajouter un bouton dont le
rle sera de stopper le thread :
1
2
3
4
5
6

< StackPanel >


< Button Content = " Lancer le calcul " Tap = " Button_Tap " / >
< Button Content = " Arr ter le calcul " Tap = " Button_Tap2 " / >
< Button Content = " Cliquez - moi " Tap = " Button_Tap_1 " / >
< TextBlock x : Name = " Resultat " / >
</ StackPanel >

Le code associ pour arrter un thread est simplement :


1
2
3
4
5

private void Button_Tap2 ( object sender , System . Windows . Input .


GestureEventArgs e )
{
if ( bw . W o r k e r S u p p o r t s C a n c e l l a t i o n )
bw . CancelAsync () ;
}

On utilise la mthode CancelAsync. Pour dmarrer le calcul, cest le mme principe, il


suffit dutiliser la mthode RunWorkerAsync :
1
2
3
4
5

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )
{
if (! bw . IsBusy )
bw . RunWorkerAsync () ;
}

549

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !


Et voil. Lorsque vous dmarrerez le calcul, non seulement linterface ne sera pas bloque, mais vous pourrez galement voir lavancement du thread ainsi que larrter en
cours de route (voir figure 37.3).

Figure 37.3 Thread en cours dexcution puis arrt

Enchaner les threads dans un pool


Une autre solution pour dmarrer des threads consiste utiliser la classe ThreadPool
- http://msdn.microsoft.com/fr-fr/library/system.threading.threadpool(v=
vs.95).aspx. Elle est trs pratique lorsque nous avons besoin denchaner beaucoup
de traitements darrire-plan. Grce au pool de thread nous pourrons empiler les diffrents threads que nous souhaitons excuter en arrire-plan, et cest le systme qui
va se dbrouiller pour les enchaner les uns aprs les autres ou potentiellement en parallle, sans que nous nayons quelque chose de particulier faire. Cest trs pratique.
Imaginons par exemple une application qui doit lire beaucoup de flux RSS. Si nous
dmarrons tous les tlchargements en mme temps, il y a fort parier que nous allons obtenir un timeout. Dans ce cas, la bonne solution est de pouvoir les enchaner
squentiellement. Cest un parfait candidat pour un ThreadPool. Pour lillustrer, nous
allons faire un peu plus simple et empiler des threads qui auront une dure dexcution
alatoire :
1

550

private void Button_Tap ( object sender , System . Windows . Input .


GestureEventArgs e )

ENCHANER LES THREADS DANS UN POOL


2

3
4
5
6
7
8
9
10
11

12
13
14

Random random = new Random () ;


for ( int i = 0 ; i < 10 ; i ++)
{
int numThread = i ;
ThreadPool . QueueUserWorkItem ( o = >
{
int temps = random . Next (1 , 10 ) ;
Thread . Sleep ( TimeSpan . FromSeconds ( temps ) ) ;
Dispatcher . BeginInvoke (() = > Resultat . Text += "
Thread " + numThread + " termin en " + temps +
" secs " + Environment . NewLine ) ;
}) ;
}

Rien de bien compliqu, mais il y a cependant une petite astuce pour utilisateurs avancs. Il sagit de lutilisation de la variable numThread. On pourrait croire quelle ne sert
rien et quon pourrait utiliser juste la variable i la place, mais que nenni. Si vous
lenlevez, vous naurez que des threads numrots 10. Tout a, cause dune histoire de
closure que je vais vous pargner, mais si vous tes curieux, vous pouvez trouver une explication en anglais ici - http://blogs.msdn.com/b/ericlippert/archive/2009/11/
12/closing-over-the-loop-variable-considered-harmful.aspx. Vous trouverez
le rsultat en images dans la figure 37.4.

Figure 37.4 Lexcution des threads a t prise en charge par le pool de thread
551

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !


Attention, notez le retour en force du Dispatcher pour mettre jour linterface.

Le DispatcherTimer
Nous avons dj utilis cette classe prcdemment mais sans lavoir vraiment dcrite.
La classe DispatcherTimer - http://msdn.microsoft.com/fr-fr/library/system.
windows.threading.dispatchertimer(v=vs.95).aspx va nous permettre dappeler
une mthode intervalles rguliers. Lintrt va tre de pouvoir excuter une tche en
arrire-plan et de manire rptitive. Imaginons par exemple une tche de synchronisation, qui toutes les 5 minutes va enregistrer le travail de lutilisateur sur le cloud . . .
Lexemple le plus classique est la cration dune horloge, dont lheure est mise jour
toutes les secondes :
1

< TextBlock x : Name = " Heure " / >

La mise jour de ce contrle se fera priodiquement grce au DispatcherTimer :


private DispatcherTimer dispatcherTimer ;

1
2
3
4
5

public MainPage ()
{
I ni t i alizeComponent () ;

6
7
8
9
10

dispatcherTimer = new DispatcherTimer { Interval = TimeSpan


. FromSeconds ( 1 ) };
dispatcherTimer . Tick += dispatcherTimer_Tick ;
dispatcherTimer . Start () ;

11
12
13
14
15

private void dispatcherTimer_Tick ( object sender , EventArgs e )


{
Heure . Text = DateTime . Now . ToString () ;
}

Voil notre TextBlock qui est mis jour toutes les secondes. Lavantage de ce timer
cest quil utilise la file dattente du dispatcher, donc nul besoin dutiliser le Dispatcher
pour pouvoir mettre jour linterface, ceci est pris en charge automatiquement. Linconvnient, cest que rien ne nous garantit que la mthode soit effectivement appele
exactement toutes les secondes. Vous pourrez observer quelques variations en fonction
de lexistence dautres timers ou dlments dans le Dispatcher. Si vous laissez tourner
un peu votre application, vous verrez que, de temps en temps, il se dcale dune seconde, ce qui nest pas dramatique, et au pire, nous pouvons augmenter la frquence de
mise jour. Mais le timer va nous permettre aussi dillustrer un autre point important
de Windows Phone, utilisons-le par exemple pour faire bouger un rectangle sur notre
cran. Le XAML est le suivant :
552

LE DISPATCHERTIMER
1
2
3
4
5
6

< StackPanel >


< Button Content = " Lancer le calcul " Tap = " Button_Tap " / >
< Canvas >
< Rectangle x : Name = " Rect " Width = " 40 " Height = " 40 " Fill = "
Green " / >
</ Canvas >
</ StackPanel >

Voici le code-behind correspondant :


1
2
3
4

public partial class MainPage : PhoneApplicationPage


{
private DispatcherTimer dispatcherTimer ;
private int direction = 1 ;

public MainPage ()
{
I ni t i alizeComponent () ;

6
7
8
9
10
11
12

13

dispatcherTimer = new DispatcherTimer { Interval =


TimeSpan . FromMilliseconds ( 10 ) };
dispatcherTimer . Tick += dispatcherTimer_Tick ;
dispatcherTimer . Start () ;

14

private void dispatcherTimer_Tick ( object sender , EventArgs


e)
{
double x = ( double ) Rect . GetValue ( Canvas . LeftProperty ) ;
int pas = 10 ;
if ( x > 480 - Rect . Width )
direction = -1 ;
if ( x < 0 )
direction = 1 ;
x += pas * direction ;
Rect . SetValue ( Canvas . LeftProperty , x ) ;
}

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
Thread . Sleep ( TimeSpan . FromSeconds ( 5 ) ) ;
}

Si vous dmarrez lapplication, vous pourrez voir le rectangle qui sanime horizontalement. Ceci pourrait tre une bonne solution pour animer un objet, sauf que . . . cliquez
voir un peu sur le bouton qui bloque le thread UI en simulant un long calcul . . . et paf !
a ne bouge plus. Ah bravo ! Franchement, a ne se fait pas de bloquer le thread UI ! ! !
553

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !

Thread de composition
Vous me direz, il suffit de mettre le calcul dans un thread et je vous rpondrai que oui,
cela fonctionne sans problme. Mais il y a dautres solutions pour faire en sorte quune
animation fonctionne malgr un long calcul. Vous vous rappelez comment on dfinit
une animation via un StoryBoard ?
1
2
3
4
5
6

7
8
9
10
11
12

< StackPanel >


< Button Content = " Lancer le calcul " Tap = " Button_Tap " / >
< Canvas >
< Canvas . Resources >
< Storyboard x : Name = " MonStoryBoard " >
< DoubleAnimation From = " 0 " To = " 440 " Duration = " 0 :
0 : 2 " RepeatBehavior = " Forever " AutoReverse = "
True "
Storyboard . TargetName = " Rect " Storyboard .
TargetProperty = " ( Canvas . Left ) " / >
</ Storyboard >
</ Canvas . Resources >
< Rectangle x : Name = " Rect " Width = " 40 " Height = " 40 " Fill = "
Green " / >
</ Canvas >
</ StackPanel >

Puis dans le code-behind :


1
2
3
4
5

public partial class MainPage : PhoneApplicationPage


{
public MainPage ()
{
I nitializeComponent () ;

6
7

MonStoryBoard . Begin () ;

9
10
11
12
13
14

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
Thread . Sleep ( TimeSpan . FromSeconds ( 5 ) ) ;
}

Et l, lorsque vous dmarrez lapplication, le fait de bloquer le thread UI ne bloque pas


lanimation. Diantre, comment cela se fait-il ? Cest grce au thread de composition !
Le thread de composition va servir allger le thread UI. Il va pouvoir dcharger le
thread UI de certaines tches qui lui incombent. Alors que le thread UI va tre utilis
pour crer le premier rendu des contrles, le thread de composition va travailler directement avec ces lments qui sont mis en cache dans la mmoire sous forme dimages
bitmaps et utiliser en prfrence le processeur graphique. Les animations par exemple
554

LES OUTILS POUR AMLIORER LAPPLICATION


sont prises en charge par ce thread de composition et mises en cache pour un affichage rapide. Le processeur graphique tant indpendant il pourra continuer traiter
des informations mme si le processeur du tlphone est fortement sollicit. Lorsque
vous demandez la mise jour dun contrle qui ncessite un changement (changement de couleur, taille, . . .) alors ce contrle est supprim du cache et redessin par
le thread UI. Cette utilisation de cache apporte beaucoup de performance et de plus,
certaines des oprations peuvent tre faites directement sur les objets en cache, par
exemple une animation qui fait une rotation. Comme nous lavons vu, la meilleure
solution pour corriger lanimation prcdente est de crer lanimation en XAML et
dutiliser les contrles adquats afin quelle soit prise en charge par ce fameux thread
de composition et utilise mcanisme de mise en cache. Cest toujours le thread UI qui
est en charge de mettre jour linterface, sauf quil ne fait plus aucun calcul et affiche directement des bitmaps mis en cache. Cest le thread de composition qui fait
les calculs en saidant du processeur graphique, qui est particulirement dou pour
a. Dune manire gnrale, il vaut mieux laisser le systme grer lui-mme le cache,
mais dans certains cas il peut tre utile de positionner soi-mme la proprit CacheMode - http://msdn.microsoft.com/fr-fr/library/system.windows.uielement.
cachemode(v=vs.95).aspx dun objet BitmapCache - http://msdn.microsoft.com/
fr-fr/library/system.windows.media.bitmapcache(v=vs.95).aspx sur un contrle,
mais il vaut mieux savoir ce que lon fait et tester les performances avec ou sans, car
le cache augmente considrablement la consommation de mmoire des processeurs du
tlphone.
Vous vous rappelez de lapplication PerformanceProgressBar pour Windows Phone 7.5
ou de lapplication ProgressBar pour Windows Phone 8 ? Leur principe est justement
de faire en sorte que ce soit le thread de composition qui prenne en charge lanimation
et non le thread UI.

Les outils pour amliorer lapplication


Il existe plusieurs outils pour amliorer la ractivit de son application. Il y a notamment le Windows Phone Performance Analysis tool, qui est un outil de profilage qui
sinstalle avec lenvironnement de dveloppement SDK de Windows Phone. Il permet
de mesurer la consommation de mmoire du processeur central CPU (Central Processing Unit), mais aussi le nombre de rafrachissements par seconde (le frame-rate). Je
ne vais pas le dcrire ici car on sort un peu de lapprentissage de Windows Phone,
mais sachez que cet outil existe et quil peut vous aider optimiser vos applications.
Pour une description, en anglais, vous pouvez consulter : http://msdn.microsoft.
com/en-us/library/windowsphone/develop/hh202934(v=vs.105).aspx.
Vous avez galement votre disposition des compteurs. Ce sont ces compteurs que lon
voit apparatre dans notre mulateur sur la droite. Ils permettent de connatre entres
autres le taux de rafrachissement. Plus dinformations ici : http://msdn.microsoft.
com/fr-fr/library/gg588380(v=vs.92).aspx.
Vous avez aussi la possibilit de voir facilement quels sont les contrles qui sont mis
jour. Il suffit de positionner une variable boolenne prcise et vous verrez dans vos
555

CHAPITRE 37. UNE APPLICATION FLUIDE = UNE APPLICATION PROPRE !


applications sil y a un contrle qui se met jour alors quil ne le devrait pas. Voir
cet emplacement : http://msdn.microsoft.com/fr-fr/library/system.windows.
interop.settings.enableredrawregions(v=VS.95).aspx.

En rsum
Les threads sont importants matriser afin davoir une application fluide et
ractive.
Il nest pas possible de mettre jour linterface depuis un thread darrire-plan.
On utilisera le Dispatcher.
Il existe des classes puissantes du framework .NET pour grer les threads darrireplan.

556

Chapitre

38

Utiliser des tches Background Agent


Difficult :
Nous avons vu que Windows Phone ne grait pas vraiment le multitches entre les applications. . . Une application peut tre soit dmarre, soit en pause. Si on dmarre une nouvelle
application, la prcdente sarrte ; les deux ne tournent pas en mme temps. Windows
Phone propose un systme diffrent de gestion du multitches. Ce nest pas du multitches
proprement parler, mais la possibilit de faire des choses de manire priodique en tche
de fond, et ce, lorsque lapplication est dsactive, en pause. Il sagit des Background Agent,
qui sont donc des tches qui sexcutent en arrire-plan. Il existe deux types de tches de
fond, les priodiques (Periodic) qui vont pouvoir faire des petites tches rgulirement, et
celles qui vont avoir besoin de plus de ressources (Resource Intensive) avec fatalement des
limitations.

557

CHAPITRE 38. UTILISER DES TCHES BACKGROUND AGENT

Crer un Background Agent pour une tche priodique


Une tche priodique doit tre excute rapidement et doit faire quelque chose de
simple, comme mettre jour une liste de mails, mettre jour une tuile, ou mettre
jour une coordonne GPS. Ces tches sont excutes toutes les 30 minutes (plus
ou moins prcisment, car cest le systme dexploitation qui gre ces tches et peut
ventuellement en regrouper plusieurs), sont limites en nombre par tlphone (cela
dpend de la configuration du tlphone) et ne dpassent pas 25 secondes dexcution.
Tandis que les tches aux ressources intensives peuvent durer jusqu 10 minutes, mais
ne sont excutes que lorsque le tlphone est branch son chargeur, dispose dune
connexion WIFI, sa batterie recharge plus de 90% et que lcran est verrouill.
Cest typiquement le cas par exemple lorsquon recharge le tlphone la nuit. . .
Un exemple classique dutilisation de tche priodique est la mise jour dune tuile
afin dinformer lutilisateur quil y a quelque chose de nouveau dans lapplication
aller consulter, un nouveau mail, des nouveaux flux RSS lire, etc. Pour les tches aux
ressources intensives, il sagira plutt de mettre jour des gros volumes de donnes,
par exemple tlcharger la mise jour dune carte pour notre logiciel de navigation
GPS ou bien faire des synchronisations lorsque nous avons travaill en hors-ligne, etc.
Notez que nous navons aucune garantie que la tche soit bien excute, cest le cas
par exemple si le tlphone est en mode conomie de batterie.
Voyons prsent comment cela fonctionne.
Pour crer une tche de fond, vous aurez besoin de deux projets. Un premier projet
classique contenant votre application classique et un projet spcial contenant la tche
de fond. Pour la dmonstration, je vais crer un petit programme dont le but sera
davoir une tuile qui affiche lheure de la dernire excution de la tche de fond, ainsi
quun nombre alatoire.
Crons donc un nouveau projet de type Application Windows Phone que nous nommons DemoAgent, puis ajoutons un nouveau projet la solution frachement cre de
type Agent des tches planifies Windows Phone , que nous appelons par exemple
AgentMiseAJour (voir figure 38.1).
Cest dans ce projet que nous allons crer notre tche de fond qui aura pour but de
mettre jour la tuile. La tche a dailleurs dj t cre, via la classe ScheduledAgent,
qui hrite de ScheduledTaskAgent - http://msdn.microsoft.com/en-us/library/
windowsphone/develop/microsoft.phone.scheduler.scheduledtaskagent. Elle possde notamment une mthode OnInvoke qui est le point dentre de notre agent. Cest
donc dans cette mthode que nous allons mettre jour la tuile :
1
2
3

protected override void OnInvoke ( ScheduledTask task )


{
ShellTile tuileParDefaut = ShellTile . ActiveTiles . First () ;

4
5
6
7
8

558

if ( tuileParDefaut != null )
{
FlipTileData flipTileData = new FlipTileData
{

CRER UN BACKGROUND AGENT POUR UNE TCHE PRIODIQUE

Figure 38.1 Cration du projet de type Agent des tches planifies


9
10

};

11

BackContent = " Derni re MAJ " + DateTime . Now .


ToShortTimeString () ,
Count = new Random () . Next (0 , 20 ) ,

12
13

14

tuileParDefaut . Update ( flipTileData ) ;

15
16
17

NotifyComplete () ;

Vous pouvez constater que nous affichons simplement un message sur le dos de la tuile
avec lheure de dernire mise jour de la tuile. De mme, on dtermine un nombre
alatoire quon affichera dans le cercle en haut droite de la tuile.
Mais ceci ne suffit pas. Il va falloir au moins un premier lancement de lapplication
afin de pouvoir dmarrer la tche et ventuellement permettre de larrter. Dans mon
application de dmo, je vais donc crer deux boutons permettant de respectivement de
dmarrer la tche et de larrter. Voici le XAML :
1
2
3
4
5
6

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 " >
< StackPanel >
< Button Content = " D marrer " Tap = " Button_Tap " / >
< Button Content = " Arr ter " Tap = " Button_Tap_1 " / >
</ StackPanel >
</ Grid >

559

CHAPITRE 38. UTILISER DES TCHES BACKGROUND AGENT


Maintenant, il faut dmarrer et arrter la tche. On utilise pour cela la classe ScheduledActionService - http://msdn.microsoft.com/en-us/library/windowsphone/
develop/microsoft.phone.scheduler.scheduledactionservice(v=vs.105).aspx. Voyons
tout dabord comment arrter la tche, car cest le plus simple. Il suffit de retrouver la
tche par son nom et de la supprimer du service :
1
2
3
4
5
6
7

public partial class MainPage : PhoneApplicationPage


{
private const string NomTache = " MajHeure " ;
public MainPage ()
{
I nitializeComponent () ;
}

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{

9
10
11

12
13
14
15
16
17
18
19
20
21

private void Button_Tap_1 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
if ( S che dul ed Act io nSe rv ice . Find ( NomTache ) != null )
{
Sc he dul edA ct ion Se rvi ce . Remove ( NomTache ) ;
}
}

La constante nous sert identifier notre tche de manire unique. Maintenant, pour
dmarrer la tche, nous aurons besoin dinstancier un objet de la classe PeriodicTask http://msdn.microsoft.com/en-us/library/windowsphone/develop/microsoft.phone.
scheduler.periodictask, qui comme son nom lindique, est une tche priodique :
1
2
3
4
5
6
7

public partial class MainPage : PhoneApplicationPage


{
private const string NomTache = " MajHeure " ;
public MainPage ()
{
I nitializeComponent () ;
}

8
9
10
11

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
StopTache () ;

12
13
14

560

PeriodicTask task = new PeriodicTask ( NomTache ) ;


task . Description = " Cette description apparaitra dans
les param tres du t l phone " ;

CRER UN BACKGROUND AGENT POUR UNE TCHE PRIODIQUE


try
{

15
16
17
18
19
20
21
22
23

24

25

S c he dul ed Act ion Se rvi ce . Add ( task ) ;


// utilis des fins de d bogages , pour tenter de
d marrer la t che imm diatement
S c he dul ed Act ion Se rvi ce . LaunchForTest ( NomTache , new
TimeSpan (0 , 0 , 1 ) ) ;

}
catch ( I n v a l id O p e r a t i o n E xc e p t i o n )
{
MessageBox . Show ( " Impossible d ' ajouter la t che p
riodique , car il y a d j trop d ' agents dans le
t l phone " ) ;
}

26

private void Button_Tap_1 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
StopTache () ;
}

27
28
29
30
31
32
33
34
35
36
37
38
39

public void StopTache ()


{
if ( S c he dul ed Act io nSe rvi ce . Find ( NomTache ) != null )
{
S c he dul ed Act ion Se rvi ce . Remove ( NomTache ) ;
}
}

Il suffit dinstancier la classe PeriodicTask avec le nom de la tche, de lui donner une
description et de lajouter au service. Faites attention toujours encadrer lajout dune
tche dun try/catch car une exception peut tre leve si nous avons atteint le nombre
maximum dagents que votre tlphone peut supporter. Ensuite, pour nous faciliter le
dbogage, il est possible de dmarrer la tche immdiatement, ceci se fait grce la
mthode LaunchForTest. Cette mthode ne doit pas tre appele directement lorsque
lapplication est termine et prte tre publie.
Remarque : il pourrait tre tout fait judicieux dentourer cette instruction
des instructions conditionnelles de dbug :

1
2
3

# if DEBUG
S c h e d u l e d A ct ion Se rvi ce . LaunchForTest ( NomTache , new TimeSpan
(0 , 0 , 1 ) ) ;
# endif

Vous aurez remarqu quavant de dmarrer la tche, je commence par larrter, si jamais
561

CHAPITRE 38. UTILISER DES TCHES BACKGROUND AGENT


elle est dj lance.
Noubliez pas de rajouter une rfrence au projet contenant la tche depuis
votre application, sinon le projet ne pourra pas utiliser lagent et celui-ci ne
fonctionnera pas.
Il ne reste plus qu dmarrer notre application, cliquer sur le bouton dmarrer et
attendre. Si vous mettez un point darrt dans la tche, vous pourrez voir que vous
vous y arrterez au bout de quelques secondes. Puis, vous pourrez arrter lapplication,
lpingler dans lmulateur et attendre nouveau. La tuile se mettra jour peu prs
toutes les demi-heures, comme le montre la figure 38.2.

Figure 38.2 Mise jour de la tuile via le background agent


Remarque : vous pouvez accder la liste des tches qui sexcutent en arrireplan en allant dans les paramtres de lmulateur (ou de votre tlphone),
applications, tches en arrire-plan. Vous y trouverez notamment notre tche
(voir figure 38.3).
Si vous cliquez sur le nom de lagent, vous pourrez voir la description de la tche que
nous avons saisie. Cest lendroit idal pour expliquer lutilisateur ce que fait lagent
afin de lencourager ne pas le dsactiver. Jetez un il la figure 38.4.
Et voil pour les tches priodiques.
562

CRER UN BACKGROUND AGENT POUR UNE TCHE PRIODIQUE

Figure 38.3 Les tches darrire plan dans les paramtres de lmulateur

Figure 38.4 Description de lagent

563

CHAPITRE 38. UTILISER DES TCHES BACKGROUND AGENT


Vous vous rappelez de notre application mto ? Il sagit dune application idale pour
utiliser une tche en arrire-plan afin de mettre jour les informations de mto et
les indiquer directement dans la tuile, sans avoir lancer lapplication. Ainsi, dun
coup dil, nous pourrions voir ce que prvoit la mto pour le temps de la journe.
Je ne vais pas illustrer ceci ici, mais je vous encourage tester par vous-mme cette
possibilit dans le cadre dun petit TP en dehors de ce cours.

Crer une tche aux ressources intensives


Pour crer une tche aux ressources intensives, cest exactement la mme chose que
pour les tches priodiques. La seule diffrence vient du fait quon utilisera la classe ResourceIntensiveTask - http://msdn.microsoft.com/en-us/library/windowsphone/
develop/microsoft.phone.scheduler.resourceintensivetask. Par extension, on
pourra donc dterminer dans notre mthode OnInvoke le type de tche en testant le
type de la classe :
1
2
3
4
5
6
7
8
9
10

protected override void OnInvoke ( ScheduledTask task )


{
if ( task is PeriodicTask )
{
// t che p riodique
}
else
{
// t che aux ressources intensives
}

11
12
13

NotifyComplete () ;

Remarques gnrales sur les tches


Gardez lesprit quune tche peut ne pas tre excute du tout si jamais le tlphone
ne se retrouve pas dans les bonnes conditions dexcution de la tche. De mme, vous
aurez intrt faire en sorte que vos tches sexcutent le plus rapidement possible car
elles peuvent tre termines tout moment si jamais une des conditions dexcution
nest plus garantie (on dbranche le tlphone, on reoit un coup de fil, etc.).
Nutilisez pas trop de mmoire non plus (moins de 5 Mo) sous peine de voir votre tche
termine. Sachez enfin quune tche a une dure de vie de 2 semaines. chaque fois que
votre application est lance, vous avez lopportunit de renouveler ces 2 semaines. Cest
pour cette raison que nous commenons par supprimer la tche avant de la rajouter,
lorsque nous cliquons sur le bouton dmarrer. Ceci implique que si votre application
nest pas utilise pendant 2 semaines, votre tche sera automatiquement dsactive.
564

ENVOYER UNE NOTIFICATION AVEC UN AGENT DARRIRE-PLAN


Remarque : une tche doit pouvoir tre dsactive par lutilisateur depuis
lapplication qui la cr. Il a galement la possibilit de la dsactiver depuis
les paramtres du tlphone.

Envoyer une notification avec un agent darrire-plan


Vous vous rappelez des notifications ? Nous avions mis en place toute une architecture
complexe pour permettre denvoyer des notifications sur un tlphone, afin que lutilisateur puisse tre averti de quelque chose sans forcment devoir ouvrir lapplication.
Vous vous doutez que lagent darrire-plan est une solution intressante pour simplifier
lenvoi de notifications, en tenant compte bien sr des limitations de celui-ci. Il suffit de
faire en sorte que son agent darrire-plan envoie une notification toast locale, aprs par
exemple quil soit all tlcharger des informations sur internet. Tout dabord, crons
un agent basique :
1
2
3

public class ScheduledAgent : ScheduledTaskAgent


{
[... code abr g pour plus de clart ...]

protected override void OnInvoke ( ScheduledTask task )


{
ShellToast toast = new ShellToast
{
Title = " Des nouvelles infos ! " ,
NavigationUri = new Uri ( " / MainPage . xaml ?
nouvelleinfo = " + DateTime . Now . ToShortTimeString
() , UriKind . Relative ) ,
Content = " Il est " + DateTime . Now .
ToShortTimeString ()
};

5
6
7
8
9
10

11
12
13

toast . Show () ;

14
15
16
17
18

NotifyComplete () ;

Vous voyez, on ne fait rien de formidable, juste crer un nouvel objet de type ShellToast, y mettre lheure et envoyer la notification. Ce serait bien sr lemplacement
idal pour aller voir sur internet sil y a des nouvelles informations envoyer. Ensuite,
lenregistrement de lagent se fait de manire classique dans lapplication :
1
2
3

public partial class MainPage : PhoneApplicationPage


{
private const string NomTache = " EnvoiNotifHeure " ;

4
5
6

public MainPage ()
{

565

CHAPITRE 38. UTILISER DES TCHES BACKGROUND AGENT


I nitializeComponent () ;

7
8

StopTache () ;

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25

PeriodicTask task = new PeriodicTask ( NomTache ) ;


task . Description = " Agent permettant l ' envoi de
notifications " ;
try
{
Sc he dul edA ct ion Se rvi ce . Add ( task ) ;
// utilis des fins de d bogages , pour tenter de
d marrer la t che imm diatement
# if DEBUG
Sc he dul edA ct ion Se rvi ce . LaunchForTest ( NomTache , new
TimeSpan (0 , 0 , 1 ) ) ;
# endif
}
catch ( I n v a l i dO p e r a t i o n E xc e p t i o n )
{
MessageBox . Show ( " Impossible d ' ajouter la t che p
riodique , car il y a d j trop d ' agents dans le
t l phone " ) ;
}
}

26
27
28
29
30
31
32
33
34

public void StopTache ()


{
if ( S che dul ed Act io nSe rv ice . Find ( NomTache ) != null )
{
Sc he dul edA ct ion Se rvi ce . Remove ( NomTache ) ;
}
}

a peut tre plutt pratique et cest quand mme simple mettre en place, sans serveur
de notification (voir la notification darrire-plan dans la figure 38.5).

En rsum
Les Background Agent permettent dexcuter des tches en arrire-plan, des
tches priodiques ou des tches aux ressources intensives.
Ces tches peuvent nous permettre facilement de mettre jour une tuile ou
denvoyer une notification sans que lutilisateur nait besoin de dmarrer lapplication.
Une tche a une dure de vie de 2 semaines, renouvelable chaque fois que
lapplication est dmarre.

566

ENVOYER UNE NOTIFICATION AVEC UN AGENT DARRIRE-PLAN

Figure 38.5 Notification grce lagent darrire plan

567

CHAPITRE 38. UTILISER DES TCHES BACKGROUND AGENT

568

Chapitre

39

Utiliser Facebook dans une application


mobile
Difficult :
Avec lavnement des rseaux sociaux, une application Windows Phone va devoir tre
capable de travailler avec eux ! On ne cite plus bien sr Twitter ou Facebook. Chacun
des grands rseaux sociaux offre une solution pour interagir avec eux. Facebook ne droge
pas cette rgle et propose diverses solutions pour que nos applications tirent parti de la
puissance du social.
Nous allons prsent voir comment raliser une petite application fonctionnant avec Facebook.

569

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE

Crer une application Facebook


La premire chose faire est de crer une application Facebook. Cest une tape
gratuite mais indispensable afin de pouvoir tablir une relation de confiance entre
votre application Windows Phone et Facebook. Pour ce faire, rendez-vous sur : http:
//www.facebook.com/developers/createapp.php. Si vous ntes pas connect, cest
le moment de le faire.
Crez ensuite une application, comme la figure 39.1.

Figure 39.1 Cration dune application Facebook


Donnez-lui au moins un nom (voir figure 39.2).

Figure 39.2 Donner un nom lapplication Facebook


Finissez la cration (voir figure 39.3).
Vous obtenez un identifiant dapplication, ainsi quune cl dAPI. Il sagit du point
dentre permettant dexploiter les donnes issues de Facebook. Lidentifiant dapplication et la cl vont vous permettre dutiliser les mthodes de lAPI et didentifier de
manire unique votre application afin de pouvoir tablir la relation entre le tlphone
et un compte Facebook.

Simplifier les connexions Facebook avec lAPI


LAPI Facebook est utilisable en REST. Il est possible de faire tous nos appels
Facebook depuis notre application avec REST, mais il existe un projet open source qui
encapsule les appels REST afin de nous simplifier la tche. Personne ici ne rve de se
570

SIMPLIFIER LES CONNEXIONS FACEBOOK AVEC LAPI

Figure 39.3 Finalisation de la cration de lapplication Facebook

571

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


compliquer la vie, alors nous allons utiliser ce fameux projet. Il sagit du Facebook C#
SDK , anciennement situ sur la plateforme open source CodePlex, et qui se trouve
dsormais sur : http://facebooksdk.net/.
Mais vous navez mme pas besoin dy aller pour le rcuprer, car les concepteurs nous
ont encore simplifi la tche en le rendant disponible via un package Nuget (voir figure
39.4).

Figure 39.4 Le SDK Facebook via NuGet


Et voil, le SDK est rfrenc !

Scuriser laccs Facebook avec OAuth


OAuth ? Kzako, pourquoi tu parles de a ?

Eh bien parce quil sagit du systme dauthentification lAPI de Facebook. OAuth


est un protocole qui permet laccs des donnes de manire scurise et en fonction
des restrictions daccs appliques par lutilisateur. Lorsquelle aura besoin daccder
certaines informations sur lutilisateur, notre application va donc utiliser OAuth
pour demander Facebook dauthentifier cet utilisateur. Une fois que lutilisateur est
authentifi, Facebook nous fournit un jeton permettant daccder son API. Tout ce
572

SE CONNECTER FACEBOOK
principe de fonctionnement dOAuth est illsutr dans la figure 39.5.

Figure 39.5 Description du principe de fonctionnement dOAuth


Regardons dans la pratique comment cela fonctionne. . .

Se connecter Facebook
Pour authentifier un utilisateur, nous allons devoir naviguer sur la page dauthentification de Facebook. Mais comment accder cette fameuse page ? Pour le savoir,
nous allons utiliser le package SDK, mais avant a, nous aurons besoin de rajouter un
WebBrowser dans notre page XAML :
1

< phone : WebBrowser x : Name = " wb " Visibility = " Collapsed " Navigated =
" wb_Navigated " / >

Celui-ci est masqu au chargement de la page (Visibility Collapsed). Nous avons galement associ une mthode lvnement de navigation termin, qui nous permettra
de traiter lauthentification et de rcuprer le jeton. Puis nous allons crer une variable
prive dans notre page, du type FacebookClient pour reprsenter lutilisateur que lon
souhaite authentifier :
1

private FacebookClient client ;

573

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


Vous aurez besoin de lespace de noms Facebook pour accder aux fonctionnalits de
Facebook :
using Facebook ;

Enfin, nous allons naviguer sur lURL obtenue grce au package SDK. Pour ce faire,
nous allons devoir spcifier un certain nombre de paramtres en crant un dictionnaire
dobjets :
1
2
3

public MainPage ()
{
I ni t i alizeComponent () ;

Dictionary < string , object > parameters = new Dictionary <


string , object >() ;
parameters [ " response_type " ] = " token " ;
parameters [ " display " ] = " touch " ;
parameters [ " scope " ] = " user_about_me , friends_about_me ,
user_birthday , friends_birthday , publish_stream " ;
parameters [ " redirect_uri " ] = " https :// www . facebook . com /
connect / login_success . html " ;
parameters [ " client_id " ] = " votre id d ' application " ;

5
6
7
8
9
10
11

client = new FacebookClient () ;


Uri uri = client . GetLoginUrl ( parameters ) ;

12
13
14
15
16
17

wb . Visibility = Visibility . Visible ;


wb . Navigate ( uri ) ;

Dans ces paramtres, on trouve :


Le paramtre response_type : on lui attribue la valeur token car nous avons
besoin dun jeton daccs.
Le paramtre display : on lui attribue la valeur touch. Il sagit de linterface
que Facebook va proposer pour notre authentification. La valeur touch est celle
la plus adapte aux priphriques mobiles (les autres modes sont disponibles
sur : https://developers.facebook.com/docs/reference/dialogs/).
Enfin, et cest le plus important, le paramtre scope : il correspond aux
informations que nous souhaitons obtenir de la part de lutilisateur. Il sagit des
permissions que lon peut retrouver cet emplacement : https://developers.
facebook.com/docs/reference/login#permissions. Par exemple, jai choisi
les permissions suivantes :
user_about_me, qui permet dobtenir les informations de base dun utilisateur.
friends_about_me, qui permet dobtenir les informations de base des amis
de lutilisateur.
user_birthday, qui permet dobtenir la date de naissance dun utilisateur.
friends_birthday, qui permet dobtenir la date de naissance des amis de
lutilisateur.
574

SE CONNECTER FACEBOOK
publish_stream, qui va me permettre de poster un message sur le mur de
lutilisateur.
Attention, demander ces permissions nest pas anodin. Cela permet daccder aux informations personnelles de lutilisateur. On ne peut pas faire cela
dans son dos, il faut que lutilisateur autorise notre application accder
ces informations. Cela se fait juste aprs la connexion de lutilisateur (nous
verrons une copie dcran illustrant ce propos). Ce qui fait que si on demande
trop dinformations, notre utilisateur risque de prendre peur et de rejeter nos
demandes. On se retrouvera ainsi sans la moindre possibilit dexploiter les
donnes de lutilisateur. Il faut donc que nos demandes de permissions soient
le plus proche possible des besoins de lapplication !
Puis nous fournissons lURL de redirection Facebook ainsi que lidentifiant de notre
application, dans le paramtre client_id. Ainsi, aprs lapprobation de nos permissions par lutilisateur, la relation de confiance entre le compte Facebook et notre
application Facebook va pouvoir se crer. Enfin, nous instancions un objet FacebookClient. La mthode GetLoginUrl() va nous retourner lURL de la page de connexion
adquate sur Facebook afin que notre utilisateur puisse sy connecter avec son compte.
Cette mthode ne fait rien dextraordinaire part concatner tous les paramtres. Nous
nous retrouvons avec une URL de la sorte :
1

https : // www . facebook . com / dialog / oauth ? response_type = token \&


display = touch \& scope = user_about_me \% 2C \% 20friends_about_me \%
2C \% 20user_birthday \% 2C \% 20friends_birthday \% 2C \%
20publish_stream \& redirect_uri = https \% 3A \% 2F \% 2Fwww . facebook
. com \% 2Fconnect \% 2Flogin_success . html \& client_id = monappid

et il ne reste plus qu naviguer sur cette URL avec le WebBrowser. . . Nous arrivons
sur une page ressemblant celle prsente dans la figure 39.6.
Cest bien une page du site de Facebook. Cest donc Facebook qui nous authentifie
grce sa fentre de login. Entrons nos informations de connexion et validons. Une
fois que lon est connect, Facebook nous demande si nous acceptons la demande de
permission de lapplication Facebook (voir figure 39.7).
Une fois la demande de permission accepte, nous sommes redirigs sur une page
blanche marque success.
Parfait tout a. . . mais, le jeton ? Comment on le rcupre ?

Eh bien cela se fait dans lvnement de navigation auquel nous nous sommes abonns
au dbut. Dans cet vnement, nous allons utiliser la mthode TryParseOAuthCallbackURL de la classe FacebookOAuthResult pour extraire le jeton :
1

private string token ;

575

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE

Figure 39.6 Connexion Facebook

Figure 39.7 Installation de lapplication


576

SE CONNECTER FACEBOOK
3
4
5
6
7
8
9
10
11
12
13
14

private void wb_Navigated ( object sender , NavigationEventArgs e )


{
F ac e b oo k OAuthResult result ;
if ( client . T r yP a r se O A ut h C al l b ac k U rl ( e . Uri , out result ) )
{
if ( result . IsSuccess )
{
token = result . AccessToken ;
}
wb . Visibility = Visibility . Collapsed ;
}
}

En fait, cette mthode TryParseOAuthCallbackURL ne fait rien de compliqu. Elle


rcupre juste lURL dans laquelle le jeton est disponible. Voici lallure de cet URL :
1

https : // www . facebook . com / connect / login_success . html #


access_token =
AAABhqO0ZBZBP0BAJO3qZBEdRXXXXXXXXXXXXXXXXXXXxxXQZDZD &
expires_in = 5184000

Remarque : il est normal que vous nayez pas besoin de ressaisir vos informations chaque fois, le navigateur de lmulateur conserve les cookies de
session.
Toujours est-il quune fois que lURL de ce jeton est rcupre, nous allons pouvoir
faire tout ce que nous voulons. Chouette. Commenons par masquer le WebBrowser,
nous nen aurons plus besoin.
ce moment-l, je trouve quil est plus propre de changer de page en stockant le jeton
dans le dictionnaire dtat et en le rcuprant la page suivante.
1
2
3
4
5
6
7
8
9
10
11
12
13

private void wb_Navigated ( object sender , NavigationEventArgs e )


{
F ac e b oo k OAuthResult result ;
if ( client . T r yP a r se O A ut h C al l b ac k U rl ( e . Uri , out result ) )
{
if ( result . IsSuccess )
{
P ho ne Ap p li ca t io nS er v ic e . Current . State [ " Jeton " ] =
result . AccessToken ;
}
wb . Visibility = Visibility . Collapsed ;
Navig ationService . Navigate ( new Uri ( " / Page1 . xaml " ,
UriKind . Relative ) ) ;
}
}

Remarquez que le jeton a une dure de vie limite. Il est possible de le renouveler
rgulirement avec une requte qui nous retourne un nouveau jeton, voire le mme si
577

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


la requte a dj t faite dans la journe. Il sagit de la requte suivante, que lon peut
faire lorsque lon arrive sur la Page1.xaml :
1
2
3
4

public partial class Page1 : PhoneApplicationPage


{
private FacebookClient facebookClient ;
private string token ;

public Page1 ()
{
I nitializeComponent () ;
}

6
7
8
9
10

protected override void OnNavigatedTo ( NavigationEventArgs e


)
{
token = ( string ) P ho ne A pp li c at io nS e rv ic e . Current . State [ "
Jeton " ];
facebookClient = new FacebookClient ( token ) ;
facebookClient . GetCompleted +=
facebookClient_GetCompleted ;

11
12
13
14
15
16

Dictionary < string , object > parameters = new Dictionary <


string , object >() ;
parameters [ " client_id " ] = " votre cl API " ;
parameters [ " client_secret " ] = " votre cl secr te " ;
parameters [ " grant_type " ] = " fb_exchange_token " ;
parameters [ " fb_exchange_token " ] = facebookClient .
AccessToken ;

17
18
19
20
21
22

facebookClient . GetAsync ( " https :// graph . facebook . com /


oauth / access_token " , parameters , " MisAJourToken " ) ;

23
24
25
26
27

base . OnNavigatedTo ( e ) ;

On pourra extraire le nouveau token lorsque lvnement GetCompleted est lev :


1
2
3
4
5
6
7
8
9
10

private void f a c e b o o k C l i e n t _ G e t C o m p l e t e d ( object sender ,


F a c e b o okApiEventArgs e )
{
if ( e . Error == null )
{
if (( string ) e . UserState == " MisAJourToken " )
{
JsonObject data = ( JsonObject ) e . GetResultData () ;
token = ( string ) data [ " access_token " ];
facebookClient . AccessToken = token ;
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ " token " ] =
token ;

578

SE CONNECTER FACEBOOK
11
12
13

Cest une bonne ide de lancer une requte de ce genre chaque connexion de lutilisateur afin de prolonger la dure de vie du jeton, ou pourquoi pas dans une tche
priodique que nous avons vu dans la partie prcdente. . .
Oui mais moi, jaime bien les mthodes asynchrones async et await . . .

Qu cela ne tienne, il suffit de se faire une petite mthode dextension :


1
2
3

4
5
6
7
8
9
10
11
12
13
14

public static class Extensions


{
public static Task < JsonObject > GetAsyncEx ( this
FacebookClient facebookClient , string uri , object
parameters )
{
TaskCompletionSource < JsonObject > taskCompletionSource =
new TaskCompletionSource < JsonObject >() ;
EventHandler < FacebookApiEventArgs > getCompletedHandler
= null ;
g et C o mpletedHandler = (s , e ) = >
{
facebookClient . GetCompleted -= getCompletedHandler ;
if ( e . Error != null )
taskCompletionSource . TrySetException ( e . Error ) ;
else
taskCompletionSource . TrySetResult (( JsonObject ) e
. GetResultData () ) ;
};

15

facebookClient . GetCompleted += getCompletedHandler ;


facebookClient . GetAsync ( uri , parameters ) ;

16
17
18
19
20
21

return taskCompletionSource . Task ;

On pourra remplacer le code prcdent par :


1
2
3
4

protected async override void OnNavigatedTo ( NavigationEventArgs


e)
{
token = ( string ) P ho ne A pp li ca t io nS er v ic e . Current . State [ "
Jeton " ];
facebookClient = new FacebookClient ( token ) ;

579

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


Dictionary < string , object > parameters = new Dictionary <
string , object >() ;
parameters [ " client_id " ] = " votre id d ' application " ;
parameters [ " client_secret " ] = " votre cl d ' application " ;
parameters [ " grant_type " ] = " fb_exchange_token " ;
parameters [ " fb_exchange_token " ] = facebookClient .
AccessToken ;

6
7
8
9
10
11

try
{

12
13
14

15
16
17

JsonObject data = await facebookClient . GetAsyncEx ( "


https :// graph . facebook . com / oauth / access_token " ,
parameters ) ;
token = ( string ) data [ " access_token " ];
facebookClient . AccessToken = token ;
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ " token " ] = token ;

}
catch ( Exception )
{
MessageBox . Show ( " Impossible de renouveler le token " ) ;
}

18
19
20
21
22
23
24
25

base . OnNavigatedTo ( e ) ;

Pratique , non ?

Exploiter le graphe social avec le SDK


Le graphe social reprsente le rseau de connexions et de relations entre les utilisateurs
sur Facebook. Les connexions peuvent tre entre les utilisateurs (amis, famille,. . .) ou
entre des objets via des actions (un utilisateur aime une page, un utilisateur coute de
la musique,. . .). Le graphe social se base sur le protocole Open Graph pour modliser
ces relations et ces actions, laction la plus connue tant le fameux Jaime .
LAPI du graphe permet dexploiter ces informations. Cette API est utilisable en REST
mais encore une fois, le package SDK propose une faon de simplifier son utilisation.
La documentation de rfrence de cette API est disponible cet emplacement - http:
//developers.facebook.com/docs/reference/api/. Voyons prsent comment sen
servir. . .

Rcuprer des informations


Si vous navez pas rafrachi le token, il va vous falloir instancier un objet FacebookClient
avec le jeton pass dans le dictionnaire dtat. Sinon, vous avez dj tout ce quil
faut et vous tes prt interroger lAPI du graph social avec une nouvelle requte.
580

RCUPRER DES INFORMATIONS


Commenons par quelque chose de simple : rcuprer des informations sur lutilisateur
en cours.
Premirement, le XAML. Nous allons afficher limage de lutilisateur, son nom et prnom, ainsi que sa date de naissance :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " * " / >
< RowDefinition Height = " * " / >
</ Grid . RowDefinitions >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< Image x : Name = " ImageUtilisateur " Grid . RowSpan = " 2 " / >
< TextBlock x : Name = " NomUtilisateur " Grid . Column = " 1 " / >
< TextBlock x : Name = " Da t e Na i s s an c e Ut i l is a t eu r " Grid . Row = "
1 " Grid . Column = " 1 " / >
</ Grid >
</ Grid >

Pour obtenir ces infos, nous allons interroger le graphe social. On utilise pour cela
la mthode Get de lobjet FacebookClient, que nous avons dj utilise (ou plutt la
mthode dextension que nous avons cre). Le principe est de faire un appel REST la
ressource suivante : https://graph.facebook.com/me, et de rcuprer le rsultat. Ce
rsultat sobtient avec la mthode GetResultData qui retourne un JsonObject. Nous
pourrons alors accder aux informations contenues dans cet objet, comme lidentifiant,
le nom, le prnom ou la date de naissance.
1
2

try
{

3
4
5
6
7
8
9
10
11
12
13

JsonObject data = await facebookClient . GetAsyncEx ( " https ://


graph . facebook . com / me " , null ) ;
string id = ( string ) data [ " id " ];
string prenom = ( string ) data [ " first_name " ];
string nom = ( string ) data [ " last_name " ];
Dispatcher . BeginInvoke (() = >
{
ImageUtilisateur . Source = new BitmapImage ( new Uri ( "
https :// graph . facebook . com / " + id + " / picture " ) ) ;
NomUtilisateur . Text = prenom + " " + nom ;
D a t e N ai s s an c e U ti l i sa t e ur . Text = Ob tie nt Dat eD eNa iss an ce (
data ) ;
}) ;

581

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


14
15
16
17

catch ( Exception )
{
MessageBox . Show ( " Impossible d ' obtenir les informations sur
moi " ) ;
}

Voici le dtail de la mthode ObtientDateDeNaissance utilise pour rcuprer la date


de naissance de lutilisateur :
1
2
3
4
5
6
7

8
9
10
11

private string Obt ie ntD at eDe Nai ss anc e ( JsonObject data )


{
if ( data . ContainsKey ( " birthday " ) )
{
DateTime d ;
CultureInfo enUS = new CultureInfo ( " en - US " ) ;
if ( DateTime . TryParseExact (( string ) data [ " birthday " ] , "
MM / dd / yyyy " , enUS , System . Globalization .
DateTimeStyles . None , out d ) )
return d . ToShortDateString () ;
}
return string . Empty ;
}

Remarquez que vous naurez une valeur dans le champ de date de naissance que si
vous avez demand la permission user_birthday, do la vrification de lexistence de
la cl avant son utilisation. Remarquez que limage dune personne sobtient grce
son identifiant : https://graph.facebook.com/id_utilisateur/picture (voir figure
39.8).

Obtenir la liste de ses amis


Nous allons en profiter pour faire la mme chose avec les amis de lutilisateur, nous
allons afficher leurs noms et leurs dates de naissance. Cest aussi simple que prcdemment, il suffit dinvoquer la ressource situe : https://graph.facebook.com/
me/friends. Cette requte nous permet dobtenir lidentifiant et le nom des amis
de lutilisateur, sous la forme dun tableau JSON. Sauf que ce nest pas suffisant,
nous souhaitons obtenir leurs anniversaires. Il faut appeler la ressource suivante :
https://graph.facebook.com/id_utilisateur pour obtenir des informations complmentaires. Ce que nous allons faire de ce pas. . . Jen profite pour rajouter une liste
dutilisateurs sous forme dune collection ObservableCollection dans ma classe :
public ObservableCollection < Utilisateur > UtilisateurList { get ;
set ; }

Cette collection est compose dobjets reprsentant chaque utilisateur. Chaque objet
Utilisateur est dfini de la manire suivante :
1
2

public class Utilisateur


{

582

OBTENIR LA LISTE DE SES AMIS

Figure 39.8 Rcupration de mes informations


3
4
5
6
7

public
public
public
public

string Id { get ; set ; }


string Nom { get ; set ; }
BitmapImage Image { get ; set ; }
string DateNaissance { get ; set ; }

Je vais complter les informations de chaque objet Utilisateur grce aux informations
reues par lAPI. Je dmarre donc la requte permettant davoir la liste de mes amis.
Une fois la liste reue, jextrais la liste des identifiants de chaque ami et je rinterroge
lAPI pour avoir le dtail de chaque utilisateur. Une fois le dtail reu, je lajoute
mon ObservableCollection.
1
2
3
4
5
6
7
8
9
10

try
{

JsonObject data = await facebookClient . GetAsyncEx ( " https ://


graph . facebook . com / me / friends " , null ) ;
JsonArray friends = ( JsonArray ) data [ " data " ];
foreach ( JsonObject f in friends )
{
string id = ( string ) f [ " id " ];
data = await facebookClient . GetAsyncEx ( " https :// graph .
facebook . com / " + id , null ) ;
string name = ( string ) data [ " name " ];
Dispatcher . BeginInvoke (() = > UtilisateurList . Add ( new
Utilisateur { Id = id , Nom = name , Image = new

583

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


BitmapImage ( new Uri ( " https :// graph . facebook . com / " +
id + " / picture " ) ) , DateNaissance =
Ob ti ent Da teD eNa is san ce ( data ) }) ) ;

11
12
13
14
15
16

}
}
catch ( Exception )
{
MessageBox . Show ( " Impossible d ' obtenir les informations sur
les amis " ) ;
}

Ouf ! Vous naurez bien sr pas oubli de faire les initialisations adquates :
1
2
3

public Page1 ()
{
I ni t i alizeComponent () ;

4
5
6
7

UtilisateurList = new ObservableCollection < Utilisateur >() ;


DataContext = this ;

Noubliez pas non plus de dclarer laffichage de notre liste dans une ListBox dans la
page XAML :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

< ListBox ItemsSource = " { Binding UtilisateurList } " Grid . Row = " 1 " >
< ListBox . ItemTemplate >
< DataTemplate >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
</ Grid . ColumnDefinitions >
< Grid . RowDefinitions >
< RowDefinition Height = " auto " / >
< RowDefinition Height = " auto " / >
</ Grid . RowDefinitions >
< Image Source = " { Binding Image } " Grid . RowSpan = " 2
" />
< TextBlock Text = " { Binding Nom } " Grid . Column = " 1 "
/>
< TextBlock Text = " { Binding DateNaissance } " Grid .
Row = " 1 " Grid . Column = " 1 " / >
</ Grid >
</ DataTemplate >
</ ListBox . ItemTemplate >
</ ListBox >

Et voil, nous pouvons afficher la liste de nos amis. Par respect pour les miens, je
ne prsenterai pas de copie dcran avec leurs ttes et leurs anniversaires, mais vous
pouvez essayer avec les vtres, cela fonctionne trs bien !
584

PUBLIER UN POST SUR SON MUR FACEBOOK

Publier un post sur son mur Facebook


Les connexions du graphe social et autres donnes de Facebook peuvent tre exploites
commercialement. On peut aussi utiliser ces donnes pour publier des posts sur son
mur Facebook. Imaginons par exemple que je dveloppe un jeu pour Windows Phone,
je fais un score terrible et je souhaite dfier mes amis pour quils tentent de me battre.
Rien de tel que de poster un petit message sur mon mur Facebook pour les inciter
venir jouer et me battre. . .
Cest ce que nous allons faire ici. Pour lexemple, je vais reprendre notre jeu du plus
ou du moins que nous avions fait en TP (voir Partie 1, Chapitre 8). Lintrt ici, outre
de nous amuser, sera de pouvoir poster son score automatiquement sur son mur.
Je reprends donc le XAML du TP que je mets dans ma Page1.xaml, puis je rajoute le
petit bouton permettant de poster mon score sur Facebook. Voici le XAML :
1
2
3
4
5
6

< Grid x : Name = " LayoutRoot " Background = " Transparent " >
< Grid . RowDefinitions >
< RowDefinition Height = " Auto " / >
< RowDefinition Height = " * " / >
< RowDefinition Height = " Auto " / >
</ Grid . RowDefinitions >

7
8
9

10

< StackPanel x : Name = " TitlePanel " Grid . Row = " 0 " Margin = " 12 , 17 ,
0 , 28 " >
< TextBlock x : Name = " ApplicationTitle " Text = " TP du jeu du
plus ou du moins " Style = " { StaticResource
P honeTextTitle2Style } " / >
</ StackPanel >

11
12
13
14

15
16
17
18

19

20
21
22

< Grid x : Name = " ContentPanel " Grid . Row = " 1 " Margin = " 12 ,0 , 12 , 0 "
>
< StackPanel >
< TextBlock Text = " Veuillez saisir une valeur ( entre
0 et 500 ) " Style = " { StaticResource
PhoneTextNormalStyle } " HorizontalAlignment = "
Center " / >
< TextBox x : Name = " Valeur " InputScope = " Number " / >
< Button Content = " V rifier " Tap = " Button_Tap_1 " / >
< TextBlock x : Name = " Indications " Height = " 50 "
TextWrapping = " Wrap " / >
< TextBlock x : Name = " NombreDeCoups " Height = " 50 "
TextWrapping = " Wrap " Style = " { StaticResource
PhoneTextNormalStyle } " / >
< Button x : Name = " BoutonFb " Content = " Publier mon
score sur Facebook " Tap = " Button_Tap " IsEnabled = "
False " / >
</ StackPanel >
</ Grid >
< Button Content = " Rejouer " Tap = " Button_Tap_2 " Grid . Row = " 2 "
/>

585

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


</ Grid >

23

Passons dsormais au code-behind :


1
2
3
4
5
6
7

public partial class Page1 : PhoneApplicationPage


{
private FacebookClient facebookClient ;
private string token ;
private Random random ;
private int valeurSecrete ;
private int nbCoups ;

8
9
10
11

public Page1 ()
{
I nitializeComponent () ;

12
13
14
15
16
17
18

random = new Random () ;


valeurSecrete = random . Next (1 , 500 ) ;
nbCoups = 0 ;
Color couleur = ( Color ) Application . Current . Resources [ "
PhoneAccentColor " ];
Indications . Foreground = new SolidColorBrush ( couleur ) ;

19
20
21
22
23

protected async override void OnNavigatedTo (


N avigationEventArgs e )
{
token = ( string ) P ho ne A pp li c at io nS e rv ic e . Current . State [ "
Jeton " ];
facebookClient = new FacebookClient ( token ) ;

24
25
26
27
28
29

Dictionary < string , object > parameters = new Dictionary <


string , object >() ;
parameters [ " client_id " ] = " id application " ;
parameters [ " client_secret " ] = " id API " ;
parameters [ " grant_type " ] = " fb_exchange_token " ;
parameters [ " fb_exchange_token " ] = facebookClient .
AccessToken ;

30
31
32
33

34
35
36
37
38
39

586

try
{

JsonObject data = await facebookClient . GetAsyncEx ( "


https :// graph . facebook . com / oauth / access_token " ,
parameters ) ;
token = ( string ) data [ " access_token " ];
facebookClient . AccessToken = token ;
P ho ne Ap p li ca ti o nS er v ic e . Current . State [ " token " ] =
token ;

}
catch ( Exception )
{

PUBLIER UN POST SUR SON MUR FACEBOOK


MessageBox . Show ( " Impossible de renouveler le token "
);

40
41
42

43

}
base . OnNavigatedTo ( e ) ;

44

private void Button_Tap_1 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
int num ;
if ( int . TryParse ( Valeur . Text , out num ) )
{
if ( valeurSecrete == num )
{
Indications . Text = " Gagn !! " ;
BoutonFb . IsEnabled = true ;
}
else
{
nbCoups ++;
if ( valeurSecrete < num )
Indications . Text = " Trop grand ... " ;
else
Indications . Text = " Trop petit ... " ;
if ( nbCoups == 1 )
NombreDeCoups . Text = nbCoups + " coup " ;
else
NombreDeCoups . Text = nbCoups + " coups " ;
}
}
else
Indications . Text = " Veuillez saisir un entier ... " ;
}

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

private void Button_Tap_2 ( object sender , System . Windows .


Input . GestureEventArgs e )
{
valeurSecrete = random . Next (1 , 500 ) ;
nbCoups = 0 ;
Indications . Text = string . Empty ;
NombreDeCoups . Text = string . Empty ;
Valeur . Text = string . Empty ;
}

72
73
74
75
76
77
78
79
80
81
82
83
84
85

private void Button_Tap ( object sender , System . Windows . Input


. GestureEventArgs e )
{
// faire , publier sur facebook
}

587

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE


Il na rien de transcendant, cest comme le TP mix ce quon a vu sur Facebook
juste avant. Lorsque nous trouvons la bonne valeur, on change la valeur de la proprit
IsEnabled du bouton pour publier sur Facebook. Celui-ci devra construire le message
et poster sur notre mur. Pour cela, on utilisera la mthode PostAsync et pourquoi pas
une mthode dextension asynchrone que nous pouvons rajouter notre classe statique
dextensions :
1
2
3
4
5
6
7
8
9
10
11
12

public static Task < JsonObject > PostAsyncEx ( this FacebookClient


facebookClient , string uri , object parameters )
{
TaskCompletionSource < JsonObject > taskCompletionSource = new
TaskCompletionSource < JsonObject >() ;
EventHandler < FacebookApiEventArgs > postCompletedHandler =
null ;
p o s t C ompletedHandler = (s , e ) = >
{
facebookClient . PostCompleted -= postCompletedHandler ;
if ( e . Error != null )
taskCompletionSource . TrySetException ( e . Error ) ;
else
taskCompletionSource . TrySetResult (( JsonObject ) e .
GetResultData () ) ;
};

13

facebookClient . PostCompleted += postCompletedHandler ;


facebookClient . PostAsync ( uri , parameters ) ;

14
15
16
17
18

return taskCompletionSource . Task ;

Nous pourrons alors poster sur le mur en passant un message dans la proprit message,
puis en envoyant tout a en REST sur ladresse du mur. Une fois que lenvoi est termin,
on est en mesure de dterminer si lenvoi est bien pass ou pas :
1
2
3
4
5
6
7
8
9
10
11
12
13

private async void Button_Tap ( object sender , System . Windows .


Input . GestureEventArgs e )
{
string message = " Message correctement post " ;
try
{
Dictionary < string , object > parameters = new Dictionary <
string , object >() ;
parameters [ " message " ] = " Je viens de trouver le nombre
secret en " + nbCoups ;
await facebookClient . PostAsyncEx ( " https :// graph .
facebook . com / me / feed " , parameters ) ;
}
catch ( Exception )
{
message = " Impossible de poster le message " ;
}

588

PUBLIER UN POST SUR SON MUR FACEBOOK


14
15
16
17
18

Dispatcher . BeginInvoke (() = >


{
MessageBox . Show ( message ) ;
}) ;

Et voil le rsultat la figure 39.9 !

Figure 39.9 Le message est post sur Facebook depuis lapplication Windows Phone
Ct Facebook, nous pouvons constater lapparition du message sur mon mur comme
je vous le montre dans la figure 39.10.

Figure 39.10 Le message est affich sur mon mur

589

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE

Utiliser les tasks


Il existe une autre solution pour poster un message sur son mur. Trs simple, sans
avoir besoin dune application Facebook, partir du moment o vous avez configur
votre tlphone avec votre compte Facebook, ce qui est souvent le cas. Il suffit ensuite
dutiliser un launcher spcifique du tlphone. Poster un message sur son mur se fait
en quelques lignes de code, par exemple pour mettre jour son statut :
1
2
3

ShareStatusTask shareStatusTask = new ShareStatusTask () ;


shareStatusTask . Status = " Bonjour mon mur " ;
shareStatusTask . Show () ;

videmment, il demande une confirmation lutilisateur. Vous ne pourrez pas le voir


sur lmulateur, alors je vous montre dans la figure 39.11 une copie de mon tlphone.

Figure 39.11 Partage de statut via le launcher


Ce nest cependant pas vraiment un partage Facebook, cest un partage pour tous les
rseaux sociaux configurs du tlphone, et on peut en loccurrence choisir Facebook.
Cest ce que jai fait ici dans la deuxime zone. Il est galement trs facile de publier
un lien (voir code suivant et figure 39.12).
1
2

590

ShareLinkTask shareLinkTask = new ShareLinkTask () ;

UTILISER LES TASKS


3
4

5
6

shareLinkTask . Title = " Mon livre pour apprendre le C # " ;


shareLinkTask . LinkUri = new Uri ( " http :// boutique . fr .
openclassrooms . com / boutique - 614 - 797 - apprenez -a - developper - en
- c . html " , UriKind . Absolute ) ;
shareLinkTask . Message = " A lire absolument ... " ;
shareLinkTask . Show () ;

Figure 39.12 Partage de lien via launcher


Grce ces deux launchers, il devient trs facile de publier un statut ou un lien, mme
si on est fatalement un peu plus limit car on ne peut que poster sur son mur et pas
par exemple sur le mur de nos amis pour leur souhaiter un bon anniversaire.
Nous avons vu comment lire des informations dans le graphe social et comment y poster un message. Il y a beaucoup dautres informations intressantes dans ce graphe
social. Des applications but commercial pourraient les exploiter avec intrt. tant
donn quil est trs facile de rcuprer ce que vous aimez (les jaime ), il pourrait
tre trs facile de vous proposer des produits en adquation avec ce que vous aimez.
Les entreprises de-commerce ne sy trompent pas et essayent de vous attirer sur leurs
applications Facebook. partir du moment o vous autorisez laccs vos informations, vous avez pu voir comme il est simple de les rcuprer. Cela ouvre beaucoup de
possibilits pour rendre vos applications Windows Phone sociales et dynamiques.
591

CHAPITRE 39. UTILISER FACEBOOK DANS UNE APPLICATION MOBILE

En rsum
Il faut crer une application Facebook afin de pouvoir interagir avec Facebook
depuis notre application Windows Phone.
On utilise le contrle WebBrowser pour tablir lauthentification OAuth.
Les permissions, si elles sont acceptes, nous permettent daccder aux informations du graphe social.
Le SDK nous simplifie laccs aux ressources REST du graphe social.
On peut utiliser des launchers pour partager trs facilement des informations
sur les rseaux sociaux.

592

Chapitre

40

Publier son application


Difficult :
a y est, votre application est prte. Lmulateur vous a bien aid dans votre phase de
dveloppement et vous vous sentez prts pour passer la suite. Nous allons voir dans ce
chapitre comment faire pour tester son application sur un tlphone et pour soumettre son
application sur le Windows Phone Store. Mais la premire chose indispensable faire est
de tester son application sur un vrai tlphone. Mme si lmulateur est capable de simuler
pas mal de choses, cest quand mme le vrai tlphone qui va tre capable de se rapprocher
au maximum de ce que vont avoir les utilisateurs au quotidien, forcment. . .

593

CHAPITRE 40. PUBLIER SON APPLICATION


Ensuite, cela permet de valider plein de petites choses que lon ne voit pas forcment
dans lmulateur (comme les images de lapplication). Enfin, et cest presque le plus
important, cela permet de valider la performance de notre application. En effet, un
tlphone est gnralement bien moins puissant quun ordinateur de dveloppeur. Il
mest souvent arriv de constater que mon application tait super fluide dans lmulateur, mais sur mon tlphone, celle-ci tait inutilisable tellement elle tait lente. Pour
la terminer correctement, il faut donc dployer son application sur un tlphone.

Crer un compte dveloppeur


La premire chose faire est de crer un compte dveloppeur. Cest une opration qui
coute 19$ par an (14). Elle est indispensable pour dployer son application sur un
tlphone et pour pouvoir publier sur le Windows Phone Store. Rendez-vous donc sur :
https://dev.windowsphone.com/. Le souci, cest que pour linstant, cest en anglais,
donc si vous ntes pas trop anglophone, suivez le guide. On doit dans un premier temps
crer un compte en cliquant sur Sign in (voir figure 40.1).

Figure 40.1 Crer un compte


Note : Il est possible que les images ne refltent pas exactement ce que vous
voyez car le site est en perptuelle volution. Mais ne vous inquitez pas,
lide est l !
Il faut ensuite sauthentifier avec son compte Windows Live (voir figure 40.2).
Saisissez votre e-mail et votre mot de passe. Vous tes redirigs sur la page daccueil
prsente dans la figure 40.3, o vous pourrez cliquer sur nimporte quel lien. Tout
dabord, cliquez sur longlet Dashboard .
Nous pouvons alors nous inscrire en cliquant sur le bouton Join Now (voir figure
40.4).
Cela nous permet dobtenir le formulaire dinscription, o lon indique notamment son
594

CRER UN COMPTE DVELOPPEUR

Figure 40.2 Authentification avec compte Windows Live

Figure 40.3 Cration dun compte via longlet Dashboard

595

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.4 Inscription du compte dveloppeur

596

CRER UN COMPTE DVELOPPEUR


pays, puis son statut (entreprise, particulier ou tudiant).
Il est noter que linscription au Windows Phone Store est gratuite quand
on est tudiant.
Bref, de ladministratif, dtaill dans la figure 40.5.

Figure 40.5 Les informations remplir


Toujours dans ladministratif, on a besoin ensuite de saisir nos informations personnelles, nom, prnom, etc., ainsi que notre nom dauteur sur le Windows Phone Store
(voir figure 40.6).
Passons la suite. Cest le moment dlicat, le paiement de labonnement annuel, prsent dans la figure 40.7. Il faut choisir le type de rglement, paiement ou bon de
rduction.
Labonnement est donc de 14. Il est possible de rgler avec un bon de rduction, par
exemple si on a de largent sur son compte Xbox. Enfin, pour les tudiants, pour les dtenteurs dun code promotionnel, ou pour les membres inscrits au programme acclra597

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.6 Saisir ses informations

598

CRER UN COMPTE DVELOPPEUR

Figure 40.7 Mode de rglement

599

CHAPITRE 40. PUBLIER SON APPLICATION

teur Windows Phone - http://msdn.microsoft.com/fr-fr/accelerateur-windows-phone-7.


aspx, cest gratuit ! Il ne reste qu saisir les informations de facturation.
Et voil, le compte est dsormais cr. Nous recevons un e-mail de confirmation qui
va nous permettre dactiver notre compte via un lien. Une fois ce lien cliqu, nous
obtenons confirmation de la bonne cration du compte. Et cest termin pour cette
tape. Nous avons donc associ notre compte Windows Live un compte Windows
Phone Store.

Inscrire un tlphone de dveloppeur


Avant de pouvoir installer son application sur son tlphone, il faut lenregistrer. Premire chose faire, relier son tlphone son ordinateur via le cble appropri.
Si vous navez pas de tlphone, vous pouvez vous inscrire au programme acclrateur Windows Phone - http://msdn.microsoft.com/
fr-fr/accelerateur-windows-phone-7.aspx afin quon vous en prte
un.
Comme vous tes sous Windows 8, le programme permettant de synchroniser son Windows Phone est dj prsent sur votre ordinateur. Ce programme sappelle Windows
Phone et est ncessaire pour la suite des oprations et pour faire les mises jour du
tlphone. Il existe en version Modern UI et en version Bureau Traditionnel
(vous pouvez utiliser les deux versions, elles ne diffrent quau niveau de lapparence
esthtique de linterface).
Une fois votre tlphone branch, si vous avez un code de verrouillage protgeant votre
tlphone, vous devrez le saisir.
Ensuite, vous devrez enregistrer le tlphone comme tant un tlphone de dveloppeur. Pour cela, dmarrez le programme Windows Phone Developer Registration ,
accessible par exemple via la barre de recherche de Windows 8 (voir figure 40.8).
noter que vous pourrez enregistrer jusqu 5 tlphones diffrents pour
le mme compte dveloppeur, ce qui permet de tester son application sur
plusieurs appareils.
Une fois lapplication dmarre, vous pourrez voir lcran qui permet dinscrire le tlphone comme tant un tlphone de dveloppeur dans la figure 40.9.
noter que vous devrez avoir lcran de votre tlphone dverrouill, et que
votre PC et que votre tlphone doivent tre connects Internet. Si toutes
ces conditions ne sont pas runies, vous aurez un bouton Ressayer .
Vous validez bien sr en cliquant sur Inscrire . ce moment-l, vous devez vous
connecter votre compte Windows Live avec lequel vous avez souscrit votre abonne600

INSCRIRE UN TLPHONE DE DVELOPPEUR

Figure 40.8 Dmarrage de lapplication denregistrement du tlphone

Figure 40.9 Inscrire le tlphone comme tant un tlphone de dveloppement

601

CHAPITRE 40. PUBLIER SON APPLICATION


ment de dveloppeur (voir figure 40.10).

Figure 40.10 Authentification Windows Live


Le programme tente alors de dverrouiller le tlphone. Et voil, comme vous pouvez
le voir dans la figure 40.11, cest fait !
Remarquez que si le tlphone est dj enregistr, vous pourrez le ds-enregistrer de la
mme faon (prsente dans la figure 40.12).
Il est important de le faire si jamais vous voulez vendre votre tlphone ou sil ne doit
plus servir des fins de dbogage. En effet, dans la mesure o vous tes limits sur le
nombre de tlphones utilisables via un compte dveloppeur, il est important de dsenregistrer un tlphone qui ne servira plus. Mais bon, ce nest bien sr pas le moment
de la faire.
Voil, votre tlphone est prt recevoir votre application. Pour cela, dans Visual Studio, il faut changer la destination du dploiement dans la liste droulante o lmulateur
avait t auparavant slectionn (voir figure 40.13).
Ce qui est formidable, cest que vous allez pouvoir bnficier de tout lenvironnement
de Visual Studio avec votre application directement sur votre tlphone. Cela veut
notamment dire avoir accs au dbogueur, ce qui vous permet de mettre des points
602

INSCRIRE UN TLPHONE DE DVELOPPEUR

Figure 40.11 Tlphone dverrouill

603

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.12 Ds-enregistrement du tlphone

Figure 40.13 Dmarrer lapplication sur le tlphone

604

PROPOSER UNE VERSION DESSAI DE VOTRE APPLICATION


darrts ou de voir des valeurs de variables alors que vous tes en train de vous servir
de votre tlphone. Bon, ok, il y aura forcment le cble qui relie le PC au tlphone,
mais cest quand mme trs pratique ! Tellement pratique quune fois que vous y aurez
got, vous ne voudrez plus revenir sur lmulateur. Sur le tlphone, vous vous rendrez
mieux compte des problmes de performance de vos applications. Il sera galement plus
facile dutiliser le service de localisation ou de vous dconnecter dInternet pour faire
des tests hors rseau.

Proposer une version dessai de votre application


Si vous avez un Windows Phone, vous avez pu remarquer que, dans le Windows Phone
Store, certaines applications payantes sont disponibles en version dessai. Ceci permet
en gnral de tester un peu lapplication avant de se dcider si celle-ci mrite dtre
achete ou non. Lors de la soumission de votre application, vous pourrez dcider du prix
de votre application et si elle dispose dun mode dessai ou non. Windows Phone possde
une API qui permet de savoir si lapplication est en mode essai ou non. Cest la mthode IsTrial de la classe LicenseInformation - http://msdn.microsoft.com/fr-fr/
library/windows/apps/windows.applicationmodel.store.licenseinformation, qui
est disponible en incluant lespace de noms suivant :
1

using Microsoft . Phone . Marketplace ;

Ainsi, vous pourrez vrifier si lapplication est en mode dessai avec le code suivant :
1
2

Li ce ns eIn fo rm ation license = new LicenseInformation () ;


bool modeEssai = license . IsTrial () ;

Aprs, libre vous de faire ce que vous voulez en fonction de la valeur du boolen.
Peut-tre voudrez-vous limiter le nombre de fonctionnalits ? Peut-tre voudrez-vous
limiter lapplication dans le temps ? Dans tous les cas, une fois la priode termine, vous
aurez intrt renvoyer lutilisateur vers le Windows Phone Store afin quil achte votre
application, comme nous lavons vu cest avec le launcher MarketplaceDetailTask. Remarquez que, pendant la phase de dveloppement, la variable boolenne IsTrial() vaut
toujours Vrai. Vous pourrez encapsuler cet appel dans une instruction conditionnelle
pour vos diffrents tests :
1
2
3
4
5
6
7
8
9

private bool EstTrial ()


{
# if DEBUG
return true ; // ou false , comme vous voulez
# else
var license = new Microsoft . Phone . Marketplace .
LicenseInformation () ;
return license . IsTrial () ;
# endif
}

605

CHAPITRE 40. PUBLIER SON APPLICATION


Cest une trs bonne ide de proposer une version dessai. Achteriez-vous
une voiture les yeux ferms ? moins quelle nait une excellente rputation,
achteriez-vous une application sans savoir si elle fonctionne correctement ?

Certifier son application avec le Store Test Kit


Alors, maintenant que nous avons ralis notre application dans le dbogueur
et que nous avons valid son fonctionnement sur notre tlphone, nous avons
termin ?
Eh non, pas encore ! Nous avons une superbe application, mais encore faut-il savoir si
elle est autorise tre distribue sur le Windows Phone Store. En effet, les applications
ne peuvent tre distribues quaprs avoir pass une phase de certification, faite par les
quipes Windows Phone. Cela permet de vrifier que lapplication na pas de problmes
(malware, etc.), et quelle est suffisamment ractive et fonctionnelle pour lutilisateur.
Pour nous aider passer cette certification, Visual Studio dispose dun outil, le Store
Test Kit . Il sagit dune srie de tests, certains automatiss, certains manuels qui
permettent de vrifier certains points en amont de la certification, ce qui va nous
permettre de gagner du temps et dviter des allers-retours avec lquipe de Microsoft.
Ce kit est disponible via le menu Projet > Ouvrir Store Test Kit (vous devez avoir
slectionn le projet de dmarrage en cliquant dessus), ou bien en faisant un clic droit
sur le Projet > Ouvrir Store Test Kit illustr dans la figure 40.14.

Figure 40.14 Ouvrir le Store Test Kit


Visual Studio nous ouvre un cran contenant plusieurs options, y compris celle de faire
des tests de lapplication pour savoir si elle respecte les besoins pour la certification
606

CERTIFIER SON APPLICATION AVEC LE STORE TEST KIT


Windows Phone Store. Il y a des tests automatiss qui vont vrifier si les images sont
bonnes, si lapplication nest pas trop grosse, etc. Il y a des tests de performance
permettant de voir si lapplication nutilise pas trop de mmoire, si elle dmarre assez
vite, etc. Enfin, il y a des tests manuels effectuer (voir liste dans la figure 40.15).

Figure 40.15 Liste des tests effectuer


Dans le premier onglet de dtails de lapplication, nous devrons prciser les images dont
nous allons avoir besoin lors de la soumission de lapplication et qui permettent dillustrer notre application. Pour raliser de telles images, vous pouvez utiliser lmulateur
ainsi que loutil supplmentaire permettant de raliser des copies dcran de lmulateur. Pour faire des bonnes copies dcran, il faut que les compteurs qui apparaissent
sur la droite de lmulateur ne soient pas visibles. Pour cela, allez modifier la ligne
suivante dans le App.xaml.cs :
1

Application . Current . Host . Settings . Ena ble Fr ame Ra teC ou nte r = true
;

Changez la valeur de la proprit de true false :


1

Application . Current . Host . Settings . Ena ble Fr ame Ra teC ou nte r =


false ;

Vous obtiendrez le rsultat prsent dans la figure 40.16.


Pour avoir des images de bonne qualit, le zoom de lmulateur doit tre en zoom 100%.
Pour lancer les tests automatiss, le fichier .xap doit tre compil en configuration
release. Pour cela, il faut modifier la liste droulante ct du choix de lmulateur
pour passer de debug release (voir figure 40.17).
Remarque : partir de ce moment-l, le fichier .xap sera dploy dans le
rpertoire nomProjet\Bin\Release
607

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.16 Outil de capture dcran

Figure 40.17 Passer en mode releaser

608

CERTIFIER SON APPLICATION AVEC LE STORE TEST KIT


Une fois que le fichier .xap est compil, nous sommes autoriss lancer les tests automatiss, ce qui nous permet de constater leurs succs ou leurs checs affichs dans la
figure 40.18.

Figure 40.18 Succs ou checs des tests automatiss


Par exemple, dans la fentre Succs ou checs des tests automatiss , mes deux
derniers tests ont chou car je navais pas fourni les images que je dois envoyer au
Windows Phone Store lors de la soumission. Je dois donc faire mes captures dcrans
dans les bonnes rsolutions, puis crer une image de lapplication en 300x300 et les
positionner dans longlet Dtails de lapplication visible sur la figure 40.19.
Lorsque ceci sera rsolu, nous pourrons passer aux tests manuels. Pour ceux-ci, nous
devons dmarrer lapplication et lutiliser sur notre tlphone (et non dans lmulateur).
Il y a chaque fois une description de ce quil faut faire (non traduite). Il sagit en fait
dune liste dlments vrifier soi-mme pour viter que lapplication ne soit refuse
lors de la soumission au Windows Phone Store.
Il ne faut pas oublier de tester lapplication en envisageant toutes les circonstances
possibles :

Le tlphone peut se verrouiller aprs une priode dinactivit.


On reoit un coup de tlphone pendant lutilisation de lapplication.
On passe dans un tunnel et le rseau nest plus accessible.
Etc.

Vous avez galement votre disposition un outil danalyse de performance qui se trouve
en bas de longlet des tests automatiss. Vous pouvez dmarrer cette analyse automatique sur votre tlphone, elle gnre un rapport avec des informations intressantes
comme par exemple le temps de dmarrage de lapplication, si lapplication rpond
rapidement, etc. Cela peut vous donner des pistes damliorations de votre application
(voir le rapport de performance prsent dans la figure 40.20).
Le Store Test Kit constitue donc une espce de grande check-list qui va nous permettre
de vrifier que notre application fonctionne dans toutes les situations dcrites. Cela
va nous faire gagner du temps, car si nous dtectons une anomalie, il sera possible de
la corriger rapidement. Dans le cas contraire, cest Microsoft qui la dtectera et qui
bloquera la publication de notre application tant que le problme ne sera pas corrig.
noter que nous serons avertis du rapport de certification par e-mail.
609

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.19 Les images de lapplication sont dfinies dans le Store Test Kit

610

CERTIFIER SON APPLICATION AVEC LE STORE TEST KIT

Figure 40.20 Rapport de performance

611

CHAPITRE 40. PUBLIER SON APPLICATION

Publier son application sur le Windows Phone Store


a y est, vous avez russi tous les tests ! Il est enfin temps de passer la soumission de
notre application. Cest la dernire tape de ce processus. Cela consiste envoyer notre
application, la dcrire, indiquer son prix, etc. Bref, fournir toutes les informations
ncessaires. Mais avant a, il y a une dernire petite chose faire. Il sagit de prciser la
langue principale de notre application. Cela se fait dans les proprits du projet. Cliquez
sur informations de lassembly dans longlet Application (voir figure 40.21).

Figure 40.21 Accder aux informations de lassembly


Choisissez la langue (voir figure 40.22).

Figure 40.22 Dfinir la langue par dfaut de lapplication


Recompilez en mode Release et cest fini pour le code !
Revenons la soumission de notre application et rendons-nous sur http://create.
msdn.com/. Connectons-nous avec notre compte Windows Live en cliquant sur Sign
in . Puis cliquons sur SUBMIT APP (voir figure 40.23).
Si vous ntes pas connect, il faudra le faire. La premire chose faire est de donner
quelques informations sur notre application. Cliquez sur licne rouge contenant le
chiffre 1 (voir figure 40.24).
612

PUBLIER SON APPLICATION SUR LE WINDOWS PHONE STORE

Figure 40.23 Dmarrer la soumission de lapplication

613

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.24 Saisir les informations de lapplication

614

PUBLIER SON APPLICATION SUR LE WINDOWS PHONE STORE


Nous pouvons saisir un nom, une catgorie et un prix pour notre application dans la
page App info prsente dans la figure 40.25.

Figure 40.25 Saisie du nom, de la catgorie et du prix de lapplication


La catgorie doit tre la plus proche possible du type de notre application. Suivant les
cas, il est galement possible dindiquer une sous-catgorie. Cela va permettre didentifier plus facilement le type de votre application dans le Windows Phone Store. Il est
galement important de fixer le prix de notre application. Elle peut tre gratuite ou
allant de 0.99 ou 424,99. Sur cet cran, nous pourrons galement indiquer si lapplication possde un mode dvaluation. Nous en avons parl un peu plus haut, ce mode
permet de tester une application payante en limitant par exemple ses fonctionnalits
ou sa dure.
Nous avons galement la possibilit dindiquer dans quels pays notre application sera
tlchargeable. En gnral, on laissera lexception portant sur les pays qui ont des
politiques de rgles plus strictes pour viter tout problme. noter quil est galement
possible de faire en sorte que son application soit disponible en version bta restreinte
certains utilisateurs ce qui est pratique si lon souhaite la faire valider par des
personnes de son entreprise par exemple.
615

CHAPITRE 40. PUBLIER SON APPLICATION


Une fois ces informations saisies, nous pouvons passer ltape suivante en cliquant
sur licne rouge contenant le chiffre 2 o nous pourrons envoyer notre fichier .xap (voir
figure 40.26).

Figure 40.26 Passer lenvoi du fichier .xap


Pour rappel, ce fichier .xap doit tre compil en mode release. Cliquez sur Parcourir
(voir figure 40.27) pour envoyer le fichier .xap.
Une fois le fichier envoy, nous pouvons voir des informations extraites de notre fichier
et notamment les capacits. Rappelez-vous, ce sont les lments Windows Phone que
va utiliser notre application et dont lutilisateur pourra tre potentiellement averti. Par
exemple, si vous utilisez le GPS dans votre application, la capacit ID_CAP_LOCATION
sera dtecte et un message davertissement sera affich lutilisateur lorsquil voudra
tlcharger votre application lui indiquant quelle utilise le GPS. Libre lui daccepter
ou de refuser linstallation en sachant cela.
Cest le moment galement de saisir une description pour votre application, description qui devra tre dans la langue utilise par lapplication. Cest cette description
(voir figure 40.28) que lutilisateur retrouvera lorsquil affichera votre application sur
le Windows Phone Store.
Vous aurez intrt bien soigner cette description pour deux raisons. Premirement,
parce que cest la premire chose que les utilisateurs liront ce qui pourra leur donner
envie ou non de tlcharger lapplication. Dcrivez les fonctionnalits de lapplication,
616

PUBLIER SON APPLICATION SUR LE WINDOWS PHONE STORE

Figure 40.27 Envoi du fichier xap

617

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.28 Description de lapplication, y compris la liste de ses capacits

618

PUBLIER SON APPLICATION SUR LE WINDOWS PHONE STORE


mais pas trop non plus. Il faut quils puissent obtenir une information synthtique et
utile pour faciliter leur choix de tlcharger ou non votre application. Et deuximement,
loutil de recherche du Windows Phone Store va se fonder sur ce descriptif. Il faut donc
que les mots-cls que les utilisateurs sont susceptibles dutiliser pour chercher une
application comme la vtre soient prsents. Pensez aux mots-cls que vous utiliseriez
si vous cherchiez une telle application. . .
Enfin, vous devrez fournir les images qui illustrent votre application dans linterface
prsente dans la figure 40.29, comme celles que vous avez donnes dans le Store Test
Kit. Soignez-les, elles doivent donner envie lutilisateur de tlcharger votre application et elles doivent galement tre reprsentatives de votre application.

Figure 40.29 Tlchargez les images de votre application


Une fois cette tape termine, il vous reste deux tapes facultatives (voir figure 40.30).
La premire permet de crer ou dutiliser un compte de publicit. Cela va vous permettre dafficher des bandeaux publicitaires sur votre application et potentiellement de
gnrer des petits revenus (voir figure 40.31).
Lautre tape facultative permet dindiquer plus finement les pays o votre application
619

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.30 Les tapes facultatives

Figure 40.31 Souscrire un compte pour afficher de la publicit


620

PUBLIER SON APPLICATION SUR LE WINDOWS PHONE STORE


sera accessible et dfinir potentiellement un prix diffrent pour chaque pays (voir figure 40.32). Si lapplication nest quen franais par exemple, cela a peu de sens de la
distribuer aux Chinois !

Figure 40.32 Dfinition des prix et des marchs atteindre


Et voil, cest termin. Il ne vous reste plus qu cliquer sur Submit en bas de
la page et votre application est soumise au Windows Phone Store. Il faut compter
environ une petite semaine avant davoir des retours de Microsoft par e-mail, vous
informant de la soumission de lapplication ou des ventuelles erreurs. tout moment,
vous pouvez suivre le processus de soumission de votre application en allant sur votre
compte Windows Phone Store et en allant voir dans votre tableau de bord (dashboard ),
comme le montrent les figures 40.33 et 40.34.

Figure 40.33 Soumission en cours dans le tableau de bord


Voil, ce cours est maintenant termin. Vous faites maintenant partie de ceux qui savent
dvelopper des applications pour Windows Phone ! Bravo.
621

CHAPITRE 40. PUBLIER SON APPLICATION

Figure 40.34 Application certifie


Vous commencez matriser correctement le XAML que nous avons appris tout au
long de ce cours. De mme, les contrles Windows Phone et lexploitation des donnes
asynchrones nont plus de secrets pour vous. Que dire des diffrents capteurs du tlphone . . . Le GPS sur le bout des doigts, lacclromtre haut la main ! Et jen passe
. . . En plus, vous connaissez les subtilits des tuiles et des notifications, ce qui nest
pas peu dire.
Cest vrai, nous navons pas pu tout voir car cela demanderait le double de pages
et risquerait de fortement vous lasser, mais vous avez dj beaucoup dlments en
main vous permettant de vous rgaler et commencer crer des applications dignes
de votre imagination. Allez, avouez que vous tes maintenant compltement fan de
cette plateforme tellement la cration dapplications y est un plaisir. Personnellement,
je le suis et jespre avoir pu vous transmettre un peu de cette connaissance vous
dgrossissant le travail.
Car non, ce nest pas fini ! Hier Windows Phone 7, aujourdhui Windows Phone 8, mais
tout laisse penser que Microsoft ne sarrtera pas l et continuera nous offrir plein de
bonnes choses dans son systme dexploitation, que nos smartphones se feront un plaisir
dexploiter travers vos futures applications. Vous avez de bonnes bases maintenant,
vous tes prt pour crer des applications, mais restez lcoute des futures nouveauts.
Vos applications et vos utilisateurs vous en remercieront.

622

Vous aimerez peut-être aussi