IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Windows Phone 7.


précédentsommairesuivant

IV. XAML et interface utilisateur

Image non disponible

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 :

 
Sélectionnez
<Button>
</Button>

On peut combiner balise de début et de fin :

 
Sélectionnez
<Button />

Cela suffit à afficher un bouton.

Les balises peuvent être intriquées :

 
Sélectionnez
<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 :

 
Sélectionnez
<!--Commentaire
    ici -->

IV-D. Objet, attribut, propriété

On a vu que pour créer un objet, il suffit d'écrire :

 
Sélectionnez
<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 :

 
Sélectionnez
<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 :

 
Sélectionnez
<TypeName.Property>

Voyons, par exemple, la propriété Background d'un bouton :

 
Sélectionnez
<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 :

 
Sélectionnez
<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.

 
Sélectionnez
<Button Content="Cliquez ici !"/>

Et aussi sous forme d'objet.

 
Sélectionnez
<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 :

 
Sélectionnez
<Button>Cliquez ici !</Button>

De même la propriété Text d'un TextBlock peut s'écrire ainsi :

 
Sélectionnez
<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 :

 
Sélectionnez
<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.

 
Sélectionnez
<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 :

 
Sélectionnez
<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.

 
Sélectionnez
<!--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) :

 
Sélectionnez
<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) :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
<Button Name="Button1" Click="MyButtonClick">

Il faut dans ce cas ensuite créer une fonction MyButtonClick en C#.

 
Sélectionnez
 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.

 
Sélectionnez
<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.

 
Sélectionnez
<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.

 
Sélectionnez
<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).

 
Sélectionnez
<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 :

 
Sélectionnez
<Button Background="{StaticResource FunkyBrush}">Click Me</Button>

Code complet :

 
Sélectionnez
<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 :

 
Sélectionnez
<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 :

 
Sélectionnez
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.

 
Sélectionnez
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 :

 
Sélectionnez
<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.

 
Sélectionnez
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 :

 
Sélectionnez
<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 :

 
Sélectionnez
xmlns:mycustom="clr-namespace:CustomClasses;assembly=Custom"

On remarque après le terme assembly il y a « = ».

Puis pour utiliser MyControle.

 
Sélectionnez
<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) :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
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#.

 
Sélectionnez
<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# :

 
Sélectionnez
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

Image non disponible

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) ;

Image non disponible

-l'ajouter à l'aide de code XAML ;

Il faut taper dans la fenêtre XAML :

 
Sélectionnez
<Button>Ok</Button>

Cela ajoute à la page un bouton sur lequel est affiché Ok.

-l'ajouter dans le code C# :

 
Sélectionnez
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.

Image non disponible

-à l'aide de code XAML (ajout d'attribut simple) :

 
Sélectionnez
<Button Background="Blue" >Ok</Button>

ou de manière plus complexe par ajout d'une Brush sur le fond :

 
Sélectionnez
<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# :

 
Sélectionnez
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 :

 
Sélectionnez
<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 :

 
Sélectionnez
 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 :

 
Sélectionnez
<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.

 
Sélectionnez
<Button Click="OnClick">Ok</Button>

Dans ce cas, en double-cliquant sur le bouton, la routine suivante aurait été créée :

 
Sélectionnez
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 ».

Image non disponible

Cela crée dans le code-behind la fonction évènement :

 
Sélectionnez
  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 :

 
Sélectionnez
  <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 :

 
Sélectionnez
 //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 :

 
Sélectionnez
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 :

 
Sélectionnez
<Button Click="Button_Click>Ok</Button>

Le Click exécute :

 
Sélectionnez
 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 :

 
Sélectionnez
 <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.

 
Sélectionnez
 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.

Image non disponible

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 :

 
Sélectionnez
<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#.

 
Sélectionnez
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.

Image non disponible

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).

 
Sélectionnez
<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.

 
Sélectionnez
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# ?

 
Sélectionnez
 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# :

 
Sélectionnez
MyButton.Content = "Ok";

affiche le texte Ok dans le bouton.

En XAML :

 
Sélectionnez
<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 :

 
Sélectionnez
<Button Content="Ok"/>

ou :

 
Sélectionnez
<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.

 
Sélectionnez
 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 :

 
Sélectionnez
<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 :

 
Sélectionnez
// 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 :

Image non disponible

Cela affiche le code XAML suivant :

 
Sélectionnez
 <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).

 
Sélectionnez
 <Button Name="button1" Content="Button" Height="80"  Width="150" />

En C# :

 
Sélectionnez
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 :

 
Sélectionnez
 <Button Name="button1" Content="Button" Height="4cm"  Width="1cm" />

Largeur et hauteur du bouton en centimètres.

 
Sélectionnez
 <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 :

Image non disponible

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.

 
Sélectionnez
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.

Image non disponible

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 :

 
Sélectionnez
 <!--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 :

Image non disponible
  • 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.

 
Sélectionnez
<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# :

 
Sélectionnez
            //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 :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

Image non disponible

En XAML :

 
Sélectionnez
 <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.)

 
Sélectionnez
<Button Visibility="Collapsed" Content="Calcul" Height="69" Margin="32,63,0,0"

En C# :

 
Sélectionnez
BtCalculer.Visibility = Visibility.Visible;
BtCalculer2.Visibility = Visibility.Collapsed;

On peut ajouter l'attribut Opacity dont la valeur va de 0 à 1.

 
Sélectionnez
<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.

 
Sélectionnez
 <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 :

Image non disponible

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…

Image non disponible

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").

 
Sélectionnez
<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 :

Image non disponible

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.

 
Sélectionnez
<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>
Image non disponible


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.

 
Sélectionnez
<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/>
Image non disponible

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.

 
Sélectionnez
<Button Content="Click"  RenderTransformOrigin="0.5, 0.5">
                <Button.RenderTransform >
                    <ScaleTransform ScaleX="-1" ScaleY="1" />
                </Button.RenderTransform>
</Button>
Image non disponible

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.

 
Sélectionnez
<TextBlock Text="Texte vertical" FontSize="32" Foreground="Teal" Margin="74,3,183,547">
  <TextBlock.RenderTransform>
    <RotateTransform Angle="90" />
  </TextBlock.RenderTransform>
</TextBlock>
Image non disponible

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é.

 
Sélectionnez
<TextBlock Text="Texte italique" FontSize="32" Foreground="Teal" Margin="74,3,183,547">
  <TextBlock.RenderTransform>
     <SkewTransform AngleX="45" />
  </TextBlock.RenderTransform>
</TextBlock>
Image non disponible

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.

 
Sélectionnez
<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>
 
Sélectionnez
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.

 
Sélectionnez
<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>
Image non disponible

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 :

 
Sélectionnez
<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.

 
Sélectionnez
<TextBlock Text="15:03" FontFamily="Georgia"/>
Image non disponible

Quand on tape FontFamilly=" la liste des polices disponibles s'affiche.

Image non disponible

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 :

 
Sélectionnez
<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) :

 
Sélectionnez
<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 :

 
Sélectionnez
<Button Background="Red"/>

En fait il y a une conversion implicite du texte "Red" en System.Windows.Media.Brushes.Red.

On aurait pu écrire :

 
Sélectionnez
<Button>A Button
  <Button.Background>
    <SolidColorBrush Color="Red" />
  </Button.Background>
</Button>

ou en donnant le code couleur du rouge en hexadécimal :

 
Sélectionnez
<Button>A Button
  <Button.Background>
    <SolidColorBrush Color="#FFFF0000" />
  </Button.Background>
</Button>
Image non disponible

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 :

Image non disponible


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 :

 
Sélectionnez
<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 :

 
Sélectionnez
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.

 
Sélectionnez
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.

 
Sélectionnez
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 :

Image non disponible

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 :

 
Sélectionnez
< 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é :

Image non disponible

Dans le menu, choisir Appliquer la ressource.

Image non disponible

Double cliquer sur la ressource désirée.

Pour remplir une surface d'une forme (Rectangle, par exemple) avec une couleur on utilise Fill.

 
Sélectionnez
<Rectangle
    Width="200"
    Height="100"
    Fill="Blue"
    Stroke="Black"
    StrokeThickness="4" />

En C#

 
Sélectionnez
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.

 
Sélectionnez
<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.

 
Sélectionnez
<Rectangle Width="50" Height="50" Fill="Blue" />

En C# c'est plus compliqué :

 
Sélectionnez
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 :

 
Sélectionnez
<Button Background="Red"/>

ou en hexadécimal :

 
Sélectionnez
<Button Background="#FF0000FF"/>

ou en C# :

 
Sélectionnez
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.

 
Sélectionnez
 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 :

 
Sélectionnez
<Rectangle Width="75" Height="75">  
<Rectangle.Fill> 
    <SolidColorBrush Color="Red" /> 
</Rectangle.Fill> 
</Rectangle>
Image non disponible

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 :

 
Sélectionnez
<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>
Image non disponible

En C# :

 
Sélectionnez
  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);
Image non disponible

La syntaxe suivante pour le GradientStop, bien que correcte n'est pas acceptée :

 
Sélectionnez
 myBrush.GradientStops.Add(new GradientStop(Colors.White, 0.5d));

Exemple 2 : plein de couleurs dans un rectangle.

 
Sélectionnez
<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>
Image non disponible

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 :

 
Sélectionnez
<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>
Image non disponible

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é.

 
Sélectionnez
<Button Content="Linear" >
        <Button.Foreground>
                    <LinearGradientBrush>
                        <GradientStop Offset="0" Color="Red" />
                        <GradientStop Offset="1" Color="Yellow"/>
                    </LinearGradientBrush>
        </Button.Foreground>
</Button>
Image non disponible

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

 
Sélectionnez
<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

 
Sélectionnez
<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 :

 
Sélectionnez
<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 :

 
Sélectionnez
<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.

 
Sélectionnez
 <RadialGradientBrush 
          Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5"/>

Un exemple complet en C# :

 
Sélectionnez
            //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);
Image non disponible

Autre exemple, une Sphère : on remplit une ellipse avec un RadialGradientBrush :

 
Sélectionnez
<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>
Image non disponible

Dernier exemple : utiliser une RadialGradientBrush comme Foreground d'un texte.

 
Sélectionnez
<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>
Image non disponible

V-E-5. ImageBrush

On peut mettre une image dans un rectangle, par exemple.

En XAML :

 
Sélectionnez
<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 :

 
Sélectionnez
<TextBlock FontFamily="Verdana" FontSize="88"
 FontStyle="Italic" FontWeight="Bold">
    Philippe
  <TextBlock.Foreground>
    <ImageBrush ImageSource="C:\P1020907.jpg"/>
  </TextBlock.Foreground>
</TextBlock>
Image non disponible

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.

 
Sélectionnez
<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>
Image non disponible

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.

 
Sélectionnez
<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>
Image non disponible

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.

 
Sélectionnez
<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.

 
Sélectionnez
<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.

 
Sélectionnez
<Button Background="{StaticResource MyBrush}"/>


<Ellipse Fill="{StaticResource MyBrush}"/>

En C# :

 
Sélectionnez
//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.

 
Sélectionnez
<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.

 
Sélectionnez
<Grid.Resources>
  <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</Grid.Resources>

En C# comment utiliser la ressource ?

 
Sélectionnez
//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.

 
Sélectionnez
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 ?

 
Sélectionnez
<TextBlock x:Name="ApplicationTitle" Text="{StaticResource LeTitre}"/>

En C# :

 
Sélectionnez
TextBlock1.Text = (string) this.Resources["LeTitre"];

-Dans le fichier Application.

C'est le fichier App.xaml.

 
Sélectionnez
  <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 :

 
Sélectionnez
 <TextBlock x:Name="PageTitle"  Foreground="{ StaticResource MyBrush}" 
 Text="nom de la page" />

Comment récupérer la brush en C# ?

 
Sélectionnez
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.

 
Sélectionnez
  <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.

 
Sélectionnez
  <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.

 
Sélectionnez
<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.

 
Sélectionnez
 <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 :

 
Sélectionnez
<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.

 
Sélectionnez
 <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.

 
Sélectionnez
 <!--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 :

 
Sélectionnez
 <Button Style="{StaticResource MyStyle}" Content="Ok" Height="153" 
 HorizontalAlignment="Left" Margin="56,48,0,0" Name="button1" VerticalAlignment="Top" Width="337" />
Image non disponible

En C# pour appliquer le style à un contrôle :

 
Sélectionnez
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.

 
Sélectionnez
<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 :

 
Sélectionnez
<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 ?

Image non disponible

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.

 
Sélectionnez
 <!--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 :

 
Sélectionnez
<Button Style="{StaticResource MyButton}" Content="Button" Height="66" Name="button1"  Width="289" />

Image non disponible

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.

 
Sélectionnez
<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>
Image non disponible

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.

 
Sélectionnez
<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.

 
Sélectionnez
<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é.

Image non disponible

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.

 
Sélectionnez
<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# :

 
Sélectionnez
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

 
Sélectionnez
<Application.Resources>
<ImageSource x:Key= "someImage">xvide.jpg</ImageSource>
</Application.Resources>

-Mettre la ressource dans une image

 
Sélectionnez
<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 :

Image non disponible

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 :

 
Sélectionnez
<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 :

 
Sélectionnez
<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.

 
Sélectionnez
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 :

 
Sélectionnez
<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 :

 
Sélectionnez
<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 :

 
Sélectionnez
<!--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).

Image non disponible

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é.

Image non disponible

Dans le menu, choisir Appliquer la ressource.

Image non disponible

Double cliquer sur la ressource désirée.

Typographie :

il est conseillé d'utiliser la typographie recommandée.

Image non disponible

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.

Image non disponible

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 :

 
Sélectionnez
<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 :

 
Sélectionnez
<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# :

 
Sélectionnez
DockPanel1.DataContext=  MyCollection;

Dans ce cas en XAML, il faut simplement indiquer qu'il y a un Binding avec l'expression "{Binding}" :

 
Sélectionnez
<ListBox Margin="14,16,8,2" Name="ListBox1"  ItemsSource="{Binding }" />

Il est possible d'indiquer comme DataContext la classe elle-même.

 
Sélectionnez
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 :

 
Sélectionnez
<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 :

 
Sélectionnez
 <ListBox Name="ListBox1"  ItemsSource="{Binding Source={StaticResource ListData}}" />

Mode indique le mode de mise a jour (OneWay, TwoWay, OneTime).

 
Sélectionnez
<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 :

 
Sélectionnez
<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.

 
Sélectionnez
<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) :

 
Sélectionnez
<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 :

 
Sélectionnez
<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 :

 
Sélectionnez
<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>
Image non disponible

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 :

 
Sélectionnez
<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" />
Image non disponible

À 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#).

 
Sélectionnez
<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.

 
Sélectionnez
 <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).

 
Sélectionnez
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();
     }
     }
Image non disponible

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.

 
Sélectionnez
<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# :

 
Sélectionnez
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 :

 
Sélectionnez
 <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.

 
Sélectionnez
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.

 
Sélectionnez
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.

 
Sélectionnez
<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 :

Image non disponible

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.

 
Sélectionnez
<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>
Image non disponible

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

 
Sélectionnez
using System.Collections.ObjectModel;

Ensuite on utilise l'ObservableCollection :

 
Sélectionnez
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.

 
Sélectionnez
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é.)

 
Sélectionnez
// 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.

Image non disponible

Ici l'exemple montre un binding avec comme cible un TextBlock, le converter enlève simplement les blancs avant et après.

 
Sélectionnez
<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.

 
Sélectionnez
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).

 
Sélectionnez
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.

 
Sélectionnez
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).

 
Sélectionnez
<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 :

Image non disponible


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 :

 
Sélectionnez
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 :

 
Sélectionnez
ListBox1.DataContext = Membres.OrderBy(e => e.Nom);
Image non disponible




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 :

 
Sélectionnez
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;
        }
}   
    }
       

}
Image non disponible

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) :

 
Sélectionnez
<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 :

 
Sélectionnez
using System.Windows.Media;
using System.Windows.Media.Imaging;


Il faut créer une classe supplémentaire dans class1.cs.

 
Sélectionnez
  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 :

 
Sélectionnez
 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).

 
Sélectionnez
 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 :

 
Sélectionnez
<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>
Image non disponible

Génial !

On peut mettre plusieurs clauses :

 
Sélectionnez
 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é :

Image non disponible

Dans le menu, choisir Appliquer la liaison de données.

Image non disponible

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) :

Image non disponible

précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2011 Lasserre Philippe. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.