IV. XAML et interface utilisateur▲
IV-A. Définition du XAML▲
Windows Phone 7 utilise pour décrire l'interface une version spécifique de Silverlight. Le code source de Silverlight est en XAML= "eXtensible Application Markup Language". Prononcez "Zammel". Il s'agit d'un langage XML permettant de définir des interfaces d'applications (WPF, Silverlight). En d'autres termes, quand vous dessinez une interface utilisateur en Silverlight, elle est enregistrée en XAML.
Le code XAML respecte la syntaxe XML ; il est enregistré dans un fichier ayant l'extension .xaml.
IV-B. Balisage▲
XAML est un langage balisé.
Qu'est ce qu'une balise ? Élément Sémantique de base des langages de balisage. En XML une balise est un mot clé, un élément. Elle est comprise entre crochets et possède un nom et parfois des attributs. En XAML l'élément est le nom d'un objet visuel (Buttom, TextBlock…)d'une propriété de cet objet visuel (Background,Content…), ou de tout élément intervenant dans l'UI (une Brush, un ListItem…) .
Toutes les balises doivent être ouvertes puis refermées . On retrouvera donc toujours une balise de début et une balise de fin. La balise de fin porte le même nom que la balise de début à l'exception du signe / qui le précède.
Exemple :
<Button>
</Button>
On peut combiner balise de début et de fin :
<Button />
Cela suffit à afficher un bouton.
Les balises peuvent être intriquées :
<Grid>
<Button>
</Button>
</Grid>
La dernière balise ouverte doit être la première fermée. Ici on a mis un bouton dans une grille.
IV-C. Casse, espace, tabulation, commentaire▲
XAML est sensible à la casse : majuscules et minuscules ne sont pas équivalentes. Les espaces et tabulations sont ignorés.
Attention donc quand on tape du code XAML : grid ou click ne sont pas acceptés, il faut taper Grid et Click. Le nom des types et attributs commence par une majuscule.
Les commentaires sont ignorés. Voici un commentaire en XAML :
<!--Commentaire
ici -->
IV-D. Objet, attribut, propriété▲
On a vu que pour créer un objet, il suffit d'écrire :
<Button>
</Button>
Un objet visuel a des propriétés : la couleur de fond, les dimensions d'un bouton, le texte dans un TextBlock sont les propriétés. Il y a différentes manières d'indiquer les valeurs d'une propriété d'un objet en XAML.
1- Les propriétés des objets visuels peuvent s'écrire sous forme d' attributs. Un attribut est le nom d'une propriété de la balise souvent associé à une valeur, il est mis dans la balise d'ouverture après le nom de la balise :
<Button
Background
=
"Blue"
Foreground
=
"Red"
Content
=
"C'est un bouton"
/>
Ici dans un bouton, on a utilisé les attributs Background, Foreground Content, pour indiquer la couleur du fond, du texte et le texte à afficher dans le bouton. À un attribut on affecte une valeur qui est entre guillemets et est un simple texte. On remarque que la syntaxe est simple mais on ne peut mettre que des valeurs simples. Les attributs sont séparés par un espace.
2- Les propriétés des objets visuels peuvent aussi s'écrire sous forme d'objet balisé dans un format alternatif appelé "Property element syntax" (syntaxe d'élément propriété), qui étend la syntaxe de XML. Une propriété a aussi une balise de début et de fin, la syntaxe est celle de l'XML c'est à dire de la forme :
<TypeName.Property>
Voyons, par exemple, la propriété Background d'un bouton :
<Button.Background>
</Button.Background>
L'intérêt de cette syntaxe est qu'entre les deux balises, on peut mettre d'autres balises ou un élément complexe (comme une Brush, par exemple).
Voici l'exemple de notre bouton :
<Button>
<Button.Background>
<SolidColorBrush
Color
=
"Blue"
/>
</Button.Background>
<Button.Foreground>
<SolidColorBrush
Color
=
"Red"
/>
</Button.Foreground>
<Button.Content>
C'est un bouton
</Button.Content>
</Button>
3- Propriété par défaut.
Pour le texte affiché dans un bouton, on a vu qu'on pouvait l'écrire avec l'attribut content.
<Button
Content
=
"Cliquez ici !"
/>
Et aussi sous forme d'objet.
<Button>
<Button.Content>
Cliquez ici
</Button.Content>
</Button>
Mais comme Content est la propriété par défaut des boutons, on peut l'écrire aussi comme cela :
<Button>
Cliquez ici !</Button>
De même la propriété Text d'un TextBlock peut s'écrire ainsi :
<TextBlock>
Hello!
</TextBlock>
On parle ici d'élément de contenu.
Remarque : on a accès dans un objet aux propriétés qui sont héritées de la classe de base. Par exemple, dans un bouton, on a accès à la propriété BackGround qui est une propriété héritée de controls.
4- Propriété attachée.
On peut utiliser dans un objet une propriété attachée appartenant au parent :
<DockPanel>
<Button DockPanel.
Dock
=
"Left"
>
Annuler</Button>
</DockPanel>
Dans le bouton on utilise DockPanel.Dock qui n'est pas une propriété des boutons mais une propriété du DockPanel, le parent.
Le bouton est collé sur le bord gauche du DockPanel grâce à la propriété attachée Dock qui prend la valeur Left de l'énumération Dock.
5- Propriété de liste.
Beaucoup de contrôles conteneurs peuvent contenir des contrôles enfants. Exemple avec un StackPanel (conteneur permettant d' empiler des contrôles) dans lequel on met plusieurs boutons ; on devrait normalement utiliser la balise StackPannel.Child.
<StackPanel>
<StackPanel.Children>
<Button>
First Button</Button>
<Button>
Second Button</Button>
</StackPanel.Children>
</StackPanel>
En fait, comme c'est une liste, on peut omettre cette balise StackPanel.Child car Silverlight sait que dans un StackPanel il y a une collection de contrôles :
<StackPanel>
<Button>
First Button</Button>
<Button>
Second Button</Button>
</StackPanel>
IV-E. Élément racine▲
Il doit y avoir un élément racine (root) et un seul dans un fichier XAML, il contient tous les autres. En WP c'est une grille.
<!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
<Grid
x
:
Name
=
"LayoutRoot"
Background
=
"Transparent"
>
<Grid.RowDefinitions>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition
Height
=
"*"
/>
</Grid.RowDefinitions>
<!--TitlePanel contient le nom de l'application et le titre de la page-->
<StackPanel
x
:
Name
=
"TitlePanel"
Grid.
Row
=
"0"
Margin
=
"12,17,0,28"
>
<TextBlock
x
:
Name
=
"ApplicationTitle"
Text
=
"MON APPLICATION"
Style
=
"{StaticResource PhoneTextNormalStyle}"
/>
<TextBlock
x
:
Name
=
"PageTitle"
Text
=
"nom de la page"
Margin
=
"9,-7,0,0"
Style
=
"{StaticResource PhoneTextTitle1Style}"
/>
</StackPanel>
<!--ContentPanel - placez tout contenu supplémentaire ici-->
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
>
</Grid>
</Grid>
IV-F. Code-behind, évènements▲
L'interface est en XAML. Le code (dit code-behind) est en C#. Le code XAML et le code-behind sont identifiés en spécifiant un espace de noms et une classe.
Soit un logiciel nommé CalculeTout, par exemple.
En haut du fichier XAML qui décrit l'interface (fichier .xaml) :
<
phone
:
PhoneApplicationPage
x
:
Class
=
"CalculeTout.MainPage"
La classe se nomme "CalculeTout.MainPage" (cela correspond à EspacedeNom.NomPage).
On remarque que WP a pris, pour le nom de l'espace de noms, le nom de l'application. Et la page de démarrage se nomme par défaut MainPage.
En haut du fichier C# contenant le code (fichier .xaml.cs) :
namespace
CalculeTout
{
public
partial
class
MainPage :
PhoneApplicationPage
{}
}
On a bien l'espace de noms qui est le même que celui de la page XAML (nom de l'application par défaut) et la page qui est en fait une classe dont le nom est MainPage. C'est une classe partielle (partial), l'autre partie de la classe étant dans le code XAML.
Si dans la page on ajoute un Bouton et si on double-clique sur ce bouton, on se retrouve dans le code-behind en C# qui a été crée automatiquement dans le fichier .cs :
namespace
CalculeTout
{
public
partial
class
MainPage :
PhoneApplicationPage
{
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
}
private
void
button1_Click
(
object
sender,
RoutedEventArgs e)
{
MessageBox.
Show
(
"Hello"
);
}
}
}
Il y a eu création d'une fonction button1_Click qui contient le code lié à l'évènement. Elle sera exécutée quand l'utilisateur cliquera sur le bouton.
On aurait pu créer soi-même la gestion des évènements en nommant la fonction liée au Click du bouton dans le code XAML :
<Button
Name
=
"Button1"
Click
=
"MyButtonClick"
>
Il faut dans ce cas ensuite créer une fonction MyButtonClick en C#.
private
void
MyButtonClick
(
object
sender,
RoutedEventArgs e)
{
MessageBox.
Show
(
"Hello"
);
}
sender est l'objet qui a déclenché l'évènement (le bouton ici).
e est de type RoutedEventArgs et contient les arguments de l'évènement.
On remarque que pour pallier la disparition des groupes de contrôles et pour que le clic, sur plusieurs boutons, exécute la même fonction, on peut indiquer Click="MyButtonClick" pour plusieurs boutons.
<Button
Name
=
"Button1"
Click
=
"MyButtonClick"
>
<Button
Name
=
"Button2"
Click
=
"MyButtonClick"
>
IV-G. Extension de balisage▲
On a vu qu'un attribut avait une valeur, mais il peut aussi avoir comme valeur une extension.
Les principales extensions sont les Binding et les ressources.
Les caractères { et } indiquent une extension de balisage (markup extension).
Exemple d'une extension : dans un Border, on utilise comme Style une ressource (staticResource) nommée PageBackground.
<Border
Style
=
"{StaticResource PageBackground}"
>
Exemple d'une extension avec ici une liaison de données (Binding). Ici le texte du TextBlock prendra la valeur de Title.
<TextBlock
Text
=
"{Binding Title}"
/>
x:Key.
Définit une clé unique (un nom unique ) pour chaque ressource dans un ResourceDictionnary ; pour utiliser cette ressource, on l'appellera par son nom.
Créons une ressource pour une grille, c'est un LinearGradientBrush (un dégradé) que l'on nomme "FunkyBrush" (c'est son nom, une clé unique).
<Grid.Resources>
<LinearGradientBrush
x
:
Key
=
"FunkyBrush"
>
<GradientStop
Color
=
"Yellow"
Offset
=
"0"
/>
<GradientStop
Color
=
"Green"
Offset
=
"1"
/>
</LinearGradientBrush>
</Grid.Resources>
Ensuite on peut l'utiliser dans la grid :
<Button
Background
=
"{StaticResource FunkyBrush}"
>
Click Me</Button>
Code complet :
<Grid>
<Grid.Resources>
<LinearGradientBrush
x
:
Key
=
"FunkyBrush"
>
<GradientStop
Color
=
"Yellow"
Offset
=
"0"
/>
<GradientStop
Color
=
"Green"
Offset
=
"1"
/>
</LinearGradientBrush>
</Grid.Resources>
<Button
Background
=
"{StaticResource FunkyBrush}"
>
Click Me</Button>
</Grid>
La dernière ligne peut aussi être écrite sans extension de balisage, sous la forme :
<Button
Content
=
"Click Me"
>
<Button.Foreground>
<StaticResource
ResourceKey
=
"FunkyBrush"
/>
</Button.Foreground>
</Button>
On ajoute que :
x:Name.
Identifie de manière unique les éléments définis en XAML comme les contrôles.
x:Class.
Définit l'espace de noms et le nom d'une classe.
IV-H. Espace de noms▲
Chapitre difficile ! À lire plus tard et plusieurs fois.
On a vu qu'au début du fichier XAML, on pouvait inclure un espace de noms grâce à xmlns: (XML Name Space) et ainsi donner accès à des objets extérieurs.
La syntaxe est :
xmlns:<alias>
="<namespace identifier>
"
alias est maintenant le nom local de l'espace de noms namespace identifier.
Pour utiliser un objet de l'espace de noms.
QName = <alias>
:<local name>
Voyons les espaces de noms qui sont déjà présents.
Déclarations des espaces de noms Silverlight et XAML
Ce sont les deux premières lignes du code XAML. Elle indiquent une adresse Web.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
C'est l'espace de noms XAML.
:x est là pour créer l'alias x (remarquer le « : »).
La ligne indique l'espace de noms xaml, dessous il se nommera « x: »,
on le retrouve dans x:Key, x:Name…
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
C'est l'espace de noms Silverlight (à noter que cet espace de noms contient les classes des objets visuels. Il contient les classes Button, ListBox…)
On remarque que la ligne commence par xmlns= sans « : » il n'y a donc pas d'alias et les classes de cet espace de noms peuvent être utilisées sans préfixe.
On pourra instancier un objet dont la classe est dans cet espace de noms :
<Button
Name
=
"ButtonOk"
/>
On remarque qu'on utilise Name car c'est l'espace de noms par défaut.
On va maintenant voir comment inclure d'autres espaces de noms dans le code XAML.
Il faudra utiliser « clr-namespace » : espace de noms CLR (Common Language Runtime) déclaré dans l'assembly qui contient les types publics. On déclarera l'espace de noms et si nécessaire l'assembly (nom de la DLL) ; ici on n'utilise plus l'URL.
Exemple : espace de noms MyNameSpace contenant une classe SuperButton.
xmlns:Mns="clr-namespace:MyNameSpace"
Remarquons qu'on a donné un nom, un alias (Mns) à l'espace de noms MyNameSpace grâce à xmlns:Mns.
On utilise les « : ». Maintenant quand on utilise Mns on utilise en fait MyNameSpace.
L'assembly peut être omis si l'espace de noms référencé est défini dans le même assembly que l"application.
Ensuite, toujours en XAML on peut utiliser un objet de cet espace de noms :
<
Mns
:
SuperButtom
x
:
Name
=
"ButtonOk"
/>
Remarquons qu'on utilisera la syntaxe Espacedenom:Objet avec Mnp:MyObjet.
On utilise « x:Name » car l'instance appartient à un autre espace de noms que celui par défaut.
Cela permettra d'utiliser le nom dans le code-behind.
Si l'espace de noms fait partie d'un autre assembly (d'une autre ddl), il faudra ajouter le nom de l'assembly.
Par exemple, on a un espace de noms CustomClasses qui est dans un assembly nommé Custom (la DLL custom.dll) ; on veut utiliser la classe MyControle de cet espace de noms, on doit écrire :
xmlns:mycustom="clr-namespace:CustomClasses;assembly=Custom"
On remarque après le terme assembly il y a « = ».
Puis pour utiliser MyControle.
<
mycustom
:
MyControle
x
:
Name
=
"MyControle"
/>
Exemples : comment utiliser dans le code XAML une classe créée dans le code C#.
Je crée une classe MyButton (qui hérite de Button) dans l'espace de noms MyControle.
Voyons le code C# (il est dans le code C# de l'application) :
using
System;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Input;
using
Microsoft.
Phone.
Shell;
using
Microsoft.
Phone.
Controls;
namespace
MyControle
{
public
class
MyButton :
Button
{
// Constructeur
public
MyButton
(
)
{
}
}
}
Pour utiliser MyButton dans le code XAML :
xmlns:MyControle="clr-namespace:MyControle"
<
MyControle
:
MyButton
x
:
Name
=
"ButtonOk"
>
Ok</
MyControle
:
MyButton>
L'interface a un comportement bizarre. Il faut compiler le projet ce qui compile le code correspondant à la classe ensuite pas de problème.
Autre exemple : si je veux utiliser des string dans un dictionnaire de ressources, il faut que j'importe l'espace de noms system de mscorelib.dll :
xmlns:system="clr-namespace:System;assembly=mscorlib"
<!--l'alias de system est ici system-->
<!-- Ensuite, dans une ressource je pourrais déclarer une string nommée 'LeTitre'-->
<!--grâce à system:String-->
<
phone
:
PhoneApplicationPage.Resources>
<
system
:
String
x
:
Key
=
"LeTitre"
>
Cours Wp</
system
:
String>
</
phone
:
PhoneApplicationPage.Resources>
Ne pas oublier que :
x:Class NameSpace.PageName, qui est la première ligne de la page XAML, permet d'indiquer l'espace de noms et le nom de la classe de la page et de faire ainsi le lien entre la classe XAML et la classe partielle de même nom contenant le code en C#.
x:Name identifie de manière unique des éléments objets afin d'accéder à l'objet instancié à partir du code-behind ou du code général.
x:Key est lui, utilisé dans les ressources.
IV-I. Remarque importante▲
Il est important de comprendre que tout le code écrit en XAML peut aussi être écrit en C#.
<StackPanel>
<Button
Name
=
"MonBouton"
Width
=
"300"
Height
=
"100"
>
Ok</Button>
</StackPanel>
Ce code, dans la fenêtre XAML crée un bouton ; on peut aussi créer ce bouton dans le code C# :
StackPanel ContentPanel;
Button MyButton=
new
Button (
);
MyButton.
Width =
300
;
MyButton.
Height =
100
;
MyButton.
Content =
"Ok"
;
ContentPanel.
Children.
Add
(
MyButton);
Ici on déclare un bouton, on donne ses dimensions et le texte, enfin on l'ajoute au StackPanel nommé ContentPanel.
V. Silverlight pour WP▲
Windows Phone 7.1 utilise Silverlight 4 avec certaines particularités pour Windows Phone:
Il y a en plus dans WP 7.1 les éléments relatifs aux opérations de touché de l'écran.
On peut charger la Documentation Silverlight 4 Offline (fichier .CHM) en français,
Documentation Silverlight 4 Attention c'est Silverlight et pas Silverlight pour Windows Phone, mais c'est une bonne doc !
V-A. La classe Control▲
Les contrôles permettent de créer une interface utilisateur.
Les contrôles Silverlight dérivent de la classe Control qui fait partie de l'espace de noms System.Windows.Controls.
Voici les classes dont dérive Control :
System.Object
System.Windows.DependencyObject
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Control.
C'est la classe UIElement qui donne le pouvoir d'apparaître comme objet visuel et de recevoir des entrées.
La classe Control hérite des propriétés des classes parentes.
Aussi tous les contrôles ont des propriétés communes :
Les propriétés de dépendances.
Les propriétés de positionnement : Height, Width, Margin, HorizontalAlignment…
Les évènement de vie : Load, Unload, DataContext.
Le style.
Les ControlTemplate.
Les polices de caractères utilisées : FontFamily, FontSize…
V-A-1. Ajouter un contrôle▲
Pour mettre un contrôle dans une page, un bouton par exemple, il y a quatre manières :
- en mode Design, aller le chercher dans la boîte à outils et le déposer sur la page (drag and drop) ;
-l'ajouter à l'aide de code XAML ;
Il faut taper dans la fenêtre XAML :
<Button>
Ok</Button>
Cela ajoute à la page un bouton sur lequel est affiché Ok.
-l'ajouter dans le code C# :
Button MyButton=
new
Button (
);
MyButton.
Width =
300
;
MyButton.
Height =
100
;
MyButton.
Content =
"Ok"
;
ContentPanel.
Children.
Add
(
MyButton);
On note qu'après avoir déclaré le bouton, on l'a ajouté aux enfants d'un conteneur nommé ContentPanel.
- utiliser Expression Blend l'application graphique.
Il est possible ensuite de modifier ses propriétés.
Modifions la couleur de l'arrière plan du bouton, par exemple.
- En mode Design, dans la fenêtre de propriété en bas à droite.
-à l'aide de code XAML (ajout d'attribut simple) :
<Button
Background
=
"Blue"
>
Ok</Button>
ou de manière plus complexe par ajout d'une Brush sur le fond :
<Button
FontSize
=
"14"
FontWeight
=
"Bold"
>
<Button.Background>
<LinearGradientBrush
StartPoint
=
"0,0.5"
EndPoint
=
"1,0.5"
>
<GradientStop
Color
=
"Green"
Offset
=
"0.0"
/>
<GradientStop
Color
=
"White"
Offset
=
"0.9"
/>
</LinearGradientBrush>
</Button.Background>
Ok
</Button>
-A l'aide de code C# :
MyButton.
Background=
new
SolidColorBrush (
Colors.
Blue);
On crée une nouvelle instance de SolidColorBrush de couleur bleue.
V-A-2. Particularités des contrôles▲
Propriétés de dépendance :
Les propriétés des contrôles et leur valeur ne sont pas figées (comme dans les Windows Forms où elles ont une valeur donnée) mais elles peuvent dépendre d'autres choses. Les propriétés de dépendance permettent de calculer la valeur d'une propriété en fonction de la valeur d'autres entrées. Ces autres entrées peuvent être des propriétés système (des thèmes et des préférences utilisateur), des valeurs déterminées au dernier moment (liaison de données, animations), des ressources et des styles ou des valeurs issues de relations parent-enfant d'autres éléments.
Propriétés attachées :
Les propriétés attachées permettent à des éléments enfants de spécifier des valeurs pour une propriété définie dans un élément parent. Cela permet de faire en sorte que les éléments enfants informent l'élément parent sur la manière dont ils doivent être présentés dans l'interface.
La propriété DockPanel.Dock en est un exemple. Dans un Panel, il y a plusieurs enfants, plusieurs boutons, par exemple. La propriété DockPanel.Dock de chaque bouton (enfant) permet au Panel (le parent) de positionner les boutons dans le Panel.
Particularités des contrôles Silverlight:
Les contrôles qui ont une propriété Content (les boutons, par exemple) peuvent contenir un Objet (du texte oui mais aussi une Grid, une Image, un StackPanel…), c'est extrêmement puissant.
V-A-3. Évènements▲
Créons un bouton avec le code XAML suivant :
<Button>
Ok</Button>
Si je double-clique sur ce bouton dans le designer, je me retrouve dans le code-behind en C# qui a créé automatiquement la fonction évènement suivante :
private
void
button_Click
(
object
sender,
RoutedEventArgs e)
{
}
Quand l'utilisateur du logiciel clique sur le bouton, c'est cette fonction qui sera exécutée.
De plus il y a eu un ajout dans le code XAML du bouton :
<Button
Click
=
"Button_Click>Ok</Button>
Click="Button_Click indique que lorsque l'évènement Click survient sur le bouton la fonction Button_Click est exécutée.
On aurait pu ajouter l'attribut Click="OnClick" en le tapant au clavier, cela aurait donné le code XAML suivant.
<Button
Click
=
"OnClick"
>
Ok</Button>
Dans ce cas, en double-cliquant sur le bouton, la routine suivante aurait été créée :
private
void
OnClick
(
object
sender,
RoutedEventArgs e)
{
}
Il est aussi possible de taper Click="bouton_click", puis de mettre le curseur sur bouton_click et de faire un clic droit. Cela ouvre un menu, cliquez sur « Naviguer vers le gestionnaire d'évènements ».
Cela crée dans le code-behind la fonction évènement :
private
void
bouton_click
(
object
sender,
RoutedEventArgs e)
{
}
sender est l'objet qui a déclenché l'évènement, je dis bien Object (il faudra le convertir en Button pour utiliser la propriété Name par exemple).
e contient les arguments de l'évènement, remarquons que e est de type RoutedEventArgs (et pas EventArgs comme dans les Windows Forms).
Sur un Windows Phone l'utilisateur tape avec son doigt sur l'écran.
Quel évènement utiliser ? Bien sûr pour un bouton c'est Click. Sur une ListBox il y a les évènements KeyDown, KeyUp, MouseLeftButtonDown, SelectionChanged, MouseLeftButtonUp ; ce dernier semble bien fonctionner pour intercepter la tape du doigt.
Évènement routé :
Quand survient un évènement sur un contrôle, un clic sur un bouton, par exemple, si cet évènement n'est pas traité au niveau du bouton, un parent comme le StackPanel qui contient le bouton peut traiter l'évènement. Cela est possible car l'évènement remonte de parent en parent.
On peut ainsi dans le StackPanel parent gérer l'évènement Click de tous les boutons enfants :
<StackPanel
Background
=
"LightGray"
Orientation
=
"Horizontal"
Button.
Click
=
"ClickHandlerCommun"
>
<Button
Name
=
"ButtonOui"
Width
=
"Auto"
>
Oui</Button>
<Button
Name
=
"ButtonNon"
Width
=
"Auto"
>
Non</Button>
</StackPanel>
Dans le code C# on peut créer un contrôle et le gestionnaire d'évènements routé correspondant :
//Déclaration d'un bouton
Button myButton =
new
Button
(
);
// 'création d'un bouton
// Modification des propriétés du bouton
myButton.
Name=
"Bouton1"
;
myButton.
Height =
133
;
// modification des dimensions du bouton
myButton.
Width =
155
;
myButton.
HorizontalAlignment =
HorizontalAlignment.
Stretch;
myButton.
Content =
"Button"
;
//Gestion de l'évènement Click
myButton.
Click +=
new
RoutedEventHandler
(
button1_Click);
//Ajout du bouton au conteneur
ContentPanel.
Children.
Add
(
myButton);
// 'met le bouton dans le conteneur ContentPanel
myButton.Click += new RoutedEventHandler(button1_Click) permet de créer un RoutedEventHandler qui indique d'exécuter la fonction Button1_Click quand l'utilisateur clique sur le button1.
On peut écrire plus simplement :
myButton.
Click +=
button1_Click;
V-A-4. Saisie tactile▲
On utilise habituellement les évènements souris.
Un doigt qui touche un bouton déclenche un évènement Click.
Soit un bouton en XAML :
<Button
Click
=
"Button_Click>Ok</Button>
Le Click exécute :
private
void
Button_Click
(
object
sender,
RoutedEventArgs e)
{
}
Un doigt qui touche un objet visuel (UIElement) est converti en un évènement de souris ; évènement MouseLeftButtonDown lorsque vous placez votre doigt, évènement MouseLeftButtonUp lorsque vous levez votre doigt, MouseMove lorsque vous faites glisser votre doigt.
Il y a aussi MouseLeave (sortie de la zone) et MouseEnter (entrée dans la zone).
Les arguments de ces évènements sont : Sender l'objet d'où vient l'évènement et e de type MouseButtonEventArgs.
Exemple, gestion de l'évènement MouseLeftButtonUp sur un rectangle :
<Rectangle
Height
=
"67"
Name
=
"rectangle1"
Width
=
"331"
Fill
=
"Transparent"
MouseLeftButtonUp
=
"Rectangle_MouseLeftButtonUp"
/>
Noter que pour que cela fonctionne, il faut remplir le rectangle ; ici on met une couleur transparente grâce à Fill.
La méthode suivante sera exécutée lors du levé du doigt sur le rectangle.
private
void
Rectangle_MouseLeftButtonUp
(
object
sender,
MouseButtonEventArgs e)
{
}
Il existe aussi des évènements de bas niveau, des évènements de haut niveau sur le UIElement (Tap, DoubleTap …) et des évènements de gesture dans le toolkit (voir chapitre sur les gestures).
V-A-5. Modèle de contenu▲
Pour être complet, il y a quatre modèles de contenu :
- ContentControl :
sa propriété Content contient un objet. Exemple : Button ;
- HeaderContentControl :
sa propriété Content contient un objet. Sa propriété Header fournit un titre au contrôle. Exemples : GroupBox, TabItem ;
- ItemsControl :
sa propriété Items contient une collection d'objets de même type. Exemple : ListBox contient des ListBoxItem, il y a les objets conteneurs Panel (comme les Grid, StackPanel…) qui contiennent une collection d'objets (Children) ;
- HeaderItemsControl :
sa propriété Header fournit un titre au contrôle. Sa propriété Items contient une collection d'objets.
V-B. Les applications et pages▲
Une application Windows Phone contient une Frame dans laquelle se trouve des Pages.
Chaque page est dans un fichier .xaml.
La page correspond au contenu d'un écran. Un appareil Windows Phone 7 possède un écran de 480 × 800 pixels.
Quand on crée un nouveau projet nommé MyProjet, Visual Studio crée une page nommé MainPage.xaml (la page de démarrage) qui contient :
<
phone
:
PhoneApplicationPage
x
:
Class
=
"MyProjet.MainPage"
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
=
"http://schemas.microsoft.com/winfx/2006/xaml"
xmlns
:
phone
=
"clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns
:
shell
=
"clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns
:
d
=
"http://schemas.microsoft.com/expression/blend/2008"
xmlns
:
mc
=
"http://schemas.openxmlformats.org/markup-compatibility/2006"
mc
:
Ignorable
=
"d"
d
:
DesignWidth
=
"480"
d
:
DesignHeight
=
"768"
FontFamily
=
"{StaticResource PhoneFontFamilyNormal}"
FontSize
=
"{StaticResource PhoneFontSizeNormal}"
Foreground
=
"{StaticResource PhoneForegroundBrush}"
SupportedOrientations
=
"Portrait"
Orientation
=
"Portrait"
shell
:
SystemTray.
IsVisible
=
"True"
>
<!--LayoutRoot est la grille racine où tout le contenu de la page est placé-->
<Grid
x
:
Name
=
"LayoutRoot"
Background
=
"Transparent"
>
<Grid.RowDefinitions>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition
Height
=
"*"
/>
</Grid.RowDefinitions>
<!--TitlePanel contient le nom de l'application et le titre de la page-->
<StackPanel
x
:
Name
=
"TitlePanel"
Grid.
Row
=
"0"
Margin
=
"12,17,0,28"
>
<TextBlock
x
:
Name
=
"ApplicationTitle"
Text
=
"MON APPLICATION"
Style
=
"{StaticResource PhoneTextNormalStyle}"
/>
<TextBlock
x
:
Name
=
"PageTitle"
Text
=
"nom de la page"
Margin
=
"9,-7,0,0"
Style
=
"{StaticResource PhoneTextTitle1Style}"
/>
</StackPanel>
<!--ContentPanel - placez tout contenu supplémentaire ici-->
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
>
</Grid>
</Grid>
<!--Exemple de code illustrant l'utilisation d'ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Bouton 1"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Bouton 2"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="ÉlémentMenu 1"/>
<shell:ApplicationBarMenuItem Text="ÉlémentMenu 2"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
</
phone
:
PhoneApplicationPage>
Dans la balise PhoneApplicationPage :
x:Class="MyProjet.MainPage indique l'espace de noms (le nom du projet par défaut) et la classe de la page.
En effet une page se présente comme une classe (dans le code XAML) plus le code C# correspondant qui se trouve dans une classe partielle.
On a ensuite la déclaration des espaces de noms (xml name space) utilisés dans la page :
L'espace de nom Silverlight :
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
L'espace de nom XAML :
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
L'espace de noms contenant les contrôles :
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
Et d'autres :
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
On remarque que la première ligne commence par xmlns= sans « : », il n'y a donc pas d'alias et les classes de cet espace de noms peuvent être utilisées sans préfixe. Dans les autres lignes il y a un alias (:x par exemple) ce qui oblige à utiliser l'alias ( x:Name par exemple).
Le "d" (de "designer") et le "mc" (de "markup compatibility") dans la déclaration des espaces de noms sont utilisés par les programmes comme Expression Blend et le designer de Visual Studio. DesignerWidth et DesignerHeight sont ignorés durant la compilation.
Puis on indique la police à utiliser dans la page, sa taille et le Foreground (couleur des caractères) :
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
On indique aussi l'orientation supportée et l'orientation en cours :
SupportedOrientations="Portrait" Orientation="Portrait".
shell:SystemTray.IsVisible="True" indique d'afficher la barre système.
On a ensuite une grille qui est l'élément racine.
Cette grille contient des StackPanel affichant le titre de la page puis le contenu de l'application.
Il doit y avoir un élément racine (root) et un seul dans un fichier XAML, il contient tous les autres : c'est la grille LayoutRoot.
Il y a aussi création d'un fichier MainPage.xaml.cs qui contient le code en C#.
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
using
Microsoft.
Phone.
Controls;
namespace
MyProjet
{
public
partial
class
MainPage :
PhoneApplicationPage
{
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
}
}
}
L'espace de noms est celui du projet (MyProjet). Il y a une partial class nommée MainPage.
La page est donc composée d'une classe en XAML complétée d'une partie C# : une classe partielle.
L'interface graphique écrite en XAML sera générée à la compilation et fusionnera avec le reste de la classe partielle définie dans le code-behind. Nous avons donc une seule et même classe définie dans deux fichiers différents (le fichier généré à partir du XAML étant caché de l'utilisateur et se nommant MainPage.Xaml.g.i.cs).
L'explorateur de solution en haut à droite permet de voir de quoi se compose le projet MyProjet.
Properties donne accès aux propriétés du projet.
Références indique les DLL chargées dans le projet.
On voit aussi :
le code XAML de l'application : App.xaml et en developpant, App.xaml.cs ;
les icônes et images du projet : ApplicationIcon.png, Background.png, SplashScreenImage.jpg ;
la page (MainPage ici) composée de l'interface (MainPage.xaml) et du code C# (MainPage.xaml.cs).
Voyons le détail du fichier App.xaml, le fichier Application dans lequel on peut mettre les ressources de l'application (balise Application.resources).
<Application
x
:
Class
=
"MyProjet.App"
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
=
"http://schemas.microsoft.com/winfx/2006/xaml"
xmlns
:
phone
=
"clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns
:
shell
=
"clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
>
<!--Ressources d'applications-->
<Application.Resources>
</Application.Resources>
<Application.ApplicationLifetimeObjects>
<!--Objet requis qui gère les événements de durée de vie pour l'application-->
<
shell
:
PhoneApplicationService
Launching
=
"Application_Launching"
Closing
=
"Application_Closing"
Activated
=
"Application_Activated"
Deactivated
=
"Application_Deactivated"
/>
</Application.ApplicationLifetimeObjects>
</Application>
Voyons le détail du fichier App.xaml.cs (pour le voir, cliquer sur le petit triangle à droite de App.xaml), le fichier comporte, par défaut, des commentaires qui expliquent bien la fonction de chaque ligne.
namespace
CalculeTout
{
public
partial
class
App :
Application
{
///
<
summary
>
/// Permet d'accéder facilement au frame racine de l'application téléphonique.
///
<
/summary
>
///
<
returns
>
Frame racine de l'application téléphonique.
<
/returns
>
public
PhoneApplicationFrame RootFrame {
get
;
private
set
;
}
///
<
summary
>
/// Constructeur pour l'objet Application.
///
<
/summary
>
public
App
(
)
{
// Gestionnaire global pour les exceptions non interceptées.
UnhandledException +=
Application_UnhandledException;
// Initialisation Silverlight standard
InitializeComponent
(
);
// Initialisation spécifique au téléphone
InitializePhoneApplication
(
);
// Affichez des informations de profilage graphique lors du débogage.
if
(
System.
Diagnostics.
Debugger.
IsAttached)
{
// Affichez les compteurs de fréquence des trames actuels.
Application.
Current.
Host.
Settings.
EnableFrameRateCounter =
true
;
// Affichez les zones de l'application qui sont redessinées dans chaque frame.
//Application.Current.Host.Settings.EnableRedrawRegions = true;
// Activez le mode de visualisation d'analyse hors production,
// qui montre les zones d'une page sur lesquelles une accélération GPU est produite avec une superposition colorée.
//Application.Current.Host.Settings.EnableCacheVisualization = true;
// Désactivez la détection d'inactivité de l'application en définissant la propriété UserIdleDetectionMode de l'objet
// PhoneApplicationService de l'application sur Désactivé.
// Attention :- À utiliser uniquement en mode de débogage. Les applications qui désactivent
//la détection d'inactivité de l'utilisateur continueront de s'exécuter
// et seront alimentées par la batterie lorsque l'utilisateur ne se sert pas du téléphone.
PhoneApplicationService.
Current.
UserIdleDetectionMode =
IdleDetectionMode.
Disabled;
}
}
// Code à exécuter lorsque l'application démarre (par exemple, à partir de Démarrer)
// Ce code ne s'exécute pas lorsque l'application est réactivée
private
void
Application_Launching
(
object
sender,
LaunchingEventArgs e)
{
}
// Code à exécuter lorsque l'application est activée (affichée au premier plan)
// Ce code ne s'exécute pas lorsque l'application est démarrée pour la première fois
private
void
Application_Activated
(
object
sender,
ActivatedEventArgs e)
{
}
// Code à exécuter lorsque l'application est désactivée (envoyée à l'arrière-plan)
// Ce code ne s'exécute pas lors de la fermeture de l'application
private
void
Application_Deactivated
(
object
sender,
DeactivatedEventArgs e)
{
// Assurez-vous que l'état de l'application requis est persistant ici.
}
// Code à exécuter lors de la fermeture de l'application (par exemple, lorsque l'utilisateur clique sur Précédent)
// Ce code ne s'exécute pas lorsque l'application est désactivée
private
void
Application_Closing
(
object
sender,
ClosingEventArgs e)
{
}
// Code à exécuter en cas d'échec d'une navigation
private
void
RootFrame_NavigationFailed
(
object
sender,
NavigationFailedEventArgs e)
{
if
(
System.
Diagnostics.
Debugger.
IsAttached)
{
// Échec d'une navigation ; arrêt dans le débogueur
System.
Diagnostics.
Debugger.
Break
(
);
}
}
// Code à exécuter sur les exceptions non gérées
private
void
Application_UnhandledException
(
object
sender,
ApplicationUnhandledExceptionEventArgs e)
{
if
(
System.
Diagnostics.
Debugger.
IsAttached)
{
// Une exception non gérée s'est produite ; arrêt dans le débogueur
System.
Diagnostics.
Debugger.
Break
(
);
}
}
#region Initialisation de l'application téléphonique
// Éviter l'initialisation double
private
bool
phoneApplicationInitialized =
false
;
// Ne pas ajouter de code supplémentaire à cette méthode
private
void
InitializePhoneApplication
(
)
{
if
(
phoneApplicationInitialized)
return
;
// Créez le frame, mais ne le définissez pas encore comme RootVisual ; cela permet à l'écran de
// démarrage de rester actif jusqu'à ce que l'application soit prête pour le rendu.
RootFrame =
new
PhoneApplicationFrame
(
);
RootFrame.
Navigated +=
CompleteInitializePhoneApplication;
// Gérer les erreurs de navigation
RootFrame.
NavigationFailed +=
RootFrame_NavigationFailed;
// Garantir de ne pas retenter l'initialisation
phoneApplicationInitialized =
true
;
}
// Ne pas ajouter de code supplémentaire à cette méthode
private
void
CompleteInitializePhoneApplication
(
object
sender,
NavigationEventArgs e)
{
// Définir le Visual racine pour permettre à l'application d'effectuer le rendu
if
(
RootVisual !=
RootFrame)
RootVisual =
RootFrame;
// Supprimer ce gestionnaire, puisqu'il est devenu inutile
RootFrame.
Navigated -=
CompleteInitializePhoneApplication;
}
#endregion
}
}
En résumé : une page est donc une classe, elle est composée d'une partie XAML et d'une partie C#, tout cela est dans l'espace de noms ayant le nom de l'application.
Comment ouvrir une page nommée PageConvert.xaml dans le code C# ?
NavigationService.
Navigate
(
new
Uri
(
"/PageConvert.xaml"
,
UriKind.
Relative));
V-C. Conteneur, position, dimensions des contrôles▲
Le positionnement des contrôles est fondamental, il faut bien le comprendre.
Comparaisons avec les WindowsForms.
En Windows Forms on pouvait mettre autant de contrôles que l'on voulait dans une fenêtre et on utilisait simplement les coordonnées du coin supérieur gauche d'un contrôle pour définir sa position. En Windows Forms, en cas de changement d'écran ou de modification des dimensions d'une fenêtre, si on voulait un réagencement, il faudrait tout gérer soit même.
En Silverlight il faut utiliser la notion de conteneur et de position dans ces conteneurs. Le positionnement est relatif. Il y a capacité d'adaptation aux modifications d'affichage et de disposition des fenêtres. Silverlight gère la négociation entre les contrôles pour déterminer la disposition. Un contrôle informe son parent de l'emplacement et de la taille dont il a besoin ; deuxièmement, le parent informe le contrôle de l'espace dont il peut disposer. Cette disposition dynamique fait qu'un contrôle est positionné sur une page en fonction de son contenu, de son conteneur de disposition parent.
La disposition Silverlight est basée sur les grilles (Grid), l'empilement (Stack) et l'ancrage des contrôles.
V-C-1. Hiérarchie des contrôles, arbre▲
Contrôles conteneurs et non conteneurs.
Certains contrôles (dits non conteneurs) ne peuvent contenir qu'un contrôle. Essayer de mettre deux TextBlock dans un bouton avec le designer ou une image et un texte dans un bouton : impossible !
Certains contrôles (dits conteneurs) peuvent contenir plusieurs contrôles. Une Grid, par exemple, qui peut contenir un élément dans chacune de ses cellules ; un StackPanel peut contenir en empilement de contrôles.
Pour un bouton par exemple, en C# :
MyButton.
Content =
"Ok"
;
affiche le texte Ok dans le bouton.
En XAML :
<Button
Content
=
"Ok"
/>
Content.
Dans un contrôle non conteneur, la propriété Content ne peut contenir qu'un objet et un seul.
Pour un bouton par exemple, on a deux manières d'écrire le texte du bouton :
<Button
Content
=
"Ok"
/>
ou :
<Button>
Ok </Button>
car Content est la propriété par défaut.
Content peut contenir un texte mais pas seulement ; en fait il peut contenir un Objet (un seul) : du texte mais aussi un objet conteneur comme une Grid, un Canvas, un StackPanel… qui lui peut contenir plusieurs contrôles. Cela revient à mettre autant de contrôles que l'on veut dans un contrôle.
Les conteneurs comme une Grid par exemple, ont une propriété nommé Children qui permet d'ajouter plusieurs contrôles.
MyGrid.
Children.
Add (
Button1);
À titre d'exemple, on peut mettre une Grid dans un bouton et mettre dans les cellules de cette Grid deux textes, une image, une vidéo…
Voyons maintenant un exemple en XAML d'un conteneur, un StackPanel contenant un TextBlock et trois boutons :
<StackPanel >
<TextBlock>
Faire un choix:</TextBlock>
<Button >
Option 1</Button>
<Button >
Option 2</Button>
<Button >
Option 3</Button>
</StackPanel>
Pour être complet certains contrôles ont un contenu spécifique : un TextBlock par exemple, qui ne peut afficher que du texte, a une propriété text permettant d'inclure du texte (et uniquement du texte).
Graphes d'objets.
Ainsi il y a des objets dans d'autres et donc une hiérarchie des contrôles qui peut être représentée par un graphe.
Cela a son importance car on a vu qu'un évènement (un clic, par exemple) qui survient sur un contrôle (l'image en bas à droite) peut être routé, remonter et être exploité plus haut (au niveau de la grille, par exemple).
La classe VisualTreeHelper permet de trouver le parent ou les enfants d'un contrôle :
// GetParent permet de récupérer le parent de button1, un DependencyObjet
DependencyObject tb =
VisualTreeHelper.
GetParent (
button1) as
DependencyObject;
// Combien button1 a d'enfants?
int
nbEnfant =
VisualTreeHelper.
GetChildrenCount
(
bouton1);
//Récupérer le premier enfant (index 0) de bouton1
TextBlock tb =
VisualTreeHelper.
GetChild
(
bouton1,
0
) as
TextBlock;
// comme on sait que GetChild retourne un TextBlock on cast le dependencyObjet en TextBlock
//grâce à "as TextBlock"
À noter que GetChild retourne parfois des choses bizarres ! bogue ?
V-C-2. Nom, position et dimensions d'un contrôle▲
Ajoutons un bouton à la page, déplaçons le à la souris :
Cela affiche le code XAML suivant :
<Button
Content
=
"Button"
Height
=
"95"
HorizontalAlignment
=
"Left"
Margin
=
"49,128,0,0"
Name
=
"button1"
VerticalAlignment
=
"Top"
Width
=
"345"
/>
Name indique le nom du contrôle.
Content indique le texte affiché dans le bouton.
Height et Width indiquent les dimensions du bouton.
Margin indique les marges autour du bouton.
HorizontalAlignment indique sur quoi est ancré le bouton.
Voyons cela en détails :
Dimensions d'un contrôle.
Les dimensions sont, par défaut, en pixels (px) ou "device independente unit".
Noter que les valeurs des dimensions et coordonnées sont des Double (nombre réel en virgule flottante double précision).
<Button
Name
=
"button1"
Content
=
"Button"
Height
=
"80"
Width
=
"150"
/>
En C# :
Button1.
Width=
"50"
;
Button1.
Height=
"30;"
On peut utiliser une unité différente :
px (valeur par défaut) représente les device-independente units ou pixels (1/96th inch per unit ;)
in est l'abréviation du mot anglais « inches » (pouces) ; 1 in = 96 px ;
cm correspond à centimètres ; 1 cm = (96/2,54) px ;
pt correspond à points ; 1 pt = (96/72) px.
Exemple :
<Button
Name
=
"button1"
Content
=
"Button"
Height
=
"4cm"
Width
=
"1cm"
/>
Largeur et hauteur du bouton en centimètres.
<Button
Name
=
"button1"
Content
=
"Button"
FontSize
=
"36"
/>
Hauteur de la police : 36 pixels soit 36x72/96 = 24 points.
Width et Height peuvent aussi prendre la valeur Auto. L'élément remplit alors la largeur ou la hauteur disponible dans le conteneur (cela semble fonctionner uniquement s'il n'y a pas de HorizontalAlignment ou de VerticalAlignment ni de Margin).
On peut spécifier :
MinWidth et MaxWidth (0 et infinity par défaut),
MinHeight et MaxHeigth.
MinWidth et MinHeight servent à indiquer des dimensions minimales afin qu'un texte, par exemple, ne soit pas tronqué.
Enfin ActualWidth et ActualHeigth indiquent les dimensions actuelles réelles, si des contraintes (relation avec les autres contrôles, disposition…) imposent des dimensions différentes de celles qu'on désirait. La disposition peut parfois rejeter les dimensions proposées.
Alignement :
VerticalAlignement et HorizontalAlignment définissent sur quoi est aligné le contrôle. Le contrôle y sera ancré, si on modifie les dimensions du conteneur, le contrôle suivra.
Cela remplace la propriété Anchor des Windows Forms.
Rappelons que si on modifie la taille du conteneur, le contrôle reste à égale distance du bord sur lequel il est aligné (ancré).
Valeur possible pour VerticalAlignement : Top, Bottom, Center, Stretch (étire le contrôle et le remplit verticalement).
Valeur possible pour HorizontalAlignment : Left, Right, Center, Stretch (étire le contrôle et le remplit horizontalement).
Attention, si on renseigne Width et Height Stretch ne fonctionne pas !
Margin définit les distances au conteneur, les marges.
Margin="30,64,128,0" indique les marges gauche, supérieure, droite, inférieure.
Margin="30" indique que toutes les marges sont à 30.
Dans la fenêtre des propriétés d'un contrôle, la flèche à droite de la propriété Margin ouvre l'éditeur de marges :
Paddding définit l'épaisseur d'une marge dans le contrôle. (valable dans certains contrôles seulement).
En pratique on positionne et on dimensionne le contrôle à la souris ce qui modifie en conséquence les propriétés Width, Height et Margin.
En résumé :
Silverlight commence par regarder la propriété "Width". Si elle est définie, c'est cette valeur qui est utilisée pour la largeur du composant, même si "HorizontalAlignment=Strech". Si aucune valeur n'est spécifiée ou qu'elle vaut "Auto", Silverlight regarde la valeur de "HorizontalAlignment". Si la valeur est "Strech", la largeur sera tout l'espace disponible. Sinon, la propriété "Width" étant sur "Auto", la largeur s'adaptera au contenu.
Bien comprendre qu'on ne travaille plus en coordonnées absolues de position du contrôle (Left, Top c'est fini…) mais plutôt en accrochant les contrôles (avec une marge autour) dans un conteneur (Grid, StackPanel).
Dans ce chapitre, on voit le positionnement d'un contrôle dans un conteneur, pour être complet, il faut aussi voir le chapitre sur les conteneurs et les règles de positionnement dynamique. Dans une grille par exemple, dans chaque cellule on pourra positionner un contrôle comme indiqué ici avec une margin autour.
Exemple de création de contrôle entièrement en C# :
On peut avoir besoin au cours du déroulement de l'application de créer de toute pièce un bouton.
Ici on a mis le code dans le constructeur de la page.
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
using
Microsoft.
Phone.
Controls;
namespace
courscsharp
{
public
partial
class
MainPage :
PhoneApplicationPage
{
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
//Déclaration d'un bouton
Button myButton =
new
Button
(
);
// 'création d'un bouton
// Modification des propriétés du bouton
myButton.
Name=
"Bouton1"
;
myButton.
Height =
133
;
// modification des dimensions du bouton
myButton.
Width =
155
;
myButton.
HorizontalAlignment =
HorizontalAlignment.
Stretch;
myButton.
Content =
"Button"
;
// texte affiché
myButton.
Margin =
new
Thickness
(
0
,
0
,
0
,
370
);
//Gestion de l'évènement Click
myButton.
Click +=
new
RoutedEventHandler
(
button1_Click);
//Ajout du bouton au conteneur
ContentPanel.
Children.
Add
(
myButton);
// 'met le bouton dans le conteneur ContentPanel
}
private
void
button1_Click
(
object
sender,
RoutedEventArgs e)
//routine exécutée lors de l'évènement Click
{
MessageBox.
Show
(
"Ok"
);
}
}
On remarque que si on crée un contrôle dans le code C#, il est nécessaire d'ajouter ce contrôle à un conteneur déjà présent sinon le contrôle n'apparaît pas.
On voit aussi que Margin (comme Padding) accepte un objet Thickness (épaisseur). On en crée un avant de l'affecter à la propriété Margin.
V-C-3. Plan▲
Les objets peuvent être au premier plan ou au second plan : l'objet de premier plan apparaîtra au dessus des autres et les cachera s'il n'est pas transparent.
Dans le designer, cliquer sur un objet visuel, clic droit puis Ordre puis Mettre au premier plan.
En fait, il n'y a pas de propriété indiquant le plan, Silverlight affiche les objets qui se trouvent dans un conteneur dans l'ordre du code XAML ; le fait de mettre un objet au premier plan met le code XAML de l'objet à la fin du conteneur :
<!--ContentPanel - placez tout contenu supplémentaire ici-->
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,-18"
>
<ScrollViewer
Height
=
"193"
VerticalScrollBarVisibility
=
"Auto"
HorizontalAlignment
=
"Left"
Margin
=
"2,463,0,0"
Name
=
"scrollViewer1"
VerticalAlignment
=
"Top"
Width
=
"448"
>
<TextBlock
Name
=
"TextLong"
TextWrapping
=
"Wrap"
Width
=
"417"
Height
=
"525"
/>
</ScrollViewer>
<TextBlock
HorizontalAlignment
=
"Center"
Margin
=
"15,354,24,244"
Name
=
"resultat2"
Style
=
"{StaticResource TextResultat}"
Text
=
""
TextAlignment
=
"Center"
Width
=
"417"
/>
<Button
Content
=
"-"
Height
=
"81"
Click
=
"FaireMoins"
HorizontalAlignment
=
"Left"
Margin
=
"315,0,0,540"
Name
=
"ButtonMoins"
Style
=
"{StaticResource MyButton}"
VerticalAlignment
=
"Bottom"
Width
=
"116"
/>
<TextBlock
Name
=
"resultat"
HorizontalAlignment
=
"Center"
Margin
=
"22,180,17,392"
Text
=
""
Width
=
"417"
Style
=
"{StaticResource TextResultat}"
TextAlignment
=
"Center"
/>
<Image
Name
=
"Image"
HorizontalAlignment
=
"Left"
Margin
=
"245,134,0,244"
Stretch
=
"Fill"
Width
=
"186"
/>
</Grid>
Ici l'image nommée Image sera au premier plan.
V-D. Aspect des contrôles▲
Quand on dépose un bouton dans un formulaire, il a un aspect standard, on a souvent besoin de modifier sa couleur ou sa forme, son aspect visuel ; c'est ce que nous allons voir. Dans ce chapitre on travaille sur UN contrôle. Dans le chapitre sur les ressources, on verra comment créer des modèles de contrôles (avec les Styles et les Templates) pour les appliquer à un, plusieurs ou tous les contrôles.
V-D-1. Propriétés des contrôles▲
Ici on garde l'aspect général du contôle, on modifie simplement ses propriétés (Couleur, texte, font…).
On veut avoir ce beau bouton (bof !), ci-dessous l'exemple :
Avec le designer, on prend un bouton dans la boîte à outils, on le pose dans un conteneur (une grid, par exemple).
Pour définir les dimensions, la couleur et le texte dans le bouton, on va modifier les propriétés dans la fenêtre de propriétés en bas à droite :
- Height et Width permettent de modifier les dimensions ;
- Foreground et Background donnent la couleur du texte et du fond ;
- BorderBrush et BorderThickness indique la couleur et l'épaisseur des bords ;
- FontFamily indique le nom de la police de caractères ;
- FontSize indique la hauteur de la police ;
- FontStyle permet de choisir entre Normal, Italic, Oblique ;
- FontWeight permet de choisir entre Normal, Bold, Heavy… ;
- Name donne le nom du bouton en haut de la fenêtre ;
- Content indique le texte à afficher.
En bas de l'écran dans la fenêtre XAML, apparaît le code XAML correspondant au bouton.
<Button
Height
=
" 100"
Width
=
"200"
Foreground
=
"White"
Background
=
"Red"
BorderBrush
=
"Green"
BorderThickness
=
"100"
FontFamily
=
"Algerian"
FontSize
=
"24"
FontStyle
=
"Italic"
FontWeight
=
"Bold"
Name
=
"mybutton"
>
Beau Bouton</Button>
Dans une fenêtre vide, on aurait pu coller le code XAML entre les balises Grid et voir apparaître le bouton.
On aurait pu créer le bouton avec du code C# :
//Déclaration d'un bouton
Button myButton =
new
Button
(
);
// 'création d'un bouton
// Modification des propriétés du bouton
myButton.
Name=
"Bouton1"
;
myButton.
Height =
133
;
// modification des dimensions du bouton
myButton.
Width =
155
;
myButton.
HorizontalAlignment =
HorizontalAlignment.
Stretch;
myButton.
Content =
"Button"
;
myButton.
Background =
new
SolidColorBrush
(
Colors.
Red);
myButton.
Foreground =
new
SolidColorBrush
(
Colors.
White);
myButton.
BorderBrush =
new
SolidColorBrush
(
Colors.
Green);
myButton.
BorderThickness =
new
Thickness
(
100
);
myButton.
FontFamily =
new
FontFamily
(
"Algerian"
);
myButton.
FontSize =
24
;
myButton.
FontStyle =
FontStyles.
Italic;
myButton.
FontWeight =
FontWeights.
Bold;
//Gestion de l'évènement Click
myButton.
Click +=
new
RoutedEventHandler
(
button1_Click);
//Ajout du bouton au conteneur
ContentPanel.
Children.
Add
(
myButton);
// 'met le bouton dans le conteneur ContentPanel
On a à notre disposition une classe Colors qui contient les couleurs :
On ne peut pas affecter directement une couleur à une propriété définissant une couleur : myButton.Background = Colors.Red; ne fonctionne pas.
Il faut instancier et utiliser une SolidColorBrush :
myButton.
Background =
new
SolidColorBrush
(
Colors.
Red);
Il en est de même avec FontFamily, FontStyle, FontWeight et Thickness (épaisseur). Il faut instancier une FontFamily, un FontStyle, un FontWeight ou un Thickness pour donner une valeur à la propriété correspondante.
L'énumération Brushes que l'on peut utiliser en WPF ne fonctionne pas ici :
myButton.
Background =
Brushes.
Red;
//ne fonctionne pas
Au lieu de mettre une couleur unie dans le fond d'un contrôle, on peut y mettre une LinearGradientBrush ce qui produit un bel aspect de Background dégradé.
Cela donne :
En XAML :
<Button
Content
=
"Inverse le Calcul"
Height
=
"69"
HorizontalAlignment
=
"Left"
Margin
=
"32,63,0,0"
Name
=
"button1"
VerticalAlignment
=
"Top"
Width
=
"390"
Click
=
"button1_Click"
>
<Button.Background>
<LinearGradientBrush >
<GradientStop
Offset
=
"0"
Color
=
"Blue"
/>
<GradientStop
Offset
=
"1"
Color
=
"SkyBlue"
/>
</LinearGradientBrush>
</Button.Background>
</Button>
Un contrôle peut être visible ou non : (Visible ou Collapsed qui correspond à une taille de zéro et n'occupe pas d'espace dans le layout.)
<Button
Visibility
=
"Collapsed"
Content
=
"Calcul"
Height
=
"69"
Margin
=
"32,63,0,0"
En C# :
BtCalculer.
Visibility =
Visibility.
Visible;
BtCalculer2.
Visibility =
Visibility.
Collapsed;
On peut ajouter l'attribut Opacity dont la valeur va de 0 à 1.
<Image
Source
=
"C:/test.JPG"
Opacity
=
"0.5"
/>
Avec la valeur 0.5 l'image est semi-transparente.
On peut aussi modifier les propriétés d'un contrôle ou d'un type de contrôle (tous les boutons de l'application) à l'aide des Ressources des Styles ; voir le chapitre Ressources. Enfin on peut utiliser les data templates et contrôles templates (voir plus bas).
V-D-2. Contrôle contenant des contrôles▲
On a vu que la propriété Content d'un bouton pouvait contenir un objet mais un seul. On peut y mettre du texte, mais comment mettre un texte et une image dans un bouton ?
Il faut mettre un StackPanel (ou une Grid) dans le bouton (puisque celui-ci ne peut contenir qu'un seul objet), dans ce StackPanel (qui lui est un conteneur qui peut contenir x contrôles empilés) mettre un TextBlock et une Image. Dans le designer c'est difficile de mettre un StackPanel dans un Button, il se met dessus et pas dedans (c'est galère !), donc tapez ou collez le code XAML du StackPanel dans le bouton. De plus l'image doit être dans les ressources : passer par le menu Projet puis Ajouter un élément existant, indiqué resources dans les options de génération en bas. Voir le chapitre sur les ressources.
<Button
Height
=
"161"
HorizontalAlignment
=
"Left"
Margin
=
"32,63,0,0"
Name
=
"button1"
VerticalAlignment
=
"Top"
Width
=
"390"
Click
=
"button1_Click"
>
<StackPanel
Name
=
"StackPanel"
>
<TextBlock
HorizontalAlignment
=
"Center"
TextAlignment
=
"right"
>
Ok
</TextBlock>
<Image
HorizontalAlignment
=
"Stretch"
Source
=
"wpfsilverlight.jpg"
Height
=
"67"
Width
=
"188"
>
</Image>
</StackPanel>
</Button>
Cela donne :
On se rend compte de la puissance de Silverlight : on peut mettre autant de contrôles que l'on veut dans un contrôle en utilisant des conteneurs. Dans notre bouton on aurait pu mettre deux images, deux textes…
V-D-3. Aspect visuel des contrôles: template visuel, style▲
Ici on modifie l'aspect profond des contrôles.
Chaque contrôle possède un template visuel ou Control Template qui permet de définir l'aspect visuel du contrôle, le template indique comment il sera dessiné (forme, arrière plan, bords, coins, contenu…).
Ce template est entre les balises Button.Template pour un bouton. On peut bien sûr modifier ce template, ce qui modifie l'aspect du bouton. On peut ainsi obtenir des boutons ronds, elliptiques…
Dans le template du bouton, ici, on va définir la forme qui est un rectangle mais aussi la forme des coins (radius).
On va donc faire un bouton Cyan avec des coins ronds (grâce à Rectangle RadiusX="39" RadiusY="39").
<Button
Name
=
"Button2"
Margin
=
"32,93,60,329"
FontSize
=
"40"
>
<Button.Template>
<ControlTemplate>
<Grid>
<Rectangle
RadiusX
=
"39"
RadiusY
=
"39"
Fill
=
"Cyan"
>
</Rectangle>
<ContentPresenter
HorizontalAlignment
=
"Center"
VerticalAlignment
=
"Center"
Content
=
"BoutonTemplate"
/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
Cela donne :
On voit qu'à partir du moment ou on utilise le ControlTemplate, il faut tout refaire, la forme du contrôle (Rectangle), mais aussi l'aspect du contenu ce qui explique l'usage du ContentPresenteur qui permet de gérer le contenu. On aurait pu ajouter l'aspect du bouton s'il est « Enabled », « Focused » ou « cliqué » (voir le détail d'un template dans le chapitre sur les ressources).
Pour les listes on a un ItemsPresenter.
Si au lieu de mettre Rectangle Radius… on met Ellipse, le bouton est en forme d'ellipse.
<Button
Name
=
"Button2"
Margin
=
"32,93,60,329"
FontSize
=
"40"
>
<Button.Template>
<ControlTemplate>
<Grid>
<Ellipse
Fill
=
"Cyan"
>
</Ellipse>
<ContentPresenter
HorizontalAlignment
=
"Center"
VerticalAlignment
=
"Center"
Content
=
"BoutonTemplate"
/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
Cette exemple fonctionne mais est incomplet car comme dans le template, on n'a pas défini l'aspect du bouton en cas de clic ou quand il a le focus, le bouton ne change donc jamais d'aspect même quand on clique dessus !
On pourrait créer un Style pour un bouton. En fait c'est beaucoup plus simple d'utiliser une propriété (les Styles sont plutôt utilisés dans les ressources).
Dans ce chapitre, on a modifié UN contrôle directement, il est possible de modifier un contrôle avec un style ou plusieurs contrôles de même type. Voir le chapitre sur les ressources qui parle des modèles de contrôles et donne un exemple complet. Enfin, pour être complet, il est possible de modifier l'aspect des données dans un contrôle grâce aux Data Template (modèle de données).
V-D-4. Transformation▲
Pour effectuer une transformation 2D sur un UIElement, on utilise la propriété RenderTransform.
On peut modifier l'échelle, la rotation, l'oblicité, le déplacement.
Échelle :
on peut effectuer une transformation horizontale ou verticale grâce à un ScaleTransform.
Pour cela on définit ScaleX et ScaleY.
Dans un bouton, on dessine une loupe (avec un Path), puis on va l'agrandir grâce à RenderTransform.
ScaleX="3" ScaleY="3" indiquent d'agrandir de trois fois.
<Button
Name
=
"btnStart"
Click
=
"btnStart_Click"
Width
=
"260"
Margin
=
"98,284,98,239"
>
<Button.Content>
<StackPanel
Orientation
=
"Horizontal"
>
<Path
Fill
=
"Blue"
Data
=
"F1 M 2.339,6.489 C 1.193,5.343 1.193,3.485 2.339,2.339 C 3.485,1.192 5.344,1.193 6.489,2.339
C 7.635,3.485 7.635,5.343 6.489,6.489 C 5.344,7.636 3.485,7.636 2.339,6.489 Z M 11.711,10.209 L 8.185,6.684
C 9.207,4.986 9.000,2.757 7.535,1.293 C 5.812,-0.431 3.017,-0.431 1.293,1.293
C -0.431,3.017 -0.431,5.812 1.293,7.536 C 2.757,8.999 4.988,9.206 6.685,8.185
L 10.209,11.710 L 11.711,10.209 Z"
Margin
=
"0,0,5,0"
Height
=
"36"
Width
=
"17"
>
<Path.RenderTransform>
<ScaleTransform
ScaleX
=
"3"
ScaleY
=
"3"
/>
</Path.RenderTransform>
</Path>
<TextBlock
Foreground
=
"White"
Text
=
"Chercher"
Margin
=
"20 0 0 0"
/>
</StackPanel>
</Button.Content>
</Button><br/>
Si ScaleX ou ScaleY sont négatifs, il y a inversion de l'élément.
Attention pour que l'élément reste au même endroit, il faut modifier l'origine de la transformation dans l'élément (origine est par défaut sur le coin supérieur gauche ce qui déplacerait l'élément en haut à gauche).
Exemple avec un bouton.
<Button
Content
=
"Click"
RenderTransformOrigin
=
"0.5, 0.5"
>
<Button.RenderTransform >
<ScaleTransform
ScaleX
=
"-1"
ScaleY
=
"1"
/>
</Button.RenderTransform>
</Button>
Rotation :
On peut effectuer une rotation grâce à un RotateTransform.
Pour cela on définit Angle et si nécessaire CenterX et CenterY.
Exemple : texte vertical.
<TextBlock
Text
=
"Texte vertical"
FontSize
=
"32"
Foreground
=
"Teal"
Margin
=
"74,3,183,547"
>
<TextBlock.RenderTransform>
<RotateTransform
Angle
=
"90"
/>
</TextBlock.RenderTransform>
</TextBlock>
Inclinaison :
On peut effectuer une inclinaison grâce à un SkewTransform.
Pour cela on définit AngleX AngleY et si nécessaire CenterX et CenterY.
Exemple : texte penché.
<TextBlock
Text
=
"Texte italique"
FontSize
=
"32"
Foreground
=
"Teal"
Margin
=
"74,3,183,547"
>
<TextBlock.RenderTransform>
<SkewTransform
AngleX
=
"45"
/>
</TextBlock.RenderTransform>
</TextBlock>
Déplacement :
TranslateTransform permet de déplacer un élément ; on renseigne ici X et Y qui indiquent le déplacement en pixels.
Ici on va déplacer le texte quand on pose le doigt dessus ; il revient à sa place quand on lève le doigt.
<TextBlock
MouseLeftButtonDown
=
"Bouge"
MouseLeftButtonUp
=
"Bouge2"
Text
=
"Texte qui bouge"
FontSize
=
"32"
Foreground
=
"Teal"
Margin
=
"74,3,114,547"
>
<TextBlock.RenderTransform>
<TranslateTransform
x
:
Name
=
"myTransform"
/>
</TextBlock.RenderTransform>
</TextBlock>
private
void
Bouge
(
object
sender,
MouseButtonEventArgs e)
{
myTransform.
X =
myTransform.
X +
15
;
myTransform.
Y =
myTransform.
Y +
15
;
}
private
void
Bouge2
(
object
sender,
MouseButtonEventArgs e)
{
myTransform.
X =
myTransform.
X -
15
;
myTransform.
Y =
myTransform.
Y -
15
;
}
On peut aussi créer des effets visuels.
<TextBlock
Text
=
"Translate Transform"
Foreground
=
"{StaticResource PhoneForegroundBrush}"
FontSize
=
"48"
HorizontalAlignment
=
"Center"
VerticalAlignment
=
"Center"
/>
<TextBlock
Text
=
"Translate Transform"
Foreground
=
"{StaticResource PhoneBackgroundBrush}"
FontSize
=
"48"
HorizontalAlignment
=
"Center"
VerticalAlignment
=
"Center"
>
<TextBlock.RenderTransform>
<TranslateTransform
X
=
"2"
Y
=
"2"
/>
</TextBlock.RenderTransform>
</TextBlock>
V-D-5. Font▲
Les fonts correspondent aux polices de caractères.
Quand on crée une page, les polices standards (ressources Windows Phone) sont utilisées pour la page :
<
phone
:
PhoneApplicationPage
x
:
Class
=
"MainPage"
FontFamily
=
"{StaticResource PhoneFontFamilyNormal}"
FontSize
=
"{StaticResource PhoneFontSizeNormal}"
PhoneFontFamilyNormal correspond à la police Métro Segoe WP. PhoneFontSizeNormal correspond à 20 pixels.
Pour un contrôle comme un TextBlock, on peut modifier la police.
<TextBlock
Text
=
"15:03"
FontFamily
=
"Georgia"
/>
Quand on tape FontFamilly=" la liste des polices disponibles s'affiche.
Pour ajouter une autre police : charger une police gratuite et redistribuable (fichier .ttf), la mettre dans un répertoire /fonts du projet. L'ajouter au projet et dans les propriétés de la police mettre « Option de génération » à « contenu » et « Copier dans le répertoire de sortie » à « Copier si plus récent ».
Il semble que si on la met avec « Option de génération » à « Resources », on la voit dans la liste des polices (à vérifier).
Pour l'utiliser :
<TextBlock
Text
=
"15:03"
FontFamily
=
"fonts/DigitalDream.ttf#Digital Dream"
FontSize
=
"60"
/>
Avant # il y a le nom du fichier « .ttf ».
Après # il y a le nom de la police (ouvrir le fichier .ttf en cliquant dessus, il peut y avoir plusieurs polices dedans, voir le nom en haut).
Il faut trouver des polices redistribuables (licence GNU par exemple) et pas des polices pour usage personnel :
voir ici http://www.dafont.com/fr/top.php?l[]=10.
On peut spécifier la hauteur de la police avec FontSize (en pixels) :
<TextBlock
Text
=
"15:03"
FontSize
=
"36"
/>
En fait, la hauteur occupée est de 33 % supérieur à la hauteur indiquée par FontSize car il y a quelques pixels blancs au dessus et au dessous.
Comme Point=3/4 *Pixels, une FontSize de 36 correspond à 24 Points.
V-E. Couleur, remplissage de surface▲
V-E-1. Couleur▲
Dans le code XAML, les couleurs sont désignées directement :
<Button
Background
=
"Red"
/>
En fait il y a une conversion implicite du texte "Red" en System.Windows.Media.Brushes.Red.
On aurait pu écrire :
<Button>
A Button
<Button.Background>
<SolidColorBrush
Color
=
"Red"
/>
</Button.Background>
</Button>
ou en donnant le code couleur du rouge en hexadécimal :
<Button>
A Button
<Button.Background>
<SolidColorBrush
Color
=
"#FFFF0000"
/>
</Button.Background>
</Button>
Les couleurs sont codées sur 32 bits : composante alpha (transparence) sur 8 bits et R (rouge), B (bleu), G (vert) chacun sur 8 bits.
Quand dans le code XAML on tape Color=" une liste des couleurs s'ouvre ; il suffit de choisir une couleur. Là il y a une multitude de couleurs.
Voici les couleurs disponibles et leur valeur hexadécimale :
Bien sûr on peut agir sur la composante alpha pour rendre une couleur plus ou moins transparente, il y a aussi une couleur transparente :
<
StackPanel Background=
"Transparent"
/>
En C#
On peut utiliser l'énumération Color de System.Windows.Media pour déclarer une variable qui contient une couleur.
La classe Colors permet d'avoir la valeur de 15 couleurs différentes :
Color myColor =
Colors.
Green;
Les couleurs sont codées sur 32 bits : composante alpha (transparence) et RBG.
On peut utiliser FromArgb pour indiquer les paramètres alpha, R, B, G.
myColor=
Color.
FromArgb
(
255
,
0
,
0
,
255
);
MessageBox.
Show
(
myColor.
ToString
(
));
//Affiche #FF0000FF
Il y a 15 couleurs dans la classe Colors, aussi pour utiliser dans le code C# les autres couleurs de la palette ci dessus, il faut utiliser la valeur hexadécimale et la découper.
Pour obtenir la couleur Silver de code hexadécimal #FFC0C0C0.
col =
Color.
FromArgb
(
0xFF
,
0xC0
,
0xC0
,
0xC0
);
L'outil pinceau
Dans la fenêtre Propriétés, pour donner une couleur à la propriété (Background, par exemple) d'un contrôle on peut taper le nom de la couleur, on peut aussi ouvrir l'outil pinceau en cliquant sur la flèche à droite :
On peut choisir en haut une des icônes : null, couleur unie, dégradé, image.
Les curseurs permettent de doser la composante R, G, B et la composante alpha.
La valeur de la couleur est retournée en hexadécimal : #DF6C5F4D par exemple.
On peut aussi utiliser les couleurs Windows Phone standards (couleurs styles ou Brush) qui sont dans les ressources. Leurs noms commencent par Phone :
< Button
Foreground
=
"{StaticResource PhoneForegroundBrush}"" />
Ces ressources utilisent le thème en cours (couleur de fond et couleur d'accentuation) que l'utilisateur à sélectionné dans les paramètres (fond clair ou foncé).
Voir chapitre sur les thèmes.
Sélecteur de ressource
Dans la fenêtre Propriétés, pour donner une valeur ressource à la propriété (Background par exemple) d'un contrôle on peut ouvrir la fenêtre du sélecteur de ressource en cliquant sur le carré à côté du nom de la propriété :
Dans le menu, choisir Appliquer la ressource.
Double cliquer sur la ressource désirée.
Pour remplir une surface d'une forme (Rectangle, par exemple) avec une couleur on utilise Fill.
<
Rectangle
Width=
"200"
Height=
"100"
Fill=
"Blue"
Stroke=
"Black"
StrokeThickness=
"4"
/>
En C#
SolidColorBrush blueBrush =
new
SolidColorBrush
(
);
blueBrush.
Color =
Colors.
Blue;
blueRectangle.
Fill =
blueBrush;
LayoutRoot.
Children.
Add
(
blueRectangle);
Pour indiquer une couleur d'avant ou d'arrière plan pour un élément coloré on utilise Foreground, Background.
<Button
Background
=
"Red"
/>
On a vu qu'en C# il faut impérativement (même pour une couleur unie) utiliser une Brush (une brosse !).
Voyons les Brush en détails.
V-E-2. SolidColorBrush▲
C'est simple, c'est une couleur unie.
On peut remplir peindre un objet, le fond d'un bouton (Background), un rectangle et même du texte avec un pinceau, une brosse (Brush).
En XAML c'est simple de remplir avec une Brush de couleur unie.
<Rectangle
Width
=
"50"
Height
=
"50"
Fill
=
"Blue"
/>
En C# c'est plus compliqué :
Button2.Background = new SolidColorBrush(Colors.Cyan);
On crée une instance de SolidColorBrush ( grâce à new) avec comme paramètre la couleur désirée.
Exemple 1 : on veut choisir la couleur du Background d'un bouton.
On peut le définir en mode design dans la fenêtre de propriétés.
En XAML :
<Button
Background
=
"Red"
/>
ou en hexadécimal :
<Button
Background
=
"#FF0000FF"
/>
ou en C# :
myButton.
Background =
new
SolidColorBrush
(
Colors.
Red);
On ne peut pas affecter directement la couleur : (myButton.Background = Colors.Red ne fonctionne pas) on est obligé d'instancier une nouvelle SolidColorBrush et de lui donner la valeur Red qui appartient à la collection Colors.
Exemple 2 : créer un rectangle rouge.
Rectangle Rect =
new
Rectangle
(
);
Rect.
Width =
75
;
Rect.
Height =
75
;
//Creation d'une SolidColorBrush
SolidColorBrush myBrush =
new
SolidColorBrush
(
Colors.
Red);
Rect.
Fill =
myBrush;
ContentPanel.
Children.
Add (
Rect);
Remarquer qu'on a utilisé la méthode Fill pour remplir le rectangle avec la Brush.
En XAML :
<Rectangle
Width
=
"75"
Height
=
"75"
>
<Rectangle.Fill>
<SolidColorBrush
Color
=
"Red"
/>
</Rectangle.Fill>
</Rectangle>
V-E-3. LinearGradientBrush▲
Peinture avec dégradé linéaire.
C'est une couleur qui se transforme progressivement en une autre puis éventuellement en une autre encore.
Il y a un system de coordonnées sur la surface à remplir : (0,0) est au coin supérieur gauche, (1,1) au coin inférieur droit.
0.5, 0.5 correspond au centre.
StartPoint indique les coordonnées du début du gradient, EndPoint les coordonnées de la fin du gradient, GradientStop indique la position relative de la couleur sur la ligne qui rejoint le point de début au point de la fin.
Exemple 1 : sur une Grid, on positionne trois couleurs dans la diagonale.
On ne peut pas le faire dans la fenêtre designer avec la souris, le plus simple est de coller le code XAML dans la fenêtre XAML ou de l'écrire en C#.
Pour suivre la diagonale StartPoint="0,0" EndPoint="1,1". le rouge sera à 0 % de la diagonale, le blanc à 50 % le bleu à 100 %.
En XAML :
<Grid >
<Grid.Background>
<LinearGradientBrush
StartPoint
=
"0,0"
EndPoint
=
"1,1"
>
<GradientStop
Color
=
"Red"
Offset
=
"0"
/>
<GradientStop
Color
=
"White"
Offset
=
"0.5"
/>
<GradientStop
Color
=
"Blue"
Offset
=
"1"
/>
</LinearGradientBrush>
</Grid.Background>
</Grid>
En C# :
Rectangle Rect =
new
Rectangle
(
);
Rect.
Width =
175
;
Rect.
Height =
175
;
LinearGradientBrush myBrush =
new
LinearGradientBrush
(
);
myBrush.
StartPoint =
new
Point
(
0
,
0
);
myBrush.
EndPoint =
new
Point
(
1
,
1
);
GradientStop g1 =
new
GradientStop
(
);
g1.
Color =
Colors.
Green;
g1.
Offset =
0
.
0
;
myBrush.
GradientStops.
Add
(
g1);
GradientStop g2 =
new
GradientStop
(
);
g2.
Color =
Colors.
White;
g2.
Offset =
0
.
5
;
myBrush.
GradientStops.
Add
(
g2);
GradientStop g3 =
new
GradientStop
(
);
g3.
Color =
Colors.
Blue;
g3.
Offset =
1
.
0
;
myBrush.
GradientStops.
Add
(
g3);
Rect.
Fill =
myBrush;
ContentPanel.
Children.
Add
(
Rect);
La syntaxe suivante pour le GradientStop, bien que correcte n'est pas acceptée :
myBrush.
GradientStops.
Add
(
new
GradientStop
(
Colors.
White,
0
.
5d
));
Exemple 2 : plein de couleurs dans un rectangle.
<
Rectangle Width=
"400"
Height=
"200"
>
<
Rectangle.
Fill>
<
LinearGradientBrush StartPoint=
"0,0"
EndPoint=
"1,1"
>
<
GradientStop Color=
"Yellow"
Offset=
"0.0"
/>
<
GradientStop Color=
"Red"
Offset=
"0.25"
/>
<
GradientStop Color=
"Blue"
Offset=
"0.75"
/>
<
GradientStop Color=
"LimeGreen"
Offset=
"1.0"
/>
</
LinearGradientBrush>
</
Rectangle.
Fill>
</
Rectangle>
Exemple 3 : sur un bouton avec un dégradé de gris.
Pour que le gradient s'applique de haut en bas StartPoint="0,0" EndPoint="0,1" le blanc sera à 0 % de la verticale, le gris à 100 % de la verticale.
En XAML :
<Button
Content
=
"Ok"
>
<Button.Background>
<LinearGradientBrush
StartPoint
=
"0,0"
EndPoint
=
"0,1"
>
<GradientStop
Color
=
"White"
Offset
=
"0"
/>
<GradientStop
Color
=
"LightGray"
Offset
=
"1"
/>
</LinearGradientBrush>
</Button.Background>
</Button>
Remarque : on aurait pu mettre un GradientStop en dehors de la zone (à 2 par exemple pour le LightGray) ce qui permet d'estomper la couleur grise et de faire un bouton plus clair.
Exemple 4 : texte d'un bouton avec dégradé.
<Button
Content
=
"Linear"
>
<Button.Foreground>
<LinearGradientBrush>
<GradientStop
Offset
=
"0"
Color
=
"Red"
/>
<GradientStop
Offset
=
"1"
Color
=
"Yellow"
/>
</LinearGradientBrush>
</Button.Foreground>
</Button>
Ici on a utilisé un LinearGradientBrush pour le Foreground.
V-E-4. RadialGradientBrush▲
Peinture avec dégradé radial.
Ici on applique deux couleurs ou plus dans un cercle qui occupe la totalité du conteneur.
Visualisons ce cercle et le system de coordonnées :
Le GradientOrigin donne le centre du gradient. Ci-dessous le centre du gradient est à 0.50, 0.50 (c'est sa valeur par défaut) c'est à dire au centre du cercle. Les couleurs seront donc concentriques.
Les GradientStop indiquent la position des couleurs par rapport au centre : le blanc est à l'offset 0 : disque de blanc au centre ; le bleu est à l'offset 0.5, cela donne un cercle de bleu autour du blanc ; le vert est à l'offset 1 (100 %): le cercle vert est autour du cercle bleu.
En XAML
<
RadialGradientBrush GradientOrigin=
"0.50,0.50"
>
<
GradientStop Color=
"white"
Offset=
"0"
/>
<
GradientStop Color=
"LightBlue"
Offset=
"0.5"
/>
<
GradientStop Color=
"LightGreen"
Offset=
"1"
/>
</
RadialGradientBrush>
Pour l'exemple suivant le centre du gradient est à 0.50, 1 excentré en bas du cercle.
On a ajouté artificiellement, pour mieux comprendre, le cercle gris qui occupe la totalité du conteneur et le centre du gradient symbolisé par un point noir.
L'offset du blanc est 0.75 : le blanc monte haut.
En XAML
<RadialGradientBrush
GradientOrigin
=
"0.50,1"
>
<GradientStop
Color
=
"white"
Offset
=
"0.75"
/>
<GradientStop
Color
=
"LightBlue"
Offset
=
"1.0"
/>
</RadialGradientBrush>
Ici l'offset du blanc est 0.50 : le blanc monte moins haut.
En XAML :
<RadialGradientBrush
GradientOrigin
=
"0.50,1"
>
<GradientStop
Color
=
"white"
Offset
=
"0.50"
/>
<GradientStop
Color
=
"LightBlue"
Offset
=
"1.0"
/>
</RadialGradientBrush>
Exemple sur une Grid. Centre du gradient excentré en haut à droite (0.75, 0.25) et il y a trois couleurs.
En XAML :
<Grid >
<Grid.Background >
<RadialGradientBrush
GradientOrigin
=
"0.75,0.25"
>
<GradientStop
Color
=
"Yellow"
Offset
=
"0.0"
/>
<GradientStop
Color
=
"Orange"
Offset
=
"0.5"
/>
<GradientStop
Color
=
"Red"
Offset
=
"1.0"
/>
</RadialGradientBrush>
</Grid.Background>
</Grid >
On peut avoir une ellipse au lieu d'un cercle en définissant RadiusX et RadiusY.
<RadialGradientBrush
Center
=
"0.5,0.5"
RadiusX
=
"0.5"
RadiusY
=
"0.5"
/>
Un exemple complet en C# :
//Instancier un Radialgradientbrush
RadialGradientBrush MyBrush =
new
RadialGradientBrush
(
);
//Mettre le GradientOrigin au centre.
MyBrush.
GradientOrigin =
new
Point
(
0
.
5
,
0
.
5
);
// Mettre le centre du gradient au centre.
MyBrush.
Center =
new
Point
(
0
.
5
,
0
.
5
);
// Mettre les radius pour que le gradient soit contre les bords.
MyBrush.
RadiusX =
0
.
5
;
MyBrush.
RadiusY =
0
.
5
;
// Créer les gradients stops.
GradientStop g1 =
new
GradientStop
(
);
g1.
Color =
Colors.
Yellow;
g1.
Offset =
0
.
0
;
MyBrush.
GradientStops.
Add
(
g1);
GradientStop g2 =
new
GradientStop
(
);
g2.
Color =
Colors.
Blue;
g2.
Offset =
0
.
75
;
MyBrush.
GradientStops.
Add
(
g2);
GradientStop g3 =
new
GradientStop
(
);
g3.
Color =
Colors.
Green;
g3.
Offset =
1
.
0
;
MyBrush.
GradientStops.
Add
(
g3);
//Créer un rectangle et le peindre avec la brush
Rectangle aRectangle =
new
Rectangle
(
);
aRectangle.
Width =
200
;
aRectangle.
Height =
100
;
aRectangle.
Fill =
MyBrush;
ContentPanel.
Children.
Add
(
aRectangle);
Autre exemple, une Sphère : on remplit une ellipse avec un RadialGradientBrush :
<Grid >
<Ellipse
Height
=
"200"
Width
=
"200"
Margin
=
"12,0,9,0"
>
<Ellipse.Fill>
<RadialGradientBrush
Center
=
"0.4 0.4"
GradientOrigin
=
"0.4 0.4"
>
<GradientStop
Offset
=
"0"
Color
=
"White"
/>
<GradientStop
Offset
=
"1"
Color
=
"YellowGreen"
/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
Dernier exemple : utiliser une RadialGradientBrush comme Foreground d'un texte.
<TextBlock
Text
=
"GRADIENT BRUSH"
FontFamily
=
"Arial"
FontSize
=
"48"
Margin
=
"39,477,-39,-477"
>
<TextBlock.Foreground>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop
Offset
=
"0"
Color
=
"Transparent"
/>
<GradientStop
Offset
=
"1"
Color
=
"White"
/>
</GradientStopCollection>
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</TextBlock.Foreground>
</TextBlock>
V-E-5. ImageBrush▲
On peut mettre une image dans un rectangle, par exemple.
En XAML :
<Rectangle
Width
=
"75"
Height
=
"75"
>
<Rectangle.Fill>
<ImageBrush
ImageSource
=
"c:\graphe.bmp"
/>
</Rectangle.Fill>
</Rectangle>
Si je donne à la propriété Foreground le nom d'une ImageBrush, le texte va être dessiné avec cette image :
<
TextBlock FontFamily=
"Verdana"
FontSize=
"88"
FontStyle=
"Italic"
FontWeight=
"Bold"
>
Philippe
<
TextBlock.
Foreground>
<
ImageBrush ImageSource=
"C:\P1020907.jpg"
/>
</
TextBlock.
Foreground>
</
TextBlock>
Il n'y a pas de VirtualBrush en WP7.
V-E-6. Masque d'opacité▲
Dans les UIElements, une image par exemple, on peut mettre un masque d'opacité (propriété OpacityMask), il permet de modifier la transparence d'une image en fonction d'un masque qui peut être un dégradé ou une image.
Seule la composante alpha du masque est prise en compte. L'image hérite de la transparence du masque.
Ici sur l'image de cet oiseau on applique un masque qui est une RadialGradientBrush de plus en plus transparente vers l'exterieur.
<Image
Source
=
"oiseau.jpg"
>
<Image.OpacityMask>
<RadialGradientBrush>
<GradientStop
Offset
=
"0"
Color
=
"White"
/>
<GradientStop
Offset
=
"0.8"
Color
=
"White"
/>
<GradientStop
Offset
=
"1"
Color
=
"Transparent"
/>
</RadialGradientBrush>
</Image.OpacityMask>
</Image>
Effet miroir :
Ici on affiche deux fois la même image. On inverse la seconde par rapport à l'axe des Y grâce à RenderTransform et on lui applique un masque qui est un LinearGradientBrush.
<Image
Source
=
"oiseau.jpg"
Stretch
=
"None"
VerticalAlignment
=
"Top"
/>
<Image
Source
=
"oiseau.jpg"
Stretch
=
"None"
VerticalAlignment
=
"Top"
RenderTransformOrigin
=
"0.5 1"
>
<Image.RenderTransform>
<ScaleTransform
ScaleY
=
"-1"
/>
</Image.RenderTransform>
<Image.OpacityMask>
<LinearGradientBrush
StartPoint
=
"0, 0"
EndPoint
=
"0, 1"
>
<GradientStop
Offset
=
"0"
Color
=
"#00000000"
/>
<GradientStop
Offset
=
"0.5"
Color
=
"#40000000"
/>
</LinearGradientBrush>
</Image.OpacityMask>
</Image>
V-E-7. Nombre de couleurs et dégradés▲
Les téléphones Windows Phone 7 ont des écrans 32 bpp (bits par pixel) ou des écrans 16 bpp (bits par pixel), l'émulateur est en 16 bpp pour aider les développeurs à concevoir des applications qui fonctionnent quel que soit le matériel.
Aussi dans l'émulateur les dégradés ne sont pas beaux et présentent des bandes colorées
(pour être sûr d'avoir un beau dégradé, certains proposent d'afficher l'image d'un dégradé qu'on aura flouté dans PaintNet ou PhotoShop).
V-F. Ressources▲
Les ressources sont un ensemble d'éléments :
- Couleurs, Brush, Style, ControlTemplate (aspect du contrôle), DataTemplate (affichage des données) ressources dites internes ;
- Images, icônes, textes, sons (ressources contenues dans des fichiers externes).
Nous allons donc voir
1- Les ressources internes.
2- Les fichiers de ressources externes.
3- Les ressources fournies par WP.
V-F-1. Les dictionnaires de ressources▲
On a vu antérieurement comment modifier certaines propriétés d'UN contrôle. Mais il est possible de créer des modèles pour ensuite les appliquer à un ou plusieurs contrôles.
Ici on va voir comment créer des styles, des modèles de contrôles, des modèles de données pour les appliquer à un, plusieurs ou tous les contrôles ou données .
V-F-1-a. Ressources simples▲
Plutôt que d'indiquer X fois quelle couleur ou Brush utiliser dans plusieurs contrôles d'une fenêtre, il est plus simple de définir une ressource contenant la couleur ou la Brush puis X fois, indiquer pour chaque contrôle quelle ressource utiliser.
Les ressources sont dans un dictionnaire de ressources. Dans les ressources de l'application, d'une page ou d'un objet, entre les balises de début et de fin de ressources.
<Grid.Resources>
</Grid.Resources>
Remarquez : Resources avec un "s" et non Ressources.
Ici dans ces ressources simples, la ressource est une Brush, une couleur un texte…
Chaque ressource doit avoir une clé unique. Il faut affecter la clé unique via l'attribut x:Key.
En principe, la clé est une chaîne de caractères.
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
>
<Grid.Resources>
<SolidColorBrush
x
:
Key
=
"MyBrush"
Color
=
"Gold"
/>
</Grid.Resources>
Ici la clé unique de la SolidColorBrush est "MyBrush", c'est son nom, son nom d'index.
Elle est dans les ressources de la grille.
Ensuite dans le code XAML de l'UI on utilise cette ressource grâce à {StaticResource MyBrush}.
Seul le contrôle utilisant la ressource la prendra en compte !
Ici dans la grille où se trouve la ressource, on a un bouton et une ellipse. On peut donc utiliser la ressource dans le bouton et l'ellipse.
<Button
Background
=
"{StaticResource MyBrush}"
/>
<Ellipse
Fill
=
"{StaticResource MyBrush}"
/>
En C# :
//ContentPanel.Resources contient les ressources de la grille.
//Comme la ressource "MyBrush" est un objet, on la caste en Brush.
button1.
Background =
(
Brush)ContentPanel.
Resources[
"MyBrush"
];
// Si la ressource avait été dans App.xaml
button1.
Background =
(
Brush) Application.
Current.
Resources[
"MyBrush"
];
Exemple complet
Dans une Grid, on crée une ressource de type LinearGradientBrush nommée BrushBizarre, ensuite on applique cette Brush au fond d'un bouton.
<
Grid>
<
Grid.
Resources>
<
LinearGradientBrush x:
Key=
"BrushBizarre"
>
<
GradientStop Color=
"Yellow"
Offset=
"0"
/>
<
GradientStop Color=
"Green"
Offset=
"1"
/>
</
LinearGradientBrush>
</
Grid.
Resources>
<
Button Background=
"{StaticResource BrushBizarre}"
>
Click Me</
Button>
</
Grid>
Où mettre les ressources ?
-Dans un objet. Dans une Grid comme dans l'exemple précédent, syntaxe : Grid.Resources ; dans ce cas la Grid mais aussi tous les objets contenus dans la Grid peuvent utiliser la ressource.
<Grid.Resources>
<SolidColorBrush
x
:
Key
=
"MyBrush"
Color
=
"Gold"
/>
</Grid.Resources>
En C# comment utiliser la ressource ?
//ContentPanel.Resources contient les ressources de la grille nommée ConcentPanel.
//Comme la ressource "MyBrush" est un objet, on la caste en Brush.
button1.
Background =
(
Brush)ContentPanel.
Resources[
"MyBrush"
];
-Dans une Page, ici on crée une ressource string dans la page.
xmlns:system="clr-namespace:System;assembly=mscorlib"
<
phone
:
PhoneApplicationPage.Resources>
<
system
:
String
x
:
Key
=
"LeTitre"
>
Cours Wp</
system
:
String>
</
phone
:
PhoneApplicationPage.Resources>
On remarque que pour utiliser une string comme ressource, il faut au préalable importer l'espace de noms system.
Comment l'utiliser dans la page ?
<TextBlock
x
:
Name
=
"ApplicationTitle"
Text
=
"{StaticResource LeTitre}"
/>
En C# :
TextBlock1.
Text =
(
string
) this
.
Resources[
"LeTitre"
];
-Dans le fichier Application.
C'est le fichier App.xaml.
<Application.Resources>
<SolidColorBrush
x
:
Key
=
"MyBrush"
Color
=
"Gold"
/>
</Application.Resources>
Dans ce cas la ressource est utilisable dans toute l'application.
On peut l'utiliser dans un TextBlock :
<TextBlock
x
:
Name
=
"PageTitle"
Foreground
=
"{ StaticResource MyBrush}"
Text
=
"nom de la page"
/>
Comment récupérer la brush en C# ?
SolidColorBrush sb =
Application.
Current.
Resources[
"MyBrush"
]
as
SolidColorBrush;
- Dans un fichier de ressources indépendant.
Menu Projet, puis Ajouter un nouvel élément puis Fichier texte. Nommer le Dictionnary1 puis Ok.
Y mettre une SolidColorBrush.
<ResourceDictionary
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
=
"http://schemas.microsoft.com/winfx/2006/xaml"
>
<SolidColorBrush
x
:
Key
=
"MyBrush"
Color
=
"Gold"
/>
</ResourceDictionary>
Pour que cela fonctionne, il faut ajouter dans le fichier App.xaml la référence au dictionnaire.
<Application
x
:
Class
=
"Application"
<Application.Resources>
<ResourceDictionary.MergeDictionaries>
<ResourceDictionary
Source
=
"Dictionary1.xaml"
/>
</ResourceDictionary.MergeDictionaries>
</Application.Resources>
</Application>
L'emplacement où est déclaré la ressource affecte l'emplacement où la ressource peut être utilisée.
Si vous déclarez la ressource dans votre fichier App.xaml avec la balise Application.Resources, elle pourra être utilisée n'importe où dans votre application.
Si vous déclarez la ressource dans une Grid grâce à Grid.Resources, elle pourra être utilisée uniquement dans la Grid et dans les objets enfants qui sont dans cette grille.
Il n'existe pas de ressources Dynamiques en WP.
V-F-1-b. Les styles▲
Le style est une ressource qui est applicable à un objet ou un type d'objet. On peut créer un style pour les Buttons, les List…
Il correspond à l'identification de plusieurs propriétés par un nom, que l'on peut appliquer ensuite facilement à plusieurs contrôles.
Il peut être placé dans les ressources d'un objet, d'une page ou d'une application.
Ici on va créer un style pour les TextBlock.
TargetType="TextBlock" indique quel type cela concerne.
x:Key= , indique le nom du style.
Setter va servir à définir une Property à laquelle on va donner une Value. Dans ce style, grâce à Setter Property= on affecte des valeurs aux propriétés des TextBlock.
<Application.Resources>
<Style
TargetType
=
"TextBlock"
x
:
Name
=
"MyTB"
>
<Setter
Property
=
"FontSize"
Value
=
"18"
/>
<Setter
Property
=
"Foreground"
Value
=
"Red"
/>
<Setter
Property
=
"FontFamily"
Value
=
"Trebuchet MS"
/>
</Style>
</Application.Resources>
Ensuite, dans un TextBlock on peut utiliser le style.
Il faut utiliser l'attribut Style= dans le TextBlock sur lequel on veut appliquer le style.
<TextBlock
Style
=
"{StaticResource MyTB}"
>
Titre</TextBlock>
Depuis WP 7.1 (Mango), si on omet x:Name dans un style, cela permet d'appliquer le style automatiquement à tous les objets correspondant au TargetType.
Si on enlève x:Name :
<Application.Resources>
<Style
TargetType
=
"TextBlock"
>
<Setter
Property
=
"FontSize"
Value
=
"18"
/>
<Setter
Property
=
"Foreground"
Value
=
"Red"
/>
<Setter
Property
=
"FontFamily"
Value
=
"Trebuchet MS"
/>
</Style>
</Application.Resources>
Le style sera appliqué dans le textblock qui suit et tous les textblocks.
<TextBlock >
Titre</TextBlock>
On vient d'utiliser un Setter pour modifier une propriété, mais il est possible dans un style de mettre un DataTemplate (modèle de donnée), un ControlTemplate (modèle de contrôle).
Voyons un exemple avec un ControlTemplate (lire le chapitre suivant avant).
Ici on crée un style contenant un ControlTemplate donnant des boutons ronds et rouges.
<!--Ressources d'applications-->
<Application.Resources>
<Style
TargetType
=
"Button"
x
:
Name
=
"MyStyle"
>
<Setter
Property
=
"FontSize"
Value
=
"18"
/>
<Setter
Property
=
"Foreground"
Value
=
"White"
/>
<Setter
Property
=
"FontFamily"
Value
=
"Trebuchet MS"
/>
<Setter
Property
=
"Template"
>
<Setter.Value>
<ControlTemplate
TargetType
=
"Button"
>
<Grid>
<Ellipse
Width
=
"100"
Height
=
"100"
Fill
=
"Red"
></Ellipse>
<ContentPresenter
Content
=
"{TemplateBinding Content}"
HorizontalAlignment
=
"Center"
VerticalAlignment
=
"Center"
>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
Pour simplifier, le template n'est pas complet ; en particulier, le bouton ne change pas d'aspect quand on clique.
Et pour l'utiliser sur un bouton :
<Button
Style
=
"{StaticResource MyStyle}"
Content
=
"Ok"
Height
=
"153"
HorizontalAlignment
=
"Left"
Margin
=
"56,48,0,0"
Name
=
"button1"
VerticalAlignment
=
"Top"
Width
=
"337"
/>
En C# pour appliquer le style à un contrôle :
Button1.
Style=
(
Style)(
this
.
Resources[
"MyStyle"
]
);
On peut dans un style récupérer un style existant et l'étendre. On utilise BaseOn pour récupérer un style.
<Style
x
:
Key
=
"MybaseStyle"
>
<Setter
Property
=
"FontSize"
Value
=
"24"
/>
<Setter
Property
=
"Background"
Value
=
"Orange"
/>
</Style>
<Style
x
:
Key
=
"boldStyle"
BasedOn
=
"{StaticResource MybaseStyle}"
>
<Setter
Property
=
"FontWeight"
Value
=
"Bold"
/>
</Style>
V-F-1-c. Les modèles de contrôle : ControlTemplate▲
On a vu plus haut qu'on pouvait mettre un template (modèle, gabarit) dans un contrôle :
<Button>
<Button.Template>
<ControlTemplate>
<ControlTemplate>
</Button.Template>
</Button>
Mais on peut aussi créer des Modèles de contrôle (ControlTemplate) dans les ressources permettant de modifier tous les contrôles d'un même type.
On spécifie la structure visuelle et les aspects comportementaux d'un type de contrôle. Là, on ne modifie pas une simple propriété du contrôle, mais son aspect profond.
Si on écrit un ControlTemplate, on va définir son aspect visuel (fond, forme…), il faut aussi obligatoirement redéfinir l'aspect de son contenu (ControlPresenter) et si nécessaire les modifications liées aux évènements (VisualStateManager).
Comment avoir des boutons en forme d'ellipse ?
Dans les ressources de l'application (dans App.xaml), on va créer un style pour les boutons, on va sur le template et son ControlTemplate.
- Pour la forme du bouton, on va mettre dans une grille une ellipse (la remplir avec une Brush).
- Pour le texte on ajoute un ContentPresenter.
- Pour modifier la couleur quand le bouton est Pressed, on utilise un VisualStateManager contenant un Storyboard qui modifie la Brush de l'ellipse. Le bouton devient blanc quand on clique dessus.
<!--Ressources d'applications-->
<Application.Resources>
<Style
TargetType
=
"Button"
x
:
Key
=
"MyButton"
>
<Setter
Property
=
"Template"
>
<Setter.Value>
<ControlTemplate
TargetType
=
"Button"
>
<Grid>
<Ellipse>
<Ellipse.Fill>
<SolidColorBrush
x
:
Name
=
"EllipseBrush"
Color
=
"Blue"
/>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter
Margin
=
"2"
HorizontalAlignment
=
"Center"
VerticalAlignment
=
"Center"
/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
x
:
Name
=
"CommonStates"
>
<VisualStateGroup.Transitions>
<VisualTransition
From
=
"Normal"
To
=
"Pressed"
GeneratedDuration
=
"0:0:0.5"
/>
</VisualStateGroup.Transitions>
<VisualState
x
:
Name
=
"Normal"
/>
<VisualState
x
:
Name
=
"Pressed"
>
<Storyboard>
<ColorAnimation Storyboard.
TargetName
=
"EllipseBrush"
Storyboard.
TargetProperty
=
"Color"
To
=
"White"
/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
Pour un contrôle liste on aurait utilisé un ItemsPresenter.
Pour appliquer le style a un bouton :
<Button
Style
=
"{StaticResource MyButton}"
Content
=
"Button"
Height
=
"66"
Name
=
"button1"
Width
=
"289"
/>
V-F-1-d. Les modèles de données : Data Template▲
Enfin pour être complet il est possible de modifier l'aspect des données affichées dans un contrôle grâce au Data Template (modèle de données) ; voir le chapitre sur les liaisons de données pour le détail.
Utilisation de Template dans une ListBox.
Grâce à l'ItemTemplate on peut afficher les items d'une ListBox comme on veut.
Ici on veut afficher dans une ListBox une collection.
Pour chaque Item on affiche une petite sphère, la propriété Nom en gros et la propriété TextBref dessous en plus petit.
On suppose que la collection a déjà été créée (voir exemple en fin de chapitre sur les collections) et qu'on effectue une liaison (Binding) de la collection sur la ListBox, ce qui remplit automatiquement la ListBox avec la collection.
Dans l'ItemTemplate on met un StackPanel dans lequel on met une ellipse puis deux TextBlock qui sont en liaison avec les propriétés Titre et TextBref.
<ListBox
Name
=
"ListBoxConversion"
Margin
=
"10,10,23,-9"
ItemsSource
=
"{Binding}"
Height
=
"524"
Width
=
"400"
MouseLeftButtonUp
=
"ListBoxConversion_Appel"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation
=
"Horizontal"
Margin
=
"0,0,0,17"
>
<!--La petite sphère-->
<Ellipse
Height
=
"30"
Width
=
"30"
Margin
=
"12,0,9,0"
>
<Ellipse.Fill>
<RadialGradientBrush
Center
=
"0.4 0.4"
GradientOrigin
=
"0.4 0.4"
>
<GradientStop
Offset
=
"0"
Color
=
"White"
/>
<GradientStop
Offset
=
"1"
Color
=
"Blue"
/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<StackPanel
Width
=
"311"
>
<!--Les 2 lignes Nom et TextBref-->
<TextBlock
Foreground
=
"Azure"
Text
=
"{Binding Titre}"
TextWrapping
=
"Wrap"
Style
=
"{StaticResource PhoneTextLargeStyle}"
/>
<TextBlock
Text
=
"{Binding TextBref}"
TextWrapping
=
"Wrap"
Margin
=
"12,-6,12,0"
Style
=
"{StaticResource PhoneTextSubtleStyle}"
/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Comment afficher une liste d'images ?
Mesphotos étant une liste de noms d'images, la propriété NomImage contient le nom de la photo ; on montre ici le principe simplement.
<ListBox
ItemsSource
=
"{Binding Source={StaticResource MesPhotos}}"
/>
Ici on affiche la liste des noms d'images.
Comment afficher les images elle mêmes ? en créant un DataTemplate.
Il affiche pour chaque élément une ligne contenant le nom de l'image puis l'image elle-même.
<ListBox
ItemsSource
=
"{Binding Source={StaticResource MesPhotos}}"
>
<DataTemplate>
<StackPanel Orientation
=
"Horizontale"
>
<TextBlock
Text
=
"{Binding NomImage}"
>
<Image
Source
=
"{Binding NomImage}"
/>
</StackPanel>
</DataTemplate>
</ListBox>
Ici le Data Template est dans le contrôle, on aurait pu créer un DataTemplate dans les ressources.
V-F-2. Les fichiers de ressources▲
On peut ajouter dans le projet des fichiers image, son, icône…
Exemple de fichier Image.
J'ai une image nommée xvide.jpg, je veux la mettre dans les ressources pour la charger dans un contrôle image.
Mettre le fichier image dans le répertoire des sources (dans C:\Documents and Settings\phil\Mes documents\Visual Studio 2010\Projects\MyApplication\MyApplication).
Mettons à jour les fichiers de l'explorateur de solution en cliquant sur le bouton en haut (voir ci-dessous).
Le fichier apparaît dans l'explorateur de solution mais en grisé.
Clic droit sur le fichier puis sur Inclure dans le projet.
Le fichier devient une resource (voir en bas dans les propriétés, Action de génération=Resource).
On peut aussi passer par : Menu Projet=>Ajouter un élément existant.
Si on a créé un répertoire nommé image, pointer C:\Documents and Settings\phil\Mes documents\Visual Studio 2010\Projects\MyApplication\MyApplication\image\xvide.jpg, puis cliquer sur Ajouter
Le fichier apparaît dans l'explorateur de solution (en haut à droite).
Là aussi dans la fenêtre des propriétés du fichier Action de génération prend la valeur Resource par défaut.
Mettre la ressource dans l'image.
<Image
Margin
=
"34,36,48,46"
Name
=
"Image"
Stretch
=
"Fill"
Source
=
"/NomDuProjet;component/xvide.jpg"
/>
Notez bien : pas de chemin.
L'image xvide.jpg apparaît dans le contrôle image.
Dans le code C# :
Image1.
Source =
New BitmapImage
(
New Uri
(
"/NomDuProjet;component/xvide.jpg"
,
UriKind.
Relative));
Lors de la génération du projet, l'image sera dans l'exécutable.
On peut passer par les ressources de l'application.
-Charger xvide.jpg dans le projet comme ci-dessus.
-Créer une ressource nommée "someImage" dans Application.xaml
<Application.Resources>
<ImageSource
x
:
Key
=
"someImage"
>
xvide.jpg</ImageSource>
</Application.Resources>
-Mettre la ressource dans une image
<Image
Margin
=
"34,36,3,8"
Name
=
"Image1"
Stretch
=
"Fill"
Source
=
"{StaticResource someImage}"
/>
Bien sûr, on peut utiliser des icônes.
On peut aussi donner l'adresse Internet d'une image.
Revenons sur les valeurs d' Option de génération dans les propriétés d'un fichier :
Si on déroule la liste, on a les valeurs :
- Aucun : le fichier n'est pas inclus dans le groupe de sorties de projet et n'est pas compilé au cours du processus de génération. Fichier texte contenant de la documentation par exemple ;
- Compiler : le fichier est compilé dans la sortie de projet. Cette valeur est utilisée par défaut pour les fichiers de code (.cs) ;
- Page : le fichier est compilé dans la sortie de projet. Cette valeur est utilisée par défaut pour les fichiers .xaml ;
- ApplicationDefinition : cette valeur est utilisée par défaut pour App.xaml ;
- Contenu : le fichier n'est pas compilé, mais il est inclus dans le groupe de sorties Contenu. Valeur par défaut d'un fichier .htm ou d'autres types de fichier Web, par exemple ;
- Resources : cette action de génération va incorporer le fichier dans l'assembly du projet (correspond aux ressources incorporées) ;
- Ressource incorporée : semble ne pas pouvoir être utilisé en Silverlight.
Notez que dans l'explorateur de solutions , si on ajoute une image (menu « Projet », « Ajouter un elément existant »), l'option « Action de génération » prend la valeur « Resource ».
On peut, quand on met une image dans le projet, en faire un « Contenu » ou une « Resource ».
Avec « Contenu », le fichier image est dans le fichier d'installation XAP mais pas dans la DLL. Cela serait plus performant.
Noter la syntaxe de la valeur de Source :
<Image
Name
=
"Image"
Source
=
"xvide.jpg"
/>
Avec « Resource », le fichier image est incorporé dans le fichier DLL qui est dans le XAP.
Noter la syntaxe de la valeur de Source :
<Image
Name
=
"Image"
Source
=
"/NomDuProjet;component/xvide.jpg"
/>
V-F-3. Ressources Windows Phone▲
WP fournit un dictionnaire de ressources dont on peut utiliser les valeurs dans une application. Ce sont des valeurs standards proposées par Microsoft.
Elles sont en rapport avec le thème en cours (couleur de fond : claire ou foncée, couleur d'accentuation).
Le nom de ces ressources commence par « Phone ».
Il y a des PhoneBrush dans ces ressources.
Par exemple pour un fond de page on va utiliser le fond de page standard : PhoneForegroundBrush.
Foreground="{StaticResource PhoneForegroundBrush}"
Il y a aussi des PhoneColor , PhoneFonts, les tailles pour les caractères (PhoneFontSizeMedium = 17),
mais aussi les épaisseurs pour les propriétés Thickness, Padding…
enfin les styles de texte et la visibilité ou l'opacité du thème.
Voici un exemple de l'utilisation d'un style dans un TextBox :
<TextBlock
x
:
Name
=
"PageTitle"
Text
=
"MonTitre"
Style
=
"{StaticResource PhoneTextTitle1Style}"
/>
Voici où trouver toutes les ressources et leur valeur :
Ressources pour Windows Phone 7.1.
D'ailleurs, automatiquement, quand on crée une page, la police normale est utilisée pour la page, ainsi que la couleur du fond :
<
phone
:
PhoneApplicationPage
x
:
Class
=
"MainPage"
FontFamily
=
"{StaticResource PhoneFontFamilyNormal}"
// c
'est la police Métro: '
Segoe WP
'
FontSize="{StaticResource PhoneFontSizeNormal}" // 15 pts, 20 pixels
Foreground="{StaticResource PhoneForegroundBrush}"
Pour afficher le nom de l'application et le titre on utilise PhoneTextNormalStyle et PhoneTextTitle1Style :
<!--TitlePanel contient le nom de l'application et le titre de la page-->
<StackPanel
x
:
Name
=
"TitlePanel"
Grid.
Row
=
"0"
Margin
=
"12,17,0,28"
>
<TextBlock
x
:
Name
=
"ApplicationTitle"
Text
=
"MON APPLICATION"
Style
=
"{StaticResource PhoneTextNormalStyle}"
/>
<TextBlock
x
:
Name
=
"PageTitle"
Text
=
"nom de la page"
Margin
=
"9,-7,0,0"
Style
=
"{StaticResource PhoneTextTitle1Style}"
/>
</StackPanel>
C'est la ressource PhoneFontFamilyNormal (correspondant à la police Métro Segoe WP) qui est donc utilisée pour le texte de la page.
Comment utiliser les couleurs du thème actuel ?
Dans les paramètres du Windows Phone on peut choisir :
L'arrière plan (clair ou sombre) ;
La couleur d'accentuation (11 couleurs proposées).
Ces couleurs se retrouvent dans les ressources Windows Phones : la couleur de fond est PhoneBackgrountColor ;
la couleur d'accentuation est PhoneAccentColor.
Il y a les Brush correspondantes :
Dans le code XAML, quand on utilise des ressources comme couleur, ce sont les Brush qu'il faut utiliser et pas les couleurs.
PhoneAccentBrush : Brush d'accentuation définie par le thème.
PhoneForegroundBrush : Brush utilisée pour les éléments de premier plan tels que le texte ou les icônes.
PhoneBackgroundBrush : Brush utilisée pour les éléments d'arrière-plan.
PhoneContrastBackgroundBrush : Brush de fond utilisée pour mettre en contraste (fond des menus contextuels par exemple).
PhoneContrastForegroundBrush : Brush de premier plan utilisée pour les éléments à mettre en contraste. Par exemple les textes des menus contextuels.
PhoneDisabledBrush : Brush des éléments désactivés.
PhoneSubtleBrush : Brush partiellement transparente qui permet de mettre en retrait des éléments visuels moins importants.
PhoneBorderBrush : Brush de bordure des contrôles TextBox, CheckBox, PasswordBox, et RadioButton.
PhoneSemitransparentBrush : Brush presque transparente permettant de faire ressortir des éléments de couleurs similaires (par exemple du texte blanc sur une image claire).
On peut aussi utiliser les styles :
PhoneTextNormalStyle qui contraste avec la couleur du fond pour le rendre lisible, PhoneTextTitle1Style.
Voir le chapitre sur les thèmes.
Sélecteur de ressource : dans la fenêtre Propriétés, pour donner la valeur d'une ressource à la propriété (BackGround par exemple) d'un contrôle on peut ouvrir le sélecteur de ressource en cliquant sur le carré à coté du nom de la propriété.
Dans le menu, choisir Appliquer la ressource.
Double cliquer sur la ressource désirée.
Typographie :
il est conseillé d'utiliser la typographie recommandée.
V-G. Les liaisons de données ou Binding▲
V-G-1. Principes du Binding▲
Binding veut dire liaison.
"Les liaisons de données sont des processus qui établissent une connexion entre l'interface utilisateur de l'application et la logique métier." En d'autres termes, elles permettent d'établir une connexion entre un contrôle et une sources de données. Cela permet, par exemple, d'afficher automatiquement le contenu d'une base de données, d'une collection… dans une ListBox…
Il faut donc un objet visuel, la cible (ListBox, TextBox…) ayant une propriété de dépendance et faire une liaison avec la source de liaison qui est la propriété d'un objet (collection, tableau, base de données…).
La liaison peut être unidirectionnelle (OnWay= en lecture seule de la source) ou bidirectionnelle (TwoWay), ce qui permet dans ce dernier cas de mettre à jour la source quand on fait une modification dans l'UI.
Binding entre objets :
Pour qu'une propriété d'un objet (dit cible) soit liée à une source, il faut lui affecter un objet Binding (Text="{Binding…) puis indiquer l'objet source avec ElementName et la propriété source avec Path :
<TextBox
Text
=
"{Binding Path=Text, ElementName=txtsource}"
/>
Ici la propriété Text du TextBox est liée à la propriété Text d'un autre contrôle. Si on tape un texte dans le contrôle txtsource, il apparaitra dans la propriété Text de notre TextBox.
DataContext
La propriété DataSource des contrôles WindowsForms n'existe plus ; les contrôles Silverlight ont un DataContext. Il peut être renseigné en XAML ou dans le code #. Le DataContext indique donc la source mais ce qui est fondamental c'est que les contrôles enfants vont hériter du DataContext :
Si un DockPanel a un DataContext, les Buttons qui sont dedans hériterons de ce DataContext, il suffira pour chaque contrôle enfant d'indiquer une partie de la source (une propriété, un champ…).
Autrement dit, si vous avez une source possédant plusieurs propriétés, la source est spécifiée en utilisant DataContext du contrôle (pratique pour un groupe d'éléments donc). Chaque propriété à afficher sera indiquée dans les éléments enfants.
Si on a dans le code C# de la page une collection d'objets ayant les propriétés Nom, Prenom, Rue, Ville, on renseigne le DataContext dans le code C# puis on peut utiliser le binding comme cela :
<StackPanel
DataContext
=
"{Binding}"
>
<TextBox
Text
=
"{Binding Nom}"
/>
<TextBox
Text
=
"{Binding Prenom}"
/>
<TextBox
Text
=
"{Binding Rue}"
/>
<TextBox
Text
=
"{Binding Ville}"
/>
</StackPanel>
Ici on crée une liaison du StackPanel avec une collection. Les TextBox héritent du DataContext du StackPanel. Cela permet ensuite de lier la propriété Text de chaque TextBox à différentes propriétés des objets de la collection.
Vous pouvez indiquer le DataContext dans le code C# :
DockPanel1.
DataContext=
MyCollection;
Dans ce cas en XAML, il faut simplement indiquer qu'il y a un Binding avec l'expression "{Binding}" :
<ListBox
Margin
=
"14,16,8,2"
Name
=
"ListBox1"
ItemsSource
=
"{Binding }"
/>
Il est possible d'indiquer comme DataContext la classe elle-même.
public
MainWindow
(
)
InitializeComponent
(
);
DataContext =
this
;
}
On insiste sur le fait que si vous souhaitez créer une liaison avec un objet source (comme une collection, par exemple) qui a déjà été instancié dans le code C#, vous devez définir la propriété DataContext dans le code C# (et pas en XAML).
Si on n'utilise pas le DataContext, on peut aussi utiliser la propriété Source du Binding :
<Button
Background
=
"{Binding Source={StaticResource myDataSource}/>
Si on utilise un contrôle de type ItemsControl tel qu'un ListBox, ListView ou un TreeView pour afficher une collection de données, il faut utiliser ItemsSource pour indiquer la source :
<ListBox
Name
=
"ListBox1"
ItemsSource
=
"{Binding Source={StaticResource ListData}}"
/>
Mode indique le mode de mise a jour (OneWay, TwoWay, OneTime).
<TextBox
Name
=
"txtcible"
Margin
=
"21,0,25,21"
Text
=
"{Binding Path=Text, ElementName=txtsource, Mode=TwoWay}"
/>
UpdateSourceTrigger détermine le moment des mises à jour de la liaison.
La valeur est par défaut Default et le binding se fera dans ce cas lors de la sortie du contrôle source.
L'autre valeur est Explicit.
V-G-2. Liaison entre contrôles▲
Pour qu'une propriété d'un objet (dit cible) soit liée à une source, il faut lui affecter un objet Binding ( Text="{Binding…) puis indiquer l'objet source avec ElementName et la propriété source avec Path.
A - Créons deux TextBox ; on va créer une liaison entre les deux propriétés Text, quand on tape un texte dans la première, il apparaît dans la seconde :
<TextBox
Name
=
"txtsource"
Text
=
""
/>
<TextBox
Name
=
"txtcible"
Text
=
"{Binding Path=Text, ElementName=txtsource}"
/>
Dans la cible, la propriété Text= Binding entre accolades permet d'indiquer qu'il y a une liaison, ElementName indique la source de la liaison (la source est désignée par son nom dans le XAML). Path la propriété de la source. Il y a une « , » entre chaque élément à partir du second.
La syntaxe suivante est aussi acceptée.
<TextBox
Name
=
"txtcible"
Text
=
"{Binding Text, ElementName=txtsource}"
/>
On remarque que la mise à jour est effectuée (dans le cas d'un TextBox) quand on quitte le TextBox source. Il y a bien une propriété UpdateSourceTrigger qui détermine le moment de la mise à jour, elle peut avoir une des deux valeurs Default et Explicit (pas de valeur PropertyChanged).
Par défaut le Mode est égal à OneWay (la liaison se fait uniquement dans le sens source=>cible).
On peut donner la valeur TwoWay au mode, ce qui permet de faire en plus la liaison cible=>source : quand on tape dans la cible, la source est modifiée (cela est visible dans notre exemple ci-dessous quand on quitte la cible) :
<TextBox
Name
=
"txtsource"
Text
=
""
/>
<TextBox
Name
=
"txtcible"
Text
=
"{Binding Path=Text, ElementName=txtsource, Mode=TwoWay}"
/>
Pour être complet il existe aussi les modes OneTime (mise à jour une seule fois au départ).
B - Créons deux ListBox ; quand on ajoute des éléments dans la première, ils apparaissent dans la seconde :
<Grid>
<ListBox
Name
=
"listBox1"
SelectionMode
=
"Multiple"
/>
<ListBox
Name
=
"listBox2"
ItemsSource
=
"{Binding ElementName=listBox1, Path=Items, Mode=OneWay}"
/>
</Grid>
C - Créons maintenant un Slider (curseur) et un rectangle ; quand on bouge le curseur du Slider cela modifie la hauteur du rectangle, il faut donc comme source la propriété Value du Slider et comme cible la propriété Height du rectangle :
<StackPanel>
<Slider
Name
=
"Slider1"
Value
=
"5"
Maximum
=
"200"
Minimum
=
"0"
Margin
=
"0,294,0,172"
/>
<Rectangle
Fill
=
"Blue"
Stroke
=
"Red"
StrokeThickness
=
"3"
Width
=
"97"
Height
=
"{Binding ElementName=Slider1, Path=Value}"
/>
</StackPanel>
D - Créons une ListBox, on ajoute des éléments à cette ListBox avec du code C# ; quand on clique sur un item il s'affiche dans le TextBlock en bas :
<ListBox
Height
=
"203"
HorizontalAlignment
=
"Left"
Margin
=
"27,24,0,0"
Name
=
"listBox1"
VerticalAlignment
=
"Top"
Width
=
"373"
>
</ListBox>
<TextBlock
Text
=
"{Binding ElementName=listBox1, Path=SelectedItem}"
Margin
=
"6,296,-6,266"
/>
À noter que si on met des items dans la ListBox en utilisant le code XAML ci dessous, cela ne marche pas ; le binding affiche le type (il faut ajouter des éléments à la ListBox avec du code C#).
<ListItem
Content
=
"Paris"
/>
E -Créons un TextBox et un TextBlock avec liaison ; comment mettre à jour quand on veut ?
UpdateSourceTrigger détermine le moment des mises à jour de la liaison.
La valeur est par défaut Default et le binding se fera dans ce cas lors de la sortie du contrôle source.
L'autre valeur est Explicit.
Ici la mise à jour se fait quand on la provoque explicitement.
Exemple : on a un TextBox dans lequel on va taper du texte ; on a un TextBlock dans lequel le texte va s'afficher par binding. On va indiquer une mise à jour explicite.
<TextBox
x
:
Name
=
"Mytxt"
/>
<TextBlock
Name
=
"Mytblock"
Text
=
"{Binding Text, ElementName=Mytxt,
UpdateSourceTrigger=Explicit}"
Height
=
"50"
/>
Pour que la mise à jour intervienne lors du changement du texte dans la TextBox, il faut créer un EventHandler sur l'évènement TextChanged et dans la routine exécutée par ce dernier évènement effectuer la mise à jour du binding (propriété Update d'un BindingExpression).
using
System.
Windows.
Data;
//.......
public
partial
class
Window1 :
Window
{
public
Window1
(
)
{
InitializeComponent
(
);
MyTextBox.
TextChanged +=
new
TextChangedEventHandler
(
OnTextChanged);
}
private
void
OnTextChanged
(
object
sender,
TextChangedEventArgs e)
{
BindingExpression bex =
Mytblock.
GetBindingExpression
(
TextBlock.
TextProperty);
bex.
UpdateSource
(
);
}
}
Maintenant quand on tape du texte dans le TextBox, il s'affiche dans le TextBlock immédiatement.
En résumé : dans le Binding, ElementName indique le contrôle source. La propriété Path spécifie la propriété de l'objet.
Path peut aussi avoir une valeur comme Text.Height ou Text[1] (deuxième caractère).
V-G-3. Liaison Collection-ListBox▲
Pour une ListBox, c'est ItemsSource qui indique la source. Voici le code XAML de la ListBox : ItemsSource ="{Binding}" indique que la source est une liaison mais n'indique pas la source.
<ListBox
Name
=
"listbox1"
ItemsSource
=
"{Binding}"
>
</ListBox>
On crée une collection nommée noms de type List(Of), on la remplit avec des noms ; comment faire une liaison List() ListBox et afficher automatiquement les noms dans la ListBox ?
Voici le code C# :
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
using
Microsoft.
Phone.
Controls;
namespace
testbinding
{
public
partial
class
MainPage :
PhoneApplicationPage
{
public
List<
string
>
noms {
get
;
set
;
}
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
noms =
new
List <
string
>(
);
noms.
Add
(
"Paul"
);
noms.
Add
(
"Pierre"
);
noms.
Add
(
"Paule"
);
listbox1.
DataContext =
noms;
}
}
}
ListBox1.DataContext = noms indique quelle est la source de la liaison.
La liaison est ici par défaut : OneWay.
Cela marche aussi avec un tableau.
Le problème est que si ensuite j'ajoute un nom à la liste (noms.Add("Toto");), Toto n'apparaît pas dans la ListBox.
Il n'y a pas de mise à jour. On verra comment résoudre cela.
Si vous souhaitez créer une liaison avec un objet source (comme une collection, par exemple) qui a déjà été instancié dans le code C#, vous devez définir la propriété DataContext dans le code C#.
Si l'utilisateur clique sur un élément de la liste, il est possible de l'utiliser dans un autre contrôle :
<
TextBlock Text=
"{Binding ElementName=listBox1, Path=SelectedItem}"
Margin=
"6,296,-6,266"
/>
Ici dans le TextBlock, le nom qui a été sélectionné dans la ListBox sera affiché car on utilise comme Path le SelectedItem.
V-G-4. Liaison collection d'objets, ListBox▲
On veut une collection contenant des objets nommés ElementDeListe ayant les propriétés « Titre », « Formule », « Mode » et les afficher par liaison dans une liste.
On va créer une classe ElementDeListe qui a trois propriétés: « Titre », « Formule » et « Mode ».
On va aussi créer une collection nommée ListMath qui hérite de la Classe List(Of). On y mettra des ElementDeListe.
Dans Class1.cs on crée une classe nommée ElementDeListe.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace listboxbinding // Espace de noms = nom du programme
{
public class ElementDeListe // la classe se nomme 'ElementDeListe'
{
private string _Titre; // Variable privée
public string Titre // Variable public avec son get et son set
{
get
{
return _Titre;
}
set
{
if (value != _Titre)
{
_Titre = value;
}
}
}
private string _Formule;
public string Formule
{
get
{
return _Formule;
}
set
{
if (value != _Formule)
{
_Formule = value;
}
}
}
private string _Mode;
public string Mode
{
get
{
return _Mode;
}
set
{
if (value != _Mode)
{
_Mode = value;
}
}
}
public ElementDeListe(string t, string n, string m) { _Titre = t; _Formule = n; _Mode = m; }
}
}
Dans MainPage.xaml.cs on déclare une collection nommée ListMath de type List, on y met quelques éléments.
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
using
Microsoft.
Phone.
Controls;
namespace
listboxbinding
{
public
partial
class
MainPage :
PhoneApplicationPage
{
public
List<
ElementDeListe>
ListeMath {
get
;
set
;
}
//la collection sera public
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
ListeMath =
new
List<
ElementDeListe>(
);
//On va créer de nouveaux ElementDeliste en utilisant le constructeur à 3 paramètres.
ListeMath.
Add
(
new
ElementDeListe
(
"Francs-Euros"
,
"C"
,
"C"
));
ListeMath.
Add
(
new
ElementDeListe
(
"Celsius-Fahrenheit"
,
"Celsius"
,
"C"
));
ListeMath.
Add
(
new
ElementDeListe
(
"Celsius-Kelvin"
,
"Celsius"
,
"C"
));
listBox1.
DataContext =
ListeMath;
// en prime on ajoute un binding (une liaison) sur une listbox
}
}
}
En fin de code on lie la collection à la ListBox grâce à sa propriété DataContext.
Dans MainPage.xaml on crée une ListBox nommée listBox1, on indique que la source des items est une liaison.
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
>
<ListBox
ItemsSource
=
"{ Binding}"
Height
=
"193"
HorizontalAlignment
=
"Left"
Margin
=
"40,32,0,0"
Name
=
"listBox1"
VerticalAlignment
=
"Top"
Width
=
"369"
/>
</Grid>
Cela donne :
La ListBox affiche les types !
Pour afficher Titre et Formule, on va ajouter un ItemTemplate contenant un StackPanel horizontal qui contient trois TextBlocks. Le premier et le troisième sont liés aux propriétés Titre et Formule.
<Grid
x
:
Name
=
"ContentPanel"
Grid.
Row
=
"1"
Margin
=
"12,0,12,0"
>
<ListBox
ItemsSource
=
"{ Binding}"
Height
=
"193"
HorizontalAlignment
=
"Left"
Margin
=
"40,32,0,0"
Name
=
"listBox1"
VerticalAlignment
=
"Top"
Width
=
"369"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation
=
"Horizontal"
>
<TextBlock
Text
=
"{Binding Titre}"
/>
<TextBlock
Text
=
", "
/>
<TextBlock
Text
=
"{Binding Formule}"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Le problème, là aussi, est que si ensuite j'ajoute un objet à la liste ( ListeMath.Add(new ElementDeListe("Ares-m2", "Ares", "C"));) ou si je modifie une propriété d'un objet de la liste, cela ne modifie pas la ListBox.
Il n'y a pas de mise à jour.
On peut forcer la mise à jour de deux manières :
1 -En utilisant une ObservableCollection(Of)
Il faut utiliser une ObservableCollection générique qui possède l' interface INotifyCollectionChanged qui entraîne, en cas d'ajout ou de suppression d'élément de la collection, une mise à jour de la ListBox.
Remarquons que pour utiliser une ObservableCollection il faut ajouter : System.Collections.ObjectModel
using
System.
Collections.
ObjectModel;
Ensuite on utilise l'ObservableCollection :
namespace
listboxbinding
{
public
partial
class
MainPage :
PhoneApplicationPage
{
public
ObservableCollection<
ElementDeListe>
ListeMath {
get
;
set
;
}
//la collection sera public
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
ListeMath =
new
ObservableCollection<
ElementDeListe>(
);
Maintenant cela marche : si on ajoute un élément à ListeMath, il apparaît immédiatement dans la ListBox.
C'est que l'observable collection a en natif une interface : INotifyCollectionChanged.
2 -En ajoutant des interfaces à la classe
En effet, avec une List rien n'indique à la liaison collection-listbox que la collection a changé et qu'il faut mettre à jour.
Ajout de INotifyPropertyChanged (après le nom de la classe et les « : »).
Permet, lorsqu'on modifie une propriété d'un objet présent dans la List (on change un titre, par exemple), que la modification soit dans la ListBox.
using
System.
ComponentModel;
namespace
listboxbinding // Espace de noms = nom du programme
{
public
class
ElementDeListe :
INotifyPropertyChanged // la classe se nomme 'ElementDeListe'
{
public
event
PropertyChangedEventHandler PropertyChanged;
private
string
_Titre;
// Variable privée
public
string
Titre // Variable public avec son get et son set
{
get
{
return
_Titre;
}
set
{
if
(
value
!=
_Titre)
{
_Titre =
value
;
EnvoiePropertyChanged
(
"Titre"
);
}
}
}
private
string
_Formule;
public
string
Formule
{
get
{
return
_Formule;
}
set
{
if
(
value
!=
_Formule)
{
_Formule =
value
;
EnvoiePropertyChanged
(
"Formule"
);
}
}
}
private
string
_Mode;
public
string
Mode
{
get
{
return
_Mode;
}
set
{
if
(
value
!=
_Mode)
{
_Mode =
value
;
EnvoiePropertyChanged
(
"Mode"
);
}
}
}
private
void
EnvoiePropertyChanged
(
string
propertyName)
{
if
(
PropertyChanged !=
null
)
{
PropertyChanged
(
this
,
new
PropertyChangedEventArgs
(
propertyName));
}
}
public
ElementDeListe
(
string
t,
string
n,
string
m) {
_Titre =
t;
_Formule =
n;
_Mode =
m;
}
}
}
Ajout de INotifyCollectionChanged.
Permet, si on modifie la collection (ajout d'un élément par exemple) de voir apparaitre cet élément dans la ListBox.
(Code contenant uniquement la partie INotifyCollectionchanged ; code non testé.)
// Extrait d'un article de Nathanael Marchand sur developpez.com.
using
System.
Collections.
Specialized;
public
interface
INotifyCollectionChanged
{
event
NotifyCollectionChangedEventHandler CollectionChanged;
}
public
delegate
void
NotifyCollectionChangedEventHandler
(
object
sender,
NotifyCollectionChangedEventArgs e);
public
class
NotifyCollectionChangedEventArgs :
EventArgs
{
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
object
changedItem);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
object
changedItem,
int
index);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
IList changedItems);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
IList changedItems,
int
startingIndex);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
object
newItem,
object
oldItem);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
object
newItem,
object
oldItem,
int
index);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
IList newItems,
IList oldItems);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
IList newItems,
IList oldItems,
int
startingIndex);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
object
changedItem,
int
index,
int
oldIndex);
public
NotifyCollectionChangedEventArgs
(
NotifyCollectionChangedAction action,
IList changedItems,
int
index,
int
oldIndex);
public
NotifyCollectionChangedAction Action {
get
;
}
public
IList NewItems {
get
;
}
public
IList OldItems {
get
;
}
public
int
NewStartingIndex {
get
;
}
public
int
OldStartingIndex {
get
;
}
}
public
enum
NotifyCollectionChangedAction
{
Add,
Remove,
Replace,
Move,
Reset,
}
On voit qu'il est bien plus simple d'utiliser une ObservableCollection pour que l'ajout ou la suppression d'élément soit pris en compte.
Par contre pour la modification d'élément déjà dans la liste , il faut implémenter INotifyPropertyChanged.
V-G-5. Convertisseur▲
On peut aussi utiliser un converter (en anglais "converter", en français "convertisseur") qui, entre la source et la cible va effectuer une conversion.
Ici l'exemple montre un binding avec comme cible un TextBlock, le converter enlève simplement les blancs avant et après.
<
StackPanel>
<
StackPanel.
Resources>
<
local:
StringTrimmingConverter x:
Key=
"trimmingConverter"
/>
<
StackPanel.
Resources>
<
TextBlock Text=
"{Binding Path=Text, Converter={StaticResource trimmingConverter}}"
/>
</
StackPanel>
Le converter est écrit en C#, il doit implémenter impérativement Convert et ConvertBack.
using
Microsoft.
VisualBasic;
using
System;
using
System.
Collections;
using
System.
Collections.
Generic;
using
System.
Data;
using
System.
Diagnostics;
using
System.
Globalization;
public
class
StringTrimmingConverter :
IValueConverter
{
public
object
ConvertBack
(
object
value
,
Type targetType,
object
parameter,
CultureInfo culture)
{
return
value
.
ToString
(
).
Trim
(
);
}
public
object
Convert
(
object
value
,
Type targetType,
object
parameter,
CultureInfo culture)
{
return
value
;
}
}
V-G-6. Binding avec tri, filtrage, image▲
Nous allons créer une page contenant une ListBox affichant une liste de personnes.
L'application se nomme binding avancé et la page MainPage.
Créons une classe Personne avec les propriétés Prenom, Nom, Adresse, Sexe, Image et une classe LesMembres contenant une liste de personnes (ce code est dans un module de classe class1.cs).
using
System.
Windows.
Ink;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
using
System.
Collections.
ObjectModel;
namespace
bindingavancé
{
public
class
Personne
{
public
String Prenom {
get
;
set
;
}
public
String Nom {
get
;
set
;
}
public
String Addresse {
get
;
set
;
}
public
String Sexe {
get
;
set
;
}
public
String Image {
get
;
set
;
}
public
Personne
(
String prenom,
String nom,
String addresse,
string
sexe,
string
image)
{
this
.
Prenom =
prenom;
this
.
Nom =
nom;
this
.
Addresse =
addresse;
this
.
Sexe =
sexe;
this
.
Image =
image;
}
}
public
class
LesMembres :
ObservableCollection<
Personne>
{
}
}
Dans le code-behind C# de la page, on instancie Membres de type LesMembres et on ajoute quatre Personne.
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Net;
using
System.
Windows;
using
System.
Windows.
Controls;
using
System.
Windows.
Documents;
using
System.
Windows.
Input;
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Animation;
using
System.
Windows.
Shapes;
using
Microsoft.
Phone.
Controls;
using
System.
Collections.
ObjectModel;
namespace
bindingavancé
{
public
partial
class
MainPage :
PhoneApplicationPage
{
public
LesMembres Membres;
// instancialisation d'un objet Membres
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
Membres=
new
LesMembres
(
);
//initialisation de l'objet Membre
// Ajout de 4 'personne' dans l'objet Membres
Membres.
Add
(
new
Personne
(
"Philippe"
,
"Lasserre"
,
"12 Muloland road"
,
"M"
,
"image1.jpg"
));
Membres.
Add
(
new
Personne
(
"Odile"
,
"Dupont"
,
"31 avenue rien"
,
"F"
,
"image1.jpg"
));
Membres.
Add
(
new
Personne
(
"Pierre"
,
"Durand"
,
"58 rue partout"
,
"M"
,
"image1.jpg"
));
Membres.
Add
(
new
Personne
(
"Agathe"
,
"Dubout"
,
"1 place de l'église"
,
"F"
,
"image1.jpg"
));
//Création d'une liaison entre la collection et la ListBox1
ListBox1.
DataContext =
Membres;
}
}
}
Dans le code XAML de la page, dans la grille principale, on ajoute une ListBox nommée ListBox1.
Dans le ListBox on indique que l'ItemSource est une liaison (dans le code c# ci dessus le DataContext crée une liaison entre cette ListBox1 et Membres).
<ListBox
Name
=
"ListBox1"
ItemsSource
=
"{Binding }"
Margin
=
"36,60,38,353"
Grid.
Row
=
"1"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation
=
"Horizontal"
>
<TextBlock
Text
=
"{Binding Path=Nom}"
/>
<TextBlock
Text
=
", "
/>
<TextBlock
Text
=
"{Binding Path=Prenom}"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
On note qu'on a ajouté un ItemTemplate qui hérite de la liaison et affiche les propriétés Nom et Prenom.
Cela donne :
Nous allons aller plus loin.
Nous avons cette collection de Personne. Comment trier les personnes ? Comment n'afficher dans la ListBox que certaines personnes en fonction de critères ? Comment afficher des images ?
Pour cela ajoutons en haut le MainPage (pour avoir accès à Linq) et aux méthodes d'extension :
using
System.
Linq;
Maintenant on veut trier les personnes par le nom.
Nous allons utiliser OrderBy de Linq.
OrderBy effectue un tri.
Voilà comment modifier le DataContext :
ListBox1.
DataContext =
Membres.
OrderBy
(
e =>
e.
Nom);
Maintenant on veut filtrer les personnes par le Sexe.
On ne veut mettre dans la ListBox que les personnes de sexe « F ». Nous allons utiliser Where de Linq.
Where précise les conditions à appliquer, c'est le filtre.
Il faut ajouter une fonction TestSexe qui retourne true si Sexe="F" et modifier le DataContext qui filtrera en utilisant cette fonction :
namespace
bindingavancé
{
public
partial
class
MainPage :
PhoneApplicationPage
{
public
LesMembres Membres;
// Constructeur
public
MainPage
(
)
{
InitializeComponent
(
);
Membres=
new
LesMembres
(
);
Membres.
Add
(
new
Personne
(
"Philippe"
,
"Lasserre"
,
"12 Muloland road"
,
"M"
,
"image1.jpg"
));
Membres.
Add
(
new
Personne
(
"Odile"
,
"Dupont"
,
"31 avenue rien"
,
"F"
,
"image1.jpg"
));
Membres.
Add
(
new
Personne
(
"Pierre"
,
"Durand"
,
"58 rue partout"
,
"M"
,
"image1.jpg"
));
Membres.
Add
(
new
Personne
(
"Agathe"
,
"Dubout"
,
"1 place de l'église"
,
"F"
,
"image1.jpg"
));
//DataContext utilisant la clause Where
ListBox1.
DataContext =
Membres.
Where
(
e=>
TestSexe
(
e));
}
public
bool
TestSexe
(
Personne p)
//fonction ayant un paramètre de type Personne
//retournant true si Sexe="F"
{
if
(
p.
Sexe==
"F"
)
{
return
true
;
}
else
{
return
false
;
}
}
}
}
Petit bonus
Si l'utilisateur clique sur un élément de la liste, il est possible de voir dans un contrôle Image sous la ListBox une image correspondant à la "Personne" sélectionnée (le nom de l'image est dans la propriété Image) :
<
Image
Stretch=
"None"
Source=
"{Binding ElementName=listBox1,
Path=
SelectedItem.
Image}
" /
Ici on a fait un binding sur le SelectedItem de la ListBox1. Sa propriété Image sert de Source, ce qui affiche l'image.
Maintenant on veut afficher une image différente suivant le sexe.
Nous allons utiliser Select de Linq.
Select précise les éléments à extraire. Il faut ajouter en haut du fichier Class1.cs et du fichier MainPage :
using
System.
Windows.
Media;
using
System.
Windows.
Media.
Imaging;
Il faut créer une classe supplémentaire dans class1.cs.
public
class
PersonneImage
{
public
BitmapImage Image {
get
;
set
;
}
public
String Prenom {
get
;
set
;
}
public
String Nom {
get
;
set
;
}
}
Il faut créer (avec Paint) deux petites images, carrerose.jpg (de couleur rose) et carrebleue.jpg (de couleur bleue) et les mettre dans le répertoire du projet ; il faut ensuite les ajouter au projet (menu projet puis Ajouter un élément existant. Enfin il faut, dans les propriétés des images, indiquer contenu dans option de génération.
Dans MainPage, il faut ajouter une fonction giveImage qui reçoit en paramètre « F » ou « M » et qui retourne un BitmapImage contenant l'image du carré rose ou bleue en fonction du sexe donc :
private
BitmapImage giveImage
(
string
Sexe)
{
if
(
Sexe ==
"F"
)
{
return
new
BitmapImage
(
new
Uri
(
"carrerose.jpg"
,
UriKind.
Relative));
}
else
{
return
new
BitmapImage
(
new
Uri
(
"carrebleue.jpg"
,
UriKind.
Relative));
}
}
ListBox1.
DataContext =
Membres.
Select
(
e =>
new
PersonneImage {
Nom =
e.
Nom,
Prenom =
e.
Prenom,
Image =
giveImage
(
e.
Sexe) }
);
Il faut maintenant modifier le DataContext avec la clause Select qui retourne un objet de type PersonneImage avec le nom le prénom et un BitmapImage en fonction du sexe (pour cela on appelle la fonction giveImage).
ListBox1.
DataContext =
Membres.
Select
(
e =>
new
PersonneImage {
Nom =
e.
Nom,
Prenom =
e.
Prenom,
Image =
giveImage
(
e.
Sexe) }
);
Enfin il faut modifier, dans le code XAML, le DataTemplate de la ListBox en ajoutant un contrôle Image dont la propriété Source sera en liaison avec la propriété Image de l'objet PersonneImage :
<ListBox
Name
=
"ListBox1"
ItemsSource
=
"{Binding }"
Margin
=
"36,60,38,353"
Grid.
Row
=
"1"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation
=
"Horizontal"
>
<Image
Source
=
"{Binding Image}"
Width
=
"20"
Height
=
"20"
/>
<TextBlock
Text
=
"{Binding Path=Nom}"
/>
<TextBlock
Text
=
", "
/>
<TextBlock
Text
=
"{Binding Path=Prenom}"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Génial !
On peut mettre plusieurs clauses :
ListBox1.
DataContext =
Membres.
Select
(...
).
Where (...
).
OrderBy
(...
);
V-G-7. Générateur de liaison▲
Dans la fenêtre Propriétés d'un contrôle, pour créer une liaison avec la propriété d'un contrôle on peut ouvrir la fenêtre du générateur de liaison en cliquant sur le carré à côté du nom de la propriété :
Dans le menu, choisir Appliquer la liaison de données.
Choisir l'objet de liaison désiré.
V-G-8. Erreur dans le binding▲
Si, dans le code XAML, nous orthographions mal le nom d'une propriété dans l'expression du binding (« nome » au lieu de « nom » par exemple), il n'y a pas d'erreur à la compilation mais la liaison ne se fait pas.
Heureusement, il y a un message d'erreur dans la fenêtre de sortie (Ctrl+W, O) :