Académique Documents
Professionnel Documents
Culture Documents
des applications
Internet
avec
Silverlight 5
Patrice REY
Toutes les marques citées ont été déposées par leurs propriétaires respectifs.
La loi du 11 Mars 1957 n’autorisant aux termes des alinéas 2 et 3 de l’article 41,
d’une part, que les «copies ou reproductions strictement réservées à l’usage privé
du copiste et non destinées à une utilisation collective», et, d’autre part, que les
analyses et les courtes citations dans un but d’exemple et d’illustration, «toute
représentation ou reproduction intégrale, ou partielle, faite sans le consentement
de l’auteur ou de ses ayants droit ou ayant cause, est illicite» (alinéa 1er de l’article
40).
Cette représentation ou reproduction, par quelque procédé que ce soit,
constituerait donc une contre-façon sanctionnée par les articles 425 et suivants du
Code Pénal.
Éditeur
Books on Demand GmbH,
12/14 rond point des Champs Élysées,
75008 Paris, France
Impression :
Books on Demand GmbH, Norderstedt, Allemagne
ISBN : 978-2-8106-2222-1
Dépôt légal : Mars 2012
Auteur
Patrice REY
85 rue de vincennes
App 23 B
33000 BORDEAUX
e-mail : reypatrice@orange.fr
Table des matières
Avant-propos ............................................................................................ 13
Chapitre 1 - Démarrez un projet Silverlight 5
1. L’environnement de développement Visual Studio 2010 .............. 18
2. La création d’un projet .................................................................. 20
2.1 - Un projet Silverlight seul ............................................................ 20
2.2 - Ma première page Silverlight .................................................. 23
2.3 - Un projet hébergé dans un site ASP.NET ............................... 26
3. Compilation et déploiement .......................................................... 28
4. Configuration de la page HTML test ................................................ 31
Chapitre 2 - L’utilisation de XAML
1. Les espaces de noms ..................................................................... 34
2. Le code-behind ................................................................................ 36
3. Les propriétés .................................................................................... 37
3.1 - Les propriétés simples .............................................................. 38
3.2 - Les propriétés complexes ........................................................ 39
3.3 - Les propriétés attachées ......................................................... 40
3.4 - Les propriétés implicites ........................................................... 41
3.5 - Les caractères spéciaux .......................................................... 42
4. Les événements ................................................................................. 42
5. Les ressources .................................................................................... 43
6. Les dictionnaires de ressources ........................................................ 45
6.1 - Création d’un dictionnaire ......................................................... 46
6.2 - Fusion des dictionnaires ............................................................. 47
7. Le databinding .................................................................................. 48
6 Table des matières
Chapitre 7 - La navigation
1. Le chargement des contrôles ......................................................... 192
1.1 - Les contrôles embarqués ......................................................... 192
1.2 - Naviguer d’une page à l’autre .............................................. 196
2. La boite de dialogue personnalisée ............................................ 201
3. Le contrôle Frame ............................................................................ 205
3.1 - Utilisation d’un Frame ............................................................... 205
3.2 - Le mapping d’URI .................................................................... 207
4. Authentification avec INavigationContentLoader ....................... 209
Chapitre 8 - Le graphisme
1. Les formes géométriques basiques ............................................... 216
1.1 - La classe Shape .......................................................................... 216
1.2 - La classe Rectangle ................................................................... 217
1.3 - La classe Ellipse ........................................................................... 218
1.4 - La classe Line .............................................................................. 219
1.5 - La classe Polyline ....................................................................... 221
1.6 - La classe Polygon ...................................................................... 222
2. Les tracés et la géométrie ............................................................... 224
2.1 - La ligne droite ........................................................................... 228
2.2 - La ligne courbe ........................................................................ 228
2.3 - Les courbes de Bézier ............................................................. 230
2.4 - Mini-langage de tracé ............................................................ 232
2.5 - La découpe (le clipping) ....................................................... 233
3. Les transformations ........................................................................... 235
3.1 - Le déplacement (TranslateTransform) .................................. 236
3.2 - La mise à l’échelle (ScaleTransform) .................................... 237
3.3 - La rotation (RotateTransform) ................................................ 238
3.4 - L’inclinaison (SkewTransform) ................................................. 240
3.5 - Regroupement de transformations ....................................... 241
3.6 - La classe CompositeTransform .............................................. 243
3.7 - La transformation matricielle ................................................. 243
Table des matières 9
4. Les projections en perspective ......................................................... 245
Quoi de plus passionnant que de développer ses propres applications Internet qui,
de nos jours, ne cessent de se développer sur de nombreuses plateformes.
Microsoft a mis en ligne la version n°5 de Silverlight, une machine à gagner qui
permet de développer des applications web riches. Silverlight 5, dont le modèle
de programmation est basé sur .NET, se distingue particulièrement par la facilité
avec laquelle il permet de mélanger et d’utiliser de nombreux formats et médias
existants : texte, données XML, vidéo, audio, animations, etc.
De plus, Silverlight a été pensé pour une prise en main rapide grâce à deux familles
d’outils (Visual Studio et Suite Expression) et pour une ubiquité de déploiement,
permettant aux applications Silverlight de s’exécuter sur Windows et sur Mac OS X
dans les navigateurs web Internet Explorer, Safari et Firefox.
Le framework .NET est un code pré-écrit par Microsoft et distribué gratuitement
sous forme d’un runtime qui doit être en exécution sur la machine hôte (Flash, de
chez Adobe, procède de la même façon).
La version 5 apporte de grandes nouveautés, avec en particulier la véritable
programmation 3D par l’utilisation d’une implémentation basée sur le framework
XNA.
Pour réaliser plus facilement des programmes écrits en langage C#, il est préférable
d’utiliser un environnement de développement intégré ou IDE (Integrated
Development Environment). Cet IDE permet de développer, compiler et exécuter
un programme dans un langage donné qui sera, dans ce livre, le C# (se prononce
C Sharp). Microsoft propose comme IDE soit Visual Studio 2010 (dans sa version
payante avec 30 jours d’essai gratuit), soit Visual C# 2010 Express (dans sa version
allégée mais gratuite).
Puissante interface IDE garantissant la production d’un code de qualité, Visual
Studio 2010 intègre de nouvelles fonctionnalités qui simplifient le processus de
développement d’application, de la conception au déploiement.
14 Avant-propos
Pour télécharger et installer l’environnement de développement intégré, ouvrez
votre navigateur et allez sur les pages web suivantes au choix:
• pour télécharger Visual C# 2010 Express: http://msdn.microsoft.com/fr-fr/
express/aa975050.aspx
• pour télécharger une version d’évaluation Visual Studio 2010 Ultimate avec
une période d’essai gratuite de 30 jours: http://www.microsoft.com/france/
visualstudio/download
Pour développer des applications Silverlight 5, il faut installer plusieurs
composants fournis gratuitement par Microsoft. Vous devez aller sur la page des
téléchargements de Microsoft à l’adresse suivante http://www.silverlight.net/
downloads pour récupérer les composants (figure A1).
FIGURE A1
La liste des composants à télécharger et à installer est la suivante, dans cet ordre:
• cliquez sur le lien Service Pack1 pour Visual Studio 2010: composant à installer si
votre Visual Studio 2010 n’est pas en SP1.
• cliquez sur le lien Silverlight 5 Tools for Visual Studio 2010 SP1: composant à
installer pour l’utilisation de Silverlight 5 dans Visual Studio.
• cliquez sur le lien Silverlight 5 Toolkit: composant à installer pour l’utilisation des
contrôles Silverlight 5 supplémentaires dans Visual Studio.
• cliquez sur le lien Silverlight 5 Developer Runtime for Windows (32 bit) ou bien
Silverlight 5 Developer Runtime for Windows (64 bit) selon votre plateforme:
composant à installer pour la visualisation des applications Silverlight 5 dans le
navigateur.
Avant-propos 15
Le contenu du livre
Tout le code source de cet ouvrage pourra être téléchargé gratuitement à l’adresse
web suivante:
http://www.reypatrice.fr
Démarrez un projet
développement Visual
Studio 2010
• Création d’un projet
Silverlight 5 • Compilation et
déploiement d’une
application
• Configuration de la
Ce livre porte sur la version 5 de Silverlight, qui est page HTML test
Vous pouvez créer deux types de projet pour afficher un contenu Silverlight avec
Visual Studio 2010 (ou avec Expression Blend):
• un site web ordinaire contenant des pages HTML: dans ce cas, le point d’entrée
de votre application Silverlight est un fichier HTML classique qui contient une
région pour l’hébergement du contenu Silverlight.
• un site web ASP.NET: dans ce cas, Visual Studio génère deux projets; le premier
projet contient les fichiers concernant l’application Silverlight; le deuxième
projet contient les fichiers nécessaires à l’émulation d’un site web ASP.NET et
déploie vos fichiers Silverlight; le point d’entrée de votre application Silverlight
peut être soit une page HTML classique, soit une page ASP.NET incluant du
contenu généré par le serveur.
Votre application Silverlight fonctionnera de la même façon dans les deux cas, le
navigateur recevra un document HTML dans lequel il y aura une région incluant un
contenu Silverlight. Cependant, si votre application doit télécharger des ressources
sous forme de fichiers par exemple ou bien récupérer des données précises dans
une base de données sur un serveur, la solution sera d’être dans une configuration
de serveur. Généralement on choisira la deuxième option comme étant la meilleure
approche pour un environnement de production.
2
3
4
5
FIGURE 1.3
Visual Studio génère alors la solution de projet avec les fichiers et dossiers requis.
L’explorateur de solutions visualise de façon arborescente cet ensemble (figure
1.4).
22 Développez des applications Internet avec Silverlight 5
En y regardant de plus près, on trouve:
• App.xaml et App.xaml.cs: ce sont les fichiers de configuration de l’application
Silverlight; ils permettent de définir des ressources utilisables par toutes les
pages de l’application; ils permettent de programmer les événements de
lancement, d’arrêt et d’erreur, de l’application; ils permettent enfin de définir
la page MainPage.xaml comme page de démarrage.
• MainPage.xaml : ce fichier définit l’interface utilisateur (les contrôles, les
images et les textes) de votre première page; MainPage est une classe qui
dérive de UserControl et qui est personnalisée en fonction de ce que vous
souhaitez créer.
• MainPage.xaml.cs: ce fichier contient le code de programmation C# pour
implémenter les événements en fonction des actions de l’utilisateur.
FIGURE 1.4
de référence intitulé Debug dans le dossier Bin (figure 1.5). Pour afficher tous les
dossiers et fichiers contenus dans le répertoire de la solution, il suffit de cliquer sur
l’icône afficher tous les fichiers dans l’explorateur de solutions (figure 1.5). On voit
qu’une page HTML, intitulée SilverlightApplication6TestPage.html, a été générée.
Elle contient dans la balise <object> les paramètres de configuration pour afficher
l’application Silverlight.
CHAPITRE 1 □ Démarrez un projet Silverlight 5 23
FIGURE 1.5
Pour personnaliser notre première page Silverlight, on double clique sur le fichier
MainPage.xaml et dans l’éditeur XAML, on ajoute les balises requises. La figure 1.6
montre les boutons de l’éditeur que vous pouvez utiliser pour plus de pratique et
de clarté lors de la programmation:
• la possibilité de régler le zoom de la vue graphique par l’intermédiaire de la
glissière de grossissement (repère n°1).
• un double clic sur l’onglet Design ou sur l’onglet XAML permet de passer l’onglet
correspondant en une vue unique (repère n°2).
• un clic sur les doubles flèches verticales pour intervertir les 2 vues (repère n°3).
• un clic sur les repères de positionnement des conteneurs permet de basculer
en mode horizontal ou en mode vertical (repère n°4).
• un clic sur le repère de visibilité permet de rendre visible ou non l’onglet du
dessous (repère n°5).
Depuis le boite à outils, par un glisser déplacer, on ajoute un contrôle de type
TextBlock et un bouton de type Button. Après les avoir positionnés sur la grille
intitulée LayoutRoot (propriété Name), on renomme le nom du texte par x_text
et le nom du bouton par x_btn. On change le texte qui s’affiche sur le bouton
par la chaîne cliquez moi (propriété Content du bouton). Comme on veut pouvoir
cliquer sur le bouton et provoquer le changement du texte x_text, on ajoute un
gestionnaire d’événement pour l’événement Click.
24 Développez des applications Internet avec Silverlight 5
FIGURE 1.6
1
3
4 5
2 2
Nous souhaitons que le clic sur le bouton remplace le texte de x_text par un autre
texte. Pour cela on ajoute un code qui modifie la propriété Text de x_text, de type
TextBlock.
La modification de la propriété Text du TextBlock x_text se fait de la manière
suivante:
CHAPITRE 1 □ Démarrez un projet Silverlight 5 25
//evenement clic sur bouton
private void x_btn_Click(object sender, RoutedEventArgs e) {
x_text.Text = «vous avez cliqué sur le bouton»;
}
FIGURE 1.7
1 2
Sur la figure 1.8, on visualise le résultat obtenu dans Internet Explorer (repère
n°1) et dans Firefox (repère n°2). En cliquant sur le bouton, le champ x_text est
remplacé par un autre texte (repère n°3).
Il faut remarquer que l’appel de la page test se fait directement par l’appel d’un
fichier (SilverlightApplication6TestPage.html) dans le système de fichiers de
l’ordinateur.
26 Développez des applications Internet avec Silverlight 5
FIGURE 1.8
FIGURE 1.10
2
28 Développez des applications Internet avec Silverlight 5
En testant le projet (figure 1.11), on voit bien que Visual Studio procède à
l’émulation d’un serveur web, en appelant une page de type aspx (repère n°1) ou
une page de type html (repère n°2), en fonction du choix de la page de démarrage
par défaut choisie.
FIGURE 1.11
3 - Compilation et déploiement
Quand vous compilez le projet, Visual Studio utilise le même compilateur csc.exe
que celui des applications .NET écrites en C#. Si on regarde, après la compilation,
à l’intérieur du sous-dossier Debug du dossier Bin (figure 1.12), on s’aperçoit qu’il
y a un ensemble de fichiers générés:
• le fichier SilverlightApplication7.pdb qui est un fichier qui contient des
informations nécessaires à Visual Studio pour le débogage.
• le fichier AppManifest.xaml qui est un fichier qui liste les assemblys nécessaires
au déploiement.
• le fichier SilverlightApplication7TestPage.html qui est le fichier de point
d’entrée, appelé par le navigateur client.
• le fichier SilverlightApplication7.xap qui est une archive contenant tout ce qui
Copyright 2012 Patrice REY
FIGURE 1.13
30 Développez des applications Internet avec Silverlight 5
Pour comprendre le modèle de déploiement de Silverlight (figure 1.14), après
avoir localisé le fichier au format xap, on en fait une copie et on le renomme au
format zip (format des archives compressées). On décompresse l’archive et on
s’aperçoit qu’elle contient, entre autre, le fichier AppManifest.xaml et le fichier
SilverlightApplication7.dll. Si l’application avait besoin d’autres assemblys pour
son fonctionnement, on y trouverait les fichiers complémentaires au format dll
(comme par exemple System.Windows.Controls.dll).
Le fait d’utiliser un format compressé (format xap) est très pratique. Le temps
de téléchargement se voit ainsi réduit (transfert du fichier xap). Quand le fichier
xap est complètement téléchargé sur le poste client, il est alors décompressé. A
remarquer que le fait d’avoir un simple fichier xap nécessaire au déploiement,
confère une simplicité lors du déploiement sur le serveur.
FIGURE 1.14
ensemble d’assemblys
déjà présentes dans le
runtime Silverlight
Quand vous créez un nouveau projet Silverlight (ici le projet UtiliserXaml.sln dans
le dossier chapitre02 du code source téléchargeable gratuitement sur http://www.
reypatrice.fr), vous remarquez que le nœud racine est <UserControl>. Après la
fermeture de cette balise (</UserControl>), aucun contenu ne doit être écrit.
CHAPITRE 2 □ L’utilisation de XAML 35
Cette page, représentée par l’instanciation d’un objet UserControl, contient une
grille, de type Grid, qui est indiquée par la balise <Grid>. Cette balise représente
un nœud enfant pour le nœud parent <UserControl>. Chaque page, de type
UserControl, ne peut contenir qu’un seul conteneur. Par conséquent, la balise
parent (<UserControl>) n’aura qu’une seule balise enfant (<Grid> ici, mais cela
pourrait être un autre conteneur comme <Canvas> par exemple).
Le parseur de Silverlight, en lisant ce code XAML, doit pouvoir le reconnaitre de
façon à instancier les objets décrits, avec une démarche propre à chaque objet
et une façon de faire en fonction de l’objet à instancier. Pour cela, il faut indiquer
des espaces de noms qui contiennent les informations nécessaires au décodage,
sous forme de schémas XML définissant les objets utilisés. Dans les attributs de
<UserControl>, on trouve quatre indications de référence à des espaces de noms,
qui commence par xmlns (pour indiquer xml namespace). On y trouve:
• le premier schéma, défini par l’attribut xmlns sans préfixe, est celui de WPF,
repris pour Silverlight; il autorise les éléments XML dont le nom est celui de
classes Silverlight; il est obligatoire.
• le deuxième schéma, de préfixe x:, est celui du langage XAML; il permet
d’utiliser des instructions spécifiques au sein des éléments Silverlight décrits
en XAML; il est obligatoire.
• le troisième schéma, de préfixe d:, est facultatif; il permet d’utiliser des attributs
spécifiques à l’éditeur XAML en mode de conception visuelle comme la largeur
et la hauteur (d:DesignWidth et d:DesignHeight).
• le quatrième schéma, de préfixe mc:, est facultatif; il est utilisé pour permettre
la compatibilité du code avec des outils ne supportant pas les attributs
spécifiques à l’éditeur XAML.
Ces schémas XML sont virtuels et représentent en fait des espaces de noms .NET.
L’association est obtenue au moyen de la technique dite du mapping XAML (nous
y reviendrons plus en détail dans un autre chapitre).
36 Développez des applications Internet avec Silverlight 5
2 - Le code-behind
l’éditeur ouvre le fichier généré automatiquement par Visual Studio. On voit donc
le détail de cette méthode par programmation.
public partial class MainPage : System.Windows.Controls.UserControl {
internal System.Windows.Controls.Grid LayoutRoot;
private bool _contentLoaded;
/// <summary>
/// InitializeComponent
CHAPITRE 2 □ L’utilisation de XAML 37
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Windows.Application.LoadComponent(this,
new System.Uri(«/UtiliserXaml;component/MainPage.xaml»,
System.UriKind.Relative));
this.LayoutRoot = ((System.Windows.Controls.Grid)
(this.FindName(«LayoutRoot»)));
}
}
FIGURE 2.1
3 - Les propriétés
En XAML, les propriétés des contrôles sont fixées par les attributs dans les balises
correspondantes. La syntaxe utilisée sera fonction de la complexité de la propriété.
Par exemple une propriété concernant une couleur pourra être traduite en
indiquant le nom de la couleur. Mais si l’on désire affecter un objet représentant un
dégradé de couleurs, l’objet étant complexe à réaliser, cela nécessitera une syntaxe
plus poussée pour son expression en XAML.
Le projet UtiliserXaml.sln (figure 2.2), dans le dossier chapitre02, va nous permettre
de voir les syntaxes utilisées pour exprimer les propriétés.
Globalement, le fichier MainPage.xaml se compose d’un conteneur intitulé x_
grid_root, de type Grid, divisé en 3 lignes et 1 colonne. Sur la première ligne, on
positionne un contrôle TextBox, intitulé x_text_mot, qui affiche le verbe apprendre.
Sur la deuxième ligne, on positionne un bouton intitulé x_btn_trouver, de type
Button. Sur la troisième ligne, on positionne un TextBox, intitulé x_text_def, qui
38 Développez des applications Internet avec Silverlight 5
FIGURE 2.2
</UserControl>
Pour le contrôle TextBox x_text_mot, il faut lui fixer des propriétés pour sa taille
(Width et Height), sa police d’écriture (FontFamily) et sa taille d’écriture (FontSize).
Une taille s’exprime en pixels comme Width= «376». Quand le parseur va lire cette
CHAPITRE 2 □ L’utilisation de XAML 39
largeur, il sait qu’il doit lire un nombre pour représenter une largeur en pixels (376
pixels ici). Il traduit donc une chaîne représentant un nombre en une valeur (de
type double ici). La propriété Text représente le contenu du TextBox, donc la valeur
attendue est une chaîne de caractères à traduire. La police d’écriture (FontFamily)
est une chaîne nommant une police qui sera convertie pour écrire du texte avec des
caractères de cette police. Toutes ces propriétés sont dites propriétés simples car
elles utilisent la syntaxe dite attribut. Autrement dit, on peut fixer ces propriétés
par une simple chaîne (un nom, un nombre) en écrivant: propriété = «une_chaine».
<TextBox Height= «23» HorizontalAlignment= «Center» Name= «x_text_mot»
VerticalAlignment= «Center» Width= «376» Text= «apprendre» FontSize= «12»
FontFamily= «Verdana»
... />
x_grid_root.Background = brush;
4 - Les événements
Pour l’instant nous avons vu que les attributs pouvaient fixer les propriétés des
contrôles. Il en est de même pour attacher les gestionnaires d’événements des
contrôles. On utilise la syntaxe suivante:
nom_evenement = «nom_de_la_methode_pour_la_prise_en_charge»
Comme on l’a vu au chapitre 1, on peut laisser Visual Studio générer la méthode
pour la prise en charge d’un événement, comme ici en attachant le gestionnaire
d’événement Click au bouton:
<Button Content= «donne la définition» Grid.Row= «1» Height= «23»
HorizontalAlignment= «Center» Name= «x_btn_trouver» VerticalAlignment=
«Center» Width= «234»
Click= «x_btn_trouver_Click» />
Et dans le code-behind MainPage.xaml.cs, on implémente le gestionnaire de
l’événement Click sur le bouton en changeant la propriété Text du x_text_def.
public partial class MainPage : UserControl {
public MainPage() {
InitializeComponent();
}
//evenement clic sur bouton
Copyright 2012 Patrice REY
5 - Les ressources
1
Copyright 2012 Patrice REY
2
3
CHAPITRE 2 □ L’utilisation de XAML 47
Dans ce dictionnaire, on ajoute une ressource intitulée r_dico_degrade_gris qui
correspond à un dégradé linéaire à base de gris (objet LinearGradientBrush).
<ResourceDictionary
xmlns= «http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x= «http://schemas.microsoft.com/winfx/2006/xaml»>
<LinearGradientBrush x:Key= «r_dico_degrade_gris»
EndPoint= «0.5,1» StartPoint= «0.5,0»>
<LinearGradientBrush.GradientStops>
<GradientStop Color= «DimGray» Offset= «0» />
<GradientStop Color= «White» Offset= «1» />
<GradientStop Color= «DarkGray» Offset= «0.451» />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</ResourceDictionary>
On procède de la même manière pour créer un autre dictionnaire, intitulé
DicoCouleurUnie.xaml dans lequel on va stocker des ressources de couleurs
unies (objet SolidColorBrush). Une couleur grise, avec la propriété Color fixée à
WhiteSmoke, est ajoutée, et elle est nommée par la clé r_dico_gris_uni.
<ResourceDictionary
xmlns= «http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x= «http://schemas.microsoft.com/winfx/2006/xaml»>
<SolidColorBrush x:Key= «r_dico_gris_uni» Color= «WhiteSmoke»>
</SolidColorBrush>
</ResourceDictionary>
FIGURE 2.5
Background=
"{StaticResource r_dico_degrade_gris}"
Background=
"{StaticResource r_dico_gris_uni}"
7 - Le databinding
Copyright 2012 Patrice REY
2 2 3
1 - La classe Panel
2 - Le conteneur Canvas
Un Canvas est l’un des éléments Panel qui activent la disposition. Chaque objet
enfant est restitué dans la zone Canvas. Vous contrôlez le positionnement d’objets
au sein du Canvas en spécifiant des coordonnées x et y. Ces coordonnées sont
exprimées en pixels. L’origine du repère correspond au coin supérieur gauche du
Canvas. Les coordonnées x et y sont souvent spécifiées à l’aide des propriétés
attachées Canvas.Top et Canvas.Left:
• Canvas.Left spécifie la distance entre l’objet et le côté gauche du Canvas
conteneur (coordonnée x).
• Canvas.Top spécifie la distance entre l’objet et le haut du Canvas conteneur
(coordonnée y).
Un élément situé au-delà des limites du Canvas s’affiche quand même. Le Canvas
est un conteneur peu consommateur de ressources, qui n’impacte pas la taille des
éléments contenus.
Vous pouvez imbriquer des objets Canvas. Lorsque vous imbriquez des objets, les
coordonnées utilisées par chacun d’eux sont relatives à leur Canvas conteneur
immédiat. Chaque objet enfant doit être un UIElement. En XAML, vous déclarez
des objets enfants comme des éléments objet qui sont le XML interne d’un élément
objet Canvas. Dans le code, vous pouvez manipuler la collection d’objets enfants
Canvas en obtenant la collection accessible à la propriété Children. Vous pouvez
imbriquer des objets Canvas parce qu’un Canvas est un type de UIElement.
La figure 3.2 montre un Canvas x_cnv_root sur lequel sont positionnés deux
boutons (projet PanneauxCanvas.sln dans le dossier chapitre03).
FIGURE 3.2
3 - Le conteneur StackPanel
StackPanel est l’un des éléments Panel qui activent la disposition. StackPanel
est utile si vous souhaitez réorganiser un jeu d’objets dans une liste verticale ou
horizontale (par exemple, un menu d’éléments horizontal ou vertical). C’est un
conteneur très pratique pour positionner rapidement des éléments. La pile est
verticale par défaut, mais peut être rendue horizontale au moyen de la propriété
Orientation.
Le projet PanneauxStackPanel.sln dans le dossier chapitre03 (figure 3.3), montre
quatre rectangles avec leurs dimensions explicites dans un StackPanel.
FIGURE 3.3
4 - Le conteneur Grid
Un conteneur de type Grid positionne les éléments qu’il contient de façon tabulaire.
La grille de positionnement est définie au moyen d’objets ColumnDefinition
(pour la définition des colonnes) et RowDefinition (pour la définition des lignes),
respectivement regroupés dans les propriétés ColumnDefinitions et RowDefinitions.
Par défaut, une grille est composée d’une cellule.
Chaque élément est positionné dans une cellule au moyen des propriétés
Copyright 2012 Patrice REY
RowSpan de 2
ColumnSpan de 2
Width et
Height
explicites
largeur de largeur de *, 2* et
100 et 50 3*
pixels
5 - Le conteneur DockPanel
Le conteneur DockPanel fait partie du Silverlight ToolKit. Il faut donc ajouter une
référence (figure 3.7) à cet espace de noms en faisant un clic droit sur le dossier
références et en choisissant le sous-menu ajouter une référence (repère n°1). Dans
la fenêtre intitulée ajouter une référence, sélectionnez l’espace System.Windows.
Controls.Toolkit (repère n°2), et appuyer sur OK.
Pour autoriser l’insertion dans un fichier XAML d’un élément contenu dans la
bibliothèque de classes de cet espace de noms, il faut déclarer un espace de noms
XML correspondant à l’espace de noms .NET de l’élément. Cela se fait au moyen
d’un mapping XAML. Dans les attributs de la balise <UserControl>, on ajoute
une référence xmlns intitulée toolkit (xmlns:toolkit) et on choisit la référence
correspondante qui est proprosée par l’intellisense de Visual Studio:
xmlns:toolkit =
«http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit»
Le conteneur DockPanel est désormais accessible en XAML par la balise
<toolkit:DockPanel>.
<UserControl x:Class= «PanneauxDockPanel.MainPage»
xmlns= «http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x= «http://schemas.microsoft.com/winfx/2006/xaml»
xmlns:d= «http://schemas.microsoft.com/expression/blend/2008»
xmlns:mc= «http://schemas.openxmlformats.org/markup-compatibility/2006»
xmlns:toolkit=
«http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit»
mc:Ignorable= «d» d:DesignHeight= «300» d:DesignWidth= «400» >
<toolkit:DockPanel Name= «x_dockpanel_root»/>
</UserControl>
Le DockPanel est un conteneur qui répartit les éléments qu’il contient sur ses
quatre côtés au moyen de la propriété attachée DockPanel.Dock. Celle-ci peut
prendre quatre valeurs: Left (valeur par défaut), Top, Right et Bottom. Sur chaque
côté, les éléments sont empilés en fonction de l’ordre dans lequel ils ont été
ajoutés au panneau.
62 Développez des applications Internet avec Silverlight 5
FIGURE 3.7
Par défaut, le dernier élément ajouté au DockPanel occupe tout l’espace restant.
La propriété LastChildFill peut être basculée à false pour inhiber ce comportement.
Le DockPanel est très souvent employé pour définir l’organisation du contrôle
principal d’une application: barre d’outils en haut, panneau de commande sur le
côté et fonctionnalité principale dans l’espace restant.
La figure 3.8 visualise le projet PanneauxDockPanel.sln dans le dossier chapitre03,
avec le positionnement d’un bouton dans les cinq parties qui composent le
DockPanel x_dockpanel_root.
<toolkit:DockPanel HorizontalAlignment= «Left» Name= «x_dockpanel_root»
VerticalAlignment= «Top» Background= «WhiteSmoke» LastChildFill= «True»
Width= «400» Height= «200»>
<Button Content= «1 - TOP» Name= «x_btn1» toolkit:DockPanel.Dock= «Top» />
Copyright 2012 Patrice REY
LastChildFill = true
LastChildFill = false
6 - Le conteneur WrapPanel
FIGURE 3.10
1 2
La propriété Margin définit les marges autour d’un élément. Si une seule valeur est
assignée à la propriété, une marge de cette dimension est appliquée tout autour
de l’élément. Si deux valeurs sont spécifiées, la première valeur est affectée aux
marges gauche et droite, et la deuxième valeur est affectée aux marges haute et
basse. Si quatre valeurs sont spécifiées, elles sont assignées respectivement aux
marges gauche, haute, droite et basse.
La figure 3.11 illustre les différents cas de positionnement en fonction des marges
sur un Grid composé de quatre cellules (projet ProprieteDisposition.sln dans le
dossier chapitre03).
FIGURE 3.11
7.3 - L’alignement
FIGURE 3.12
7.4 - La profondeur
La profondeur est gérée par la propriété ZIndex. ZIndex est une propriété attachée
de la classe Canvas qui détermine l’ordre de recouvrement de plusieurs éléments
se chevauchant. Plus cette valeur est élevée, et plus l’élément sera mis à l’avant-
plan. Cette propriété attachée a pour valeur zéro par défaut, et le dernier élément
ajouté au conteneur recouvre les autres.
La figure 3.13 illustre différents cas de profondeur (projet ProprieteDisposition.sln
68 Développez des applications Internet avec Silverlight 5
dans le dossier chapitre03): le cas où aucun ZIndex n’est pas spécifié (repère n°1)
et le cas où un bouton (button8) sur les cinq a sa propriété ZIndex spécifiée à 5
(repère n°2).
FIGURE 3.13
1 2
7.5 - La visibilité
La visibilité d’un élément est définie par sa propriété Visibility qui peut prendre
deux valeurs: Visible (valeur par défaut) pour un élément visible, et Collapsed pour
un élément qui n’est pas affiché et que le système de disposition ignore.
CHAPITRE 3 □ Les panneaux et le système de disposition 69
La classe Border est un conteneur visuel qui matérialise une bordure au moyen de
ses propriétés:
• BorderBrush pour la couleur de la bordure,
• BorderThickness pour l’épaisseur de la bordure autour d’un élément enfant.
L’objet Border peut définir aussi une couleur de fond par sa propriété Background.
Les angles peuvent être arrondis au moyen de sa propriété CornerRadius. Il dispose
d’une propriété Padding qui définit les marges autour de l’élément enfant. Il peut
être utilisé pour matérialiser un conteneur non visuel comme un StackPanel.
La classe ScrollViewer permet de faire défiler son élément enfant, tel qu’un
conteneur Panel disposant de davantage d’éléments qu’il ne peut en afficher
simultanément. Des ascenseurs apparaissent automatiquement si besoin est.
Leur affichage peut être contrôlé par les propriétés HorizontalScrollBarVisibility et
VerticalScrollBarVisibility, conditionnant le sens de défilement.
La figure 3.16 (projet Scrollviewer.sln dans le dossier chapitre03) illustre deux
ScrollViewer qui contiennent chacun un Canvas de hauteur différente.
FIGURE 3.16
Propriétés de
propriété de
dépendance
• Implémentation
dépendances et d’une propriété de
dépendance simple
• Implémentation
événements routés d’une propriété de
dépendance attachée
• Les événements
routés
• Le routage de type
La conception d’une interface utilisateur se fait bubbling
• Les événements liés
par l’ajout de contrôles à personnaliser. Le fait au clavier
d’imbriquer des contrôles dans d’autres contrôles • Le survol et le
positionnement de la
permet de réaliser des interfaces utilisateur riches, souris
dotées de fonctionnalités spécifiques. • La gestion du clic de
Concevoir un contrôle necéssite de lui fournir la souris
• La gestion de la
des propriétés de dépendances de façon à le molette de la souris
personnaliser et à lui apporter des fonctionnalités • La capture de la souris
dans un contexte donné. • Le focus
• Les commandes
Nous allons voir dans ce chapitre la création et
l’implémentation des propriétés de dépendances
au travers de la propriété de dépendance simple et
la propriété de dépendance attachée.
Un contrôle est interactif s’il réagit à des
événements spécifiques dans un contexte donné.
Nous verrons le principe des événements routés
qui sont implémentés dans Silverlight.
Nous aborderons la gestion des événements liés
au clavier et à la souris. La souris permet de faire
beaucoup de choses grâce à son déplacement, son
positionnement, ses boutons et sa molette. Nous
verrons l’implémentation du clic souris, la capture
de la souris et la gestion de sa molette.
74 Développez des applications Internet avec Silverlight 5
Nous avons vu dans les précédents chapitres que, lors de l’ajout d’un contrôle donné,
il fallait personnaliser ce contrôle par l’affectation de valeurs à ses propriétés (par
exemple un nombre pour fixer une largeur, une chaîne de caractères pour fixer une
couleur, etc.). Ces propriétés qui personnalisent le contrôle sont des propriétés
de dépendances (dependency property). Il y a principalement deux sortes de
propriétés de dépendances: les propriétés simples et les propriétés attachées.
En XAML, quand on ajoute un objet Rectangle sur un Canvas (figure 4.1), on utilise
la balise <Rectangle> et on personnalise les attributs de cette balise pour indiquer:
• le nom attribué au rectangle par sa propriété Name (Name = «x_rect»),
• la largeur du rectangle par sa propriété Width (Width = «376»),
• la hauteur du rectangle par sa propriété Height (Height = «31»),
• l’épaisseur de sa bordure par la propriété StrokeThickness (StrokeThickness =
«1»),
• la couleur de remplissage du rectangle par sa propriété Fill (Fill = «Gainsboro»),
• son positionnement horizontal, à partir de la gauche, sur le Canvas par la
propriété Canvas.Left (Canvas.Left = «12»),
• son positionnement vertical, à partir du haut, sur le Canvas par la propriété
Canvas.Top (Canvas.Top = «12»),
• etc.
FIGURE 4.1
Copyright 2012 Patrice REY
Rectangle x_rect
Canvas x_cnv_root
CHAPITRE 4 □ Propriétés de dépendances et événements routés 75
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke»>
<Rectangle Height= «31» HorizontalAlignment= «Left» Name= «x_rect»
Stroke= «Black»
StrokeThickness= «1» VerticalAlignment= «Top» Width= «376»
Canvas.Left= «12»
Canvas.Top= «12» Fill= «Gainsboro» />
</Canvas>
La personnalisation du rectangle se fait par l’affectation de valeurs à ses propriétés.
Autrement dit, l’élément Rectangle expose des propriétés qui sont des propriétés
de dépendances. La propriété Width est qualifiée de propriété de dépendance
simple (elle fixe la largeur propre au rectangle), et la propriété Canvas.Left est
qualifiée de propriété de dépendance attachée (elle permet de positionner le
rectangle dans son conteneur parent, puisque le rectangle se trouve ici dans un
conteneur Canvas).
Si on visualise le graphe d’héritage de la classe Rectangle (figure 4.2), on
voit que la classe Rectangle hérite de la classe Shape, puis Shape hérite de
FrameworkElement. La propriété Width du rectangle est en fait une propriété
héritée de FrameworkElement et la propriété StrokeThickness est une propriété
héritée de Shape.
FIGURE 4.2
projet un nouveau dossier intitulé images, puis ajoutez les trois images suivantes au
format PNG: chapitre.png, livre.png et non_defini.png. Pour ajouter des éléments
existants, faire un clic droit sur le nom du dossier, choisir ajouter puis ajouter un
élément existant dans le sous-menu. Dans la fenêtre qui s’ouvre, localisez les trois
images puis cliquez sur le bouton ajouter (les images se trouvent dans le dossier
chapitre04).
CHAPITRE 4 □ Propriétés de dépendances et événements routés 77
FIGURE 4.3
pour ajouter un nouveau dossier: pour ajouter un élément existant:
FIGURE 4.9
84 Développez des applications Internet avec Silverlight 5
2 - La gestion de l’interactivité
un objet visuel. Elle introduit les bases du système de disposition visuelle des
éléments, l’interactivité évoluée avec les périphériques de saisie, et la notion de
focus. La classe FrameworkElement, qui dérive de UIElement, offre les principales
fonctionnalités de haut niveau du framework avec la gestion de la disposition
visuelle, la liaison de données, les styles, etc.
Tous les contrôles héritent de FrameworkElement, et par conséquent de UIElement,
donc ils peuvent implémenter les événements hérités de ces deux classes.
CHAPITRE 4 □ Propriétés de dépendances et événements routés 89
FIGURE 4.12
2 3
s_touche_enfonce += keyString;
}
x_listbox.Items.Insert(0, s_touche_enfonce);
}
A noter que la touche «Entrée» est gérée sur l’événement KeyUp. Dans l’événement
KeyDown, on ajoutera une restriction liée à la touche Key.Enter.
CHAPITRE 4 □ Propriétés de dépendances et événements routés 95
private void x_text_KeyUp(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter) {
x_listbox.Items.Insert(0, «Entree»);
}
}
La figure 4.14 illustre le résultat obtenu lors de l’appui sur des touches: la touche
«p», la combinaison de touches «Maj+T», la touche «:» et la touche «Entrée».
FIGURE 4.14
la touche «Entrée»
enfoncée
la touche «Maj+T» la touche «:» enfoncée
enfoncée
la touche «p»
enfoncée
La classe UIElement expose des événements qui permettent de coder une réponse
au clic, au déplacement et à la rotation de la molette de la souris.
Lorsque la souris entre dans la surface d’un élément, celui-ci émet un événement
MouseEnter. Lors des déplacements pendant le survol, des événements MouseMove
sont émis. Lorsque la souris quitte la surface de l’élément, l’événement MouseLeave
est émis. Tous ces événements propagent un argument de type MouseEventArgs,
qui dispose notamment d’une méthode GetPosition permettant de retrouver les
coordonnées de la souris. Les coordonnées en pixels sont renvoyées dans un objet
Point.
Le projet EvenementSouris.sln, dans le dossier chapitre04, montre une utilisation
de la capture du positionnement de la souris sur un élément.
96 Développez des applications Internet avec Silverlight 5
<Canvas x:Name= «x_cnv_pos» Background= «WhiteSmoke» Width= «550»
Height= «160» Grid.Row= «0» MouseMove= «x_cnv_pos_MouseMove»>
<TextBlock Canvas.Left= «12» Canvas.Top= «12» Height= «23»
Name= «x_text_titre_pos» Text= «un rectangle de 200x100 pixels et un canvas
de 550x160 pixels:» Width= «526»
FontFamily= «Verdana» FontSize= «14» />
<Rectangle Canvas.Left= «12» Canvas.Top= «41» Height= «100»
Name= «x_rect_pos»
Stroke= «Black» StrokeThickness= «1»
Width= «200» Fill= «LightGray» MouseMove= «x_rect_pos_MouseMove»
Cursor= «Hand»/>
<TextBlock Canvas.Left= «227» Canvas.Top= «72» Height= «25»
Name= «x_text_pos_rect» Text= «position sur rectangle (000,000)»
Width= «311»
FontFamily= «Verdana» FontSize= «14» TextAlignment= «Left»
TextWrapping= «Wrap» />
<TextBlock Canvas.Left= «227» Canvas.Top= «103» FontFamily= «Verdana»
FontSize= «14» Height= «25» Name= «x_text_pos_cnv»
Text= «position sur canvas (000,000)» TextAlignment= «Left»
TextWrapping= «Wrap» Width= «311» />
</Canvas>
Un gestionnaire pour l’événement MouseMove est attaché au Canvas x_cnv_pos
et au rectangle x_rect_pos. Le TextBlock x_text_pos_rect affiche les coordonnées
de la souris lorsque celle-ci est positionnée sur l’élément Rectangle, et le TextBlock
x_text_pos_cnv affiche les coordonnées de la souris lorsque celle-ci est positionnée
sur l’élément Canvas. Les coordonnées données sont relatives au point d’origine
de l’élément concerné. La figure 4.15 illustre le résultat obtenu.
FIGURE 4.15
Lorsqu’un bouton de la souris est enfoncé sur un élément, celui-ci émet l’événement
routé MouseLeftButtonDown (bouton gauche) ou MouseRightButtonDown (bouton
droit). Lorsque le bouton est relâché, il émet l’événement routé MouseLeftButtonUp
ou MouseRightButtonUp. Ces événements propagent un argument de type
MouseButtonEventArgs, hérité de MouseEventArgs (figure 4.17).
FIGURE 4.17
98 Développez des applications Internet avec Silverlight 5
Sur le Canvas x_cnv_clic est positionné un rectangle sur lequel on attache un
gestionnaire d’événement MouseLeftButtonDown. Dès que la souris survole le
rectangle, un simple clic ou un double clic sur le rectangle est relevé et est affiché
dans le x_text_clic.
<Canvas Background= «WhiteSmoke» Height= «160» Name= «x_cnv_clic» Width= «550»
Grid.Row= «1»>
<TextBlock Canvas.Left= «12» Canvas.Top= «12» FontFamily= «Verdana»
FontSize= «14» Height= «23» Name= «x_titre_clic»
Text= «un rectangle de 200x100 pixels:» Width= «526» />
<Rectangle Canvas.Left= «12» Canvas.Top= «41» Cursor= «Hand»
Fill= «LightGray» Height= «100»
MouseLeftButtonDown= «x_rect_clic_MouseLeftButtonDown» Name= «x_rect_clic»
Stroke= «Black» StrokeThickness= «1» Width= «200» />
<TextBlock Canvas.Left= «227» Canvas.Top= «72» FontFamily= «Verdana»
FontSize= «14» Height= «25» Name= «x_text_clic»
Text= «clic souris» TextAlignment= «Left» TextWrapping= «Wrap» Width= «311»
/>
</Canvas>
La propriété ClickCount de MouseButtonEventArgs informe s’il y a eu un simple clic
ou bien un double clic. La figure 4.18 illustre le simple et double clic. Généralement
le temps qui s’écoule entre deux clics, pour former un double clic, est inférieur ou
égal à 500 millisecondes.
private void x_rect_clic_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e) {
if (e.ClickCount == 1) {
x_text_clic.Text = «simple clic sur le rectangle»;
} else if (e.ClickCount == 2) {
x_text_clic.Text = «double clic sur le rectangle»;
}
}
FIGURE 4.18
Par défaut, le clic droit affiche le menu contextuel de Silverlight (figure 4.19,
partie gauche). Pour l’inhiber, il faut affecter un gestionnaire à l’événement
MouseRightButtonDown et assigner true à la propriété Handled (figure 4.19, partie
CHAPITRE 4 □ Propriétés de dépendances et événements routés 99
droite).
private void x_rect_clic_MouseRightButtonDown(object sender,
MouseButtonEventArgs e) {
e.Handled = true;
}
FIGURE 4.19
FIGURE 4.21
2
1
3
Copyright 2012 Patrice REY
clic gauche
1 état initial maintenu 2
MouseButtonEventArgs e) {
//le deplacement commence
en_deplacement = true;
Canvas le_canvas = (Canvas)sender;
//on releve la position de la souris par rapport
//au canvas (coin haut gauche 0,0)
qte_deplac_souris = e.GetPosition(le_canvas);
//debut de la capture de la souris
le_canvas.CaptureMouse();
CHAPITRE 4 □ Propriétés de dépendances et événements routés 103
this.Cursor = Cursors.Hand;
}
Le gestionnaire MouseMove permet de récupérer la position de la souris sur le
Canvas x_cnv_root, et de modifier par conséquent les propriété TopProperty et
LeftProperty du dé en fonction de la quantité de déplacement de la souris.
private void x_cnv_des_MouseMove(object sender, MouseEventArgs e) {
if (en_deplacement == true) {
Canvas le_canvas = (Canvas)sender;
//on recupere la position du dé sur le canvas_root
Point point = e.GetPosition(x_cnv_root);
//on bouge le dé
le_canvas.SetValue(Canvas.TopProperty, point.Y - qte_deplac_souris.Y);
le_canvas.SetValue(Canvas.LeftProperty, point.X - qte_deplac_souris.X);
}
}
Le gestionnaire MouseLeftButtonUp signale que le déplacement est terminé. La
méthode ReleaseMouseCapture libère la capture de la souris. La forme du curseur
est remise à sa valeur par défaut par l’affectation de la valeur null à la propriété
Cursor.
private void x_cnv_des_MouseLeftButtonUp(object sender,
MouseButtonEventArgs e) {
if (en_deplacement == true) {
Canvas le_canvas = (Canvas)sender;
//on libere la capture de la souris
le_canvas.ReleaseMouseCapture();
//on signale le deplacement termine
en_deplacement = false;
this.Cursor = null;
}
}
2.8 - Le focus
FIGURE 4.23
Une commande est un objet qui représente une action, telle que Sauvegarder,
Charger ou Imprimer, mais qui expose une interface standard nommée ICommand.
Le mécanisme des commandes renforce le découplage entre le code relatif au
visuel et la logique applicative.
Une commande est un objet qui implémente l’interface ICommand:
• la méthode Execute déclenche l’exécution de la commande.
• la méthode CanExecute indique si cette exécution est possible.
CHAPITRE 4 □ Propriétés de dépendances et événements routés 105
1 2
4
3
Dans Silverlight, une commande peut être déclenchée par le code ou directement
par un élément auquel elle aura été associée au préalable.
Les éléments Button, CheckBox, RadioButton ou Hyperlink disposent d’une
propriété Command et d’une propriété CommandParameter, de type Object,
qui permet de passer des paramètres à la commande. Le déclenchement de la
commande est pris en charge par le code du contrôle (par exemple, la commande
spécifiée dans la propriété Command d’un Button est déclenchée par un clic).
Ici, notre bouton a sa propriété Command fixée à la ressource statique k_commande_
imprimer, ressource qui référence la classe CommandeImpressionTexte qui
implémente l’interface ICommand. La propriété CommandParameter du bouton
106 Développez des applications Internet avec Silverlight 5
référence le texte du contrôle x_textbox par le databinding.
<UserControl x:Class= «Commandes.MainPage»
xmlns= «http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x= «http://schemas.microsoft.com/winfx/2006/xaml»
xmlns:d= «http://schemas.microsoft.com/expression/blend/2008»
xmlns:mc= «http://schemas.openxmlformats.org/markup-compatibility/2006»
xmlns:local= «clr-namespace:Commandes»
mc:Ignorable= «d» d:DesignHeight= «300» d:DesignWidth= «400»>
<UserControl.Resources>
<local:CommandeImpressionTexte
x:Key= «k_commande_imprimer»></local:CommandeImpressionTexte>
</UserControl.Resources>
<Grid x:Name= «LayoutRoot» Margin= «5»>
<Grid.RowDefinitions>
<RowDefinition Height= «Auto»></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Margin= «5» Content= «commande impression»
Command= «{StaticResource k_commande_imprimer}»
CommandParameter= «{Binding ElementName=x_textbox,Path=Text}»
Cursor= «Hand» Name= «x_btn_imprimer»></Button>
<TextBox x:Name= «x_textbox» Grid.Row= «1» Margin= «5»></TextBox>
</Grid>
</UserControl>
La classe CommandeImpressionTexte implémente l’interface ICommand. Comme
le visualise la figure 4.25, l’intellisense de Visual Studio inscrit le code à implémenter
pour cette interface.
FIGURE 4.25
Silverlight • Button,
HyperlinkButton,
ToggleButton,
CheckBox,
RadioButton
• ToolTip
• Popup
• La classe ItemsControl
Les contrôles sont les principaux éléments
• ListBox, ComboBox,
TabControl
d’interaction avec l’utilisateur. Silverlight fournit • TextBox,
les contrôles classiques d’une application web PasswordBox,
AutoCompleteBox
(boutons, zones de saisie, etc.), mais offre de • Paragraph, Span,
puissantes possibilités de composition et de Run, LineBreak,
personnalisation. InlineUIContainer
• RichTextBox,
Nous allons parcourir en détail tous ces contrôles RichTextBlock
de façon à comprendre leur fonctionnement, leur • ProgressBar, Slider,
personnalisation et leurs interactions dans un ScrollBar
• Calendar, DatePicker
contexte donné.
Il y a des contrôles qui gèrent le texte de façon
statique ou de façon dynamique. Certains
permettent d’afficher un contenu enrichi avec des
dispositions sophistiquées et des positionnements
imbriqués.
Il est possible de pouvoir intégrer des images au
sein d’un texte enrichi par sa police d’écriture et
ses ornements. Certains contrôles nous facilitent
la vie pour choisir des dates d’une façon visuelle
et interactive, pour faire défiler un contenu avec
des dispositions particulières, pour effectuer des
réglages à différents niveaux, pour appliquer des
styles en vue d’une optimisation du contenu, etc.
110 Développez des applications Internet avec Silverlight 5
Les contrôles sont les principaux éléments d’interaction avec l’utilisateur. Silverlight
fournit les contrôles classiques d’une application web (boutons, zones de saisie,
etc.), mais offre de puissantes possibilités de composition et de personnalisation.
Nous avons déjà rencontré un certain nombre de contrôles Silverlight comme
les conteneurs (Canvas, Grid, StackPanel, etc.). Quelques contrôles sont plus
spécialisés pour effectuer des tâches particulières comme ceux utilisés pour le
dessin 2D, ceux pour l’agrandissement des images avec un zoom, ceux pour la
lecture du son et de la vidéo. Ces contrôles spécialisés seront vus plus loin dans
des chapitres précis.
Nous allons aborder dans ce chapitre tous les contrôles basiques et fondamentaux
avec par exemple les boutons, les boites pour les textes, les listes et les cases à
cocher, etc.
énumérées Wrap et NoWrap). La hauteur de ligne peut être définie par LineHeight.
La propriété TextDecorations permet de souligner le texte au moyen de la valeur
Underline.
Dans le projet Controles.sln, dans le dossier chapitre05, DemoTexteStatique.xaml
illustre un TextBlock entouré d’une bordure (figure 5.2).
CHAPITRE 5 □ Les contrôles Silverlight 111
FIGURE 5.1
FIGURE 5.2
<TextBlock
FontFamily= «Arial» Width= «376» Text= «texte formaté avec des runs»
Height= «156»
Canvas.Left= «12» Canvas.Top= «79» FontSize= «14» TextWrapping= «Wrap»>
<LineBreak/>
<Run Foreground= «Navy» FontFamily= «Courier New» FontSize= «24»>
Courier New 24</Run>
<LineBreak/>
CHAPITRE 5 □ Les contrôles Silverlight 113
Les objets Silverlight de haut niveau qui manipulent du texte définissent la police
au moyen des propriétés suivantes:
• FontFamily est le nom de la police utilisée indépendamment de son poids,
de son style et de sa densité, afin de pouvoir gérer un nombre élevé de
combinaisons.
• FontSize définit la taille de la police, en pixels (valeur 11 par défaut), pour le
contenu de texte de l’élément.
• FontStyle définit le style dans lequel le texte est rendu (Normal ou Italic).
• FontWeight définit l’épaisseur des caractères et peut prendre une valeur
énumérée telle que Light, Normal, Bold, etc.
• FontStretch fournit un jeu d’étirements de police prédéfinis (une densité des
caractères) comme valeurs de propriété statique (Normal, Expanded, etc.).
• Foreground définit le pinceau utilisé pour dessiner la police, par l’affectation
d’un objet Brush.
Les polices dont le support est garanti par le runtime Silverlight (figure 5.5) sont
Portable User Interface, Arial, Arial Black, Comic Sans MS, Courier New, Georgia,
Lucida Sans Unicode, Times New Roman, Trebuchet MS, Verdana et Webdings. La
police par défaut est Portable User Interface (correspondant à Lucida).
FIGURE 5.5
114 Développez des applications Internet avec Silverlight 5
Des polices de substitution peuvent être définies en constituant une liste de noms
séparés par une virgule. La spécification suivante renvoie la police Comic Sans MS
quand Calibri n’est pas connue du runtime et n’est pas installée sur le poste de
l’utilisateur: FontFamily = «Calibri, Comic Sans MS»
Pour utiliser des polices potentiellement non installées sur le poste de travail, il est
nécessaire d’embarquer comme fichiers de ressource dans un assembly du projet
les fichiers .ttf (figure 5.6) et d’utiliser la syntaxe URI_fichier_police#nom_police
pour son utilisation.
<TextBlock Canvas.Left= «12» Canvas.Top= «425»
FontFamily= «fontes/Bayern.ttf#Bayern» FontSize= «24» Height= «23»
Name= «textBlock12»
Text= «police incorporée: Bayern.ttf» Width= «375» />
FIGURE 5.6
2 - Le contrôle Image
Silverlight intègre le support des images bitmap aux formats JPEG (Joint
Photographics Experts Group) et PNG (Portable Network Graphics). Généralement,
un contrôle Image affiche un fichier image spécifié au moyen d’un URI dans la
propriété Source. La propriété Source se voit affectée par un objet de type
Copyright 2012 Patrice REY
1
3
Le contrôle Image dispose d’une propriété Strech dont la valeur par défaut est
Uniform. Stretch permet de contrôler la façon dont l’image est étirée dans son
conteneur. Ainsi quelle que soit la taille de l’élément, l’image n’est pas déformée.
Les valeurs énumérées de la propriété Stretch sont:
• None: permet d’afficher l’image à sa taille réelle.
• Fill: permet d’étirer l’image pour l’ajuster aux dimensions de son conteneur.
• Uniform: le contenu est redimensionné pour s’ajuster aux dimensions de
116 Développez des applications Internet avec Silverlight 5
destination pendant qu’il conserve ses proportions natives.
• UniformToFill: le contenu est redimensionné pour remplir les dimensions de
destination pendant qu’il conserve ses proportions natives; si les proportions
du rectangle de destination diffèrent de la source, le contenu source est
découpé pour s’ajuster aux dimensions de destination.
La figure 5.8 illustre les différents cas de la propriété Stretch.
FIGURE 5.8
3 - La classe ContentControl
La classe ContentControl est une classe fondamentale. Avec ses classes dérivées,
elles exposent une propriété de contenu Content, de type Object, qui peut contenir
un objet et un seul. Un ContentControl peut ainsi contenir visuellement n’importe
quel objet, FrameworkElement ou contrôle.
ScrollViewer, CheckBox, RadioButton, Button et TabItem sont les classes majeures
qui héritent de ContentControl.
CHAPITRE 5 □ Les contrôles Silverlight 117
FIGURE 5.9
2
1
4 - Les boutons
FIGURE 5.11
3
2
La classe ToggleButton est la classe de base des contrôles qui peuvent changer
d’état comme CheckBox et RadioButton.
La propriété IsChecked spécifie l’état de ToggleButton. La propriété IsThreeState
indique si ToggleButton a deux ou trois états. Si le ToggleButton est configuré pour
avoir trois états, il permet à l’utilisateur de choisir un troisième état, à savoir l’état
indéterminé. Par exemple, vous pouvez utiliser un bouton bascule à trois états
pour indiquer Oui, Non ou Non applicable.
La classe CheckBox représente un contrôle qu’un utilisateur peut sélectionner
(cocher) ou désélectionner (décocher). Le contrôle CheckBox hérite de
ToggleButton et peut avoir trois états : activé, désactivé et indéterminé. Utilisez
le contrôle CheckBox pour fournir une liste d’options qu’un utilisateur peut
sélectionner, telles qu’une liste de paramètres à appliquer à une application.
Le fichier DemoBoutons.xaml (figure 5.13) illustre l’utilisation de cases à cocher à 2
et 3 états (repère n°1). La case à cocher 2 états (repère n°2) peut se trouver dans un
Copyright 2012 Patrice REY
état coché ou décoché. La case à cocher 3 états (repère n°3) peut se trouver dans
un état coché, décoché ou indéterminé. Le gestionnaire Checked traite l’état coché,
le gestionnaire Unchecked traite l’état décoché, et le gestionnaire Indeterminate
traite l’état indéterminé.
<CheckBox x:Name= «cb1» Content= «case à cocher 2 états»
Checked= «TraiterCocher» Unchecked= «TraiterDecocher»
Canvas.Top= «4» Canvas.Left= «6» />
CHAPITRE 5 □ Les contrôles Silverlight 121
FIGURE 5.13
2 1 3
5 - Le contrôle ToolTip
La classe ToolTip (figure 5.15) représente un contrôle qui crée une fenêtre
indépendante dans laquelle s’affichent les informations concernant un élément de
l’interface utilisateur.
Un ToolTip permet de fournir des informations à l’utilisateur. Par exemple, vous
pouvez utiliser un ToolTip pour indiquer le nom d’un Button. Le contenu d’un
contrôle ToolTip peut varier d’une chaîne de caractères simples à un contenu plus
complexe, tel qu’un StackPanel intégrant caractères et images. Comme le contenu
d’un ToolTip ne peut pas obtenir de focus, il est inutile de positionner un bouton
ou un lien dans le contenu de l’info-bulle. Les propriétés de la classe ToolTip
permettent de définir la position et le comportement de l’info-bulle.
La classe statique ToolTipService (figure 5.15) permet de configurer une info-bulle
pour un contrôle existant par la propriété attachée ToolTip.
Le fichier DemoTooltip.xaml (figure 5.16) illustre l’utilisation minimale pour la
réalisation d’une info-bulle pour un bouton.
<Button Canvas.Left= «35» Canvas.Top= «22» Content= «un bouton avec son
tooltip»
Height= «23» Name= «x_btn1» Width= «221»
FontFamily= «Verdana» FontSize= «12» Cursor= «Hand»
ToolTipService.ToolTip= «contenu du tooltip» />
FIGURE 5.16
124 Développez des applications Internet avec Silverlight 5
FIGURE 5.15
FIGURE 5.17
Dans l’exemple de la figure 5.18, quand le pointeur survole le bouton, une info-bulle
se positionne et affiche son contenu, qui est composé d’un StackPanel dans lequel
sont positionnés du texte et une image. Pour pouvoir affecter un objet complexe
à la propriété attachée ToolTip, on utilise la notation pointée <ToolTipService.
ToolTip>.
<Button Canvas.Left= «12» Canvas.Top= «64» Content= «un bouton avec un tooltip
composé» Height= «23» Name= «button1» Width= «267» FontFamily= «Verdana»
FontSize= «14»>
<ToolTipService.ToolTip>
<StackPanel>
<TextBlock Margin= «3» Text= «pour créer un livre»></TextBlock>
<Image Source= «/Controles;component/images/livre.png»></Image>
<TextBlock Margin= «3» Text= «cliquez sur ce bouton»></TextBlock>
</StackPanel>
</ToolTipService.ToolTip>
</Button>
FIGURE 5.18
126 Développez des applications Internet avec Silverlight 5
6 - Le contrôle Popup
7 - La classe ItemsControl
un contrôle TextBlock
un contrôle TextBox
un contrôle Button
un contrôle Rectangle
un contrôle Ellipse
un contrôle Image
un contrôle Button avec
dedans un Image
le champ
m_entete_listing de la
Copyright 2012 Patrice REY
classe Client
On crée une classe Client avec les champs m_nom pour un nom, m_prenom pour
un prénom, m_adresse pour une adresse et m_entete_listing pour réaliser un
entête du client.
CHAPITRE 5 □ Les contrôles Silverlight 131
1 2
Copyright 2012 Patrice REY
4 3
CHAPITRE 5 □ Les contrôles Silverlight 133
Lors de l’initialisation du ComboBox, la sélection est vide puisque l’on a pas défini
la propriété SelectedIndex qui a pour valeur -1 (repère n°1). En cliquant sur le
ComboBox, la liste déroulante s’affiche avec son contenu (ici, repère n°2 avec une
chaîne de caractères et un StackPanel). Si on choisit la première sélection (repère
n°3) la propriété SelectedIndex a pour valeur 0 (index de base), et la deuxième
sélection a une valeur SelectedIndex égale à 1 (repère n°4).
<ComboBox x:Name= «x_combo_general» Width= «263» Margin= «10» Canvas.Left= «2»
Canvas.Top= «390» Height= «28»
SelectionChanged= «x_combo_general_SelectionChanged»>
<ComboBoxItem Content= «une chaine texte» />
<ComboBoxItem>
<ComboBoxItem.Content>
<StackPanel Orientation= «Horizontal»>
<Rectangle Height= «10» Width= «50» Fill= «DarkGray» />
<TextBlock Margin= «2» Text= «un conteneur» />
</StackPanel>
</ComboBoxItem.Content>
</ComboBoxItem>
</ComboBox>
<TextBlock Canvas.Left= «11» Canvas.Top= «490» Height= «70»
Name= «x_info_combo1» Text= «infos» Width= «376»
FontFamily= «Verdana» FontSize= «12» TextWrapping= «Wrap» />
Quand une sélection est déterminée, le gestionnaire d’événement
SelectionChanged se produit. On récupère le ComboBoxItem sélectionné par la
propriété SelectedItem. La propriété Content de ce ComboBoxItem nous retourne
son contenu, et on peut déterminer le type du contenu par la méthode GetType (qui
nous donne ici un contenu de type String et de type StackPanel respectivement).
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
x_info_combo1.Text = «SelectedIndex -> « +
x_combo_general.SelectedIndex.ToString();
}
private void x_combo_general_SelectionChanged(object sender,
SelectionChangedEventArgs e) {
x_info_combo1.Text = «SelectedIndex -> « +
x_combo_general.SelectedIndex.ToString() + RC;
if (x_combo_general.SelectedIndex != -1) {
ComboBoxItem combo_item = x_combo_general.SelectedItem as ComboBoxItem;
object obj = combo_item.Content;
x_info_combo1.Text += «type d’objet -> « + obj.GetType().ToString();
}
}
Comme pour le ListBox, on peut lier un ComboBox à une source de données (figure
5.25) par sa propriété ItemsSource (qui référence la source des données) et par sa
propriété DisplayMemberPath (qui indique le champ à récupérer). Ici on affiche le
134 Développez des applications Internet avec Silverlight 5
champ m_entete_listing de la liste MesClients.
<ComboBox Canvas.Left= «12» Canvas.Top= «582» Height= «28»
Name= «x_combo_client» Width= «375»
ItemsSource= «{StaticResource k_clients}»
DisplayMemberPath= «m_entete_listing»></ComboBox>
FIGURE 5.25
1 2
4 3
Le repère n°1 montre un onglet dont la propriété Header du TabItem est un texte,
et son contenu est composé d’un TextBlock.
136 Développez des applications Internet avec Silverlight 5
<toolkit:TabItem Header= «onglet n°1»>
<TextBlock Text= «un contenu texte»/>
</toolkit:TabItem>
Le repère n°2 montre un onglet dont la propriété Header du TabItem est un objet
TextBlock, et son contenu est composé d’un contrôle Image.
<toolkit:TabItem>
<toolkit:TabItem.Header>
<TextBlock Text= «onglet n°2» Margin= «0,3,0,0» />
</toolkit:TabItem.Header>
<Image Height= «53» Name= «image1» Stretch= «Fill» Width= «53»
Source= «/Controles;component/images/livre.png» />
</toolkit:TabItem>
Le repère n°3 montre un onglet dont la propriété Header du TabItem est un objet
Rectangle, et son contenu est composé d’un contrôle ScrollViewer qui fait défiler
un Canvas dont le fond est de couleur grise.
<toolkit:TabItem>
<toolkit:TabItem.Header>
<Rectangle Width= «20» Height= «20» Fill= «SlateGray» Margin= «0,3,0,0» />
</toolkit:TabItem.Header>
<ScrollViewer Height= «165» Name= «scrollViewer1» Width= «373»
HorizontalAlignment= «Left» VerticalAlignment= «Top»>
<Canvas Height= «220» Name= «canvas1» Width= «343»
HorizontalAlignment= «Left» VerticalAlignment= «Top»
Background= «LightGray» />
</ScrollViewer>
</toolkit:TabItem>
Le repère n°4 montre un onglet dont la propriété Header du TabItem est un objet
StackPanel composé d’un TextBlock et d’un contrôle Ellipse, avec un alignement
horizontal; le contenu du TabItem est composé d’un contrôle Ellipse avec un fond
gris et une bordure noire de 1 pixel.
<toolkit:TabItem>
<toolkit:TabItem.Header>
<StackPanel Orientation= «Horizontal» Margin= «0,4,0,0» Width= «69»>
<TextBlock Text= «couleur «/>
<Ellipse Width= «20» Height= «20» Fill= «DarkGray» Stroke= «Black» />
Copyright 2012 Patrice REY
</StackPanel>
</toolkit:TabItem.Header>
<Ellipse Width= «100» Height= «100» Fill= «DarkGray» Stroke= «Black» />
</toolkit:TabItem>
La propriété TabStripPlacement du TabControl permet de définir par une valeur
énumérée l’emplacement des onglets. La figure 5.28 montre un TabControl avec
des onglets placés côté gauche (TabStripPlacement.Left).
CHAPITRE 5 □ Les contrôles Silverlight 137
FIGURE 5.28
Le contrôle TextBox définit une zone de saisie de texte dont les caractéristiques
sont les suivantes:
• la propriété Text, de type string, définit le contenu.
• la propriété booléenne AcceptsReturn (valeur false par défaut) apporte le
support de la touche [Entrée].
• les propriétés VerticalScrollBarVisibility et HorizontalScrollBarVisibility
définissent le contrôle de la visibilité des barres de défilement.
• la méthode SelectAll et l’événement SelectionChanged participent à la gestion
138 Développez des applications Internet avec Silverlight 5
de la sélection de texte.
• le support du presse-papiers est assuré au moyen des raccourcis [Ctrl][C] pour
copier, [Ctrl][X] pour couper et [Ctrl][V] pour coller.
• l’événement TextChanged émis lors d’une modification du texte.
De nombreuses autres propriétés permettent de personnaliser le contrôle TextBox
avec notamment:
• la propriété CaretBrush définit le pinceau utilisé pour restituer la barre verticale
qui indique le point d’insertion.
• la propriété FontSource définit la source de police appliquée au TextBox pour
restituer le contenu.
• la propriété SelectedText définit le contenu de la sélection actuelle dans la zone
de texte.
• la propriété SelectionStart définit la position de départ du texte sélectionné
dans la zone de texte.
• la propriété SelectionLength définit le nombre de caractères de la sélection
actuelle dans la zone de texte.
• la propriété TextAlignment définit la manière dont le texte doit être aligné dans
la zone le texte.
• la propriété TextWrapping définit comment le saut de ligne a lieu si une ligne
de texte s’étend au delà de la largeur disponible de la zone de texte.
• la propriété Watermark définit le contenu affiché sous la forme d’un filigrane
dans le TextBox lorsqu’il est vide.
• la propriété booléenne IsReadOnly définit la valeur qui détermine si l’utilisateur
peut modifier le texte dans la zone de texte.
Le fichier DemoText.xaml, dans la solution Controles.sln du dossier chapitre05,
illustre l’utilisation d’un contrôle TextBox. Une phrase est inscrite dans un contrôle
TextBox (repère n°1). Une sélection est faite en surlignant avec la souris (repère
n°2) et un double clic sur un mot est réalisé avec la souris (repère n°3). Dans ces
deux derniers cas, un TextBlock indique l’indice de départ (base zéro) de la zone
surlignée, la longueur de la zone surlignée (sous forme d’un nombre de caractères),
Copyright 2012 Patrice REY
1 2
texte. Pour mettre en forme le texte dans un bloc de texte, vous pouvez appliquer
une mise en forme à divers objets Run. La propriété Inlines d’un objet TextBlock est
un objet InlineCollection qui contient le contenu d’un bloc de texte. Un objet Run
est aussi utilisé dans le modèle de contenu des objets Paragraph et RichTextBox.
Tout texte figurant dans le texte interne d’un élément objet TextBlock XAML est
implicitement converti en objet Run et stocké dans la collection de propriétés
TextBlock.Inlines. Vous pouvez nommer un objet Run (ou tout autre objet
TextElement) en utilisant la propriété Name ou x:Name en XAML. Nommer un
objet Run vous permet d’opérer des changements par programme. Par exemple,
vous pouvez modifier de façon dynamique le texte ou réordonner une collection
d’éléments de texte.
LineBreak est généralement utilisé en XAML sous forme de fermeture automatique
similaire <LineBreak />. L’objet LineBreak hérite de diverses propriétés de mise en
forme de texte de la classe Inline. La plupart de ces propriétés sont ignorées lors
du rendu du texte. La définition du FontSize d’un objet LineBreak n’a aucun effet
sur l’espace vertical entre les lignes de texte qui précèdent et qui suivent. Au lieu
de cela, le FontSize, défini sur les exécutions de texte qui précèdent et qui suivent,
détermine l’espacement vertical. Plusieurs sauts de ligne successifs utilisent la
hauteur par défaut de 11 pixels.
L’élément Span est utilisé pour grouper d’autres éléments de contenu Inline.
Aucun rendu inhérent n’est appliqué au contenu ou à d’autres éléments dans
un élément Span. Autrement dit, le contenu n’est pas mis en forme s’il est placé
à l’intérieur d’un élément Span sans attribut. Toutefois, les éléments hérités de
Span, notamment Hyperlink, Bold, Italic et Underline appliquent la mise en forme
au texte. Les éléments enfants contenus dans un élément Span doivent dériver de
Inline.
La classe Bold fournit un élément de contenu à insérer qui restitue le contenu en
gras (épaisseur de police). La classe Italic fournit un élément de contenu à insérer
qui restitue le contenu avec un style de police italique. La classe Underline fournit
un élément de contenu à insérer qui restitue le contenu avec une décoration de
texte souligné.
La classe InlineUIContainer fournit un élément de contenu à insérer qui permet
d’incorporer des types UIElement dans le contenu. Vous pouvez incorporer un
UIElement, tel qu’un Button, directement dans le contenu en le mettant dans un
InlineUIContainer. Un InlineUIContainer ne peut pas accueillir plusieurs enfants
UIElement. Toutefois, l’élément enfant hébergé par un InlineUIContainer peut lui-
même héberger des enfants.
146 Développez des applications Internet avec Silverlight 5
un contrôle RichTextBox
en mode ReadOnly
</RichTextBox>
Ajouter une image consiste à ajouter un conteneur InlineUIContainer qui héberge
un contrôle Image (en paramétrant les propriétés Width, Height, Source et Fill). Ce
conteneur doit être placé dans un bloc comme un paragraphe.
<RichTextBox Canvas.Left= «11» Canvas.Top= «404» Name= «x_richtextbox»
Height= «315» Width= «377»
VerticalScrollBarVisibility= «Visible» IsReadOnly= «True»>
CHAPITRE 5 □ Les contrôles Silverlight 147
FIGURE 5.35
RichTextBox
x_richtext_edition
TextBox
x_text_xaml
effectuer un surlignage
effectuer un soulignement
Le principe est le suivant pour mettre une sélection en gras. On obtient un objet
TextSelection contenant la sélection actuelle dans le contrôle RichTextBox par la
propriété Selection du RichTextBox. On instancie une variable currentState en lui
affectant la valeur d’une épaisseur normale (FontWeights.Normal). Si la propriété
FontWeightProperty de la sélection est définie, currentState récupère sa valeur.
En fonction de cela, on applique la valeur FontWeights.Bold avec la méthode
ApplyPropertyValue dans le cas où sa valeur était FontWeights.Normal. Enfin, on
donne au RichTextBox le focus pour éviter que l’utilisateur soit obligé de cliquer
de nouveau dans le contrôle.
private void x_btn_gras_Click(object sender, RoutedEventArgs e) {
Copyright 2012 Patrice REY
selection.ApplyPropertyValue(Run.FontWeightProperty, FontWeights.Normal);
}
x_richtext_edition.Focus();
}
La démarche, qui consiste à mettre une sélection de texte en gras, est identique
pour mettre une sélection en italique ou pour la souligner. Il suffit juste de se
référer aux propriétés concernées.
private void x_btn_italique_Click(object sender, RoutedEventArgs e) {
TextSelection selection = x_richtext_edition.Selection;
FontStyle currentState = FontStyles.Normal;
if (selection.GetPropertyValue(Run.FontStyleProperty) !=
DependencyProperty.UnsetValue)
currentState = (FontStyle)selection.GetPropertyValue(
Run.FontStyleProperty);
if (currentState == FontStyles.Italic) {
selection.ApplyPropertyValue(Run.FontStyleProperty, FontStyles.Normal);
} else {
selection.ApplyPropertyValue(Run.FontStyleProperty, FontStyles.Italic);
}
x_richtext_edition.Focus();
}
private void x_btn_souligne_Click(object sender, RoutedEventArgs e) {
TextSelection selection = x_richtext_edition.Selection;
TextDecorationCollection currentState = null;
if (selection.GetPropertyValue(Run.TextDecorationsProperty) !=
DependencyProperty.UnsetValue)
currentState =
(TextDecorationCollection)selection.GetPropertyValue(
Run.TextDecorationsProperty);
if (currentState != TextDecorations.Underline) {
selection.ApplyPropertyValue(Run.TextDecorationsProperty,
TextDecorations.Underline);
} else {
selection.ApplyPropertyValue(Run.TextDecorationsProperty, null);
}
x_richtext_edition.Focus();
}
La propriété Xaml de RichTextBox permet d’obtenir une représentation XAML du
contenu du contrôle. Le bouton x_btn_xaml permet d’éditer ce contenu XAML
dans le TextBox x_text_xaml. La figure 5.38 illustre le résultat obtenu.
private void x_btn_xaml_Click(object sender, RoutedEventArgs e) {
x_text_xaml.Text = x_richtext_edition.Xaml;
}
152 Développez des applications Internet avec Silverlight 5
FIGURE 5.38
x_richtext_edition.Xaml = content;
x_text_xaml.Text = «»;
}
}
Le bouton x_btn_effacer permet juste de vider les contrôles possédant du contenu
avant d’effectuer une ouverture du fichier XAML sauvegardé.
private void x_btn_effacer_Click(object sender, RoutedEventArgs e) {
CHAPITRE 5 □ Les contrôles Silverlight 153
x_richtext_edition.Blocks.Clear();
x_text_xaml.Text = «»;
}
9 - La classe RangeBase
Copyright 2012 Patrice REY
La classe abstraite RangeBase (figure 5.41) est la classe de base pour des contrôles
qui représentent des éléments avec une valeur dans une plage spécifique. Les
classes dérivées sont ProgressBar, ScrollBar et Slider.
Un contrôle RangeBase a une valeur Value qui peut être définie entre les propriétés
Minimum et Maximum. Il indique visuellement sa valeur Value. Il a un style par
défaut limité. Il utilise la contrainte de propriété pour s’assurer que les propriétés
CHAPITRE 5 □ Les contrôles Silverlight 155
un contrôle
Calendar avec
DisplayMode= un contrôle
«Month» DatePicker avec
SelectedDateFormat
un contrôle
= «Short»
DatePicker avec
un contrôle SelectedDateFormat
Calendar avec = «Long»
DisplayMode=
«Year»
DateValidationError= «x_datepicker_DateValidationError»
Name= «x_datepicker2»></sdk:DatePicker>
</StackPanel>
Dans le contrôle Calendar, on sélectionne une date (figure 5.45). Dans le gestionnaire
de l’événement SelectedDatesChanged, on implémente une information d’erreur si
on choisit par exemple un jour de week-end (DayOfWeek.Saturday et DayOfWeek.
Sunday).
CHAPITRE 5 □ Les contrôles Silverlight 161
FIGURE 5.45
on
sélectionne l’événement
une date dans SelectedDatesChanged
un Calendar nous prévient qu’on
choisit un jour de week
end
1 - La classe Application
Lors de la génération d’un projet Silverlight, Visual Studio réalise un fichier avec
l’extension .xap qui constitue le paquet de déploiement de l’application. Ce fichier
est en fait une archive au format ZIP.
Copyright 2012 Patrice REY
2
1
</Application.Resources>
</Application>
Comme le montre la figure 6.2, la classe App hérite de la classe Application qui
elle-même hérite de la classe de base Object.
FIGURE 6.2
La classe Application est une classe qui encapsule une application Silverlight et
fournit les services suivants :
• point d’entrée d’application,
• durée de vie d’une application,
• gestion d’application,
• ressources de portée application,
• détection d’exception non gérée
Le point d’entrée d’une application Silverlight est la classe de votre assembly
d’application qui dérive de la classe Application. Cette classe est connue
comme classe de l’application. Lorsque le plug-in démarre, Silverlight utilise les
métadonnées du package d’application pour instancier la classe d’application. A ce
stade, la durée de vie de l’application commence. La durée de vie d’une application
se produit dans l’ordre suivant :
• construction (Application),
• initialisation (Startup),
• exécution de l’application,
• arrêt de l’application (initié par l’utilisateur),
• sortie (Exit).
En gérant l’événement Startup, vous pouvez récupérer et traiter les paramètres
d’initialisation à partir de la propriété InitParams de l’objet StartupEventArgs
passé au gestionnaire d’événements Startup. Les paramètres d’initialisation sont
configurés à l’aide de la propriété initParams de l’élément HTML <object> utilisé
pour configurer et instancier le plug-in Silverlight.
Lors du démarrage, vous pouvez aussi spécifier l’interface utilisateur de l’application
principale à afficher en définissant la propriété RootVisual.
Une fois l’application en cours d’exécution, il est possible d’accéder à l’objet
Application et à son état à partir de la propriété statique Current. Le motif singleton
garantit que l’état géré par Application, y compris les ressources partagées
(Resources) et les propriétés personnalisées, est disponible à partir d’un seul
emplacement de portée application.
Dans le fichier App.xaml, l’attribut x:Class, de la balise <Application>, fournit la
valeur pour le code-behind qui initie l’élément visuel racine. Ici nous avons x:Class
= «ModeleApplication.App» qui référence la classe App. En ouvrant le fichier App.
xaml.cs, nous avons le code de programmation suivant:
public partial class App : Application {
public App() {
this.Startup += this.Application_Startup;
168 Développez des applications Internet avec Silverlight 5
this.Exit += this.Application_Exit;
this.UnhandledException += this.Application_UnhandledException;
InitializeComponent();
}
private void Application_Startup(object sender, StartupEventArgs e) {
this.RootVisual = new MainPage();
}
private void Application_Exit(object sender, EventArgs e) {
}
private void Application_UnhandledException(object sender,
ApplicationUnhandledExceptionEventArgs e) {
if (!System.Diagnostics.Debugger.IsAttached) {
e.Handled = true;
Deployment.Current.Dispatcher.BeginInvoke(
delegate { ReportErrorToDOM(e); });
}
}
private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e) {
try {
string errorMsg = e.ExceptionObject.Message
+ e.ExceptionObject.StackTrace;
errorMsg = errorMsg.Replace(‘»’, ‘\’’).Replace(«\r\n», @»\n»);
System.Windows.Browser.HtmlPage.Window.Eval(«throw new Error(\»Unhandled
Error in Silverlight Application « + errorMsg + «\»);»);
} catch (Exception) {
}
}
}
Le code-behind montre que la classe App hérite de la classe Application. Le
constructeur en câble les événements Sartup (émis au démarrage de l’application)
et Exit (émis en sortie de l’application). La classe MainPage est instanciée dans
l’événement Startup et assignée à la propriété RootVisual.
La classe Application gère les services principaux de l’application au moyen d’un
singleton qui peut être obtenu par la propriété statique Application.Current.
A noter que l’événement UnhandledException est déclenché en cas d’exception
non gérée par le code Silverlight. L’implémentation générée par Visual Studio
transmet le message d’erreur au plug-in, qui lui-même le transmet à la fonction
Copyright 2012 Patrice REY
javascript onSilverlightError.
Si on atteint la définition de la méthode InitializeComponent qui est générée
automatiquement par Visual Studio, on voit qu’elle fait appel à la méthode statique
Application.LoadComponent. Cette méthode statique charge un fichier XAML situé
à l’URI (Uniform Resource Identifier) spécifié et le convertit en une instance de
l’objet spécifié par l’élément racine du fichier XAML. Ici elle charge le fichier App.
xaml et le convertit en une instance de la classe App.
CHAPITRE 6 □ Le modèle d’application Silverlight 169
lecteur_xml.Close();
if (((App)Application.Current).ModeEnCours ==
App.ModeDeVue.mode_synthetique) {
x_combo.SelectedIndex = 0;
x_scroll.Visibility = Visibility.Collapsed;
}
if (((App)Application.Current).ModeEnCours ==
App.ModeDeVue.mode_detaille) {
x_combo.SelectedIndex = 1;
x_scroll.Visibility = Visibility.Visible;
}
}
private void x_combo_SelectionChanged(object sender,
SelectionChangedEventArgs e) {
if (x_scroll != null) {
switch (x_combo.SelectedIndex) {
case 0:
x_scroll.Visibility = Visibility.Collapsed;
break;
case 1:
x_scroll.Visibility = Visibility.Visible;
break;
}
}
}
Maintenant, nous allons voir comment instancier une page XAML nommée en
fonction des paramètres passés. Nous avons un UserControl PageDico.xaml que
nous voulons instancier et affecter à la propriété RootVisual de la classe App, et
avec le choix de la vue synthétique ou détaillée.
Dans les paramètres, on passe la séquence value= «page_depart=PageDico,
vue=detaillee».
<object data= «data:application/x-silverlight-2,»
type= «application/x-silverlight-2» width= «100%» height= «100%»>
<param name= «source» value= «ClientBin/DemoTransfertParametre.xap»/>
<param name= «onError» value= «onSilverlightError» />
<param name= «background» value= «white» />
<param name= «minRuntimeVersion» value= «5.0.61118.0» />
<param name= «autoUpgrade» value= «true» />
<param name= «initParams» value= «page_depart=PageDico,vue=detaillee» />
</object>
La démarche est identique pour vérifier la présence d’un paramètre dans le
dictionnaire et pour obtenir sa valeur.
Comme on souhaite instancier PageDico.xaml, et non pas MainPage.xaml, il faut
donc créer une instance d’un fichier XAML pour l’affecter à RootVisual. Le type
de la classe App est récupéré par la méthode GetType et stocké dans la variable
174 Développez des applications Internet avec Silverlight 5
v_type.
On récupère l’assembly v_assembly par la propriété Assembly de v_type (il faut
ajouter une référence using System.Reflection pour la classe Assembly). Pour
instancier un UserControl, on lui affecte la valeur résultant de l’application de
la méthode CreateInstance sur la variable v_assembly. Cette méthode reçoit en
paramètre le nom du fichier XAML à instancier (qui est v_type.Namespace + «.»
+ v_nom_page_charger qui correspond à PageDico.xaml).
//cas: <param name=»initParams»
//value=»page_depart=PageDico,vue=synthetique» />
if (e.InitParams.ContainsKey(«page_depart»)) {
string v_nom_page_charger = e.InitParams[«page_depart»];
//usercontrol a charger
UserControl uc_page_a_charger = null;
//try catch pour lever une exception
try {
//type App
Type v_type = this.GetType();
//assembly correspondante
Assembly v_assembly = type.Assembly;
//instanciation de PageDico.xaml
uc_page_a_charger =
(UserControl)v_assembly.CreateInstance(v_type.Namespace + «.»
+ v_nom_page_charger);
} catch {
uc_page_a_charger = null;
}
this.RootVisual = uc_page_a_charger;
//mise a jour des vues synthetique et detaillee
m_mode_encours = ModeDeVue.non_defini;
if (e.InitParams.ContainsKey(«vue»)) {
string recup_vue = e.InitParams[«vue»];
switch (recup_vue) {
case «synthetique»:
this.m_mode_encours = ModeDeVue.mode_synthetique;
break;
case «detaillee»:
this.m_mode_encours = ModeDeVue.mode_detaille;
break;
Copyright 2012 Patrice REY
}
}
}
La figure 6.4 illustre le résultat obtenu par l’instanciation de l’UserControl PageDico.
xaml avec sa vue détaillée. On avait passé en paramètre page_depart=PageDico,
et on affecte une instance de PageDico.xaml à RootVisual de App.
CHAPITRE 6 □ Le modèle d’application Silverlight 175
FIGURE 6.4
Le but est de remplacer cet écran d’accueil simpliste par un écran d’accueil
personnalisé. La figure 6.7 visualise l’écran d’accueil que nous allons réaliser.
FIGURE 6.7
Les fichiers de ressources sont des fichiers spécialement marqués pour être
embarqués ou référencés par l’application, et pour être chargés au moyen d’un
Copyright 2012 Patrice REY
URI.
Ces fichiers peuvent être de n’importe quel type, binaire ou texte. Ils doivent
être intégrés au projet Visual Studio, et leur propriété Action de génération doit
prendre l’une des valeurs suivantes:
• Resource: le fichier est embarqué dans l’assembly.
• Contenu: le fichier est embarqué dans le paquet mais pas dans l’assembly.
CHAPITRE 6 □ Le modèle d’application Silverlight 179
Dans le projet (figure 6.9), on ajoute un dossier contenu dans lequel on y insère un
élément existant, sous forme d’une image intitulée meuble_peint_1.png (repère
n°1). Dans la fenêtre des propriétés, son action de génération est fixée à Resource
et son nom de fichier est fixé à meuble_peint_1.png (repère n°2).
Dans le code XAML, la propriété Source de Image est fixée à contenu/meuble_
peint_1.png. La figure 6.10 illustre le résultat obtenu.
<Image Canvas.Left= «12» Canvas.Top= «49» Height= «228» Name= «x_img1»
Stretch= «Fill» Width= «200»
Source= «contenu/meuble_peint_1.png» />
FIGURE 6.9
FIGURE 6.10
180 Développez des applications Internet avec Silverlight 5
Une autre façon consiste, en XAML, à fixer la propriété Source du contrôle Image
par une référence à une image indiquant qu’elle n’est pas chargée (ici on utilise
une image point_d_interrogation.png).
<Image Canvas.Left= «227» Canvas.Top= «49» Height= «228» Name= «x_img2»
Stretch= «Fill» Width= «200»
Source= «/DemoRessourceLocale;component/contenu/point_d_interrogation.png»
/>
A noter qu’il existe deux façons pour fixer la propriété Source:
• soit en référençant directement l’emplacement par un URI (Source = « contenu/
point_d_interrogation.png»).
• soit en référençant par l’assembly (Source = «/DemoRessourceLocale;component/
contenu/point_d_interrogation.png»).
Dans le code-behind, on charge l’image embarquée dans l’assembly. Pour cela, on
instancie un objet StreamResourceInfo qui reçoit le fichier de ressources obtenu
à partir de l’emplacement du package de l’application, par la méthode statique
Application.GetResourceStream. On instancie un nouvel objet BitmapImage et on
affecte à sa propriété Source le flux contenu dans la ressource (propriété Stream du
StreamResourceInfo). Le contrôle Image x_img2 reçoit dans sa propriété Source
le BitmapImage.
StreamResourceInfo sri = Application.GetResourceStream(
new Uri(«DemoRessourceLocale;component/contenu/meuble_peint_2.jpg»,
UriKind.Relative));
BitmapImage bitmap2 = new BitmapImage();
bitmap2.SetSource(sri.Stream);
x_img2.Source = bitmap2;
Après le chargement de l’application (figure 6.11), le point d’interrogation est
remplacé par l’image meuble_peint_2.jpg embarquée dans l’assembly.
FIGURE 6.11
A noter que pour utiliser un objet WebClient, il faut ajouter une référence à
l’espace de noms System.IO.
Dans le gestionnaire de l’événement Click du bouton, on instancie un objet WebClient.
On lui ajoute un gestionnaire pour l’événement DownloadProgressChanged (émis
quand le téléchargement est en cours) et pour l’événement OpenReadCompleted
(émis quand le téléchargement est terminé). La méthode OpenReadAsync permet
de lancer le téléchargement de façon asynchrone.
CHAPITRE 6 □ Le modèle d’application Silverlight 183
FIGURE 6.15
2
Copyright 2012 Patrice REY
CHAPITRE 6 □ Le modèle d’application Silverlight 185
La figure 6.15 illustre le résultat obtenu. Au départ (repère n°1), l’image n’est pas
chargée. Quand on clique sur le bouton, le téléchargement commence et la barre de
progression (repère n°2) indique la quantité téléchargée. Quand le téléchargement
est terminé, le point d’interrogation est remplacé par l’image téléchargée.
Le téléchargement d’un fichier XML, intitulé questionnaire.xml, dans l’archive
ressource_web.zip, s’effectue de la même façon par un téléchargement asynchrone.
<Border BorderBrush= «Silver» BorderThickness= «1» Canvas.Left= «0»
Canvas.Top= «547» Height= «273» Name= «border1» Width= «700»>
<Canvas Name= «canvas1»>
<ScrollViewer Canvas.Left= «6» Canvas.Top= «6» Height= «259»
Name= «scrollViewer1» Width= «491» Background= «White»>
<TextBlock Height= «600» Name= «x_text_xml» Text= «»
HorizontalAlignment= «Left» VerticalAlignment= «Top» Width= «463» />
</ScrollViewer>
<Button Canvas.Left= «503» Canvas.Top= «6» Content= «Télécharger XML»
Height= «34» Name= «x_btn_xml» Width= «189» Click= «x_btn_xml_Click»/>
</Canvas>
</Border>
Le TextBlock x_text_xml reçoit le contenu du fichier XML téléchargé. Ici le flux reçu
sera un fichier XML (sri_xml.Stream) et devra être traité comme tel. Pour cela on
instancie un objet XmlReader par la méthode XmlReader.Create. On lit le contenu
du fichier XML par la méthode Read. A noter qu’il faut ajouter une référence à
l’espace de noms System.Xml pour l’utilisation de la classe XmlReader.
private void x_btn_xml_Click(object sender, RoutedEventArgs e) {
WebClient wc_xml = new WebClient();
wc_xml.OpenReadCompleted +=
new OpenReadCompletedEventHandler(wc_xml_OpenReadCompleted);
wc_xml.OpenReadAsync(new Uri(«contenu/ressource_web.zip», UriKind.
Relative));
}
private void wc_xml_OpenReadCompleted(object sender,
OpenReadCompletedEventArgs e) {
if (e.Error == null) {
StreamResourceInfo sri = new StreamResourceInfo(e.Result, null);
StreamResourceInfo sri_xml =
Application.GetResourceStream(sri,
new Uri(«ressource_web/questionnaire.xml», UriKind.Relative));
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = false;
XmlReader reader_xml = XmlReader.Create(sri_xml.Stream, settings);
while (reader_xml.Read()) {
x_text_xml.Text += reader_xml.Value;
}
reader_xml.Close();
} else {
186 Développez des applications Internet avec Silverlight 5
x_text_xml.Text += «erreur:» + RC;
x_text_xml.Text += e.Error.Message;
}
}
La figure 6.16 illustre le résultat obtenu après le téléchargement du fichier
questionnaire.xml.
FIGURE 6.16
Dans un projet important, il existe toujours des contrôles et des ressources qui
seront réutilisés de nombreuses fois par divers autres contrôles et projets. Pour
une meilleure visibilité du projet, il faut les intégrer dans des bibliothèques de
classes de façon à les rendre réutilisables.
Le projet DemoAssembly.sln, dans le dossier chapitre06, illustre ce principe de
partitionnement et de réutilisation.
Le projet commence comme toujours par la création d’un projet d’application
Silverlight. Dans notre fichier MainPage.xaml, nous voulons insérer un contrôle
qui aura été créé et compilé dans une bibliothèque de classes. Pour cela, on ajoute
à la solution un nouveau projet, en faisant un clic droit sur le nom de la solution et
Copyright 2012 Patrice REY
FIGURE 6.18
1 2
Maintenant, dans notre projet principal, nous voulons utiliser le contrôle Afficheur
et les ressources images qui sont contenus dans la bibliothèque de classes.
Pour pouvoir utiliser la bibliothèque de classes (figure 6.20), il faut ajouter une
référence à cette bibliothèque pour la rendre accessible. En faisant un clic droit sur
le dossier références du projet DemoAssembly.sln, choisir ajouter une référence
(repère n°1). Dans la fenêtre qui s’affiche, dans l’onglet projets, sélectionnez
AfficheurImage, puis OK (repère n°2).
FIGURE 6.20
2
Copyright 2012 Patrice REY
1
CHAPITRE 6 □ Le modèle d’application Silverlight 189
La navigation
contrôles
• Naviguer d’une page à
l’autre
• La boite de dialogue
personnalisée
• La classe Frame
• Le mapping d’URI
• L’interface
INavigationContent
Loader
La façon de faire la plus courante est de charger les contrôles en fonction des
actions de l’utilisateur. Très souvent, la page centrale est composée d’un Grid avec
des lignes et des colonnes. Une des cellules possède un contrôle du style ListBox,
et l’utilisateur, en cliquant sur une des rubriques, déclenche le chargement d’un
contrôle spécifique qui s’affiche dans une autre cellule. Les contrôles sont dits
embarqués, et sont générés à la demande.
Le projet DemoControleEmbarque.sln, dans le dossier chapitre07, illustre ce type
de pratique, en affichant un ListBox avec des rubriques indiquant un type de
contrôle, et en générant le contrôle à la volée pour l’afficher dans une cellule.
Le dossier contenu se compose des ressources utilisées par les contrôles.
AfficheurImage.xaml et TexteLivre.xaml sont deux contrôles personnalisés de
type UserControl: le premier permet de visualiser trois images différentes, et le
deuxième permet de lire un texte de livre (figure 7.1).
Le fichier MainPage.xaml représente le contrôle principal pour toute l’application.
Il est composé d’un Grid avec 2 lignes et 1 colonne. Dans la première ligne,
on positionne un ListBox x_listbox doté d’un gestionnaire d’événement
SelectionChanged. Dans la deuxième ligne, on positionne un conteneur Border
x_border_contenu à l’intérieur duquel est ajouté un TextBlock.
<Grid x:Name= «x_grid_root» Background= «WhiteSmoke»>
<Grid.RowDefinitions>
<RowDefinition Height= «100» />
Copyright 2012 Patrice REY
les ressources
le contrôle AfficheurImage
le contrôle TexteLivre
bitmap.UriSource =
new Uri(«contenu/meuble_peint_» + m_numero_encours.ToString() + «.jpg»,
UriKind.Relative);
x_img.Source = bitmap;
}
Le contrôle TexteLivre.xaml est un contrôle qui permet d’afficher un extrait de
chapitre du livre du Comte de Monte-Cristo. Le texte et les images sont insérés
CHAPITRE 7 □ La navigation 195
L’autre façon de naviguer consiste à partir d’une page pour aller vers une autre page,
et ainsi de suite. Les pages sont généralement des contrôles de type UserControl,
chargées une fois et réutilisables grâce à une mise en cache dans une structure de
CHAPITRE 7 □ La navigation 197
FIGURE 7.4
Comme les pages sont générées une seule fois puis mises dans le dictionnaire, si on
inscrit dans le TextBox un commentaire, celui-ci reste visible quand on reviendra
sur cette même page. La figure 7.5 visualise le résultat obtenu avec une navigation
CHAPITRE 7 □ La navigation 201
1
2
FIGURE 7.9
3 - Le contrôle Frame
Cette navigation parmi les pages est prise en compte dans l’historique du navigateur.
De plus, la navigation ne provoque pas le rechargement de l’application.
Les pages sont affichées au sein d’un conteneur visuel de la classe Frame. Sa
propriété Source indique l’URI de la page à charger. Sa méthode Navigate permet
de charger une page par programmation.
A l’exécution d’une page HTML comprenant une application Silverlight affichant
une page Silverlight dans un Frame, la barre d’adresse du navigateur affiche l’URI
de la page Silverlight. La syntaxe de l’URL de la page HTML est complétée par le
symbole # suivi de l’URI de la page Silverlight.
La classe Frame hérite de ContentControl et dispose de membres permettant
de contrôler la navigation. La méthode Navigate instancie une page de façon
asynchrone. Elle émet les événements suivants pendant le processus de
chargement:
• Navigating : le processus est enclenché; il est possible d’empêcher le
processus de navigation au moyen de la propriété Cancel de l’argument
NavigatingCancelEventArgs du gestionnaire d’événement.
• Navigated : la page a été chargée et la propriété Content est mise à jour.
La méthode StopLoading permet d’arrêter un chargement. L’événement
NavigationStopped est alors émis. L’URI de la page de destination peut être lu dans
la propriété Source. L’URI courant peut être lu dans la propriété CurrentSource.
Les méthodes GoForward et GoBack permettent de naviguer dans l’historique. Les
propriétés CanGoBack et CanGoForward indiquent si cela est possible.
Le projet FrameV1.sln, dans le dossier chapitre07, illustre l’utilisation d’un Frame
pour la visualisation de pages. Pour ajouter un Frame en XAML, il faut ajouter
une référence à un espace de noms: xmlns:naviguer = «clr-namespace:System.
206 Développez des applications Internet avec Silverlight 5
Windows.Controls; assembly = System.Windows.Controls.Navigation».
On positionne comme page de départ l’UserControl Page1.xaml. Le bouton x_
btn_navig permet de charger dans le Frame x_frame la page Page2.xaml par la
méthode Navigate.
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke» Height= «230»>
<Border BorderThickness= «1» CornerRadius= «10» BorderBrush= «Black»
Canvas.Left= «5» Canvas.Top= «6» Width= «390» Height= «160»>
<naviguer:Frame Canvas.Left= «12» Canvas.Top= «12» Height= «150»
Width= «376» Name= «x_frame»>
<naviguer:Frame.Content>
<visuel:Page1></visuel:Page1>
</naviguer:Frame.Content>
</naviguer:Frame>
</Border>
<Button Canvas.Left= «93» Canvas.Top= «176» Content= «naviguer vers la page
suivante» Height= «37» Name= «x_btn_navig» Width= «212»
Click= «x_btn_navig_Click»/>
</Canvas>
//clic bouton
private void x_btn_navig_Click(object sender, RoutedEventArgs e) {
x_frame.Navigate(new Uri(«/Page2.xaml», UriKind.Relative));
}
La figure 7.10 montre le résultat obtenu. Au lancement, le Frame charge la page
Page1.xaml. Un clic sur le bouton et le Frame charge la page Page2.xaml. Dans ce
dernier cas, on voit bien que le titre de la page HTML est /Page2.xaml. Et si dans
la barre d’adresse du navigateur on tape directement l’URL http://localhost:2115/
FrameV1TestPage.html#/Page2.xaml, on accède directement à la page dans
l’application. Cela peut poser certains problèmes de sécurité.
FIGURE 7.10
Le mapping d’URI consiste à associer un URI virtuel à un URI original, au moyen d’un
objet UriMapping. La propriété Uri de cet objet définit l’URI virtuel. Sa propriété
MappedUri indique l’URI original. Les différents objets UriMapping doivent être
regroupés dans la collection UriMappings d’un objet UriMapper. L’ordre dans
lequel les éléments UriMapping sont définis est important. En effet, lorsqu’un URI
est traité par un UriMapper, le premier UriMapping de la liste correspondant à la
syntaxe de l’URI est appliqué.
Le projet FrameV2.sln, dans le dossier chapitre07, illustre le principe du mapping
d’URI.
Dans le fichier App.xaml, on ajoute dans les ressources un objet UriMapper avec
une clé x:Key = «k_uri_mapper». Puis on insère les objets UriMapping dans la
collection UriMappings de l’objet UriMapper. L’objet UriMapping qui a sa propriété
Uri vide, correspond à la page de départ, donc avec une propriété MappedUri =
«/Page1.xaml». Le deuxième objet UriMapping a sa propriété Uri fixée à vers_
page2, et sa propriété MappedUri fixée à /Page2.xaml.
<Application
xmlns= «http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x= «http://schemas.microsoft.com/winfx/2006/xaml»
x:Class= «FrameV2.App»
xmlns:naviguer= «clr-namespace:System.Windows.Navigation;
assembly=System.Windows.Controls.Navigation»>
<Application.Resources>
<naviguer:UriMapper x:Key= «k_uri_mapper»>
<!-- page de depart: page1.xaml-->
208 Développez des applications Internet avec Silverlight 5
<naviguer:UriMapping Uri= «» MappedUri= «/Page1.xaml» />
<naviguer:UriMapping Uri= «vers_page2» MappedUri= «/Page2.xaml» />
</naviguer:UriMapper>
</Application.Resources>
</Application>
Il ne reste plus qu’à fixer la propriété UriMapper de x_frame à la ressource statique
k_uri_mapper.
<naviguer:Frame Canvas.Left= «12» Canvas.Top= «12» Height= «150» Width= «376»
Name= «x_frame» UriMapper= «{StaticResource k_uri_mapper}»>
<naviguer:Frame.Content>
<visuel:Page1></visuel:Page1>
</naviguer:Frame.Content>
</naviguer:Frame>
Pour naviguer vers une page, comme avec le bouton x_btn_navig, on pourra
utiliser directement l’URI simplifié vers_page2 dans la méthode Navigate.
//clic bouton
private void x_btn_navig_Click(object sender, RoutedEventArgs e) {
//x_frame.Navigate(new Uri(«/Page2.xaml», UriKind.Relative));
x_frame.Navigate(new Uri(«vers_page2», UriKind.Relative));
}
La figure 7.12 visualise le résultat obtenu. Quand on est sur la page Page2.xaml, en
cliquant sur le bouton de retour de page du navigateur, il nous ramène à la page
Page1.xaml (il n’y a plus d’exception levée).
FIGURE 7.12
L’arborescence des fichiers est visualisée sur la figure 7.14. Les pages avec un accès
protégé (Article1.xaml et Article2.xaml) sont stockées dans un dossier intitulé
FIGURE 7.14
...
<navigation:Frame x:Name= «x_frame»
UriMapper= «{StaticResource k_uri_mapper}»>
<navigation:Frame.ContentLoader>
<visuel:Authentification PageLoginXaml= «/PageLogin.xaml»
DossierPagesProteges= «PagesProtegees»></visuel:Authentification>
</navigation:Frame.ContentLoader>
</navigation:Frame>
...
Le gestionnaire de l’événement Click du bouton x_btn_connexion (dans le fichier
PageLogin.xaml) gère l’authentification. Ici on choisit un accès par un mot de passe
codé en dur (1234 et 5678). En réel, il faudrait ajouter une connexion au serveur
pour les vérifications habituelles.
public partial class PageLogin : Page {
public PageLogin() {
InitializeComponent();
}
private void x_btn_connexion_Click(object sender, RoutedEventArgs e) {
//en reel, cela passe par un envoi au serveur pour verifier
if (x_text_util.Text == «1234» && x_text_passe.Text == «5678») {
App.UtilisateurEstAuthentifie = true;
this.NavigationService.Refresh();
}
}
}
CHAPITRE 8 DANS CE CHAPITRE
• La classe Shape
• Rectangle, Ellipse, Line,
Le graphisme Polyline, Polygon
• Path, PathGeometry,
PathFigure
• RectangleGeometry,
EllipseGeometry,
LineGeometry,
GeometryGroup
• PathSegment,
LineSegment,
ArcSegment,
Silverlight expose des fonctionnalités graphiques BezierSegment,
QuadraticBezierSegment
vectorielles évoluées au moyen d’un modèle objet • Le clipping
riche et cohérent. • TranslateTransform,
ScaleTransform,
Dans ce chapitre, vous allez commencer par RotateTransform,
dessiner les nombreuses formes géométriques de SkewTransform,
base comme le rectangle, l’ellipse et la ligne. MatrixTransform
• TransformGroup,
Des objets plus complexes vous permettront CompositeTransform
d’assembler des formes géométriques pour en • La classe PlaneProjection
faire des géométries évoluées, réutilisables dans
des contextes de données différents.
Le segment, la ligne, l’arc et les courbes de Bézier
cubiques et quadratiques, permettent de composer
de nombreuses figures géométriques.
Vous verrez comment ces figures géométriques
peuvent être employées pour réaliser des découpes
(le clipping).
La dernière partie du chapitre est consacrée aux
transformations (translation, mise à l’échelle,
rotation, inclinaison), et à la transformation
matricielle. Il sera abordé aussi le système de
projection permettant d’appliquer à un élément des
transformations 3D. L’objectif des projections est
de rendre les interfaces utilisateur plus intuitives.
216 Développez des applications Internet avec Silverlight 5
Une forme est un élément qui affiche une figure géométrique. Sa classe hérite
de la classe abstraite Shape, et donc de FrameworkElement et de UIElement.
Ainsi, l’héritage de UIElement fait qu’un objet Shape est interactif (sensibilité aux
périphériques de saisie) et compatible avec le système de disposition.
Les classes qui héritent de Shape (figure 8.1) sont:
• la classe Rectangle qui représente un rectangle avec des dimensions données.
• la classe Ellipse qui représente une ellipse inscrite dans un rectangle fictif.
• la classe Line qui représente une ligne droite reliant deux points donnés.
• la classe Polygon qui représente un polygone spécifié par une suite de points.
• la classe Polyline qui représente une suite de lignes droites connectées.
• la classe Path qui représente une suite de segments droits ou courbes
connectés.
Toutes ces classes héritent de la classe Shape les caractéristiques communes
Copyright 2012 Patrice REY
suivantes:
• la propriété Stroke qui référence le pinceau utilisé pour le dessin du contour de
la forme, ou de la ligne dans le cas de Line, Polyline et Path.
• la propriété Fill qui référence le pinceau utilisé pour le remplissage de la forme
(cas de Rectangle, Ellipse, Polygon et Path).
• d’un ensemble de propriétés préfixées par Stroke, qui permettent de définir
l’apparence du trait; par exemple StrokeThickness indique son épaisseur.
CHAPITRE 8 □ Le graphisme 217
La classe Line représente une ligne droite reliant deux point donnés. Ses propriétés
X1 et Y1 spécifient le point d’origine, et ses propriétés X2 et Y2 spécifient le point
de destination. Ces coordonnées sont relatives à la surface du contenu direct de
la figure.
Les propriétés StrokeStartLineCap et StrokeEndLineCap indiquent respectivement
la forme des terminaisons de début et de fin du trait au moyen d’une énumération
PenLineCap:
• Flat définit aucune terminaison particulière.
• Square définit un rectangle dont l’épaisseur est égale à la moitié de la largeur
du trait.
• Round définit un demi-cercle.
• Triangle définit un triangle isocèle.
220 Développez des applications Internet avec Silverlight 5
Les propriétés StrokeDashArray, StrokeDashCap et StrokeDashOffset permettent
de définir un trait en pointillé.
La figure 8.4 visualise le fichier Ligne.xaml dans lequel différentes configurations
de lignes sont effectuées.
FIGURE 8.4
FIGURE 8.5
• EvenOdd: la zone est colorée si elle est séparée de l’extérieur de la figure par
un nombre impair de segments (comportement par défaut).
• NonZero: la zone est colorée en fonction de la direction suivie par les points
formant les segments; si le nombre de segments pour une direction donnée
est identique à celui pour la direction opposée, la zone n’est pas colorée; si ces
nombres sont différents, la zone est colorée; ainsi un compteur est incrémenté
pour chaque segment entourant la zone dans un sens donné, et décrémenté
pour chaque segment dans l’autre sens; au final, si le compteur est nul, la zone
n’est pas colorée.
La figure 8.6 visualise le fichier Polygon.xaml dans lequel différentes configurations
d’objets Polygon sont effectuées.
FIGURE 8.6
224 Développez des applications Internet avec Silverlight 5
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke» Width= «600»
Height= «700»>
<Polygon
Points= «150,0 300,300 0,150 300,100 0,100 150,300 300,0»
Stroke= «Black»
StrokeThickness= «2» Width= «300» Height= «300»
Fill= «LightGray» FillRule= «EvenOdd»></Polygon>
<Polygon Canvas.Left= «0» Canvas.Top= «322» Fill= «LightGray»
FillRule= «Nonzero» Height= «300»
Points= «150,0 300,300 0,150 300,100 0,100 150,300 300,0» Stroke= «Black»
StrokeThickness= «2» Width= «300» />
</Canvas>
Un tracé évolué est obtenu au moyen d’un objet Path et d’une géométrie assignée
à sa propriété Data. La géométrie définit la forme d’une figure et le Path en assure
le rendu visuel. Les types de géométries sont (figure 8.7):
• RectangleGeometry qui définit un rectangle de dimensions données.
• EllipseGeometry qui définit une ellipse inscrite dans un rectangle fictif.
• LineGeometry qui définit une ligne droite reliant deux points donnés.
• PathGeometry qui définit un ensemble de figures composées chacune d’une
suite de segments droits ou courbes connectés.
• GeometryGroup qui définit une géométrie issue du regroupement de plusieurs
géométries.
La figure 8.8 visualise le fichier Path.xaml dans lequel deux objets Path sont
visualisés. Un GeometryGroup regroupe plusieurs géométries basiques. La
coloration des zones imbriquées suit la règle de remplissage EvenOdd.
<Path Fill= «LightGray» Stroke= «Black» StrokeThickness= «2» Width= «273»
Height= «196»>
<Path.Data>
<GeometryGroup FillRule= «EvenOdd»>
<EllipseGeometry Center= «75,75» RadiusX= «50» RadiusY= «50» />
<RectangleGeometry Rect= «100,50,100,30»></RectangleGeometry>
Copyright 2012 Patrice REY
FIGURE 8.8
226 Développez des applications Internet avec Silverlight 5
La classe PathGeometry (figure 8.9) permet de constituer des figures évoluées
au moyen de segments de différents types. La classe de base abstraite de ces
segments est PathSegment. Les classes qui dérivent de PathSegment sont:
• la classe LineSegment et PolylineSegment qui représentent des segments
linéaires.
• la classe ArcSegment qui représente des segments courbes en arc.
• la classe BezierSegment et PolyBezierSegment qui représentent des segments
composés d’une courbe de Bézier cubique.
• la classe QuadraticBezierSegment et PolyQuadraticBezierSegment qui
représentent des segments composés d’une courbe de Bézier quadratique.
FIGURE 8.9
est constitué d’un ou plusieurs objets PathFigure, qui sont assignés à sa propriété
Figures de type PathFigureCollection. Un objet PathFigure regroupe ses segments
dans une collection de type PathSegmentCollection référencée par sa propriété
Segments.
PathFigure a sa propriété StartPoint qui détermine le point d’origine du premier
segment. Chaque segment dispose d’une propriété indiquant son point de
CHAPITRE 8 □ Le graphisme 227
FIGURE 8.10
228 Développez des applications Internet avec Silverlight 5
</Path.Data>
</Path>
En séparant une ellipse en deux parties par une ligne droite, on obtient deux arcs
complémentaires, l’un étant large et l’autre étroit. L’objet ArcSegment représente
CHAPITRE 8 □ Le graphisme 229
FIGURE 8.11
point de
terminaison
grand
petit arc arc
StartPoint
230 Développez des applications Internet avec Silverlight 5
<Path Stroke= «Black» StrokeThickness= «2» Width= «200» Height= «200»>
<Path.Data>
<PathGeometry>
<PathFigure IsClosed= «False» StartPoint= «10,100»>
<ArcSegment Point= «250,150» Size= «200,300» />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
3
Copyright 2012 Patrice REY
CHAPITRE 8 □ Le graphisme 235
3 - Les transformations
FIGURE 8.16
sens des aiguilles d’une montre, selon un angle défini par la propriété Angle (en
degrés). Les propriétés CenterX et CenterY définissent les coordonnées relatives du
point d’ancrage de la figure à partir duquel la transformation s’applique. Par défaut
ce point est (0,0).
Le fichier Rotation.xaml (figure 8.18) visualise la rotation pour un rectangle, selon
un angle variant de 0 à 360 degrés, à l’aide d’une glissière.
FIGURE 8.18
FIGURE 8.20
1 2
M11 M12
M21 M22
OffsetX OffsetY
1 0
matrice unité 0 1
0 0
2 0
1 0 3
150 50
Pour faire tourner le Canvas autour d’un axe, on affecte à sa propriété Projection,
un objet PlaneProjection dont la propriété RotationX obtient sa valeur de la
glissière par databinding.
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke»>
<Line Name= «x_axe» X1= «0» Y1= «0» X2= «255» Y2= «0» Width= «255»
Height= «4»
StrokeThickness= «4» Stroke= «Black»
StrokeDashArray= «3,2» Canvas.Left= «12» Canvas.Top= «168»></Line>
<Canvas Name= «x_cnv_contenu» Width= «225» Height= «307» Background= «Silver»
Canvas.Left= «28» Canvas.Top= «12»>
Copyright 2012 Patrice REY
<Canvas.Projection>
<PlaneProjection
RotationX= «{Binding ElementName=x_slider, Path=Value}»>
</PlaneProjection>
</Canvas.Projection>
<Image Canvas.Left= «48» Canvas.Top= «34» Height= «218» Name= «image1»
Stretch= «Fill» Width= «144»
Source= «/DemoTransformations;component/contenu/monte%20cristo.jpg» />
<TextBlock Canvas.Left= «6» Canvas.Top= «6» Height= «27» Name= «textBlock1»
CHAPITRE 8 □ Le graphisme 247
Les pinceaux
classe Brush
• SolidColorBrush,
LinearGradientBrush,
1 - Les pinceaux
La classe Brush (figure 9.1) est la classe abstraite de base qui gère les différents
pinceaux. Elle hérite de DependencyObject. Les classes dérivées de Brush sont:
• la classe SolidColorBrush qui définit un motif de couleur unie.
• la classe LinearGradientBrush qui définit un motif en dégradé linéaire de
couleur.
• la classe RadialGradientBrush qui définit un motif en dégradé circulaire de
couleur.
• la classe ImageBrush qui définit un motif basé sur une image.
• la classe VideoBrush qui définit un motif basé sur une vidéo.
• la classe WebBrowserBrush qui définit un motif basé sur une page HTML.
• la classe ImplicitInputBrush qui définit un motif basé sur une image pour un
rendu amélioré en jouant sur les différents caractères de bases (luminosité,
texture, position, ombrage…).
FIGURE 9.1
La classe SolidColorBrush définit un pinceau dont le motif est une couleur unie. Sa
propriété Color indique une couleur unie déterminée.
La figure 9.2 visualise un ensemble d’objets Color prédéfinis, avec leur nom et leur
correspondance dans le schéma ARGB (le canal A pour la transparence, le canal R
pour le rouge, le canal G pour le vert, et le canal B pour le bleu).
Le fichier PinceauSolidColorBrush.xaml (figure 9.3) visualise un rectangle dont le
remplissage (propriété Fill) est réalisé par la couleur unie prédéfinie Gainsboro.
FIGURE 9.3
(0,1) (1,1)
Un dégradé linéaire vertical s’effectuera de la façon suivante (figure 9.6):
<LinearGradientBrush EndPoint= «0.5,1» StartPoint= «0.5,0»>
<GradientStop Color= «DimGray» Offset= «0» />
<GradientStop Color= «White» Offset= «1» />
</LinearGradientBrush>
FIGURE 9.6 (0.5,0)
(0,0) (1,0)
dégradé linéaire
vertical
Copyright 2012 Patrice REY
(0,1) (1,1)
(0.5,1)
Un dégradé linéaire avec plusieurs vagues s’effectuera de la façon suivante (figure
9.7):
<LinearGradientBrush EndPoint= «1,1» StartPoint= «0,0»>
<GradientStop Color= «Black» Offset= «0» />
CHAPITRE 9 □ Les pinceaux et les images 255
(0,1) (1,1)
Le dégradé RadialGradientBrush agit depuis un point central vers la circonférence
d’une ellipse. Le point central est défini par la propriété GradientOrigin, et
correspond à l’emplacement 0 d’un GradientStop. La circonférence correspond à
l’emplacement 1 d’un GradientStop.
Le dégradé prend la forme d’une ellipse (figure 9.8) dont la position est définie par
la propriété Center ((0.5,0.5) par défaut) et les dimensions par RadiusX (0.5 par
défaut) et RadiusY (0.5 par défaut).
<RadialGradientBrush>
<GradientStop Color= «DimGray» Offset= «0» />
<GradientStop Color= «White» Offset= «1» />
</RadialGradientBrush>
(0,1) (1,1)
propriete>).
<Canvas Canvas.Left= «12» Canvas.Top= «12» Height= «161» Name= «canvas1»
Width= «119»>
<Canvas.Background>
<ImageBrush ImageSource=
«/DemoPinceaux;component/contenu/monte%20cristo.jpg» />
</Canvas.Background>
CHAPITRE 9 □ Les pinceaux et les images 257
</Canvas>
<!-- -->
<Rectangle Canvas.Left= «12» Canvas.Top= «179» Height= «128»
Name= «rectangle1»
Stroke= «Black» StrokeThickness= «2» Width= «119»>
<Rectangle.Fill>
<ImageBrush ImageSource=
«/DemoPinceaux;component/contenu/meuble_peint_3.jpg» />
</Rectangle.Fill>
</Rectangle>
<!-- -->
<TextBlock Canvas.Left= «35» Canvas.Top= «326» Height= «158»
Name= «textBlock2»
Text= «un exemple de texte» Width= «480»
FontFamily= «Verdana» FontSize= «64» TextWrapping= «Wrap»
TextAlignment= «Center»>
<TextBlock.Foreground>
<ImageBrush
ImageSource= «/DemoPinceaux;component/contenu/monte%20cristo.jpg»
Stretch= «None» />
</TextBlock.Foreground>
</TextBlock>
Name= «x_img_reflechi»
Source= «/DemoPinceaux;component/contenu/lino_ventura.jpg» Stretch= «Fill»
Width= «120» >
<Image.RenderTransform>
<ScaleTransform ScaleY= «-1»></ScaleTransform>
</Image.RenderTransform>
<Image.OpacityMask>
<LinearGradientBrush StartPoint= «0,0» EndPoint= «0,1»>
<GradientStop Offset= «0» Color= «Transparent»></GradientStop>
<GradientStop Offset= «1» Color= «#D7000000»></GradientStop>
</LinearGradientBrush>
</Image.OpacityMask>
</Image>
Silverlight permet d’appliquer à son rendu des effets visuels évolués, appliqués
au niveau de chaque pixel, tout en conservant sa richesse de composition, les
animations et les autres fonctionnalités de base. Ces effets héritent de la classe
abstraite Effect (figure 9.11) et sont représentés par:
• la classe DropShadowEffect qui génère un effet d’ombre portée à la surface
affichée.
• la classe BlurEffect qui applique un flou à la surface affichée.
• la classe ShaderEffect qui permet de réaliser des effets personnalisés.
FIGURE 9.11
Les propriétés de ces classes permettent de régler les effets. Ces propriétés peuvent
260 Développez des applications Internet avec Silverlight 5
être animées ou être pilotées au moyen du databinding. La propriété Effect de la
classe UIElement permet d’appliquer un effet à un élément visuel et à tous ses
éléments enfants.
BlurRadius.
<TextBlock FontSize= «24» Margin= «3» Canvas.Left= «18» Canvas.Top= «9»
Height= «42» Width= «570» FontFamily= «Verdana»>
<TextBlock.Effect>
<DropShadowEffect></DropShadowEffect>
</TextBlock.Effect>
<TextBlock.Text>effet DropShadowEffect personnalisé</TextBlock.Text>
</TextBlock>
<TextBlock Canvas.Left= «18» Canvas.Top= «57» FontFamily= «Verdana»
FontSize= «24» Height= «42»
Text= «effet DropShadowEffect par défaut» Width= «570»>
<TextBlock.Effect>
<DropShadowEffect Color= «SlateGray» ShadowDepth= «20» BlurRadius= «6»>
</DropShadowEffect>
</TextBlock.Effect>
</TextBlock>
Le premier Button possède un effet DrowShadowEffect par défaut, et le deuxième
Button possède un effet personnalisé avec une couleur DarkGray pour l’ombre
portée et une valeur de 300 pour Direction.
<Button Canvas.Left= «18» Canvas.Top= «128» Content= «bouton avec
DropShadowEffect par défaut» Height= «43» Name= «button1» Width= «335»
FontFamily= «Verdana» FontSize= «14»>
<Button.Effect>
<DropShadowEffect></DropShadowEffect>
</Button.Effect>
</Button>
<Button Canvas.Left= «18» Canvas.Top= «192» Content= «bouton avec
DropShadowEffect personnalisé» FontFamily= «Verdana»
FontSize= «14» Height= «43» Name= «button2» Width= «335»>
<Button.Effect>
<DropShadowEffect ShadowDepth= «20» Color= «DarkGray» Direction= «300» />
</Button.Effect>
</Button>
Nous avons vu dans un précédent chapitre que l’image affichée dans un contrôle
Image, était définie dans la propriété Source au moyen d’un objet généralement
de type BitmapImage. En XAML, un URI est directement assignable à la propriété
Source sans mentionner la classe BitmapImage (par exemple <Image Source =
«mon_image.jpg» />).
L’arbre d’héritage (figure 9.14) montre que les classes BitmapImage et
WriteableImage héritent de BitmapSource, puis de ImageSource.
FIGURE 9.14
264 Développez des applications Internet avec Silverlight 5
La création d’une image point par point se fait en manipulant l’objet
WriteableBitmap comme un tableau, au moyen de la propriété indexeur Item.
Chaque valeur du tableau représente un pixel et contient sa couleur. La méthode
Lock doit être appelée au préalable. Quand l’assignation des données du tableau
est terminée, les méthodes Invalidate et Unlock doivent être successivement
appelées.
Si les données sont issues d’un calcul intensif, il est préférable de réaliser celui-
ci dans un thread spécifique. La classe WriteableBitmap ne peut être manipulée
que dans le thread principal mais peut recevoir ses données à partir d’un tableau
rempli dans un thread secondaire.
Le fichier GenererImage.xaml (figure 9.15) visualise un contrôle Image dans lequel
on lui affecte comme source une image bitmap générée aléatoirement.
FIGURE 9.15
Une image bitmap wb, de type WriteableBitmap, est instanciée avec une largeur
et une hauteur identique au contrôle x_img. Deux boucles imbriquées permettent
de parcourir les pixels en largeur et en hauteur.
//on cree une bitmap avec la taille du controle x_img
WriteableBitmap wb =
new WriteableBitmap((int)x_img.Width,(int)x_img.Height);
Random rand = new Random();
for (int x = 0; x < wb.PixelWidth; x++) {
for (int y = 0; y < wb.PixelHeight; y++) {
...
}
}
Le format utilisé par le WriteableBitmap de Silverlight est le format ARGB32 (RVB
prémultipliées). Un pixel est représenté par un entier (de type int, donc sur 32 bits)
qui contient les composantes ARGB.
Le tableau de pixels contient une valeur de type int pour coder une couleur ARGB
(alpha red green blue) sur 32 bits avec gestion de l’opacité. La figure 9.16 illustre
le codage utilisé pour une couleur quelconque. Une séquence de 8 bits permet de
coder un nombre entier compris entre 0 et 255 soit 256 valeurs. Par conséquent la
valeur de la composante rouge d’un pixel peut être représentée selon 256 niveaux
différents, allant de 0 pour l’absence de rouge à 255 pour le rouge d’intensité
maximum. Il en est de même pour la composante bleue et verte, tout comme pour
la composante de transparence (canal alpha avec 0 pour la transparence maximale
et 255 pour l’opacité totale).
FIGURE 9.16
Ici, on génère aléatoirement les composantes des couleurs avec une opacité totale.
for (int x = 0; x < wb.PixelWidth; x++) {
for (int y = 0; y < wb.PixelHeight; y++) {
int alpha = 255;
int red = 0;
int green = 0;
266 Développez des applications Internet avec Silverlight 5
int blue = 0;
//on determine la couleur du pixel
if ((x % 5 == 0) || (y % 7 == 0)) {
red = (int)((double)y / wb.PixelHeight * 255);
green = rand.Next(100, 255);
blue = (int)((double)x / wb.PixelWidth * 255);
} else {
red = (int)((double)x / wb.PixelWidth * 255);
green = rand.Next(100, 255);
blue = (int)((double)y / wb.PixelHeight * 255);
}
...
}
}
Pour coder un pixel sous forme d’un int 32 bits, il faut associer les composantes les
unes à côté des autres. Pour cela on utilisera l’opérateur <<, et la formule employée
sera:
//on fixe le pixel
int pixelColorValue = (alpha << 24) | (red << 16) |
(green << 8) | (blue << 0);
Le pixel sous forme d’entier étant généré, il est ajouté au tableau des pixels, par la
propriété Pixels, en n’oubliant pas que ce tableau est au format unidimensionnel
([ligne]), et non pas au format multidimensionnel ([ligne,colonne]).
//on ajoute le pixel au tableau
wb.Pixels[y * wb.PixelWidth + x] = pixelColorValue;
Dès que le tableau des pixels est rempli, on affecte l’instance wb à la propriété
Source de x_img.
//on cree une bitmap avec la taille du controle x_img
WriteableBitmap wb =
new WriteableBitmap((int)x_img.Width,(int)x_img.Height);
Random rand = new Random();
for (int x = 0; x < wb.PixelWidth; x++) {
for (int y = 0; y < wb.PixelHeight; y++) {
int alpha = 255;
int red = 0;
Copyright 2012 Patrice REY
int green = 0;
int blue = 0;
//on determine la couleur du pixel
if ((x % 5 == 0) || (y % 7 == 0)) {
red = (int)((double)y / wb.PixelHeight * 255);
green = rand.Next(100, 255);
blue = (int)((double)x / wb.PixelWidth * 255);
} else {
red = (int)((double)x / wb.PixelWidth * 255);
CHAPITRE 9 □ Les pinceaux et les images 267
2 - La classe Timeline
Toutes les animations ainsi que la classe Storyboard, héritent de la classe Timeline.
On peut traduire le terme Timeline par chronologie.
Un objet Timeline représente un segment de temps correspondant à l’exécution
d’une animation. Il dispose de propriétés permettant de définir sa durée, son
moment relatif au démarrage, s’il doit être répété, sa réversibilité, son facteur
272 Développez des applications Internet avec Silverlight 5
d’accélération, sa vitesse et son comportement en fin de séquence. De plus il
supporte l’imbrication.
Le Storyboard est un conteneur d’objets Timeline par sa propriété Children. Il est
donc possible d’imbriquer plusieurs Storyboard.
La durée de base d’un objet Timeline est déterminée par la propriété Duration, qui
peut être définie en XAML par une chaîne au format:
[jour.]heures:minutes[:secondes[.fractions]]
Duration = «0:0:0.8»
Duration = «00:5:20»
Un objet Duration peut être initialisé par le code au moyen d’un objet TimeSpan
passé au constructeur et obtenu à partir d’une méthode statique telle que
TimeSpan.FromSeconds.
Duration duree= new Duration(TimeSpan.FromSeconds(0.8));
La propriété BeginTime, de type TimeSpan, indique le temps à partir duquel
l’exécution commence. Ce temps est relatif au Storyboard conteneur, ou à son
déclenchement s’il s’agit de l’objet racine.
<Storyboard BeginTime= «0:0:8» />
La propriété RepeatBehavior permet de configurer la répétition d’un objet Timeline
ainsi que la façon de le répéter. Elle définit:
• soit le nombre d’exécutions de l’objet Timeline (propriété Count).
• soit la durée de l’exécution (propriété Duration); l’exécution est répétée tant
que cette durée n’est pas atteinte.
La propriété statique RepeatBehavior.Forever renvoie un objet RepeatBehavior
qui représente une durée de répétition infinie. Cet objet peut être assigné à la
propriété RepeatBehavior de l’objet Timeline pour répéter en boucle son exécution.
<Storyboard RepeatBehavior= «Forever» />
La propriété booléenne AutoReverse permet d’inverser l’exécution d’un objet
Timeline après une première exécution normale.
<Storyboard AutoReverse= «True» />
Copyright 2012 Patrice REY
1 2
4 3
274 Développez des applications Internet avec Silverlight 5
D’un point de vue de l’organisation des contrôles, on positionne sur un Canvas
x_cnv_root les contrôles suivants:
• un bouton x_btn_anim1_agrandir qui permettra de lancer une animation pour
agrandir l’image à sa taille réelle.
• un bouton x_btn_anim1_reduire qui permettra de lancer une animation pour
réduire l’image à sa taille réduite initiale.
• un ScrollViewer x_scroll_tortue, avec ses barres de défilement, horizontale et
verticale, visibles.
• un contrôle Image, positionné à l’intérieur du x_scroll_tortue, avec un
alignement horizontal fixé à Left et un alignement vertical fixé à Top par rapport
à son conteneur parent; la propriété Source référence l’image intitulée tortue_
moorea.jpg (dans le dossier contenu) dont sa taille réelle est de 1024 par 768
pixels; ce contrôle a comme taille réduite de départ 204 par 152 pixels.
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke» Width= «600»
Height= «600»>
...
<!-- animation 1 -->
<Line X1= «0» Y1= «0» X2= «600» Y2= «0» Stroke= «Black» Width= «600»
Height= «7» Canvas.Top= «356» StrokeDashArray= «4,2» Canvas.Left= «0»>
</Line>
<ScrollViewer Canvas.Left= «12» Canvas.Top= «41» Height= «309»
Name= «x_scroll_tortue» Width= «576»
HorizontalScrollBarVisibility= «Visible»>
<Image Height= «152» Name= «x_img_tortue» Stretch= «Fill» Width= «204»
HorizontalAlignment= «Left» VerticalAlignment= «Top»
Source= «/DemoAnimation;component/contenu/tortue_moorea.jpg» />
</ScrollViewer>
<Button Canvas.Left= «12» Canvas.Top= «12» Content= «agrandir image tortue»
Height= «23» Name= «x_btn_anim1_agrandir» Width= «168» FontFamily= «Verdana»
FontSize= «14» Click= «x_btn_anim1_Click» Cursor= «Hand»/>
<Button Canvas.Left= «420» Canvas.Top= «12» Content= «reduire image tortue»
FontFamily= «Verdana» FontSize= «14» Height= «23»
Name= «x_btn_anim1_reduire» Width= «168» Click= «x_btn_anim1_reduire_Click»
Cursor= «Hand»/>
</Canvas>
Un contrôle Image a une propriété Width et Height qui correspondent à la largeur
Copyright 2012 Patrice REY
et la hauteur. Ces propriétés sont de type double. Pour animer ces propriétés, il
faut utiliser une animation qui gère le type double, et donc une animation de type
DoubleAnimation.
Dans les ressources du Canvas (<Canvas.Resources>), on ajoute un Storyboard
intitulé x_story_agrandir_tortue qui consiste à faire passer la taille de l’image de sa
taille réduite à sa taille réelle. Pour animer la propriété Width du contrôle Image,
CHAPITRE 10 □ Les animations 275
}
//qd storyboard reduire est termine
private void x_story_reduire_tortue_Completed(object sender, EventArgs e) {
x_btn_anim1_agrandir.IsEnabled = true;
}
Maintenant nous allons développer la même animation uniquement par code
de programmation (fichier AnimationBasiqueParCode.xaml, dans le projet
DemoAnimation.sln dans le dossier chapitre10). La figure 10.3 visualise l’obtention
CHAPITRE 10 □ Les animations 277
du même résultat.
FIGURE 10.3
anim_haut.From = 768;
anim_haut.To = 152;
anim_haut.By = -30;
anim_haut.Duration = new Duration(TimeSpan.FromSeconds(3));
m_story_reduire.Children.Add(anim_larg);
m_story_reduire.Children.Add(anim_haut);
Storyboard.SetTarget(anim_larg, x_img_tortue);
Storyboard.SetTarget(anim_haut, x_img_tortue);
Storyboard.SetTargetProperty(anim_larg, new PropertyPath(«Width»));
Storyboard.SetTargetProperty(anim_haut, new PropertyPath(«Height»));
x_cnv_root.Resources.Add(«x_story_reduire», m_story_reduire);
}
//animation 1: agrandir la tortue
private void x_btn_anim1_agrandir_Click(object sender, RoutedEventArgs e) {
x_btn_anim1_agrandir.IsEnabled = false;
m_story_agrandir.Begin();
m_story_agrandir.Completed += new EventHandler(v_story_agrandir_Completed);
}
//qd storyboard agrandir est termine
private void v_story_agrandir_Completed(object sender, EventArgs e) {
x_btn_anim1_reduire.IsEnabled = true;
}
//animation 1: reduire la tortue
private void x_btn_anim1_reduire_Click(object sender, RoutedEventArgs e) {
x_btn_anim1_reduire.IsEnabled = false;
m_story_reduire.Begin();
m_story_reduire.Completed += new EventHandler(m_story_reduire_Completed);
}
//qd storyboard reduire est termine
private void m_story_reduire_Completed(object sender, EventArgs e) {
x_btn_anim1_agrandir.IsEnabled = true;
}
Pause
Reprise Reprise
Démarrer
Arrêt Arrêt
Pause
Reprise
Arrêt
x_btn_reprise.IsEnabled = false;
x_btn_arret.IsEnabled = true;
}
//btn pause
private void x_btn_pause_Click(object sender, RoutedEventArgs e) {
x_story_rotation.Pause();
x_btn_demarrer.IsEnabled = false;
x_btn_pause.IsEnabled = false;
x_btn_reprise.IsEnabled = true;
CHAPITRE 10 □ Les animations 283
x_btn_arret.IsEnabled = true;
}
//btn reprise
private void x_btn_reprise_Click(object sender, RoutedEventArgs e) {
x_story_rotation.Resume();
x_btn_demarrer.IsEnabled = false;
x_btn_pause.IsEnabled = true;
x_btn_reprise.IsEnabled = false;
x_btn_arret.IsEnabled = true;
}
//btn arret
private void x_btn_arret_Click(object sender, RoutedEventArgs e) {
x_story_rotation.Stop();
x_btn_demarrer.IsEnabled = true;
x_btn_pause.IsEnabled = false;
x_btn_reprise.IsEnabled = false;
x_btn_arret.IsEnabled = false;
}
5.1 - Principe
Temps t
FIGURE 10.7
FIGURE 10.8
Valeurs Valeurs
mode EaseIn mode EaseOut
= f(t) = f(t)
Temps t Temps t
286 Développez des applications Internet avec Silverlight 5
FIGURE 10.9
Valeurs
= f(t)
mode EaseInOut
Temps t
1 2 3
se stabilise. La figure 10.11 visualise cette fonction d’accélération dans ses trois
modes d’interpolation.
Le fichier RessortElastique.xaml, dans le projet DemoAnimation.sln dans le dossier
chapitre10, illustre l’animation d’un ressort oscillant (figure 10.12). Cette animation
utilise l’effet ElasticEase (repère n°1) avec son mode d’interpolation par défaut
(EaseOut). Quand le ressort est lâché, les oscillations se produisent en fonction des
CHAPITRE 10 □ Les animations 289
Valeurs Valeurs
mode EaseIn mode EaseOut
= f(t) = f(t)
Temps t Temps t
Valeurs
= f(t)
mode EaseInOut
Temps t
FIGURE 10.12
2 3
290 Développez des applications Internet avec Silverlight 5
Sur le Canvas x_cnv_root, on positionne une image x_img_ressort représentant
un ressort accroché par le haut et tendu par le bas (propriété Source qui référence
l’image ressort.png). Une fois le ressort lâché, le bas du ressort va remonter vers
le haut puis redescendre vers le bas, en effectuant des oscillations. Ces oscillations
vont être animées par un storyboard avec l’effet ElasticEase. Le rectangle x_rect_
sol sert à matérialiser le bas où le ressort est attaché au départ.
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke» Width= «600»
Height= «600»>
<!-- animation 1 -->
...
<Line X1= «0» Y1= «0» X2= «0» Y2= «600» Stroke= «Black» Width= «2»
Height= «600» Canvas.Top= «12» StrokeDashArray= «4,2»
Canvas.Left= «67» StrokeThickness= «2»></Line>
<Button Canvas.Left= «166» Canvas.Top= «332» Content= «Jouer l’animation
(mode ElasticEase)» Height= «29» Name= «x_btn_demarrer» Width= «408»
FontFamily= «Verdana» FontSize= «14» Click= «x_btn_demarrer_Click»
Cursor= «Hand» />
<Image Canvas.Left= «25» Canvas.Top= «20» Height= «424» Name= «x_img_ressort»
Stretch= «Fill» Width= «80»
Source= «/DemoAnimation;component/contenu/ressort.png» />
<Rectangle Canvas.Left= «16» Canvas.Top= «450» Height= «11»
Name= «x_rect_sol»
Stroke= «Black» StrokeThickness= «1»
Width= «102» Fill= «DarkGray» />
<Image Canvas.Left= «166» Canvas.Top= «20» Height= «288» Name= «image1»
Stretch= «Fill» Width= «408»
Source= «/DemoAnimation;component/contenu/elastic_ease.jpg» />
</Canvas>
Dans les ressources du Canvas, on ajoute un Storyboard x_story_ressort. La
propriété que l’on souhaite animer est la propriété Height de l’image x_img_ressort
(pour simuler des oscillations). Cette propriété étant de type double, on ajoute
une animation DoubleAnimation qui cible la propriété Height (TargetProperty) de
l’image x_img_ressort (TargetName). La plage des valeurs de la propriété à animer
est comprise entre 424 et 200 pixels (amplitude dans laquelle les oscillations sont
effectuées pour la simulation). La durée de l’animation est fixée à trois secondes.
Pour utiliser la fonction d’accélération prédéfinie ElasticEase, on ajoute un objet
Copyright 2012 Patrice REY
La classe PowerEase représente une fonction d’accélération qui crée une animation
qui accélère et/ou ralentit à l’aide de la formule f(t) = tp où p est égal à la propriété
Power. La figure 10.13 visualise cette fonction d’accélération dans ses trois modes
d’interpolation.
Le fichier Freinage.xaml, dans le projet DemoAnimation.sln dans le dossier
chapitre10, illustre l’animation d’une voiture qui freine pour éviter de percuter un
292 Développez des applications Internet avec Silverlight 5
mur (figure 10.14). Cette animation utilise l’effet PowerEase (repère n°1) avec son
mode d’interpolation par défaut (EaseOut). Quand la voiture s’approche du mur,
elle freine en fonction des valeurs fournies par l’effet PowerEase (repère n°2 et
n°3).
FIGURE 10.13
Valeurs Valeurs
mode EaseIn mode EaseOut
= f(t) = f(t)
Temps t Temps t
Valeurs
= f(t)
mode EaseInOut
Temps t
...
<Line X1= «0» Y1= «0» X2= «600» Y2= «0» Stroke= «Black» Width= «600»
Height= «2» Canvas.Top= «80» StrokeDashArray= «4,2»
Canvas.Left= «0» StrokeThickness= «2»></Line>
<Button Canvas.Left= «101» Canvas.Top= «110» Content= «Jouer l’animation
(mode PowerEase)» Height= «29» Name= «x_btn_demarrer» Width= «408»
FontFamily= «Verdana» FontSize= «14» Click= «x_btn_demarrer_Click»
Cursor= «Hand» />
<Image Canvas.Left= «14» Canvas.Top= «35» Height= «46» Name= «x_img_voiture»
CHAPITRE 10 □ Les animations 293
FIGURE 10.14
}
//evenement completed
private void x_story_voiture_Completed(object sender, EventArgs e) {
x_btn_demarrer.IsEnabled = true;
}
CHAPITRE 10 □ Les animations 295
FIGURE 10.15
Valeurs
= f(t) mode EaseIn
Temps t
296 Développez des applications Internet avec Silverlight 5
Nous avons besoin de propriétés pour gérer les montées et les paliers. On ajoute
une propriété de dépendance NombrePalier qui représentera le nombre de paliers
sur la durée de l’animation (ici une valeur de 4 par défaut). On ajoute une propriété
de dépendance SeuilPalier qui représentera la valeur du seuil du palier (ici une
valeur de 0.25 par défaut). La méthode EaseInCore calcule alors, en fonction de
normalizedTime, la valeur, de type double, à appliquer.
public class FonctionPallier : EasingFunctionBase {
//redefinition de la methode
protected override double EaseInCore(double normalizedTime) {
double normalizedTargetValue = 1.0;
for (int i = 0; i < NombrePalier; i++) {
double v_palier_depart = (double)i / NombrePalier;
double v_palier_longeur = 1.0 / NombrePalier;
double thresholdStart = v_palier_depart + LongueurPalier *
v_palier_longeur;
double v_palier_fin = v_palier_depart + v_palier_longeur;
if ((normalizedTime >= v_palier_depart) &&
(normalizedTime < thresholdStart)) {
normalizedTargetValue = v_palier_depart +
(normalizedTime - v_palier_depart) / LongueurPalier;
break;
}
if ((normalizedTime >= thresholdStart) &&
(normalizedTime < v_palier_fin)) {
normalizedTargetValue = v_palier_fin;
break;
}
}
return normalizedTargetValue;
}
//propriete de dependance
private static readonly DependencyProperty NombrePalierPropriete =
DependencyProperty.Register(
«NombrePalier»,
typeof(int),
typeof(FonctionPallier),
new PropertyMetadata(4));
public int NombrePalier {
Copyright 2012 Patrice REY
get {
return (int)base.GetValue(NombrePalierPropriete);
}
set {
base.SetValue(NombrePalierPropriete, value);
}
}
//propriete de dependance
private static readonly DependencyProperty SeuilPalierPropriete =
CHAPITRE 10 □ Les animations 297
DependencyProperty.Register(
«SeuilPalier»,
typeof(double),
typeof(FonctionPallier),
new PropertyMetadata(0.25));
public double SeuilPalier {
get {
return (double)base.GetValue(SeuilPalierPropriete);
}
set {
base.SetValue(SeuilPalierPropriete, value);
}
}
}//end class
Le fichier FonctionPersonnalisee.xaml, dans le projet DemoAnimation.sln dans
le dossier chapitre10, illustre une animation dans laquelle des images tournent
autour de l’axe X (figure 10.16). Cette animation utilise une fonction d’accélération
par palier FonctionPallier avec son mode d’interpolation EaseIn (repère n°1). Le
repère n°2 montre quelques clichés pour le passage de la première à la deuxième
image. Le repère n°3 montre le positionnement de ces clichés sur la courbe à
paliers.
On positionne sur le Canvas x_cnv_root, quatre images x_img1, x_img2, x_
img3 et x_img4. Ces quatre images ont une propriété Projection fixée avec un
PlaneProjection, dont la propriété RotationX est fixée par les valeurs de 0, 90, 180
et 270 respectivement.
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke» Width= «600»
Height= «600»>
<!-- animation 1 -->
...
<Image Name= «x_img1» Stretch= «Fill»
Source= «/DemoAnimation;component/contenu/poisson_1.png»
Canvas.Left= «201» Canvas.Top= «47»>
<Image.Projection>
<PlaneProjection CenterOfRotationZ= «-100» RotationX= «0»/>
</Image.Projection>
</Image>
<Image Name= «x_img2» Stretch= «Fill»
Source= «/DemoAnimation;component/contenu/poisson_2.png»
Canvas.Left= «201» Canvas.Top= «47»>
<Image.Projection>
<PlaneProjection CenterOfRotationZ= «-100» RotationX= «90»/>
</Image.Projection>
</Image>
<Image Name= «x_img3» Stretch= «Fill»
Source= «/DemoAnimation;component/contenu/poisson_3.png»
298 Développez des applications Internet avec Silverlight 5
FIGURE 10.16
f(t)
mode EaseIn
Copyright 2012 Patrice REY
Temps t
CHAPITRE 10 □ Les animations 299
Une animation basique utilise deux valeurs cibles, avec une valeur de début
définie par From, et une valeur de fin définie par To. Une animation d’images clés
Copyright 2012 Patrice REY
Les classes dérivées ont leur nom qui répondent au modèle suivant:
<interpolation><type>KeyFrame.
Par exemple la classe LinearDoubleKeyFrame définit une valeur cible pour une
propriété de type double avec une méthode d’interpolation linéaire. Si la propriété
cible est de type Color, ce sera la classe LinearColorKeyFrame. Si la propriété cible
302 Développez des applications Internet avec Silverlight 5
est de type Point, ce sera la classe LinearPointKeyFrame.
Par exemple la classe DiscreteObjectKeyFrame définit une valeur cible pour une
propriété de type object avec une méthode d’interpolation par palier (Discrete).
Si la propriété cible est de type Color, ce sera la classe DiscreteColorKeyFrame. Si
la propriété cible est de type Point, ce sera la classe DiscretePointKeyFrame. Si la
propriété cible est de type double, ce sera la classe DiscreteDoubleKeyFrame.
Par exemple la classe EasingColorKeyFrame définit une valeur cible pour une
propriété de type Color avec une méthode d’interpolation réaliste (Easing). Si la
propriété cible est de type double, ce sera la classe EasingDoubleKeyFrame. Si la
propriété cible est de type Point, ce sera la classe EasingPointKeyFrame.
Par exemple la classe SplineColorKeyFrame définit une valeur cible pour
une propriété de type Color avec une méthode d’interpolation en courbe
de Bézier (Spline). Si la propriété cible est de type double, ce sera la classe
SplineDoubleKeyFrame. Si la propriété cible est de type Point, ce sera la classe
SplinePointKeyFrame.
Un objet KeyFrame définit sa valeur au moyen de la propriété Value, et l’instant
auquel la valeur doit être atteinte dans sa propriété KeyTime (qui s’exprime sous
la forme d’un objet TimeSpan). L’animation regroupe les objets KeyFrame dans sa
propriété KeyFrames. Si le premier objet KeyFrame n’est pas sur le temps zéro, une
transition est automatiquement effectuée depuis la valeur courante.
<Canvas.Resources>
<Storyboard x:Name= «x_story_rect»>
<ColorAnimationUsingKeyFrames x:Name= «x_anim_color»
Storyboard.TargetName= «x_rect»
Storyboard.TargetProperty= «(Rectangle.Fill).(SolidColorBrush.Color)»>
<LinearColorKeyFrame KeyTime= «00:00:00» Value= «DarkGray»>
</LinearColorKeyFrame>
<LinearColorKeyFrame KeyTime= «00:00:02» Value= «LightGray»>
</LinearColorKeyFrame>
<LinearColorKeyFrame KeyTime= «00:00:04» Value= «SlateGray»>
</LinearColorKeyFrame>
</ColorAnimationUsingKeyFrames>
...
</Storyboard>
</Canvas.Resources>
304 Développez des applications Internet avec Silverlight 5
Pour faire varier la position horizontale du rectangle (propriété Canvas.Left de type
double), on utilise une animation DoubleAnimationUsingKeyFrames, intitulée
x_anim_left. Elle cible la propriété Canvas.Left du rectangle (TargetProperty =
«(Canvas.Left)»). Une interpolation linéaire, pour animer une propriété de type
double, se fera par un objet LinearDoubleKeyFrame. A l’instant zéro (KeyTime
= «00:00:00»), la valeur du LinearDoubleKeyFrame sera de 12. A l’instant
deux secondes (KeyTime = «00:00:02»), la valeur du LinearDoubleKeyFrame
sera de 200. A l’instant quatre secondes (KeyTime = «00:00:04»), la valeur du
LinearDoubleKeyFrame sera de 450.
<Canvas.Resources>
<Storyboard x:Name= «x_story_rect»>
...
<DoubleAnimationUsingKeyFrames x:Name= «x_anim_left»
Storyboard.TargetName= «x_rect»
Storyboard.TargetProperty= «(Canvas.Left)»>
<LinearDoubleKeyFrame KeyTime= «00:00:00» Value= «12»>
</LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime= «00:00:02» Value= «200»>
</LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime= «00:00:04» Value= «450»>
</LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Canvas.Resources>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames x:Name= «x_anim_usa» Duration= «00:00:08»
Storyboard.TargetName= «x_cnv_usa»
Storyboard.TargetProperty= «Visibility»>
<DiscreteObjectKeyFrame KeyTime= «00:00:00»>
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime= «00:00:02»>
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime= «00:00:04»>
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
...
</Storyboard>
</Canvas.Resources>
(1,0)
(1,0.5)
(0,0.5)
(0,1)
580 pixels
CHAPITRE 10 □ Les animations 313
//relevé heure
private DateTime m_reference;
//temps ecoulé entre 2 relevés
public TimeSpan TempsEcoule {
get;
private set;
}
//cumul du temps ecoulé tant qu’il n’y a
//pas eu de remise à zéro
CHAPITRE 10 □ Les animations 315
Imagette.
L’objet Imagette instancié est ajouté au Canvas x_cnv_jeu, avec une position fixée
par les méthodes statiques Canvas.SetLeft et Canvas.SetTop, et il est nommé
(propriété Name) par un identificateur unique. Le meilleur moyen de fixer un
identificateur unique consiste à associer l’heure courante au format heure/minute/
seconde (en utilisant DateTime.Now qui renvoie l’heure courante). Puis cet objet
CHAPITRE 10 □ Les animations 317
les objets
Imagette
naissent en
haut puis
descendent
vers le bas
Pour cela, on effectue un parcours des éléments contenus dans l’arbre visuel
du Canvas. Chaque élément, de type Imagette, dont la position verticale est
supérieure à 320 pixels, est retiré de la liste (par la méthode RemoveAt), et est
retiré physiquement du Canvas (par la méthode RetirerImagetteCanvas).
// rendu image par image
private void CompositionTarget_Rendering(object sender, EventArgs e) {
...
int cpt_imagette = m_liste_imagette.Count;
for (int xx = 0; xx < cpt_imagette; xx++) {
double pos_y = Canvas.GetTop(m_liste_imagette[xx]);
if (pos_y >= 320) {
string nom = m_liste_imagette[xx].Name;
RetirerImagetteCanvas(nom);
m_liste_imagette.RemoveAt(xx);
cpt_imagette--;
Copyright 2012 Patrice REY
}
}
}
La méthode RetirerImagetteCanvas reçoit en paramètre l’identificateur unique
du contrôle Imagette à retirer. Un parcours de l’arbre visuel du Canvas (propriété
Children) permet de cibler le contrôle Imagette recherché, en obtenant l’index
correspondant. Une fois le parcours terminé, la méthode RemoveAt permet de
CHAPITRE 10 □ Les animations 319
les objets
Imagette en
dehors du
Canvas sont
supprimés de
l’arbre visuel
du Canvas
Maintenant il ne nous reste plus qu’à faire en sorte de voir uniquement les objets
contenus dans les limites du Canvas. Pour cela, il faut clipper le Canvas par une
région rectangulaire dont sa dimension sera identique à celle du Canvas.
La propriété Clip du Canvas x_cnv_jeu reçoit un objet de type Geometry (c’est-à-dire
une géométrie). On instancie donc un objet rect_clip de type RectangleGeometry
320 Développez des applications Internet avec Silverlight 5
(qui représente une géométrie rectangulaire). Sa propriété Rect reçoit les
dimensions sous la forme d’un objet de type Rect. Une des surcharges du type
Rect nous permet de spécifier le coin haut gauche (coordonnées x=0 et y=0), la
longueur (580 pixels) et la largeur (320 pixels).
//
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
...
RectangleGeometry rect_clip = new RectangleGeometry();
rect_clip.Rect = new Rect(0, 0, 580, 320);
x_cnv_jeu.Clip = rect_clip;
}
La figure 10.25 montre le résultat obtenu. On voit bien que les contrôles Imagette
naissent en haut du Canvas, puis descendent en bas du Canvas. Le fait de clipper
le Canvas permet de masquer la zone non visible.
FIGURE 10.25
x_btn_demarrer.IsEnabled = true;
x_btn_stopper.IsEnabled = false;
}
8 - L’accélération matérielle
la fréquence
d'images
la quantité de
mémoire
graphique utilisée
(GPU) en Ko
le nombre de
surfaces
accélérées par le
GPU
le nombre de
surfaces
intermédiaires Copyright 2012 Patrice REY
9 - Le cache de composition
Grid x_grid_cache
Grid x_grid
<BitmapCache
RenderAtScale="10">
</BitmapCache>
L’audio et
vidéo
• La classe
MediaElement
la vidéo • DispatcherTimer
• La prise en charge de
l’audio et de la vidéo
• VideoBrush
• L’acquisition audio et
vidéo
• Les classes
CaptureSource et
Ce chapitre est dédié à l’une des caractéristiques CaptureDevice
• AudioCaptureDevice
les plus importantes de Silverlight qu’est la prise en • VideoCaptureDevice
charge de l’audio et de la vidéo.
Comme Silverlight est présent sur différents
systèmes et navigateurs, la prise en charge des
formats audio et vidéo doit être la plus large
possible. L’utilisation de l’audio et de la vidéo
est très courant dans le domaine du multimédia.
Silverlight prend en charge un grand nombre de
format de fichier audio et vidéo.
Dans ce chapitre, vous verrez comment lire les
fichiers audio et vidéo au travers de leurs différents
formats.
Ecouter une piste sonore et savoir comment
obtenir ses caractéristiques (durée, etc.) sont des
manipulations courantes à connaître.
Visionner une piste vidéo, obtenir ses
caractéristiques et savoir lire la position du curseur
vidéo en temps réel, sont des manipulations
courantes à connaître.
L’acquisition audio et vidéo, par l’intermédiaire
d’une webcam, sera abordée car son utilisation
est couramment employée pour développer des
applications Internet pointues.
328 Développez des applications Internet avec Silverlight 5
L’utilisation de l’audio et de la vidéo est très courant dans le domaine du multimédia.
Silverlight prend en charge un grand nombre de format de fichier audio et vidéo.
Les formats audio pris en charge par Silverlight sont Windows Media Audio (WMA)
versions 7, 8, and 9, et MP3 (ISO MPEG-1 Layer III).
Les formats vidéo pris en charge sont Windows Media Video 7 (WMV1), Windows
Media Video 8 (WMV2), Windows Media Video 9 (WMV3), Windows Media Video
Advanced Profile (non-VC-1 WMVA), Windows Media Video Advanced Profile (VC-
1 WMVC1), et H.264 vidéo and AAC audio (aussi connu sous le nom MPEG-4 Part
10 or MPEG-4 AVC).
Les formats suivants ne sont pas pris en charge: contenu de vidéo entrelacée,
Windows Media Screen, Windows Media Audio Professional sans perte, Windows
Media Voice, combinaison de Windows Media Video et MP3 (vidéo WMV + audio
MP3), et combinaison de WMV et AAC-LC.
2 - Le contrôle MediaElement
valeurs non définies. Une fois que vous spécifiez une source, le média apparaîtra
à sa taille naturelle, et la disposition recalculera la taille. Si vous devez modifier la
taille d’affichage d’un média, il vaut mieux encoder à nouveau ce dernier selon la
taille souhaitée à l’aide d’un outil d’encodage multimédia.
Par défaut, les médias définis par la propriété Source sont lus immédiatement
après le chargement de l’objet MediaElement. Pour empêcher le démarrage
automatique des médias, affectez à la propriété AutoPlay la valeur false.
CHAPITRE 11 □ L’audio et la vidéo 329
FIGURE 11.1
ComboBox x_select
Slider
x_slider_position TextBlock x_position_encours
4
CHAPITRE 11 □ L’audio et la vidéo 331
FIGURE 11.3
MediaElement media =
(MediaElement)x_cnv_root.FindName(«x_mediaelement»);
x_cnv_root.Children.Remove(media);
}
m_media = new MediaElement();
m_media.Name = «x_mediaelement»;
x_cnv_root.Children.Add(m_media);
... }
}
CHAPITRE 11 □ L’audio et la vidéo 335
4
338 Développez des applications Internet avec Silverlight 5
Background= «White» Width= «574» Canvas.Left= «12»
Canvas.Top= «445» Height= «77»>
<TextBlock Canvas.Left= «12» Canvas.Top= «18» Height= «63» Name= «x_infos»
Text= «infos» Width= «560» Margin= «5»
TextWrapping= «Wrap» HorizontalAlignment= «Left» VerticalAlignment= «Top»
/>
</Border>
<TextBlock Canvas.Left= «407» Canvas.Top= «319» Height= «23»
Name= «x_position_encours» Text= «00:00:00 sur 00:00:00» Width= «176»
TextAlignment= «Right» FontFamily= «Verdana» FontSize= «14» />
<TextBlock Canvas.Left= «12» Canvas.Top= «372» Height= «23»
Name= «x_text_volume» Text= «Volume:» />
<Slider Name= «x_slider_volume» Width= «269» Height= «31» Canvas.Left= «12»
Canvas.Top= «396» Cursor= «Hand» Minimum= «0»
Maximum= «1» SmallChange= «0.25» LargeChange= «0.25»
ValueChanged= «x_slider_volume_ValueChanged» Value= «0.5»></Slider>
<TextBlock Canvas.Left= «317» Canvas.Top= «372» Height= «23»
Name= «x_text_balance» Text= «Balance:» />
<Slider Canvas.Left= «317» Canvas.Top= «396» Cursor= «Hand» Height= «31»
LargeChange= «0.25» Maximum= «1» Minimum= «-1»
Name= «x_slider_balance» SmallChange= «0.25» Value= «0» Width= «269»
ValueChanged= «x_slider_balance_ValueChanged» />
<Button Canvas.Left= «14» Canvas.Top= «317» Content= «Lecture» Height= «23»
Name= «x_btn_lecture» Width= «75» Click= «x_btn_lecture_Click»
Cursor= «Hand»/>
<Button Canvas.Left= «176» Canvas.Top= «317» Content= «Arrêt» Height= «23»
Name= «x_btn_arret» Width= «75» Click= «x_btn_arret_Click» Cursor= «Hand» />
<Button Canvas.Left= «95» Canvas.Top= «317» Content= «Pause» Height= «23»
Name= «x_btn_pause» Width= «75» Click= «x_btn_pause_Click» Cursor= «Hand»/>
<ProgressBar Name= «x_progress» Width= «571» Height= «16» Canvas.Top= «348»
Canvas.Left= «12» Value= «50» Foreground= «Red» Background= «Lime»>
</ProgressBar>
<Button Canvas.Left= «257» Canvas.Top= «317» Cursor= «Hand» Height= «23»
Name= «x_btn_ecran» Width= «157»
Content= «Passer en plein écran» Click= «x_btn_ecran_Click»/>
</Canvas>
La variable v_volume_encours stocke le niveau sonore de la vidéo, la variable
v_balance_encours stocke le niveau de la balance des haut-parleurs, la variable
m_dispatcher représente la minuterie utilisée, et la variable v_etat_video_encours
Copyright 2012 Patrice REY
stocke une valeur de l’énumération EtatVideo (qui représente les états dans
lesquels se trouve la vidéo comme la lecture, la pause, l’arrêt et l’ouverture après
le chargement sans erreur).
public enum EtatVideo { non_defini, ouvert, lecture, pause, arret };
private EtatVideo v_etat_video_encours;
private DispatcherTimer m_dispatcher = null;
private double v_volume_encours = 0.5;
CHAPITRE 11 □ L’audio et la vidéo 339
v_etat_video_encours = EtatVideo.pause;
x_mediaelement.Pause();
GererLesBoutons();
}
L’événement Tick de la minuterie permet d’effectuer un relevé de la position du
curseur vidéo, et d’afficher cette position par rapport à la durée totale de la vidéo
(repère n°2 de la figure 11.4). La valeur de la progression de la vidéo est mise à jour
dans le ProgressBar.
CHAPITRE 11 □ L’audio et la vidéo 341
5 - Le pinceau vidéo
</Rectangle>
<Button Canvas.Left= «14» Canvas.Top= «317» Content= «Lecture» Height= «23»
Name= «x_btn_lecture» Width= «75» Click= «x_btn_lecture_Click»
Cursor= «Hand»/>
<Button Canvas.Left= «95» Canvas.Top= «317» Content= «Arrêt» Height= «23»
Name= «x_btn_arret» Width= «75» Click= «x_btn_arret_Click» Cursor= «Hand»
/>
</Canvas>
CHAPITRE 11 □ L’audio et la vidéo 343
FIGURE 11.5
Rectangle
x_rect_video
6 - La vidéo réfléchie
La figure 11.6 visualise le résultat obtenu pour une vidéo réfléchie avec l’UserControl
VideoReflechie.xaml (dans le projet DemoAudiVideo.sln dans le dossier chapitre11).
Pour réaliser une vidéo réfléchie, on commence par ajouter un rectangle x_rect_
video_reflect dont sa propriété Fill est affectée d’un objet VideoBrush, avec son
SourceName faisant référence à un objet MediaElement.
Pour inverser la vidéo, on applique à sa propriété RelativeTransform, une
transformation de type ScaleTransform dont la propriété CenterY est fixée à 0.5 et
la propriété ScaleY est fixée à -1 (effet de miroir horizontal).
Pour estomper le bas du rectangle, on applique un masque d’opacité (propriété
OpacityMask) composé d’un LinearGradientBrush, avec une couleur noire de
départ et une couleur transparente en fin.
<Rectangle Name= «x_rect_video_reflect» Stroke= «Black» Width= «600»
Height= «311» Canvas.Left= «0» Canvas.Top= «350» >
344 Développez des applications Internet avec Silverlight 5
<Rectangle.Fill>
<VideoBrush SourceName= «x_mediaelement»>
<VideoBrush.RelativeTransform>
<ScaleTransform ScaleY= «-1» CenterY= «0.5»></ScaleTransform>
</VideoBrush.RelativeTransform>
</VideoBrush>
</Rectangle.Fill>
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint= «0,0» EndPoint= «0,1»>
<GradientStop Color= «Black» Offset= «0»></GradientStop>
<GradientStop Color= «Transparent» Offset= «0.6»></GradientStop>
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
FIGURE 11.6
Rectangle
x_rect_video
Rectangle
x_rect_video_reflect
CHAPITRE 11 □ L’audio et la vidéo 345
7 - L’acquisition vidéo
2
1
choix de la source
audio
choix de la source
vidéo
1 3
2
348 Développez des applications Internet avec Silverlight 5
Sur le Canvas x_cnv_root sont positionnés un bouton x_btn_ouvrir pour ouvrir la
capture vidéo, un bouton x_btn_fermer pour arrêter la capture vidéo, un bouton
x_btn_photo pour prendre un cliché instantané de la capture vidéo, un rectangle
x_rect_webcam pour restituer la capture vidéo en temps réel, un Image x_img_
photo pour restituer le cliché instantané capturé à partir du flux vidéo, et un
StackPanel x_stack_photo pour stocker des contrôles Image qui représentent les
clichés instantanés réalisés.
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke»
HorizontalAlignment= «Center» VerticalAlignment= «Center»
Width= «600» Height= «630»>
<Rectangle Canvas.Left= «12» Canvas.Top= «12» Height= «245»
Name= «x_rect_webcam» Stroke= «Black» StrokeThickness= «1»
Width= «318» Fill= «DarkGray» />
<Button Canvas.Left= «368» Canvas.Top= «36» Content= «Ouvrir la capture
vidéo» Height= «32» Name= «x_btn_ouvrir» Width= «220»
FontSize= «14» Click= «x_btn_ouvrir_Click» Cursor= «Hand»/>
<Button Canvas.Left= «368» Canvas.Top= «74» Content= «Fermer la capture
vidéo» Cursor= «Hand» FontSize= «14» Height= «32»
Name= «x_btn_fermer» Width= «220» Click= «x_btn_fermer_Click»/>
<Button Canvas.Left= «183» Canvas.Top= «280» Content= «Prendre une photo»
Cursor= «Hand» FontSize= «14» Height= «32»
Name= «x_btn_photo» Width= «147» Click= «x_btn_photo_Click»/>
<Border BorderThickness= «2» Canvas.Left= «12» Canvas.Top= «318»
BorderBrush= «Black» Background= «DarkGray» Name= «x_border_photo»>
<Image Canvas.Left= «12» Canvas.Top= «327» Height= «245» Name= «x_img_photo»
Stretch= «Fill» Width= «318» />
</Border>
<ScrollViewer Canvas.Left= «345» Canvas.Top= «130» Height= «435»>
<StackPanel Width= «220» HorizontalAlignment= «Center»
VerticalAlignment= «Top» Name= «x_stack_photo»></StackPanel>
</ScrollViewer>
</Canvas>
Le gestionnaire de l’événement Click pour le bouton x_btn_lecture permet
d’ouvrir le flux vidéo en provenance de la caméra. Si la permission est accordée
pour l’utilisation de la caméra (méthodes statiques CaptureDeviceConfiguration.
AllowDeviceAccess et CaptureDeviceConfiguration.RequestDeviceAccess), la
Copyright 2012 Patrice REY
FIGURE 11.10
1 2
La fermeture complète de la capture vidéo peut être faite par la méthode Stop. De
façon à rendre visuellement cette capture interrompue, le rectangle de visualisation
de la capture vidéo x_rect_webcam est rempli par une couleur unie.
//btn fermer la capture
Copyright 2012 Patrice REY
1 - Les styles
La propriété Setters d’un style (qui est une propriété implicite en XAML) permet
de définir une liste d’objets Setter et donc des valeurs de propriétés pour l’objet
cible. Un objet FrameworkElement peut indiquer le style qu’il souhaite utiliser au
moyen de sa propriété Style qui référence un objet de ce type. Un style peut cibler
un type particulier spécifié dans sa propriété TargetType.
L’UserControl AppliquerStyle.xaml (dans la solution DemoStyle.sln dans le dossier
chapitre12) illustre l’utilisation des styles avec leur personnalisation.
FIGURE 12.2
1 2
3 4
qui est prise en compte (figure 12.2 repère n°3) avec une propriété StrokeThickness
égale à 1 et non pas à 4 (comme dans le style). Si par exemple, pour le Rectangle
x_rect4, on redéfinit la propriété StrokeDashArray, c’est cette redéfinition qui est
prise en compte (figure 12.2 repère n°4) avec une propriété StrokeDashArray égale
à «1,1» et non pas à «6,2» (comme dans le style).
<!-- contenu -->
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke»
HorizontalAlignment= «Center» VerticalAlignment= «Center»
Width= «600» Height= «630»>
...
<Rectangle Canvas.Left= «60» Canvas.Top= «194» Height= «55» Name= «x_rect3»
Style= «{StaticResource k_rect_bordure_epaisse}» Width= «110»
StrokeThickness= «1»/>
<Rectangle Canvas.Left= «385» Canvas.Top= «164» Height= «106»
Name= «x_rect4»
Style= «{StaticResource k_rect_bordure_pointille}» Width= «119»
StrokeDashArray= «1,1»/>
</Canvas>
FIGURE 12.3
ce texte est le contenu d’un TextBlock avec le style par défaut, dont les
caractéristiques sont Verdana, taille 14, couleur noire, et
TextWrapping=Wrap
</TextBlock>
<TextBlock Canvas.Left= «51» Canvas.Top= «89» Height= «88» Width= «506»
Style= «{StaticResource k_comic_sans_MS}»>
ce texte est le contenu d’un TextBlock avec le style k_comic_sans_MS,
dont les caractéristiques sont Comic sans MS, taille 18, couleur noire, et
TextWrapping=Wrap
CHAPITRE 12 □ Les styles et les modèles 357
</TextBlock>
</Canvas>
1 2
<Canvas.Resources>
<!-- -->
<FontFamily x:Key= «k_police»>Verdana</FontFamily>
<system:Double x:Key= «k_police_taille»>18</system:Double>
<FontWeight x:Key= «k_police_poids»>Bold</FontWeight>
<Style x:Key= «k_btn_style_1» TargetType= «Button»>
<Setter Property= «Padding» Value= «5» />
<Setter Property= «Margin» Value= «5» />
<Setter Property= «FontFamily» Value= «{StaticResource k_police}» />
<Setter Property= «FontSize» Value= «{StaticResource k_police_taille}» />
<Setter Property= «FontWeight» Value= «{StaticResource k_police_poids}» />
</Style>
...
</Canvas.Resources>
<Canvas x:Name= «x_cnv_root»>
<Button Canvas.Left= «7» Canvas.Top= «7» Content= «bouton avec le style
k_btn_style_1 (verdana 18 bold)» Height= «44» Name= «x_btn1» Width= «576»
Style= «{StaticResource k_btn_style_1}» />
...
</Canvas>
Le bouton x_btn2 (figure 12.5 repère n°2) utilise le style k_btn_style_2. Ce style a
des objets Setter qui font appel à des propriétés d’un objet PoliceEcriture. La classe
PoliceEcriture est déclarée par code. Elle possède les propriétés PoliceFonte pour
instancier une fonte d’écriture, PoliceTaille pour instancier une taille d’écriture, et
360 Développez des applications Internet avec Silverlight 5
PolicePoids pour instancier un poids d’écriture.
Pour accéder à cette classe en XAML, on ajoute une référence à l’espace
de noms contenant cette classe, en l’intitulant <local> (xmlns:local = «clr-
namespace:DemoStyle»). On ajoute un objet PoliceEcriture avec la clé k_police_
personnalise (<local:PoliceEcriture>) en spécifiant une police Comic Sans MS, de
taille 16 et de poids Normal. L’objet Setter, qui référence la propriété FontFamily,
a sa propriété Value qui est liée par databinding à k_police_personnalise pour
la propriété PoliceFonte. L’objet Setter, qui référence la propriété FontSize, a
sa propriété Value qui est liée par databinding à k_police_personnalise pour la
propriété PoliceTaille. Et l’objet Setter, qui référence la propriété FontWeight, a
sa propriété Value qui est liée par databinding à k_police_personnalise pour la
propriété PolicePoids.
<Canvas.Resources>
<local:PoliceEcriture x:Key= «k_police_personnalise»
PoliceFonte= «Comic Sans MS» PoliceTaille= «16»
PolicePoids= «Normal»></local:PoliceEcriture>
<Style x:Key= «k_btn_style_2» TargetType= «Button»>
<Setter Property= «Padding» Value= «5» />
<Setter Property= «Margin» Value= «5» />
<Setter Property= «FontFamily»
Value= «{Binding Source={StaticResource k_police_personnalise},
Path=PoliceFonte}» />
<Setter Property= «FontSize»
Value= «{Binding Source={StaticResource k_police_personnalise},
Path=PoliceTaille}» />
<Setter Property= «FontWeight»
Value= «{Binding Source={StaticResource k_police_personnalise},
Path=PolicePoids}» />
</Style>
...
</Canvas.Resources>
<Canvas x:Name= «x_cnv_root»>
<Button Canvas.Left= «7» Canvas.Top= «73» Content= «bouton avec le style
k_btn_style_2 (comic sans ms 16 normal)» Height= «44» Name= «x_btn2»
Style= «{StaticResource k_btn_style_2}» Width= «576» />
...
Copyright 2012 Patrice REY
</Canvas>
public class PoliceEcriture {
public FontFamily PoliceFonte { get; set; }
public double PoliceTaille { get; set; }
public FontWeight PolicePoids { get; set; }
public PoliceEcriture() {
this.PoliceFonte = new FontFamily(«Verdana»);
this.PoliceTaille = 14;
CHAPITRE 12 □ Les styles et les modèles 361
this.PolicePoids = FontWeights.Normal;
}
}//end class
Le bouton x_btn3 (figure 12.5 repère n°3) utilise le style dico_btn_style qui est
stocké dans le dictionnaire MesStyles.xaml (dans le dossier contenu).
Le fichier MesStyles.xaml est un fichier de type ResourceDictionary, qui déclare le
style dico_btn_style.
<ResourceDictionary
xmlns= «http://schemas.microsoft.com/winfx/2006/xaml/presentation»
xmlns:x= «http://schemas.microsoft.com/winfx/2006/xaml»
xmlns:system= «clr-namespace:System;assembly=mscorlib»>
<!-- mes styles -->
<FontFamily x:Key= «dico_police»>Arial</FontFamily>
<system:Double x:Key= «dico_police_taille»>20</system:Double>
<FontWeight x:Key= «dico_police_poids»>Normal</FontWeight>
<Style x:Key= «dico_btn_style» TargetType= «Button»>
<Setter Property= «Padding» Value= «5» />
<Setter Property= «Margin» Value= «5» />
<Setter Property= «FontFamily» Value= «{StaticResource dico_police}» />
<Setter Property= «FontSize» Value= «{StaticResource dico_police_taille}»
/>
<Setter Property= «FontWeight» Value= «{StaticResource dico_police_poids}»
/>
</Style>
</ResourceDictionary>
Dans les ressources de l’UserControl, on fusionne les dictionnaires de ressources
en ajoutant une référence au dictionnaire MesStyles.xaml (principe déjà vu au
chapitre 2).
<UserControl.Resources>
<!-- utiliser le dictionnaire -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source= «contenu/MesStyles.xaml»>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
A partir de là, le style dico_btn_style du dictionnaire est alors accessible sous forme
d’une ressource statique (Style = «{StaticResource dico_btn_style}»).
<Canvas x:Name= «x_cnv_root»>
<Button Canvas.Left= «12» Canvas.Top= «140» Content= «bouton avec le style
dico_btn_style (arial 20 normal)» Height= «44» Name= «x_btn3»
Style= «{StaticResource dico_btn_style}» Width= «576» /> ...
</Canvas>
362 Développez des applications Internet avec Silverlight 5
1 2
2.2 - Le ContentPresenter
Le bouton x_btn1 (figure 12.8 repère n°1) utilise comme modèle k_btn_modele1.
Le contenu du modèle contient un TextBlock avec un texte «modèle personnalisé».
Quand on applique au bouton ce modèle, on s’aperçoit que le texte du bouton
(propriété Content) n’est pas prise en compte.
Copyright 2012 Patrice REY
<UserControl.Resources>
<ControlTemplate x:Key= «k_btn_modele1» TargetType= «Button» >
<Border BorderBrush= «Black» BorderThickness= «5» CornerRadius= «10»
Background= «LightGray» Cursor= «Hand»>
<TextBlock Foreground= «Black» VerticalAlignment= «Center»
HorizontalAlignment= «Center» Text= «modéle personnalisé»></TextBlock>
</Border>
</ControlTemplate> ...
</UserControl.Resources>
CHAPITRE 12 □ Les styles et les modèles 365
1 2
3 4
</Style>
</UserControl.Resources>
On superpose un rectangle, doté d’un masque d’opacité, pour simuler l’effet de
réflexion de la lumière sur un bouton à l’aspect de verre (figure 12.10 repère n°3).
<UserControl.Resources>
...
<Style x:Key= «style_bouton» TargetType= «Button»>
...
<Setter Property= «Template»>
<Setter.Value>
<ControlTemplate TargetType= «Button»>
<Grid>
<!-- contenu des boutons -->
...
<Rectangle Fill= «White»
OpacityMask= «{StaticResource pinceau_degrade_reflexion}»
Height= «8» RadiusX= «5» RadiusY= «5» Margin= «2»
VerticalAlignment= «Top» />
...
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
On positionne un ContentPresenter avec des propriétés qui référencent les
propriétés exposées, par l’utilisation de l’extension de balisage TemplateBinding
(figure 12.10 repère n°4).
<UserControl.Resources>
...
<Style x:Key= «style_bouton» TargetType= «Button»>
...
<Setter Property= «Template»>
<Setter.Value>
<ControlTemplate TargetType= «Button»>
<Grid>
<!-- contenu des boutons -->
...
<ContentPresenter Content= «{TemplateBinding Content}»
ContentTemplate= «{TemplateBinding ContentTemplate}»
VerticalAlignment= «{TemplateBinding VerticalContentAlignment}»
HorizontalAlignment=
«{TemplateBinding HorizontalContentAlignment}»
Margin= «{TemplateBinding Padding}» />
...
</Grid>
370 Développez des applications Internet avec Silverlight 5
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
On termine en positionnant un rectangle nommé focus_rectangle, caché dans un
premier temps, qui représentera le bouton quand ce dernier aura le focus (figure
12.10 repère n°5).
<UserControl.Resources>
...
<Style x:Key= «style_bouton» TargetType= «Button»>
...
<Setter Property= «Template»>
<Setter.Value>
<ControlTemplate TargetType= «Button»>
<Grid>
<!-- contenu des boutons -->
...
<Rectangle Name= «focus_rectangle»
Style= «{StaticResource style_rect_coin_arrondi}»
Visibility= «Collapsed»
Stroke= «{StaticResource pinceau_focus}» />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
Jusqu’ici, le survol du bouton par la souris ne fait que montrer le curseur main
(la propriété Cursor = «hand»), sans aucune animation. Le paragraphe suivant
indique comment il faut procéder pour ajouter des états visuels au bouton dans la
modification du modèle.
FIGURE 12.12
</vsm:VisualState>
<vsm:VisualState x:Name= «Pressed»>
...
</vsm:VisualState>
<vsm:VisualState x:Name= «Disabled»>
...
</vsm:VisualState>
</vsm:VisualStateGroup>
CHAPITRE 12 □ Les styles et les modèles 373
FIGURE 12.14
liaison de données.
Il est possible de définir un objet source implicite pour tout le contexte d’un élément
conteneur donné, via la propriété DataContext de cet élément. Cette propriété
est héritée automatiquement par un élément cible situé dans l’arbre visuel, et
constitue ce qu’on appelle le contexte de binding. Elle permet de spécifier en une
seule instruction l’objet source des différents objets Binding dans la descendance
CHAPITRE 13 □ La gestion des données 379
Label TextBox
x_label_numero x_textbox_numero
CHAPITRE 13 □ La gestion des données 381
A noter que pour ajouter un contrôle Label, dans le fichier XAML, il faut ajouter,
dans le dossier Références du projet, une référence à l’assembly System.Windows.
Controls.Data.Input, puis dans le fichier XAML, il faut référencer cet espace de
noms en ajoutant l’attribut suivant aux attributs de l’UserControl : xmlns:data =
«clr-namespace:System.Windows.Controls; assembly=System.Windows.Controls.
Data.Input».
Un contrôle Label pourra être alors ajouté en XAML par <data.Label>. Comme la
propriété DataContext de x_cnv_root possède un objet Produit instancié, on peut
lier par databinding la propriété Text du TextBox x_textbox_numero pour que le
contrôle affiche la propriété ModeleNumero de un_produit. Pour cela on affecte
à la propriété Text une liaison de données Binding pour récupérer la propriété
ModelNumero.
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke»
HorizontalAlignment= «Center» VerticalAlignment= «Center»
Width= «600» Height= «630»>
<!-- numero produit -->
...
<TextBox Name= «x_textbox_numero» Width= «207» Canvas.Left= «180»
Canvas.Top= «42» Text= «{Binding ModeleNumero, Mode=TwoWay}»
FontFamily= «Verdana» FontSize= «14»></TextBox>
...
</Canvas>
La classe Label hérite de ContentControl. Un contrôle Label affiche une légende, un
indicateur de champ obligatoire et un indicateur d’erreur de validation à l’attention
de l’utilisateur. Il est généralement utilisé avec un contrôle d’entrée (un contrôle
TextBox par exemple).
Pour associer un Label à un autre contrôle, vous pouvez définir la propriété Target
du Label. La propriété Target est une référence à un UIElement. Vous pouvez
définir la propriété Target dans le code ou via une liaison ElementName en XAML.
Lorsque la propriété Target est définie, un Label inspecte chaque propriété de
dépendance sur le contrôle ciblé par Target à la recherche d’un Binding. Chaque fois
qu’il trouve un Binding, il inspecte la propriété Source de ce Binding à la recherche
de métadonnées. Les métadonnées permettent de définir le texte de légende,
de spécifier si le champ est obligatoire et d’indiquer les règles de validation. Si le
contrôle ciblé par Target possède plusieurs Binding, vous pouvez spécifier celui à
partir duquel récupérer des métadonnées en définissant la propriété PropertyPath.
Le Label x_label_numero a sa propriété Target qui est liée par Binding avec le
contrôle TextBox x_textbox_numero. La figure 13.3 visualise le résultat obtenu. On
voit que la propriété Content du Label contient la chaîne «ModeleNumero».
382 Développez des applications Internet avec Silverlight 5
<Canvas x:Name= «x_cnv_root» Background= «WhiteSmoke»
HorizontalAlignment= «Center» VerticalAlignment= «Center»
Width= «600» Height= «630»>
<!-- numero produit -->
<data:Label Canvas.Left= «12» Canvas.Top= «42» Width= «161»
FontFamily= «Verdana» FontSize= «14»
Target= «{Binding ElementName=x_textbox_numero}» Height= «26»
Name= «x_label_numero»></data:Label>
...
</Canvas>
FIGURE 13.3
Label TextBox
x_label_numero x_textbox_numero
//propriete
[Display(Name = «Numéro du modèle :»,
Description = «une chaine alphanumérique, de 10 caractères, utilisée
pour identifier le produit»)]
public string ModeleNumero {
get { return m_modele_numero; }
set { m_modele_numero = value; }
}
...}
FIGURE 13.4
Vous pouvez utiliser la propriété Content pour définir la légende d’un Label. Si
la propriété associée au Label possède un DisplayAttribute et que la propriété
Name n’est pas une référence null ni une chaîne vide, la valeur de la propriété
384 Développez des applications Internet avec Silverlight 5
DisplayAttribute.Name sera alors affectée à Content. Dans notre cas, la propriété
Content du Label x_label_numero sera affectée par la chaîne «Numéro du modèle
:» (figure 13.5).
FIGURE 13.5
Label TextBox
x_label_numero x_textbox_numero
Label TextBox
x_label_numero x_textbox_numero
Label TextBox
x_label_nom x_textbox_nom
La propriété ModelePrix, de type double, aura les attributs Display et Required. La
figure 13.8 illustre le résultat obtenu.
//propriete
[Display(Name = «Prix du modèle :»,
Description = «le prix du modele «)]
[Required()]
public double ModelePrix {
get { return m_modele_prix; }
set { m_modele_prix = value; }
}
Copyright 2012 Patrice REY
FIGURE 13.8
Label TextBox
x_label_prix x_textbox_prix
Label TextBox
x_label_description x_textbox_description
DescriptionViewer
x_description_numero
DescriptionViewer
Copyright 2012 Patrice REY
x_description_nom
DescriptionViewer
x_description_prix
DescriptionViewer
x_description_des
cription
propriété
GlyphTemplate de
DescriptionViewer
CHAPITRE 13 □ La gestion des données 391
Le DescriptionViewer possède des groupes d’états avec leurs états (figure 13.15).
Il est donc possible de modifier les modèles comme cela a été déjà vu au chapitre
précédent. Les groupes d’états sont:
• CommonStates avec les états Normal et Disabled.
• DescriptionStates avec les états NoDescription et HasDescription.
• ValidationStates avec les états InvalidFocused, InvalidUnfocused, ValidFocused
et ValidUnfocused.
FIGURE 13.15
2 - Notification et validation
}
public string ModeleNom {
...
set {
m_modele_nom = value;
OnPropertyChanged(new PropertyChangedEventArgs(«ModeleNom»));
}
}
public double ModelePrix {
...
set {
m_modele_prix = value;
OnPropertyChanged(new PropertyChangedEventArgs(«ModelePrix»));
}
}
public string ModeleDescription {
...
set {
m_modele_description = value;
OnPropertyChanged(new PropertyChangedEventArgs(«ModeleDescription»));
}
}
Dans le cas de la validation du stockage d’une donnée sur un binding en
mode TwoWay, le mécanisme de validation consiste à émettre l’événement
BindingValidationError sur l’élément cible quand une exception est déclenchée.
L’objet Binding doit avoir sa propriété ValidatesOnExceptions explicitement fixée à
true, et sa propriété NotifyOnValidationError explicitement fixée à true.
<!-- numero produit ****************************** -->
<TextBox Name= «x_textbox_numero»
Text= «{Binding ModeleNumero, Mode=TwoWay,
ValidatesOnExceptions=True, NotifyOnValidationError=True}»
...</TextBox>
<!-- nom produit ****************************** -->
<TextBox Name= «x_textbox_nom»
Text= «{Binding ModeleNom, Mode=TwoWay,
ValidatesOnExceptions=True, NotifyOnValidationError=True}»
...</TextBox>
<!-- prix produit ****************************** -->
<TextBox Name= «x_textbox_prix»
Text= «{Binding ModelePrix, Mode=TwoWay,
ValidatesOnExceptions=True, NotifyOnValidationError=True}»
...</TextBox>
La classe ValidationContext (dans l’espace de noms System.ComponentModel.
DataAnnotations) fournit des informations sur un type ou un membre à valider.
Elle décrit le type ou le membre sur lequel la validation est exécutée. Elle permet
394 Développez des applications Internet avec Silverlight 5
également aux routines de validation de faire référence à des services disponibles
via la méthode GetService de l’interface IServiceProvider.
Le constructeur ValidationContext initialise une nouvelle instance de la classe
ValidationContext avec l’objet spécifié à valider, un fournisseur de services qui
active des méthodes de validation pour accéder aux services externes et une
collection de valeurs en rapport avec la validation. La propriété MemberName
définit le nom de programmation du membre à valider.
Par exemple, pour la propriété ModeleNumero, on instancie un objet context de
type ValidationContext, qui reçoit en argument this (objet à valider). On fixe sa
propriété MemberName par la chaîne «ModeleNumero» (nom de la propriété à
valider).
//propriete numero du modele
[Display(Name = «Numéro du modèle :»,
Description = «une chaine alphanumérique, de 10 caractères, utilisée pour
identifier le produit»)]
[Required()]
[StringLength(10,MinimumLength=10)]
public string ModeleNumero {
get { return m_modele_numero; }
set {
ValidationContext context = new ValidationContext(this, null, null);
context.MemberName = «ModeleNumero»;
...
m_modele_numero = value;
OnPropertyChanged(new PropertyChangedEventArgs(«ModeleNumero»));
}
}
La classe Validator (dans l’espace de noms System.ComponentModel.
DataAnnotations) fournit des membres qui permettent de valider des objets et des
membres à l’aide de valeurs de l’attribut ValidationAttribute associé. La méthode
statique Validator.ValidateProperty détermine si la valeur de propriété spécifiée
est valide, et lève une exception de type ValidationException si la propriété n’est
pas valide. Ici on utilise cette méthode statique pour valider la valeur value pour
l’objet context.
Copyright 2012 Patrice REY
set {
ValidationContext context = new ValidationContext(this, null, null);
context.MemberName = «ModeleNumero»;
Validator.ValidateProperty(value, context);
m_modele_numero = value;
OnPropertyChanged(new PropertyChangedEventArgs(«ModeleNumero»));
}
}
La propriété ModeleNumero de Produit doit être une chaîne contenant 10
caractères ([StringLength(10,MinimumLength=10)]). Si on inscrit une chaîne
qui contient plus de 10 caractères ou moins de 10 caractères, la validation lève
une exception en affichant une erreur. La figure 13.17 visualise ce qui se passe
(le champ qui n’est pas correct est entouré d’une bordure rouge avec un triangle
rouge dans le coin haut droit, qui affiche une info-bulle quand la souris le survole).
FIGURE 13.17
FIGURE 13.19
Copyright 2012 Patrice REY
ValidationSummary
x_validation_summary
FIGURE 13.20
ValidationSummary
x_validation_summary
3 - Le contrôle DataGrid
La classe DataGrid, qui hérite de la classe Control (figure 13.21), affiche les
données dans une grille personnalisable. Le contrôle DataGrid offre un moyen
souple d’afficher une collection de données dans des lignes et des colonnes. Les
types de colonnes intégrés incluent une colonne de zone de texte, une colonne de
case à cocher et une colonne de modèle pour héberger le contenu personnalisé. Le
type de ligne intégré inclut une section de détails déroulante qui permet d’afficher
du contenu supplémentaire sous les valeurs de cellules.
Pour lier le contrôle DataGrid aux données, vous définissez la propriété ItemsSource
à une implémentation IEnumerable. Chaque ligne de la grille de données est liée à
un objet dans la source de données et chaque colonne de la grille de données est
liée à une propriété de l’objet de données.
FIGURE 13.21
Pour ajouter en XAML un DataGrid, il faut ajouter une référence xmlns à l’espace de
noms xmlns:data = «clr-namespace:System.Windows.Controls;assembly=System.
Windows.Controls.Data», que l’on nomme data.
FIGURE 13.22
3
402 Développez des applications Internet avec Silverlight 5
Un contrôle DataGrid sera instancié en XAML par <data:DataGrid>, et on le
nommera x_datagrid. La propriété AutoGenerateColumns définit une valeur
qui indique si les colonnes sont créées automatiquement lorsque la propriété
ItemsSource est définie.
<Grid x:Name= «x_grid_root» Background= «WhiteSmoke»
HorizontalAlignment= «Center» VerticalAlignment= «Top»>
<Grid.RowDefinitions>
<RowDefinition Height= «50» />
<RowDefinition Height= «*» MinHeight= «200» />
</Grid.RowDefinitions>
<!-- titre -->
<Rectangle Width= «600» Height= «36» RadiusX= «5» RadiusY= «5»
StrokeThickness= «2» Stroke= «Black» Grid.Row= «0»></Rectangle>
<TextBlock Width= «600» TextAlignment= «Center» FontFamily= «Comic Sans MS»
FontSize= «18» Grid.Row= «0» VerticalAlignment= «Center»>
Listing des vélos du catalogue</TextBlock>
<!-- contenu des donnees -->
<data:DataGrid Name= «x_datagrid» Grid.Row= «1»
AutoGenerateColumns= «True»></data:DataGrid>
</Grid>
La propriété ItemsSource définit une collection utilisée pour générer le contenu du
contrôle. Par exemple on instancie une liste d’objets ProduitVelo, et on l’affecte à
ItemsSource du DataGrid.
//evenement loaded
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
List<ProduitVelo> liste_velo = new List<ProduitVelo>();
ProduitVelo velo = null;
velo = new ProduitVelo(ProduitVelo.Categorie.de_ville,
«velo123256», «ligne classique AB02», 125.56,
«velo de ville, classique avec ligne épurée»,»contenu/velo_ville.jpg»,2);
liste_velo.Add(velo);
velo = new ProduitVelo(ProduitVelo.Categorie.de_ville,
«velo236458», «ligne classique VER022», 331.24,
«velo de ville, classique avec ligne épurée», «contenu/velo_ville.jpg»,3);
liste_velo.Add(velo);
...
x_datagrid.ItemsSource = liste_velo;
Copyright 2012 Patrice REY
}
Le repère n°1 de la figure 13.22 montre le DataGrid basique avec ses données
de type ProduitVelo. Les en-têtes des colonnes sont générés automatiquement
par défaut, et leurs intitulés correspondent au nom des propriétés des données à
visualiser. Le repère n°2 de la figure 13.22 montre que l’on peut élargir ou rétrécir
les colonnes (curseur avec une double flèche horizontale). Le repère n°3 de la
figure 13.22 montre qu’un clic sur un en-tête de colonne permet de trier par ordre
CHAPITRE 13 □ La gestion des données 403
décroissant puis par ordre croissant les données contenues dans la colonne. La
modification des données est possible. Plusieurs lignes peuvent être sélectionnées.
Le tri multicolonne est possible par appui simultanée sur la touche Shift.
Les lignes et les colonnes d’un contrôle DataGrid (figure 13.23) sont représentées
respectivement au moyen d’objets DataGridRow et DataGridColumn.
FIGURE 13.23
DataGridRow. Une même taille peut être donnée aux colonnes au moyen de la
propriété ColumnWidth. Une même hauteur peut être donnée aux lignes au moyen
de la propriété RowHeight. Le dimensionnement des cellules est automatique par
défaut, et fonctionne selon le même principe que les cellules d’un conteneur Grid
comme déjà vu (valeurs Auto et *).
La propriété booléenne CanUserResizeColumns indique si l’utilisateur peut
redimensionner les colonnes par glisser-déposer. La propriété booléenne
CHAPITRE 13 □ La gestion des données 405
FIGURE 13.25
propriété propriété propriété propriété
ModeleNumero ModeleCategorie ModelePrix ModeleCheminImgVelo
propriété propriété propriété propriété
ModeleNom ModeleQuantite ModeleCouleur ModeleDescription
408 Développez des applications Internet avec Silverlight 5
Binding= «{Binding ModeleQuantite}»></data:DataGridTextColumn>
<!-- colonne prix -->
<data:DataGridTextColumn Header= «Prix» Width= «75»
Binding= «{Binding ModelePrix}»></data:DataGridTextColumn>
<!-- colonne couleur personnalisee ************ -->
<data:DataGridCheckBoxColumn Header= «Couleur»
Binding= «{Binding ModeleCouleur}» Width= «70»>
</data:DataGridCheckBoxColumn>
<!-- colonne image ************************** -->
<data:DataGridTextColumn Header= «Image» Width= «150»
Binding= «{Binding ModeleCheminImgVelo}»></data:DataGridTextColumn>
<!-- colonne description *********************** -->
<data:DataGridTextColumn Header= «Description» Width= «150»
Binding= «{Binding ModeleDescription}»>
</data:DataGridTextColumn>
</data:DataGrid.Columns>
</data:DataGrid>
La colonne Prix affiche les données de la propriété ModelePrix de type double.
Ces données représentent une valeur monétaire. Dans l’extension de balisage
Binding, la propriété StringFormat spécifie le format de type string à utiliser pour
l’affichage. Le caractère «C» représente l’affichage monétaire dans la langue par
défaut (culture anglo-saxonne donc symbole monétaire $). Pour utiliser le symbole
monétaire € dans la culture française, il faut spécifier la culture que doit utiliser le
convertisseur.
L’attribut ConverterCulture représente le convertisseur de culture qui peut être
défini comme un identificateur basé sur des normes. En lui passant la valeur «fr-
FR», on utilise le convertisseur français. La figure 13.26 illustre le résultat attendu
en fonction du convertisseur choisi.
Les options les plus couramment employées pour la propriété StringFormat sont:
• «C» qui représente le symbole monétaire dans la culture choisie.
• «E» qui représente la notation scientifique (par exemple 1.2454E+004).
• «P» qui représente le pourcentage (par exemple 52.6%).
• «F?» qui représente un nombre de chiffres après la virgule (F2 pour 2.52 et F0
pour 2).
Copyright 2012 Patrice REY
• «d» qui représente une date au format court, et «D» qui représente une date
au format long.
• «f» qui représente une date au format long avec l’heure au format court, et
«F» qui représente une date au format long avec l’heure au format long.
• «M» qui représente le jour et le mois.
• «G» qui représente le format général de date et heure dans la culture choisie.
CHAPITRE 13 □ La gestion des données 409
ConverterCulture
par défaut
(symbole
monétaire $)
ConverterCulture
= «fr-FR»
<UserControl.Resources>
<Style x:Key= «k_prix_gras» TargetType= «TextBlock»>
<Setter Property= «FontWeight» Value= «Bold»></Setter>
</Style>
</UserControl.Resources>
<data:DataGrid Name= «x_datagrid» Grid.Row= «1» AutoGenerateColumns= «False»
LoadingRow= «x_datagrid_LoadingRow» IsReadOnly= «True»>
<data:DataGrid.Columns>
CHAPITRE 13 □ La gestion des données 411
...
<data:DataGridTextColumn Header= «Prix» Width= «75»
Binding= «{Binding ModelePrix,StringFormat=’C’,ConverterCulture=fr-FR}»
ElementStyle= «{StaticResource k_prix_gras}»></data:DataGridTextColumn>
...
</data:DataGrid.Columns>
</data:DataGrid>
FIGURE 13.28
La colonne Image contient des données qui représentent un chemin, qui pointe
vers une image au format JPG, qui est stockée dans le dossier contenu. Ce qu’il
faudrait ce serait de remplacer le texte du chemin par l’image elle-même qui est
pointée par ce chemin.
Pour positionner un contrôle Image dans une colonne, il faut appliquer à cette
colonne un type DataGridTemplateColumn. Un objet DataGridTemplateColumn
représente une colonne dans un DataGrid qui héberge du contenu spécifié par
modèle dans ses cellules. Sa propriété CellTemplate définit le modèle utilisé pour
afficher le contenu d’une cellule qui n’est pas en mode édition. On affecte à cette
propriété un élément objet DataTemplate qui définit l’affichage pour un mode
qui n’est pas en mode édition. Ici ce DataTemplate est composé d’un contrôle
Image. Sa propriété Source est liée par un objet Binding qui référence la propriété
ModeleCheminImgVelo d’un objet ProduitVelo. La donnée liée n’est qu’un chemin
d’accès vers une ressource mais pas un contrôle Image. Il faut donc utiliser un
convertisseur par la propriété Converter de Binding.
La propriété Converter spécifie l’objet de convertisseur appelé par le moteur de
liaison. Le convertisseur peut être défini en XAML, mais uniquement si vous faites
référence à un convertisseur qui est défini de manière à pouvoir être instancié et
placé dans un ResourceDictionary en XAML. La référence XAML exige ensuite une
référence StaticResource à cet objet dans le dictionnaire de ressources.
412 Développez des applications Internet avec Silverlight 5
Ici on utilise une ressource statique avec la clé k_convertir_chemin_img,
placée dans les ressources de l’UserControl, qui permet d’instancier un objet
ConvertirCheminImage.
<!-- colonne image ************************** -->
<data:DataGridTemplateColumn Header= «Image» Width= «80»>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Stretch= «Fill»
Source= «{Binding ModeleCheminImgVelo,
Converter={StaticResource k_convertir_chemin_img}}»
Width= «75» Height= «50»></Image>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
<UserControl.Resources>
<local:ConvertirCheminImage x:Key= «k_convertir_chemin_img»>
</local:ConvertirCheminImage>
</UserControl.Resources>
Ensuite on déclare une classe ConvertirCheminImage dont le but sera de renvoyer
un objet BitmapImage. En effet, on a déjà vu dans un chapitre précédent que l’on
pouvait affecter un objet BitmapImage avec une propriété UriSource définie, à un
contrôle Image.
On déclare la classe ConvertirCheminImage qui implémente l’interface
IValueConverter. L’interface IValueConverter expose des méthodes qui permettent
la modification des données à mesure qu’elles passent par le moteur de liaison. La
définition de cette interface est:
namespace System.Windows.Data {
public interface IValueConverter {
//Modifie les données sources avant de les passer à la cible en vue de leur
//affichage dans l’interface utilisateur.
//Retourne :
//La valeur à passer à la propriété de dépendance cible.
object Convert(object value, Type targetType, object parameter,
CultureInfo culture);
//Modifie les données cibles avant de les passer à l’objet source. Cette
Copyright 2012 Patrice REY
ressource image. Comme les images ici sont embarquées dans l’application, la
propriété BaseUri aura comme valeur «/ControleDesDonnees;component/» (dans
le constructeur). La méthode Convert reçoit la donnée value (de type object donc
avec un cast en string ici) et retourne un objet BitmapImage. On instancie un
BitmapImage et on affecte à sa propriété UriSource un Uri composé de m_base_
uri et de la donnée de type string.
FIGURE 13.29
un surlignage gris
pour un stock nul
Copyright 2012 Patrice REY
Une vue de collection permet de naviguer parmi les éléments dans une
collection en gérant une notion d’élément courant, et permet le tri, le filtrage
et le regroupement de données. Ce comportement est défini par l’interface
ICollectionView, implémentée par la classe PagedCollectionView.
La classe PagedCollectionView représente une vue permettant des opérations de
regroupement, de tri, de filtrage et de navigation dans une collection de données
paginée.
Copyright 2012 Patrice REY
5
418 Développez des applications Internet avec Silverlight 5
//evenement loaded
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
List<ProduitVelo> liste_velo = new List<ProduitVelo>();
ProduitVelo velo = null;
velo = new ProduitVelo(ProduitVelo.Categorie.de_ville, «velo123256»,
«ligne classique AB02», 125.56, «velo de ville, classique avec ligne
épurée», «contenu/velo_ville.jpg», 2, true);
liste_velo.Add(velo);
...
PagedCollectionView vue_donnee = new PagedCollectionView(liste_velo);
vue_donnee.GroupDescriptions.Add(
new PropertyGroupDescription(«ModeleCategorie»));
x_datagrid.ItemsSource = vue_donnee;
}
Le regroupement de ligne peut être fait avec un tri dans chacun des groupes
composés. L’UserControl RegroupementLigneTrier.xaml (dans la solution
ControleDesDonnees.sln dans le dossier chapitre13) illustre la façon de visualiser un
ensemble de données avec des colonnes personnalisées, et des lignes regroupées
et triées en fonction de critères (figure 13.33).
Dans cet exemple, le regroupement est fait en fonction de la propriété booléenne
ModeleCouleur de ProduitVelo. Il y a donc que 2 regroupements possibles: les
lignes qui ont la valeur true, et celles qui ont la valeur false.
Dans chacun de ces regroupements, on effectue un tri en fonction de la
propriété ModelePrix par ordre décroissant. La propriété SortDescriptions de
PagedCollectionView représente une collection d’objets SortDescription qui
décrivent comment les éléments de la collection sont triés dans la vue. Le tri se
fait en ajoutant à cette collection des objets SortDescription.
La structure SortDescription définit le sens et le nom de propriété qui seront
utilisés comme critères de tri d’une collection. La propriété PropertyName de
SortDescription définit le nom de propriété qui est utilisé comme critère de tri,
et la propriété Direction définit une valeur qui indique s’il faut trier en ordre
croissant ou décroissant. Pour trier les lignes en fonction de ModelePrix par ordre
décroissant, il faudra instancier une structure SortDescription avec la propriété
PropertyName fixée à ModelePrix et la propriété Direction fixée à la valeur
Copyright 2012 Patrice REY
FIGURE 13.34
La classe DataPager (figure 13.35), qui hérite de Control, fournit une interface
utilisateur qui permet la pagination au sein d’une collection de données. Le
contrôle DataPager fournit une interface utilisateur configurable pour effectuer
une pagination via une collecte de données. Vous pouvez généralement utiliser une
collection qui implémente IPagedCollectionView en tant que source de données
et l’interface IPagedCollectionView fournit la fonctionnalité de pagination.
Vous pouvez lier le DataPager à toute collection IEnumerable. Le DataPager se
comportera toutefois comme si toutes les données se trouvaient dans une même
page. Pour fournir la fonctionnalité de pagination d’une collection IEnumerable,
vous pouvez l’inclure dans un wrapper dans la classe PagedCollectionView.
FIGURE 13.35
422 Développez des applications Internet avec Silverlight 5
Les données paginées s’affichent généralement dans un contrôle qui représente
une collection (DataGrid ou ListBox, par exemple). Pour utiliser un DataPager
en vue de paginer des données dans un autre contrôle, vous devez assigner la
propriété ItemsSource du contrôle et la propriété DataPager.Source à la même
collecte de données.
Vous pouvez modifier l’apparence du DataPager en définissant la propriété
DisplayMode. Si vous utilisez un DisplayMode qui affiche des boutons numériques,
vous pouvez modifier le nombre de boutons affichés en définissant la propriété
NumericButtonCount. Vous pouvez affecter true à la propriété AutoEllipsis pour
que le DataPager affiche des points de suspension au lieu du dernier bouton
numérique, à moins que les points de suspension ne correspondent à la dernière
page. Vous pouvez également modifier l’apparence des boutons numériques en
définissant un NumericButtonStyle personnalisé. La figure 13.36 montre le contrôle
DataPager configuré pour utiliser différents modes d’affichage.
FIGURE 13.36
DisplayMode = FirstLastNumeric
DisplayMode = FirstLastPreviousNext
DisplayMode = FirstLastPreviousNextNumeric
DisplayMode = Numeric
DisplayMode = PreviousNext
DisplayMode = PreviousNextNumeric
4 - Le contrôle TreeView
3
1
2
//imagette album
public static Image ImagetteAlbum {
get {
Image img = Utilitaire.ObtenirImagette(«img_album.png»);
img.Width = 20;
img.Height = 20;
img.Stretch = Stretch.Fill;
return img;
}
CHAPITRE 13 □ La gestion des données 431
}
//collection de chansons contenue dans un album
public Collection<Chanson> AlbumCollectionChansons { get; private set; }
//constructeur
public Album() {
AlbumCollectionChansons = new Collection<Chanson>();
}
//
public override string ToString() {
string aff = «»;
aff += «titre -> « + this.AlbumTitre + RC;
aff += «année -> « + this.AlbumAnnee + RC;
aff += «nombre de chansons -> « +
this.AlbumCollectionChansons.Count.ToString() + RC;
return aff;
}
}//end class
La classe Chanson représente une chanson. Sa propriété ChansonTitre définit le
nom de la chanson et sa propriété ChansonDuree définit la durée de la chanson.
Une propriété statique ImagetteChanson permet de récupérer un objet Image
dont la source pointe sur une image embarquée, intitulée img_chanson.png, dans
le dossier contenu.
public class Chanson {
private string RC = Environment.NewLine;
//propriete titre de la chanson
public string ChansonTitre { get; set; }
//propriete duree de la chanson
public string ChansonDuree { get; set; }
//obtenir imagette pour une chanson
public static Image ImagetteChanson {
get {
Image img = Utilitaire.ObtenirImagette(«img_chanson.png»);
img.Width = 20;
img.Height = 20;
img.Stretch = Stretch.Fill;
return img;
}
}
//constructeur
public Chanson() { }
//
public override string ToString() {
string aff = «»;
aff += «titre -> « + this.ChansonTitre + RC;
aff += «durée -> « + this.ChansonDuree + RC;
return aff;
}
432 Développez des applications Internet avec Silverlight 5
}//end class
Dans les ressources de l’application (fichier App.xaml), on positionne le contenu
qui sera analysé par le processeur XAML. Pour ajouter une collection d’objets, il
faut ajouter une référence xmlns, nommée toolkit, qui est xmlns:toolkit = «clr-
namespace:System.Windows.Controls;assembly=System.Windows.Controls.
Toolkit». Pour accéder aux objets des classes Artiste, Album et Chanson, il
faut ajouter une référence xmlns, nommée local, qui est xmlns:local = «clr-
namespace:ControleDesDonnees». En premier on commence par ajouter les
nœuds concernant les artistes.
<Application.Resources>
<!-- catalogue de musiques pour le treeview -->
<toolkit:ObjectCollection x:Key= «k_catalogue_musique»
xmlns= «http://schemas.microsoft.com/winfx/2006/xaml/presentation»>
<local:Artiste ArtisteNom= «Frank Sinatra» ArtisteGenre= «Pop»>
...
</local:Artiste>
<local:Artiste ArtisteNom= «African Wind» ArtisteGenre= «Blues»>
...
</local:Artiste>
<local:Artiste ArtisteNom= «Johnny Cash» ArtisteGenre= «Country»>
...
</local:Artiste>
</toolkit:ObjectCollection>
</Application.Resources>
Ensuite, pour un artiste, on ajoute les nœuds qui contiennent les albums avec les
propriétés AlbumTitre et AlbumAnnee.
<local:Artiste ArtisteNom= «Frank Sinatra» ArtisteGenre= «Pop»>
<local:Album AlbumTitre= «In The Wee Small Hours» AlbumAnnee= «1954»>
...
</local:Album>
<local:Album AlbumTitre= «You Do Something To Me» AlbumAnnee= «2008»>
...
</local:Album>
</local:Artiste>
<local:Artiste ArtisteNom= «African Wind» ArtisteGenre= «Blues»>
Copyright 2012 Patrice REY
1 2 3
436 Développez des applications Internet avec Silverlight 5
//evenement selection donnee dans treeview
private void x_treeview_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e) {
object recup = x_treeview.SelectedItem;
x_infos.Text = «type = « + x_treeview.SelectedItem.GetType().ToString()
+ RC;
x_infos.Text += recup.ToString();
}
La représentation
IEnumerable<T>
• AreaSeries
• BarSeries
graphique • ColumnSeries
• LineSeries
• BubbleSeries
des données • PieSeries
Une autre façon rapide de constituer une collection de données consiste à déclarer
CHAPITRE 14 □ La représentation graphique des données 441
FIGURE 14.3
1 2
CHAPITRE 14 □ La représentation graphique des données 443
Pour utiliser la classe AreaSeries, il faut ajouter une référence à l’assembly System.
Windows.Controls.DataVisualization.Toolkit dans le dossier Références. En XAML,
il faut ajouter une référence xmlns, nommée graphiqueToolkit, qui référence
l’espace de noms System.Windows.Controls.DataVisualization.Charting (xmlns:
graphiqueToolkit = « clr-namespace:System.Windows.Controls.DataVisualization.
Charting; assembly = System.Windows.Controls.DataVisualization.Toolkit »).
Pour accéder aux collections de données, il faut ajouter une référence xmlns,
nommée local, qui référence l’espace de noms du projet (xmlns:local = «clr-names
pace:GraphiqueEtDiagramme»).
Copyright 2012 Patrice REY
Une aire graphique est représentée par le contrôle AreaSeries. Sa propriété Title
définit le nom de la série à afficher. Sa propriété ItemsSource représente une
collection de données. Ici on effectue une liaison par databinding à la collection
(retournée par la propriété statique DonneesDivisionRatioFemme). La propriété
IndependentValueBinding définit les valeurs à inscrire sur l’axe horizontal (qui sont
liées à la propriété Division par databinding). La propriété DependentValueBinding
définit les valeurs à inscrire sur l’axe vertical (qui sont liées à la propriété
RatioDeFemme par databinding). Le repère n°1 de la figure 14.5 visualise le résultat
obtenu.
<Canvas x:Name= «x_cnv_root» HorizontalAlignment= «Center»
VerticalAlignment= «Top» Height= «500» Width= «600»>
<Canvas.Resources>
<local:RatioFemmeParDivision x:Key= «k_donnees_ratio_femme»>
</local:RatioFemmeParDivision>
...
</Canvas.Resources>
<graphiqueToolkit:Chart Title= «Ratio des femmes par batiment» Width= «600»
Height= «233»>
<graphiqueToolkit:Chart.Series>
<graphiqueToolkit:AreaSeries
Title= «Ratio»
ItemsSource= «{Binding DonneesDivisionRatioFemme,
Source={StaticResource k_donnees_ratio_femme}}»
IndependentValueBinding= «{Binding Division}»
DependentValueBinding= «{Binding RatioDeFemme}»>
</graphiqueToolkit:AreaSeries>
</graphiqueToolkit:Chart.Series>
</graphiqueToolkit:Chart>
...
</Canvas>
Le deuxième graphique consiste à visualiser deux séries de données, par aire
graphique, sur le même graphique.
Pour accéder à la collection de données ChiffreAffaireCollectionH de la classe
ChiffreAffaireCollectionH, on ajoute dans les ressources du Canvas une instance
de la classe repérée par la clé k_donnees_ca_h. Pour accéder à la collection de
données ChiffreAffaireCollectionF de la classe ChiffreAffaireCollectionF, on ajoute
dans les ressources du Canvas une instance de la classe repérée par la clé k_
donnees_ca_f.
On positionne sur le Canvas un contrôle Chart dont sa propriété Title représente
le titre du graphique. Sa propriété Series représente les séries de données qui
sont ajoutées au graphique (<graphiqueToolkit:Chart.Series>). Comme nous avons
deux séries de données à représenter, nous ajouterons deux aires graphiques
446 Développez des applications Internet avec Silverlight 5
AreaSeries. La propriété ItemsSource pointe directement sur la ressource
statique k_donnees_ca_h pour l’un, et k_donnees_ca_f pour l’autre. La propriété
IndependentValueBinding permet l’affichage horizontalement des valeurs de la
propriété JourDuMois. La propriété DependentValueBinding permet l’affichage
verticalement des valeurs de la propriété Montant. Le repère n°2 de la figure 14.5
visualise le résultat obtenu.
<Canvas x:Name= «x_cnv_root» HorizontalAlignment= «Center»
VerticalAlignment= «Top» Height= «500» Width= «600»>
<Canvas.Resources>
...
<local:ChiffreAffaireCollectionH x:Key= «k_donnees_ca_h»>
</local:ChiffreAffaireCollectionH>
<local:ChiffreAffaireCollectionF x:Key= «k_donnees_ca_f»>
</local:ChiffreAffaireCollectionF>
</Canvas.Resources>
...
<graphiqueToolkit:Chart Title= «Chiffre affaire Homme et Femme» Width= «600»
Height= «233» Canvas.Top= «240» Canvas.Left= «0»>
<graphiqueToolkit:Chart.Series>
<graphiqueToolkit:AreaSeries
Title= «CA des H»
ItemsSource= «{StaticResource k_donnees_ca_h}»
IndependentValueBinding= «{Binding JourDuMois}»
DependentValueBinding= «{Binding Montant}» Name= «x_aire_2_h»>
</graphiqueToolkit:AreaSeries>
<graphiqueToolkit:AreaSeries
Title= «CA des F»
ItemsSource= «{StaticResource k_donnees_ca_f}»
IndependentValueBinding= «{Binding JourDuMois}»
DependentValueBinding= «{Binding Montant}» Name= «x_aire_2_f»>
</graphiqueToolkit:AreaSeries>
</graphiqueToolkit:Chart.Series>
</graphiqueToolkit:Chart>
</Canvas>
graphique.
FIGURE 14.6
FIGURE 14.7
2
448 Développez des applications Internet avec Silverlight 5
La classe PopulationVille représente le nombre d’habitants par ville. Sa propriété
NomVille définit le nom de la ville (de type string), et sa propriété NombreHabitant
définit le nombre des habitants dans une ville donnée (de type int). Sa propriété
statique CollectionDonnees retourne une collection d’objets PopulationVille.
public class PopulationVille {
//propriete: nom de la ville
public string NomVille { get; set; }
//propriete: quantite habitants
public int NombreHabitant { get; set; }
//constructeur
public PopulationVille() { }
//override affichage
public override string ToString() {
string aff = «»;
aff += «ville = « + NomVille + « «;
aff += «nombre habitants = « + NombreHabitant.ToString();
return aff;
}
//collection de donnees
public static ObjectCollection CollectionDonnees {
get {
ObjectCollection element = new ObjectCollection();
element.Add(new PopulationVille { NomVille = «Paris (75)»,
NombreHabitant = 2125851 });
element.Add(new PopulationVille { NomVille = «Marseille (13)»,
NombreHabitant = 797491 });
element.Add(new PopulationVille { NomVille = «Lyon (69)»,
NombreHabitant = 445274 });
element.Add(new PopulationVille { NomVille = «Toulouse (31)»,
NombreHabitant = 390401 });
return element;
}
}
}//end class
Le premier graphique x_graphique1, de type BarSeries, visualise les données de
la collection CollectionDonnees de la classe PopulationVille. La collection des
données est instanciée dans les ressources du Canvas et repérée par la clé k_
donnees_population_ville.
Copyright 2012 Patrice REY
Sur l’axe vertical, on affiche les villes (la propriété IndependentValueBinding liée à
la propriété NomVille) et sur l’axe horizontal, on affiche le nombre d’habitants (la
propriété DependentValueBinding liée à la propriété NombreHabitant). Le repère
n°1 de la figure 14.7 visualise le graphique obtenu dans sa version minimale.
<Canvas x:Name= «x_cnv_root» HorizontalAlignment= «Center»
VerticalAlignment= «Top» Height= «500» Width= «600»>
CHAPITRE 14 □ La représentation graphique des données 449
<Canvas.Resources>
<local:PopulationVille x:Key= «k_donnees_population_ville»>
</local:PopulationVille>
</Canvas.Resources>
<graphiqueToolkit:Chart Title= «Nombre d’habitants par villes» Width= «600»
Height= «233» Name= «x_graphique1»>
<graphiqueToolkit:Chart.Series>
<graphiqueToolkit:BarSeries
Title= «nombre habitants»
ItemsSource= «{Binding CollectionDonnees,
Source={StaticResource k_donnees_population_ville}}»
IndependentValueBinding= «{Binding NomVille}»
DependentValueBinding= «{Binding NombreHabitant}»>
</graphiqueToolkit:BarSeries>
</graphiqueToolkit:Chart.Series>
</graphiqueToolkit:Chart>
...
</Canvas>
Pour personnaliser les axes du graphique, il faut se référer à la propriété Axes de
Chart. L’objet CategorieAxis représente l’axe vertical. Sa propriété Title permet
de donner un nom à afficher, et sa propriété Orientation permet de définir
l’orientation d’écriture (valeur énumérée X ou Y). Sa propriété Location permet
de définir l’emplacement de l’étiquette (valeur énumérée Left, Top, Right, Bottom
ou Auto). La propriété booléenne ShowGridLines permet d’afficher ou de masquer
les lignes de repérage. L’objet LinearAxis représente l’axe horizontal. Les bornes
qui englobent les valeurs à visualiser sont contrôlées par les propriétés Minimum
et Maximum, la propriété Interval permettant de positionner des graduations de
ces valeurs sur l’axe. Les propriétés Title, Orientation, Location et ShowGridLines
sont identiques à celles de CategoryAxis. Le repère n°2 de la figure 14.7 visualise
le graphique obtenu dans sa version personnalisée.
<Canvas x:Name= «x_cnv_root» HorizontalAlignment= «Center»
VerticalAlignment= «Top» Height= «500» Width= «600»>
<Canvas.Resources>
<local:PopulationVille x:Key= «k_donnees_population_ville»>
</local:PopulationVille>
</Canvas.Resources>
...
<graphiqueToolkit:Chart Title= «Nombre d’habitants par villes» Width= «600»
Height= «233» Name= «x_graphique2» Canvas.Top= «236»>
<graphiqueToolkit:Chart.Series>
<graphiqueToolkit:BarSeries
Title= «nombre habitants»
ItemsSource= «{Binding CollectionDonnees,
Source={StaticResource k_donnees_population_ville}}»
450 Développez des applications Internet avec Silverlight 5
IndependentValueBinding= «{Binding NomVille}»
DependentValueBinding= «{Binding NombreHabitant}»>
</graphiqueToolkit:BarSeries>
</graphiqueToolkit:Chart.Series>
<graphiqueToolkit:Chart.Axes>
<graphiqueToolkit:CategoryAxis Title= «Villes» Orientation= «Y»
Location= «Left» FontFamily= «Verdana» FontSize= «14»
Background= «WhiteSmoke» FontStyle= «Italic» ShowGridLines= «True»>
</graphiqueToolkit:CategoryAxis>
<graphiqueToolkit:LinearAxis Minimum= «0» Maximum= «3000000»
Interval= «1000000» Orientation= «X» Location= «Bottom»
ShowGridLines= «True» FontFamily= «Verdana» FontSize= «12»
FontStyle= «Italic»></graphiqueToolkit:LinearAxis>
</graphiqueToolkit:Chart.Axes>
</graphiqueToolkit:Chart>
</Canvas>
<Canvas.Resources>
<local:PopulationVille x:Key= «k_donnees_population_ville»>
</local:PopulationVille>
<local:PopulationVilleAvant x:Key= «k_donnees_population_ville_avant»>
</local:PopulationVilleAvant>
</Canvas.Resources>
<graphiqueToolkit:Chart Title= «Nombre d’habitants par villes» Width= «600»
Height= «265» Name= «x_graphique2» Canvas.Top= «236»>
CHAPITRE 14 □ La représentation graphique des données 453
<graphiqueToolkit:Chart.Series>
<graphiqueToolkit:ColumnSeries
Title= «nombre habitants»
ItemsSource= «{Binding CollectionDonnees,
Source={StaticResource k_donnees_population_ville}}»
IndependentValueBinding= «{Binding NomVille}»
DependentValueBinding= «{Binding NombreHabitant}»>
</graphiqueToolkit:ColumnSeries>
</graphiqueToolkit:Chart.Series>
<graphiqueToolkit:Chart.Axes>
<graphiqueToolkit:CategoryAxis Title= «Villes» Orientation= «X»
Location= «Bottom» FontFamily= «Verdana» FontSize= «14»
Background= «WhiteSmoke» FontStyle= «Italic» ShowGridLines= «True»>
</graphiqueToolkit:CategoryAxis>
<graphiqueToolkit:LinearAxis Minimum= «0» Maximum= «3000000»
Interval= «1000000» Orientation= «Y» Location= «Left»
ShowGridLines= «True» FontFamily= «Verdana» FontSize= «12»
FontStyle= «Italic»></graphiqueToolkit:LinearAxis>
</graphiqueToolkit:Chart.Axes>
</graphiqueToolkit:Chart>
Le troisième graphique x_graphique3, de type ColumnSeries, visualise les données
de la collection CollectionDonnees de la classe PopulationVille et de la collection
CollectionDonnees de la classe PopulationVilleAvant, avec une mise en page
personnalisée pour les axes. Le repère n°3 de la figure 14.9 visualise le graphique
obtenu dans sa version personnalisée avec les deux séries de données.
<Canvas.Resources>
<local:PopulationVille x:Key= «k_donnees_population_ville»>
</local:PopulationVille>
<local:PopulationVilleAvant x:Key= «k_donnees_population_ville_avant»>
</local:PopulationVilleAvant>
</Canvas.Resources>
<graphiqueToolkit:Chart Title= «Nombre d’habitants par villes» Width= «600»
Height= «265» Name= «x_graphique2» Canvas.Top= «236»>
<graphiqueToolkit:Chart.Series>
<graphiqueToolkit:ColumnSeries
Title= «nombre habitants»
ItemsSource= «{Binding CollectionDonnees,
Source={StaticResource k_donnees_population_ville}}»
IndependentValueBinding= «{Binding NomVille}»
DependentValueBinding= «{Binding NombreHabitant}»>
</graphiqueToolkit:ColumnSeries>
<graphiqueToolkit:ColumnSeries
Title= «nombre habitants»
ItemsSource= «{Binding CollectionDonnees,
Source={StaticResource k_donnees_population_ville_avant}}»
IndependentValueBinding= «{Binding NomVille}»
DependentValueBinding= «{Binding NombreHabitant}»>
454 Développez des applications Internet avec Silverlight 5
</graphiqueToolkit:ColumnSeries>
</graphiqueToolkit:Chart.Series>
<graphiqueToolkit:Chart.Axes>
<graphiqueToolkit:CategoryAxis Title= «Villes» Orientation= «X»
Location= «Bottom» FontFamily= «Verdana» FontSize= «14»
Background= «WhiteSmoke» FontStyle= «Italic» ShowGridLines= «True»>
</graphiqueToolkit:CategoryAxis>
<graphiqueToolkit:LinearAxis Minimum= «0» Maximum= «3000000»
Interval= «1000000» Orientation= «Y» Location= «Left»
ShowGridLines= «True» FontFamily= «Verdana» FontSize= «12»
FontStyle= «Italic»></graphiqueToolkit:LinearAxis>
</graphiqueToolkit:Chart.Axes>
</graphiqueToolkit:Chart>
La classe LineSeries représente des graphiques dont les données sont reliées par
des segments. La figure 14.10 visualise l’arbre d’héritage de la classe.
FIGURE 14.10
FIGURE 14.12
FIGURE 14.13
ColumnSeries, LineSeries).
<!-- graphique 1 -->
<graphiqueToolkit:Chart Title= «La population des villes» Width= «600»
Height= «265» Name= «x_graphique1» Canvas.Top= «0»>
<graphiqueToolkit:Chart.Series>
<graphiqueToolkit:PieSeries
ItemsSource= «{Binding CollectionDonnees,
Source={StaticResource k_donnees_population}}»
CHAPITRE 14 □ La représentation graphique des données 459
</Style>
</ResourceDictionary>
<ResourceDictionary>
<Style x:Key= «DataPointStyle» TargetType= «Control»>
<Setter Property= «Background» Value= «LightGray»></Setter>
<Setter Property= «BorderBrush» Value= «Black»></Setter>
<Setter Property= «BorderThickness» Value= «2»></Setter>
</Style>
</ResourceDictionary>
<ResourceDictionary>
<Style x:Key= «DataPointStyle» TargetType= «Control»>
<Setter Property= «Background» Value= «GhostWhite»></Setter>
<Setter Property= «BorderBrush» Value= «Black»></Setter>
<Setter Property= «BorderThickness» Value= «2»></Setter>
</Style>
</ResourceDictionary>
<ResourceDictionary>
<Style x:Key= «DataPointStyle» TargetType= «Control»>
<Setter Property= «Background» Value= «WhiteSmoke»></Setter>
<Setter Property= «BorderBrush» Value= «Black»></Setter>
<Setter Property= «BorderThickness» Value= «2»></Setter>
</Style>
</ResourceDictionary>
</visualizationToolkit:ResourceDictionaryCollection>
</graphiqueToolkit:PieSeries.Palette>
</graphiqueToolkit:PieSeries>
</graphiqueToolkit:Chart.Series>
</graphiqueToolkit:Chart>
C H A P I T R E 15 DANS CE CHAPITRE
• Le contrôle
La modélisation 3D
DrawingSurface
• GraphicsDeviceManager
• VertexBuffer,
VertexPositionColor,
VertexPositionTexture
• Color, Vector3, Matrix,
Vector2
• La projection perspective
et la projection parallèle
• La caméra
La modélisation 3D constitue la grande nouveauté • GraphicsDevice et
RasterizerState
de Silverlight dans sa version 5.
• Le rendu solide et filaire
Le modèle 3D de Silverlight est basé sur le • Dupliquer les objets 3D
framework XNA de la plateforme Windows. Le • Les mouvements des
objets 3D
rendu 3D de Silverlight est un rendu avec une
accélération matérielle (hardware) et non pas un
rendu logiciel (software).
Une grande partie de XNA est implémentée pour
Silverlight, ce qui induit un apport 3D puissant et
réaliste.
Nous allons voir dans ce chapitre comment utiliser
la puissance de XNA pour programmer la 3D au
travers d’objets basiques, tout cela fonctionnant
dans le navigateur avec le runtime Silverlight 5
minimum.
Nous aborderons l’utilisation et la personnalisation
du contrôle DrawingSurface qui émule la surface
de rendu d’un contenu 3D. Nous verrons comment
modéliser des objets 3D, composés de triangles,
avec des faces aux couleurs unies et des faces
texturées. Nous verrons comment effectuer un
rendu avec une caméra, une projection, un éclairage
et un positionnement des objets. Nous verrons
également la duplication et les mouvements des
objets 3D.
464 Développez des applications Internet avec Silverlight 5
Le modèle 3D de Silverlight n’est pas basé sur le modèle 3D de WPF. Avant d’aller
plus loin, vous devez être informé des points suivants:
• l’accélération matérielle: le rendu 3D avec Silverlight n’est pas un rendu logiciel
mais un rendu matériel; si l’accélération matérielle n’est pas activée, ou bien si
elle n’est pas disponible (cas des cartes vidéo obsolètes), vous serez incapable
de visualiser une scène 3D.
• une programmation par code: contrairement à WPF 3D qui permet de visualiser
des objets 3D avec du XAML, avec Silverlight, vous devez réaliser vos objets par
code de programmation et vous avez la liberté de créer des passerelles pour
afficher vos objets en XAML.
• le support de XNA: Silverlight 3D se base sur une partie du framework XNA
(qui est utilisé pour créer des jeux sur Xbox et Windows Phone); une grande
partie de XNA est utilisée pour Silverlight mais avec un point important qu’est
le support du shader model 2.0 (et non pas le shader model 3.0 actuel).
• la présence de Windows: XNA est un framework qui est implémenté sur la
plateforme Windows; Silverlight 3D ne fonctionne donc pas avec la plateforme
Mac.
Pour modéliser en 3D avec Silverlight, il faut ajouter un certain nombre de
références à des assemblys (dans le dossier Références) qui sont:
• System.Windows.Xna.dll: cet assembly contient le contrôle DrawingSurface et
le GraphicsDeviceManager.
• Microsoft.Xna.Framework.dll: cet assembly définit la version des structures
Color et Rectangle qui sont fournies par XNA, ainsi que le support de l’audio.
• Microsoft.Xna.Framework.Graphics.dll: cet assembly définit un grand nombre
de type d’objets, dont le GraphicsDevice, qui sont nécessaires au dessin 3D
dans le contrôle DrawingSurface.
• Microsoft.Xna.Framework.Graphics.Extensions.dll: cet assembly définit
l’essentiel du rendu 3D avec notamment la classe BasicEffect, qui sert de
passerelle pour un grand nombre de détails pour le dessin 3D et pour l’affichage
du pixel shader que vous n’aurez pas à écrire.
Copyright 2012 Patrice REY
1 - Ma première scène 3D
Nous allons réaliser une première scène 3D complète pour voir la démarche à
effectuer pour visualiser un contenu 3D. L’UserControl MaPremiereScene.xaml
(dans le projet Modelisation3d.sln dans le dossier chapitre15) illustre cet exemple.
Le support de la 3D avec Silverlight nécessite l’accélération matérielle. Sans elle,
aucun affichage 3D ne peut être réalisé. Pour activer l’accélération matérielle, il
faut ajouter un paramètre dans la section du plugin Silverlight dans la page de
test de l’application. Le paramètre enableGPUAcceleration doit être fixé à la valeur
true.
<div id= «silverlightControlHost»>
<object data= «data:application/x-silverlight-2,»
type= «application/x-silverlight-2» width= «100%» height= «100%»>
<param name= «source» value= «ClientBin/Modelisation3d.xap»/>
<param name= «onError» value= «onSilverlightError» />
<param name= «background» value= «white» />
<param name= «minRuntimeVersion» value= «5.0.61118.0» />
<param name= «autoUpgrade» value= «true» />
<param name= «enableGPUAcceleration» value= «true» />
</object>
</div>
L’application Silverlight est bloquée par défaut dès lors qu’il y a une utilisation de la
carte graphique demandée. Cela est fait pour éviter des attaques par modification
de code.
En premier, il faut informer que l’application Silverlight nécessite des privilèges
élevés en cas d’exécution dans le navigateur. Comme le visualise la figure 15.2,
dans les propriétés du projet, il faut cocher la case permettant l’autorisation de ces
privilèges élevés.
466 Développez des applications Internet avec Silverlight 5
FIGURE 15.2
//evenement loaded
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
GraphicsDeviceManager gdm = GraphicsDeviceManager.Current;
if (gdm.RenderMode == RenderMode.Unavailable) {
x_listbox.SelectedIndex = -1;
string code_erreur = string.Empty;
code_erreur += «code erreur -> (« + gdm.RenderMode.ToString() + « - «;
code_erreur += gdm.RenderModeReason.ToString() + «)» + RC + RC;
switch (gdm.RenderModeReason) {
case RenderModeReason.SecurityBlocked:
code_erreur += «L’accélération matérielle n’est pas activée pour des
raisons de « + «sécurité. La procédure à suivre est la suivante:» + RC;
code_erreur += «1 - Faites un clic droit puis choisir Silverlight»
+ RC;
code_erreur += «2 - Dans l’onglet Autorisations, sélectionnez la ligne
correspondante et choisissez Autoriser» + RC;
break;
case RenderModeReason.GPUAccelerationDisabled:
code_erreur = «Erreur de développement! Le paramètre
enableGPUAcceleration n’est pas présent « +
«dans les paramètres du plugin avec la valeur fixée à true» + RC;
break;
case RenderModeReason.Not3DCapable:
code_erreur = «Votre carte vidéo ne supporte pas la 3D»;
break;
case RenderModeReason.TemporarilyUnavailable:
code_erreur = «Votre driver de la carte vidéo est obsolète»;
break;
}
x_infos_3d.Text = code_erreur;
} else {
v_3d_support = true;
x_listbox.SelectedIndex = 0;
}
}
En gérant ces alertes, on peut indiquer à l’utilisateur la démarche éventuelle à faire
pour résoudre le problème. Le plus souvent, c’est le problème de l’autorisation de
l’accélération matérielle qui n’est pas réalisée.
La figure 15.3 illustre le processus de cette autorisation pour pouvoir visualiser un
contenu 3D:
• une alerte affiche le problème de l’autorisation de l’accélération matérielle non
activée (repère n°1).
• un clic droit permet de choisir Silverlight dans le sous-menu (repère n°2).
• l’onglet Autorisations doit être sélectionné (repère n°3).
• il faut sélectionner la ligne correspondant à l’autorisation à donner, puis il faut
cliquer sur le bouton Autoriser (repère n°4) puis sur OK.
468 Développez des applications Internet avec Silverlight 5
• il ne reste plus qu’à actualiser la page par la touche F5 et le contenu 3D peut
alors être visualisé (repère n°5).
FIGURE 15.3
1 2
3 4
5
Copyright 2012 Patrice REY
FIGURE 15.6
Dans l’espace 3D, un sommet représente un point dans l’espace 3D. Un triangle
possède 3 coins et on dira que ses coins sont des sommets. Un triangle est ainsi
repéré dans l’espace 3D par 3 sommets.
Copyright 2012 Patrice REY
(0,1,0)
X
(-1,0,0) (1,0,0)
(0,1,0)
(0,1,0)
direction
(-1,0,0) (1,0,0) haut
X X
(0,0,0) cible
Z Z
474 Développez des applications Internet avec Silverlight 5
//creation d’un contenu 3d
private void Contenu3D() {
...
//configuration de la camera
Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 2), Vector3.Zero,
new Vector3(0, 1, 0));
...
}
Il existe deux types de projection: la projection perspective (figures 15.9, 15.10
et 15.11) et la projection parallèle (figures 15.12 et 15.13). Une projection est
caractérisée par un volume d’observation qui est la zone de l’espace comprise
entre le plan de projection (plan de près) et le plan de loin. Tous les objets qui se
trouvent dans le volume d’observation feront l’objet d’un rendu.
volume
FIGURE 15.9 d’observation
plan de
projection
haut
Y gauche
Z
bas droit
X
centre de
projection plan de loin
(caméra) plan de
près
volume
FIGURE 15.10 d’observation
plan de
projection
hauteur
Copyright 2012 Patrice REY
Y
largeur
Z
angle de
X vue
centre de plan de loin
projection
(caméra)
CHAPITRE 15 □ La modélisation 3D 475
FIGURE 15.11 volume
d’observation
direction du
haut (up
direction) plan de
projection direction du
regard (look
Y direction)
Z
angle de
vue
centre de
projection plan de loin
(caméra) X
gauche
Z
X droit
centre de bas
projection à
l’infini (caméra)
plan de volume
près d’observation plan de
loin
FIGURE 15.13 plan de
projection
hauteur
X largeur
centre de
projection à
l’infini (caméra)
plan de volume
près d’observation plan de
loin
476 Développez des applications Internet avec Silverlight 5
La méthode statique Matrix.CreatePerspectiveFieldOfView permet de créer une
projection de type perspective. Elle reçoit en paramètre l’angle de vue (en radians),
le ratio d’aspect concernant le rapport de la largeur par la hauteur, la distance
entre la caméra et le plan de près, et la distance entre la caméra et le plan de loin.
La classe MathHelper contient des champs statiques et des méthodes statiques
pratiques pour les données demandées. Par exemple un angle de vue de 45 degrés
sera obtenu par le champ statique MathHelper.PiOver4. Pour obtenir la projection
de la vue, il suffit de multiplier la vue par la projection.
//creation d’un contenu 3d
private void Contenu3D() {
...
//configuration de la camera
Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 2), Vector3.Zero,
new Vector3(0, 1, 0));
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
1.33f, 1, 10);
Matrix viewProjection = view * projection;
...
}
Pour obtenir un effet de rendu basique, on utilise un objet BasicEffect. Il est
instancié en lui passant en paramètre le périphérique d’affichage. Sa propriété
View reçoit la vue calculée. Sa propriété Projection reçoit la projection de la vue
(qui est fonction de la vue et du type de projection, perspective ou parallèle). La
propriété VertexColorEnabled active la coloration des pixels pour le rendu.
//creation d’un contenu 3d
private void Contenu3D() {
...
//configuration de la camera
Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 2), Vector3.Zero,
new Vector3(0, 1, 0));
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
1.33f, 1, 10);
Matrix viewProjection = view * projection;
//initialisation des effets
v_effect = new BasicEffect(device);
Copyright 2012 Patrice REY
v_effect.World = Matrix.Identity;
v_effect.View = view;
v_effect.Projection = viewProjection;
v_effect.VertexColorEnabled = true;
}
Le gestionnaire de l’événement Draw permet de dessiner le contenu 3D dans
le contrôle DrawingSurface. La démarche consiste à récupérer le périphérique
d’affichage courant (GraphicsDeviceManager.Current.GraphicsDevice).
CHAPITRE 15 □ La modélisation 3D 477
Pour notre triangle, si on passe les sommets dans le sens des aiguilles d’une
montre pour différencier une face avant d’une face arrière, il faut instancier un
nouvel objet RasterizerState dont la propriété CullMode est fixée à CullMode.
CullCounterClockwiseFace. Et si l’on veut un rendu sous une forme solide, il faut
fixer la propriété FillMode à FillMode.Solid. Le repère n°1 de la figure 15.15 illustre
le résultat obtenu. Si l’on veut un rendu filaire, on fixe la propriété FillMode à
CHAPITRE 15 □ La modélisation 3D 479
device.RasterizerState = device.RasterizerState =
new RasterizerState() { new RasterizerState() {
CullMode = CullMode =
CullMode.CullCounterClockwiseFace, CullMode.CullCounterClockwiseFace,
FillMode = FillMode.Solid FillMode = FillMode.Wireframe
}; };
2 - Modéliser un cube
Nous allons réaliser une scène 3D dans laquelle un cube est modélisé (figure
15.16). Les faces du cube ont des couleurs unies différentes pour se repérer plus
facilement au départ. Des boutons permettent de visualiser le cube en faisant
tourner la caméra autour de l’axe Y et de l’axe X. L’UserControl UnCube.xaml (dans
le projet Modelisation3d.sln dans le dossier chapitre15) illustre cet exemple.
FIGURE 15.16
G (0,1,0) F (1,1,0)
face
face dessus
gauche
F (1,1,0) G (0,1,0)
B (0,1,1) C (1,1,1)
E (1,0,0) H (0,0,0)
H (0,0,0) E(1,0,0)
X
face
droite
Z
D (1,0,1) E (1,0,0)
A (0,0,1) D (1,0,1)
B (0,1,1) C (1,1,1)
face
face
dessous
avant
la face de droite, nous avons le triangle DCF et le triangle FED. Nous entrons donc
les sommets de ces triangles dans cet ordre.
//face droite dcfe
vertices[6] = new VertexPositionColor(pt_d, coul_gris_fonce);
vertices[7] = new VertexPositionColor(pt_c, coul_gris_fonce);
vertices[8] = new VertexPositionColor(pt_f, coul_gris_fonce);
vertices[9] = new VertexPositionColor(pt_f, coul_gris_fonce);
CHAPITRE 15 □ La modélisation 3D 483
Nous allons réaliser une scène 3D dans laquelle un cube est modélisé (figure
15.18). Les faces du cube reçoivent une texture identique. Des boutons permettent
CHAPITRE 15 □ La modélisation 3D 485
Le cube modélisé est le même que celui du paragraphe précédent. La différence est
que les faces sont recouvertes d’une texture 2D. Dans le système de coordonnées
à deux dimensions, une texture 2D possède quatre sommets intitulés t_pos_a, t_
pos_b, t_pos_c et t_pos_d, de type Vector2 (figure 15.19). On instancie les quatre
structures Vector2 avec leurs composantes.
//4 positions de la texture 2d
Vector2 t_pos_a = new Vector2(0, 1);
Vector2 t_pos_b = new Vector2(0, 0);
Vector2 t_pos_c = new Vector2(1, 0);
Vector2 t_pos_d = new Vector2(1, 1);
486 Développez des applications Internet avec Silverlight 5
FIGURE 15.19
(0,1) t_pos_a
(1,1) t_pos_d
t_pos_b t_pos_c B C
Copyright 2012 Patrice REY
A D
t_pos_a t_pos_d
CHAPITRE 15 □ La modélisation 3D 487
Nous allons réaliser une scène 3D dans laquelle un cube est modélisé (figure
15.22). Les faces du cube reçoivent une texture identique. Le cube est éclairé
par un éclairage ambiant et un éclairage directionnel. Des boutons permettent
de visualiser le cube en faisant tourner la caméra autour de l’axe Y et de l’axe
X. L’UserControl UnCubeTextureLumiere.xaml (dans le projet Modelisation3d.sln
dans le dossier chapitre15) illustre cet exemple.
FIGURE 15.22
Nous allons réaliser une scène 3D dans laquelle nous positionnons trois
instances d’un cube 3D et une instance d’un sol 3D (figure 15.23). Les faces du
cube reçoivent une texture identique. Le sol est représenté par une face plane
remplie d’une couleur unie grise. L’UserControl InstanceCube.xaml (dans le projet
Modelisation3d.sln dans le dossier chapitre15) illustre cet exemple.
FIGURE 15.23
//constructeur
public Cube3D(GraphicsDevice device, Vector3 pt_ref_h, float largeur,
string uri_texture, float ratio_aspect, Matrix view) {
...
//configure la camera de type perspective
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
ratio_aspect, 1, 100);
//initialise l’effet BasicEffect
v_effect = new BasicEffect(device);
v_effect.World = Matrix.Identity;
v_effect.View = view;
v_effect.Projection = view * projection;
//on charge une texture depuis une ressource incorporee
//et on la place dans un bitmapimage
Stream s = Application.GetResourceStream(
new Uri(uri_texture, UriKind.Relative)).Stream;
BitmapImage bmp = new BitmapImage();
bmp.SetSource(s);
//on copie le bitmapimage dans un objet texture2d
Texture2D texture;
texture = new Texture2D(device, bmp.PixelWidth, bmp.PixelHeight);
bmp.CopyTo(texture);
//on configure l’effet BasicEffect pour utiliser une texture
//v_effect.VertexColorEnabled = true;
v_effect.TextureEnabled = true;
v_effect.Texture = texture;
}
La déclaration d’une méthode Draw permet d’effectuer la modélisation du cube
sur le périphérique d’affichage.
//dessiner le cube
public void Draw(GraphicsDevice device) {
device.SetVertexBuffer(v_buffer_sommet);
device.SamplerStates[0] = SamplerState.LinearClamp;
foreach (EffectPass pass in v_effect.CurrentTechnique.Passes) {
pass.Apply();
device.DrawPrimitives(PrimitiveType.TriangleList, 0,
v_buffer_sommet.VertexCount / 3);
}
}
Pour matérialiser un sol, on choisit de modéliser une face plane dont on fournit
une longueur de côté. Le procédé de modélisation est rigoureusement identique à
celui du cube au détail près que la face plane est remplie d’une couleur unie.
public class Sol3D {
private VertexBuffer v_buffer_sommet;
private BasicEffect v_effect;
//permettre au cube de changer la matrice de son monde
496 Développez des applications Internet avec Silverlight 5
//pour le faire bouger ou tourner
public Matrix World {
get { return v_effect.World; }
set { v_effect.World = value; }
}
//permettre au cube de changer sa matrice de vue
//pour faire bouger ou tourner la camera
public Matrix View {
get { return v_effect.View; }
set { v_effect.View = value; }
}
//permettre au cube de changer sa projection de vue
//pour changer le ratio d’aspect si la taille du drawingsurface
//est modifiee
public Matrix Projection {
get { return v_effect.Projection; }
set { v_effect.Projection = value; }
}
//
public Sol3D(GraphicsDevice device, float aspectRatio, Matrix view,
float larg_cote) {
// Create a large flat floor square, consisting of two triangles.
Vector3 pt_a = new Vector3(-larg_cote, 0, larg_cote);
Vector3 pt_b = new Vector3(-larg_cote, 0, -larg_cote);
Vector3 pt_c = new Vector3(larg_cote, 0, -larg_cote);
Vector3 pt_d = new Vector3(larg_cote, 0, larg_cote);
Color coul_gris = new Color(170, 170, 170);
VertexPositionColor[] vertices = new VertexPositionColor[6];
vertices[0] = new VertexPositionColor(pt_a, coul_gris);
vertices[1] = new VertexPositionColor(pt_b, coul_gris);
vertices[2] = new VertexPositionColor(pt_c, coul_gris);
vertices[3] = new VertexPositionColor(pt_c, coul_gris);
vertices[4] = new VertexPositionColor(pt_d, coul_gris);
vertices[5] = new VertexPositionColor(pt_a, coul_gris);
// Set up the vertex buffer.
v_buffer_sommet = new VertexBuffer(device, typeof(VertexPositionColor),
vertices.Length, BufferUsage.WriteOnly);
v_buffer_sommet.SetData(0, vertices, 0, vertices.Length, 0);
// Configure the camera.
Matrix projection = Matrix.CreatePerspectiveFieldOfView(
Copyright 2012 Patrice REY
Nous allons réaliser une scène 3D dans laquelle nous positionnons un cube 3D
(figure 15.24). Les faces du cube reçoivent une texture identique. Trois contrôles
de type Slider permettent d’effectuer des rotations du cube par rapport aux axes
X, Y et Z. L’UserControl Mouvements.xaml (dans le projet Modelisation3d.sln dans
le dossier chapitre15) illustre cet exemple.
Sur le Canvas x_cnv_root, on positionne un contrôle DrawingSurface dont le ratio
Copyright 2012 Patrice REY
d’aspect est égal à 2 (une longueur de 800 et une largeur de 400). Trois contrôles
de type Slider sont positionnés et leurs propriétés Value sont liées par databinding
à trois TextBlock.
<Canvas x:Name= «x_cnv_root» HorizontalAlignment= «Center»
VerticalAlignment= «Top» Height= «587» Width= «800»>
<Border BorderThickness= «2» Width= «800» Height= «430» BorderBrush= «Black»
Canvas.Top= «0»>
CHAPITRE 15 □ La modélisation 3D 499
FIGURE 15.24
Y Y
X X
Z Z
FIGURE 15.26
Y Y
X X
Z Z
502 Développez des applications Internet avec Silverlight 5
La figure 15.27 visualise le résultat obtenu si on effectue une rotation par rapport
à l’axe Z seul en fonction de la position de départ du cube. La figure 15.28 visualise
le résultat obtenu si on effectue une rotation par rapport à l’axe X, une rotation par
rapport à l’axe Y et une rotation par rapport à l’axe Z.
FIGURE 15.27 Y Y
X X
Z Z
FIGURE 15.28 Y Y
X X
Z Z
Copyright 2012 Patrice REY
INDEX
Index 505
Symboles BasedOn 357
BasicEffect 464
<Application> 167 Begin 271
<Application.Resources> 44 BeginLoad 209
<DoubleAnimation.EasingFunction> 287 BeginTime 272
<Image.OpacityMask> 258 Bevel 221
<object> 31 BezierSegment 226, 230
<param> 31 Binding 48, 378
<ResourceDictionary> 44 BindingValidationError 90
<ToolTipService.ToolTip> 125 BitmapCache 323
BitmapImage 114
A BitmapSource 263
accélération matérielle 321 BlackoutDates 159
AcceptsReturn 137 Block 143
acquisition vidéo 345 BlurEffect 259
Action de génération 178 BlurRadius 260
AlternatingRowBackground 404 Bold 112
AmbientLightColor 489 Border 69
Angle 239 BorderBrush 69
animation 270 BorderThickness 69
animations réalistes 283 Both 70
Application 164 BounceEase 284
ApplicationLifetimeObjects 169 Bounces 287
AppManifest 22, 164 Bounciness 287
ArcSegment 226, 228 Brush 250
AreaSeries 443 BubbleSeries 456
ARGB 251 bubbling 91
ARGB32 265 Button 88, 118
Arrange 55, 85 ButtonBase 118
assembly 165 byte 253
AssemblyInfo 22
AssemblyPart 165 C
Auto 60 cache de composition 322
AutoCompleteBox 137 CacheMode 323
AutoGenerateColumns 402 Calendar 159
AutoPlay 328 callback 80
AutoReverse 272 CancelEventArgs 202
autoUpgrade 31 CancelLoad 209
CanExecute 104
B CanExecuteChanged 105
BackEase 284 CanGoBack 205
background 31 CanGoForward 205
Balance 332 CanLoad 209
BarSeries 446 CanUserReorderColumns 405
506 Index
CanUserResizeColumns 404 ColumnDisplayIndexChanged 405
Canvas 54, 56 ColumnReordered 405
Canvas.Left 56 ColumnReordering 405
Canvas.Top 56 ColumnSeries 450
CaptureDevice 345 ColumnWidth 404
CaptureDeviceConfiguration 346 ComboBox 132
CaptureImageAsync 349 ComboBoxItem 132
CaptureImageCompleted 349 Command 118
CaptureImageCompletedEventArgs 349 Commande 93
CaptureMouse 101 CommandParameter 118
CaptureSource 346 CommonStates 372
CaptureState.Started 349 compilation 28
caractères spéciaux 42 CompositeTransform 235, 243
CaretBrush 138 CompositionMode 469
CategorieAxis 449 CompositionTarget 312
CenterOfRotationX 245 Content 380
CenterOfRotationY 245 ContentControl 116
CenterRotationZ 245 ContentLoader 212
CenterX 237 ContentPresenter 363
CenterY 237 ContentPropertyAttribute 429
CheckAndDownloadUpdateAsync 169 Contenu 178
CheckAndDownloadUpdateCompleted 169 Control 103
CheckBox 118 ControlTemplate 362
Child 126 ConverterCulture 408
Children 55 CornerRadius 69, 363
ChildWindow 201 CounterClockwise 229
CircleEase 284 CreatePerspectiveFieldOfView 476
classe partielle 36 CubicEase 284
Click 88 CullClockwiseFace 478
ClickMode 118 CullCounterClockwiseFace 478
ClientBin 26 CullMode 478
Clip 233 Current 169
clipping 233 CurrentCellChanged 404
Clockwise 229 CurrentColumn 404
Closed 126, 202 CurrentItem 404
Closing 202 CustomValidation 397
code-behind 36
Collapsed 68 D
Collection<T> 438 databinding 48
Color 251 DataContext 378
ColorAnimation 270 DataContextChanged 90
ColorAnimationUsingKeyFrames 270, 301 DataForm 382
ColorKeyFrame 301 DataGrid 400
ColumnDefinition 58 DataGridCheckBoxColumn 404
ColumnDefinitions 58
Index 507
DataGridColumn 403 Draw 469
DataGridRow 403 DrawingSurface 464, 469
DataGridTemplateColumn 404, 411 DrawPrimitives 477
DataGridTextColumn 403 Drop 89
DataPager 420 DropShadowEffect 259
DataTemplate 411, 433 Duration 272
DatePicker 159
DateValidationError 161 E
découpe 233 EaseIn 284
Delta 101 EaseInCore 295
DependencyObject 78 EaseInOut 284
DependencyProperty 79 EaseOut 284
DependencyPropertyChangedEventArgs 81 EasingColorKeyFrame 302, 309
Deployment 165 EasingDoubleKeyFrame 309
Description 382 EasingFunctionBase 283, 295
DescriptionStates 391 EasingPointKeyFrame 309
DescriptionViewer 387 Effect 259
DesignHeight 35 EffectiveValueEntry 78
DesignWidth 35 effet de pixellisation 325
DesiredFormat 345 ElasticEase 284, 288
DesiredSize 86 ElementName 49, 381
DialogResult 202 Ellipse 218
dictionnaires de ressources 45 EllipseGeometry 224
Direction 260 EnableDefaultLighting 492
DirectionalLight0 489 enableHtmlAccess 32
DiscreteObjectKeyFrame 302 EndLoad 209
DispatcherTimer 312, 335 EndPoint 254
Display 382 EntryPointAssembly 165
DisplayAttribute 382 EntryPointType 166
DisplayDateEnd 159 environnement de développement 18
DisplayDateStart 159 espaces de noms 34
DisplayMemberPath 131 événements 42
DisplayMode 159 événements routés 88
DockPanel 54, 61 EvenOdd 223
DockPanel.Dock 61 Execute 104
DoubleAnimation 270 Exit 167
DoubleAnimationUsingKeyFrames 270, 301 ExponentialEase 284
DoubleKeyFrame 301
DoubleTap 89 F
DownloadProgressChanged 182
DownloadProgressChangedEventArgs 183 Fill 115, 216
DownOnly 70 FillBehavior 272
DragEnter 89 FillMode 478
DragLeave 89 FillRule 222
DragOver 89 FirstDayOfWeek 159
508 Index
Flat 219 Grid.RowSpan 58
focus 103 GroupDescription 417
FocusManager 104
FocusStates 372, 375 H
FontFamily 112 Handled 98
FontSize 112 HasDescription 391
FontSource 138 HasElevatedPermissions 169
FontStretch 112 Header 136
FontStyle 112 HeaderedItemsControl 425, 433
FontWeight 112 HeaderStyle 404
Foreground 112 HeadersVisibility 404
Forever 272 Height 217
Frame 205 héritage de style 357
FramesPerSecond 345 HierarchicalDataTemplate 433
FrameworkElement 54, 90 Hold 89
FriendlyName 345 HoldEnd 273
From 300 HorizontalAlignment 58
FrozenColumnCount 405 HorizontalContentAlignment 118
G HorizontalOffset 126
HorizontalScrollBarVisibility 71, 137
GeneralTransform 236 Host 169
GeneratedDuration 375 Hyperlink 144
GeometryGroup 224 HyperlinkButton 118
gestion de l’interactivité 88
GetCellContent 404 I
GetIndex 404 ICollectionView 416
GetResourceStream 169 ICommand 104
GlobalOffsetX 245 IEasingFunction 283
GlobalOffsetY 245 IEnumerable 400
GlobalOffsetZ 245 IEnumerable<T> 440
GoBack 205 Image 114
GoForward 205 ImageBrush 250, 255
GotFocus 88, 89 ImageFailed 116, 256
GPU 321 ImageSource 255, 263
GPUAccelerationDisabled 466 ImplicitInputBrush 250
GradientStop 254 INavigationContentLoader 209
Graphic Processor Unit 321 Indeterminate 120
GraphicsDevice 464 InitializeComponent 36
GraphicsDeviceManager 464, 466 initParams 32, 167
Grid 54, 58 Inline 112
Grid.Column 58 InlineUIContainer 144
Grid.ColumnSpan 58 INotifyPropertyChanged 392
GridLinesVisibility 404 Install 169
Grid.Row 58 InstallState 169
Index 509
InstallStateChanged 170 L’alignement 66
interpolation réaliste 309 langage déclaratif 34
Invalidate 469 LargeChange 155
InvalidateSurface 477 LastChildFill 62
InvalidFocused 391 layout 54
InvalidUnfocused 391 LayoutUpdated 90
IsChecked 120 LightingEnabled 489
IsDirectionReversed 157 Line 219
IsDropDownOpen 141 LinearDoubleKeyFrame 301
ISelectionAdapter 141 LinearGradientBrush 250, 253
IServiceProvider 394 LineBreak 112
IsExpanded 426 LineGeometry 224
IsFocused 118 LineHeight 110
IsIndeterminate 156 LineSegment 226, 228
IsLargeArc 229 LineSeries 454
IsMouseOver 118 ListBox 129
IsOpen 126 LoadComponent 169
IsPressed 118 Loaded 91
IsReadOnly 138 LoadingRow 404
IsRunningOutOfBrowser 169 LocalOffsetX 245
IsTabStop 104 LocalOffsetY 245
IsTextCompletionEnabled 141 LocalOffsetZ 245
IsThreeState 120 LostFocus 88, 89
IsTodayHighlighted 159 LostMouseCapture 89
Italic 112
ItemCollection 128 M
ItemContainerGenerator 425 M11 243
ItemContainerStyle 425 M12 243
Items 128 M21 243
ItemsControl 128 M22 243
ItemsSource 130 MainWindow 169
IValueConverter 412 ManipulationCompleted 89
K ManipulationDelta 89
ManipulationStarted 89
Key 94 mapping d’URI 207
Keyboard 93 mapping XAML 35
KeyDown 88, 89, 93 marges 65
KeyFrame 300 Margin 65
KeySpline 307 markup 48
KeyTime 304 masque d’opacité 257
KeyUp 88, 89, 93 MathHelper 476
Matrix 243, 473
L MatrixTransform 235
Label 380 MaxDropDownHeight 141
510 Index
Maximum 155 O
Measure 55, 85
MediaCommand 89 ObjectAnimationUsingKeyFrames 270, 301
MediaElement 328 ObjectCollection 438
MediaEnded 332 ObjectKeyFrame 301
MediaFailed 332 ObservableObjectCollection 131
MediaOpened 332 OffsetX 243
mini-langage 232 OffsetY 243
Minimum 155 OldValue 81
minRuntimeVersion 31 onError 31
Miter 221 OneTime 391
Mode 51 OneWay 51, 391
mode de routage 91 onglet Design 23
modèle 362 onLoad 32
mode unidirectionnel 51 OnPropertyChanged 392
ModifierKeys 93 onResize 32
Modifiers 93 onSilverlightError 168
MouseButtonEventArgs 98 onSourceDownloadComplete 32
MouseEventArgs 97 onsourcedownloadprogresschanged 177
MouseLeftButtonDown 88 onSourceDownloadProgressChanged 32
MouseLeftButtonUp 88 Opacity 257, 260
MouseMove 88 OpacityMask 257
MouseRightButtonDown 88 Opened 126
MouseRightButtonUp 88 OpenReadAsync 182
MouseWheelEventArgs 99 OpenReadCompleted 182
MP3 328 Order 382
MPEG-4 328 Orientation 57
N P
NaturalDuration 333 Padding 66, 110
NaturalVideoHeight 339 PageChanged 420
NaturalVideoWidth 339 PageChanging 420
Navigated 205 PageChangingEventArgs 420
NavigateUri 119 PagedCollectionView 416
Navigating 205 PageIndex 420
NavigatingCancelEventArgs 205 PageResourceContentLoader 209
NavigationService 209 Panel 54
NavigationStopped 205 Paragraph 144
NaviguerVersPage 199 param 170
NewValue 81 Parts 165
NoDescription 391 PasswordBox 137
NonZero 223 PasswordChar 139
Not3DCapable 466 Path 49, 224
NotifyOnValidationError 393 PathFigure 226
PathFigureCollection 226
Index 511
PathGeometry 224 PropertyGroupDescription 417
PathSegment 226 PropertyMetadata 80
PathSegmentCollection 226 PropertyPath 277
Pause 271 propriété attachée 84
PenLineCap 219 propriétés 37
perspective 245 propriétés attachées 40
PieSeries 458 propriétés complexes 39
pinceau vidéo 341 propriétés de dépendances 74
PixelHeight 345 propriété simple 76
Pixels 266 propriétés implicites 41
pixel shader 262 propriétés simples 38
PixelShader 464
PixelWidth 345 Q
Placement 124 QuadraticBezierSegment 226, 230
PlacementTarget 124 QuadraticEase 284
PlaneProjection 245 QuarticEase 284
PlatformKeyCode 94 QuinticEase 284
plug-in 54
Point1 230 R
Point2 230
Point3 230 RadialGradientBrush 250, 253
PointAnimation 270 RadioButton 118
PointAnimationUsingKeyFrames 270, 301 RadiusX 217
PointCollection 221 RadiusY 217
PointKeyFrame 301 RangeBase 154
Points 221 RasterizerState 478
PolyBezierSegment 226 readonly 76
Polygon 222 Rectangle 74, 217
Polyline 221 RectangleGeometry 224
PolylineSegment 226 RegisterAttached 85
PolyQuadraticBezierSegment 226 ReleaseMouseCapture 101, 103
Populated 141 RenderAtScale 325
Populating 142 Rendering 312
Popup 126 RenderMode 466
Power 291 RenderModeReason 466
PowerEase 284, 291 RenderTransform 237
profondeur 67 RepeatBehavior 272
ProgressBar 155 RepeatButton 118, 158
ProgressPercentage 183 RequiredAttribute 382
projection parallèle 474 Resource 178
projection perspective 474 ResourceDictionary 47
projet hébergé 26 Resources 169
Property 352 ressources 43
propertyChangedCallback 80 Resume 271
PropertyChangedEventArgs 392 RichTextBlock 153
512 Index
RichTextBlockOverflow 153 ShowGridLines 59
RichTextBox 137, 146 SineEase 284
RootVisual 167, 169 site web ASP.NET 20
RotateTransform 235, 239 site web ordinaire 20
RotationAngle 229 SizeChanged 91
RotationX 245 SkewTransform 235, 240
RotationY 245 Slider 157
RotationZ 245 SmallChange 155
Round 219, 221 Software Development Kit 18
RoutedEventArgs 92 SolidColorBrush 250, 251
Row 414 SortDescription 418
RowBackground 404 source 31
RowDefinition 58 SourceName 341
RowDefinitions 58 Span 112
RowDetailsVisibilityChanged 415 SpeedRatio 272
RowDetailsVisibilityMode 415 splash screen 175
RowHeight 404 splashScreenSource 32
Run 112 SplineColorKeyFrame 302
RuntimeVersion 166 Springiness 290
RVB prémultipliées 265 Square 219
StackPanel 54, 57
S StartPoint 229, 254
SamplerState 488 Startup 167, 170
ScaleTransform 235 StartupEventArgs 167
ScaleX 237 State 349
ScaleY 237 StaticResource 356
ScrollViewer 69 Stop 271
SecurityBlocked 466 Storyboard 270
SelectedDatesChanged 160 Storyboard.SetTarget 277
SelectedItem 133 Storyboard.SetTargetProperty 277
SelectedItemChanged 427 Storyboard.TargetName 275
SelectedText 138 Storyboard.TargetProperty 275
SelectionBackground 139 StreamResourceInfo 180
SelectionChanged 133 Stretch 58, 217
SelectionLength 138 stretching 58
SelectionStart 138 StringLengthAttribute 382, 385
Selector 132 Stroke 216
Setter 352 StrokeDashArray 220
SetVertexBuffer 477 StrokeDashCap 220
SetWindow 126 StrokeDashOffset 220
ShaderEffect 259 StrokeEndLineCap 219
ShadowDepth 260 StrokeLineJoin 221
Shape 216 StrokeStartLineCap 219
Show 204 StrokeThickness 75, 216
style 352
Index 513
style implicite 355 TranslateTransform 235
SweepDirection 229 TreeView 424
système de disposition 54 TreeViewItem 424
Triangle 219
T ttf 114
TabControl 134 TwoWay 391
TabIndex 104 U
TabItem 134
TabStripPlacement 136 UIElement 54, 89
Tap 90 Unchecked 120
Target 381 Underline 112
TargetName 119 UnhandledException 168, 170
TargetType 353 Uniform 70, 115
template 362 Uniform Resource Identifier 168
Template 362 UniformToFill 116
TemplateBinding 365 Unknown 93
TemplateVisualState 370 Unloaded 91
TemporarilyUnavailable 466 UnloadingRow 404
Text 112 UpOnly 70
TextAlignment 110 UriMapper 207
TextBlock 110 UriMapping 207
TextBox 137 UriMappings 207
TextChanged 138 UriSource 114
TextDecorations 110, 112
TextElement 143 V
texte statique 110 ValidatesOnExceptions 393
TextInput 90 ValidationAttribute 394
TextInputStart 90 ValidationContext 393
TextInputUpdate 90 ValidationException 394
TextSelection 150 ValidationResult 397
Texture2D 487 ValidationStates 391
TextWrapping 110 ValidationSummary 398
Thumb 157 Validator 394
Tick 340 ValidFocused 391
Timeline 271 ValidUnfocused 391
TimeSpan 272 Value 155, 352
To 300 ValueChanged 155
ToggleButton 118 ValueMemberBinding 141
ToolTip 123, 124 ValueMemberPath 141
ToolTipService 123 Vector2 485
Track 157 Vector3 471
Transform 236 VertexBuffer 470
TransformGroup 235, 241 VertexPositionColor 472
Transitions 375 VertexPositionNormalTexture 491
514 Index
VertexPositionTexture 486 x:Name 36
VertexShader 464
VerticalAlignment 58 Y
VerticalContentAlignment 118 Y1 219
VerticalOffset 126 Y2 219
VerticalScrollBarVisibility 71, 137
VideoBrush 250, 341 Z
VideoCaptureDevice 345, 346
VideoFormat 345 ZIndex 67
vidéo réfléchie 343
Viewbox 69
visibilité 68
Visibility 68
Visible 68
VisibleWhenSelected 415
VisualState 370
VisualStateGroup 370, 375
VisualStateManager 370
VisualTransition 375
Volume 332
W
Watermark 138
WebBrowserBrush 250
WebClient 182
Width 217
windowless 32
Windows Media Audio 328
Windows Media Video 328
WireFrame 479
WMA 328
WrapPanel 54, 63
wrapper 85
WriteableBitmap 263
X
X1 219
X2 219
XAML 34
XML 34
xmlns 78
XmlReader 185
XmlReader.Create 185
XNA 464