Cours VB.NETDate de mise à jour : 05/12/2010
XI-E. Grands Principes
XI-E-1. La Classe 'Controls'
XI-E-1-a. Créer un contrôle
XI-E-1-b. Particularités des contrôles WPF
XI-E-1-c. Évènements
XI-E-1-d. Principaux contrôles
XI-E-2. Les applications WPF
XI-E-3. Les formulaires WPF
XI-E-4. Positionnement, dimensions des contrôles
XI-E-4-a. Hiérarchie des contrôles
XI-E-4-b. Position et dimensions d'un contrôle
XI-E-5. Aspect des contrôles
XI-E-5-a. Propriétés des contrôles
XI-E-5-b. Contrôle contenant des contrôles
XI-E-5-c. Aspect visuel des contrôles : Template visuel
XI-E-5-d. Modification du Bitmap d'un contrôle
XI-E-6. Remplissage de surface
XI-E-6-a. SolidColorBrush
XI-E-6-b. LinearGradientBrush
XI-E-6-c. RadialGradientBrush
XI-E-6-d. ImageBrush
XI-E-6-e. Autre
XI-E-7. Ressources
XI-E-7-a. Ressources 'internes'
XI-E-7-a-i. Ressources 'simples'
XI-E-7-a-ii. Les Styles
XI-E-7-a-iii. Les modèles de Contrôle : Control Template'
XI-E-7-a-iv. Les modèles de Données : Data Template
XI-E-7-b. Les fichiers de ressources externes
XI-E-8. Les liaisons de données ou Binding
XI-E-8-a. Principes du Binding
XI-E-8-b. Liaison entre contrôles
XI-E-8-c. Liaison Collection-ListBox et Tableau-ListBox
XI-E-8-d. Liaison avec une collection d'objets
XI-E-8-e. Liaison avec une base de données
XI-E-9. Les Triggers
XI-E. Grands Principes
Ou trouvez de la documentation?
(http://msdn.microsoft.com/fr-fr/library/ms754130.aspx)
XI-E-1. La Classe 'Controls'
Les contrôles WPF permettent de créer une interface utilisateur WPF.
Les contrôles WPF font partie de l'espace de nom System.Windows.Controls (ne pas confondre avec
System.Windows.Forms.Control des contrôles Windows Forms habituels).
XI-E-1-a. Créer un contrôle
Pour mettre un contrôle dans un formulaire (Window), un bouton par exemple, il y à 3 manières:
- En mode 'Design', aller le chercher dans la boite à outils et le déposer sur le formulaire.
-Le créer à l'aide de code Xaml:
Il faut taper dans la fenêtre XAML:
Cela crée un bouton sur lequel est affiché 'Ok'. Il apparaît dans le formulaire.
-Le créer dans le code VB:
Dim MyButton As New Button
Grid. Children . Add (myButton)
|
On note qu'après avoir déclaré le bouton avec Dim, on l'a ajouté aux enfants de la Grid (dans un formulaire, il y a par défaut une Grid).
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.
-A l'aide de code Xaml (ajout d'attribut 'simple'):
< Button Background = " Blue " > Ok< / Button >
|
ou de manière plus complexe ajout d'une brush sur le fond:
< Button FontSize = " 14 " FontWeight = " Bold " >
< Button . Background >
< LinearGradientBrush StartPoint = " 0,0.5 "
EndPoint = " 1,0.5 " >
< GradientStop Color = " Green " Offset = " 0.0 " / >
< GradientStop Color = " White " Offset = " 0.9 " / >
< / LinearGradientBrush >
< / Button . Background >
Ok
< / Button >
|
-A l'aide de code VB:
MyButton. Background = New SolidColorBrush (Colors. Blue )
|
(On crée une nouvelle instance de SolidColorBrush de couleur bleu.)
En résumé:
On voit dessous:
- La partie 'Design' où on peut 'dessiner' l'interface en haut.
- La fenêtre de code XAML en bas.
- La liste des propriétés en bas à droite.
En double-cliquant sur window1.xaml.vb dans l'explorateur de solution en haut à droite on voit apparaître le code VB.
Ajoutons notre bouton.
XI-E-1-b. Particularités des contrôles WPF
Propriété de dépendances:
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
permet 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é attachée:
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 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 WPF:
Un contrôle WPF 'complexe' est composé de contrôles plus simples: Alors que dans les WindowsForms un ListBox est un ListBox, en WPF un ListBox est en fait Un StackPanel (pouvant contenir des TextBlocks, si on affiche du texte, mais aussi des CheckBox ou des Images..) plus un ScrollView. Cela explique que si on applique un style aux TextBlocks en général, ce style sera appliqué à chaque ligne du ListBox.
Enfin si un contrôle WindowsForms avait une propriété Text, le contrôles WPF équivalent a lui une propriété 'Content' qui peut contenir un Objet (du texte oui mais aussi une Grid, une Image, un StackPanel...), c'est extrèmement puissant.
XI-E-1-c. Évènements
Créons un bouton avec le code XAML suivant:
Si je double-clique sur ce bouton dans le Designer, je me retrouve dans le code behind et VB a crée automatiquement la Sub évènement suivante:
Private Sub Button1_Click (ByVal sender As System. Object , ByVal e As System. Windows . RoutedEventArgs ) _
Handles Button1. Click
End Sub
|
Quand l'utilisateur du logiciel cliquera sur le bouton, c'est cette Sub qui sera exécutée.
Mais si j'ajoute l'attribut Click="OnClick5", cela donne le code XAML suivant.
< Button Click = " OnClick5 " > Ok< / Button >
|
Dans ce cas si je double-clique sur le bouton, la routine suivante est créée:
Sub OnClick5 (ByVal sender As Object, ByVal e As RoutedEventArgs)
End Sub
|
sender est l'objet qui a déclenché l'évènement.
e contient les arguments de l'évènement, remarquons que e est de type RoutedEventArgs (et pas EventArgs comme dans les Windows Forms).
Evènement routé:
Quand il survient un évènement sur un contrôle, un Click sur un bouton par exemple, cet évènement pourra
être traité au niveau du bouton, mais aussi par exemple dans un parent comme le Panel qui contient
le bouton.
Dans les routines évènement, l'argument e est de type RoutedEventArgs et non EventArg.
XI-E-1-d. Principaux contrôles
Contrôles
-
Button: Bouton.
-
CheckBox: Case à cocher.
-
ComboBox, contenant des ComboItem.
-
Image.
-
ListBox contenant des ListItem.
-
Menu contenant des MenuItem.
-
RadioButton: bouton radio.
-
ScrollBar: barre de défilement.
-
Table contenant: TableCell, TableRow, TableColumn.
-
TextBox: Champ de texte à taper.
-
RichTextBox: Champ de texte enrichi.
-
Text ou TextBlock: Texte.
-
VerticalSlider: ascenceur.
-
Window: fenêtre.
Positionnement
-
Canvas: Positionnement absolu.
-
DockPanel: Conteneur qui positionne selon les points cardinaux (ancrage).
-
FlowPanel: Conteneur pour agencement d'autres éléments.
-
Grid: Conteneur qui se subdivise en lignes et colonnes.
-
Équivalents aux éléments HTML
Equivalent Html
-
Block:
-
Bold: gras
-
Heading: H1, H2, etc...
-
HyperLink:
-
Image:
-
Italic:
-
LineBreak:
-
List:
-
Paragraph:
Graphisme
-
Canvas: une zone de dessin.
-
Ellipse:
-
Line:
-
Path: , série de lignes connectées.
-
Polygon:
-
Polyline:, série de lignes droites connectées.
-
Rectangle:
-
TransformDecorator: transform, rotation etc...
Nouveaux controles VB 2010
-
DataGrid.
-
Calendar:
-
DataPicker:
XI-E-2. Les applications WPF
Créons une application WPF nommée MonApplicationWPF dans VB. Un formulaire Window1 est crée (on y met
une grid et un bouton). On ajoute au projet un module et une Classe.
L'explorateur de solution en haut à droite permet de voir de quoi se compose le projet.
MyProjet donne accès aux propriétés du projet.
Application.xaml est le fichier d'application:
< Application x : Class = " Application "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml "
StartupUri = " Window1.xaml " >
< Application . Resources >
< / Application . Resources >
< / Application >
|
On peut mettre dans ce fichier les ressources de l'application qui pourront être utilisées dans toute
l'application.
Si l'application a été créée dans Blend, on peut voir dans vb que cela diffère un peu:
< Application
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml "
x : Class = " App "
StartupUri = " Window1.xaml " >
< Application . Resources >
< / Application . Resources >
< / Application >
|
Il y a aussi: AssemblyInfo.vb
Class1.vb et Module1.vb qui correspondent à la Classe et au module.
Window1.xaml contient le code xaml décrivant le formulaire Window1 avec ici, dans le formulaire,
une grid contenant un bouton.
< Window
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml "
x : Class = " Window1 "
x : Name = " Window "
Title = " Window1 "
Width = " 640 " Height = " 480 " >
< Grid >
< Button Name = " Button2 " > Button< / Button >
< / Grid >
< / Window >
|
Dans un formulaire, il y a toujours en conteneur parent, ici c'est Window.
x:Class="Window1" indique qu'il s'agit d'une classe partielle "Window1"
Window1.xaml.vb contient le code vb et les procédures en VB:
Partial Class Window1
Private Sub Button2_Click (ByVal sender As System. Object , ByVal e As System. Windows . RoutedEventArgs )
_Handles Button2. Click
End Sub
End Class
|
Si l'application a été créée dans Blend, on peut voir dans vb que cela diffère un peu:
Imports System
Imports System. IO
Imports System. Net
Imports System. Windows
Imports System. Windows . Controls
Imports System. Windows . Data
Imports System. Windows . Media
Imports System. Windows . Media . Animation
Imports System. Windows . Navigation
Imports System. Xml
Public Class Window1
Public Sub New ()
MyBase. New ()
Me. InitializeComponent ()
End Sub
End Class
|
XI-E-3. Les formulaires WPF
Dans le designer VB, on a un projet avec une fenêtre 'Window1', on va ajouter une fenêtre nommée 'Window2'
au projet en mode designer:
Menu 'Projet', puis 'Ajouter une fenêtre'.
Cliquer sur Fenêtre WPF puis sur le bouton 'Ajouter'.
On a crée une seconde fenêtre.
On peut modifier ses dimensions en appuyant le bouton gauche de la souris sur le coin inférieur droit
de la fenêtre puis en déplaçant la souris.
On peut ajouter à ce formulaire des contrôles que l'on va chercher dans la boite à outils à gauche.
Remarque: on peut agrandir ou réduire le dessin de la fenêtre avec un curseur de zoom qui est à gauche;
cela prouve bien que le dessin est vectoriel.
On peut modifier ses propriétés dans la fenêtre de propriétés en bas à droite.
Dans le code vb de la première fenêtre, comment ouvrir la seconde fenêtre Window2?
Window2 qui vient d'être dessiné est en fait la 'Classe Window2'.
Il faut donc instancier un objet formulaire (ou fenêtre) que nous nommerons 'w' à partir de la classe
Window2.
Ensuite il faut l'afficher:
ou
pour ouvrir la fenêtre Window2 en modale.
Code XAML de ce formulaire, propriétés.
Pour créer un formulaire on utilise la Classe System.Windows.Window.
Code XAML correspondant à une fenêtre vide:
< Window x : Class = " WpfApplication3.Window1 "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml " >
|
La balise 'Window' indique que c'est un formulaire.
x:Class="WpfApplication3.Window1" indique une classe partielle nommée Window1 dans l'espace de nom WpfApplication3
(qui est le nom de l'application). Cela permet au formulaire d'être 'relié' au code VB behind qui
est dans une classe partielle de même nom.
Puis on a les 2 namespaces principaux (le premier pour les composants de base et le second pour les propriétés
et extension).
On peut ajouter des propriétés directement dans le code XAML:
< Window x : Class = " WpfApplication3.Window1 "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml "
Height = " 355 " Width = " 300 "
Title = " Window "
ResizeMode = " NoResize "
SizeToContent = " Height "
WindowStartupLocation = " CenterScreen "
WindowStyle = " None "
ShowInTaskbar = " False "
Foreground = " Navy "
AllowsTransparency = " True "
Opacity = " 0.75 " >
|
Détaillons:
Height="355" Width="300"
Indiquent les dimensions de la fenêtre.
Title="Window"
Indique le titre dans la barre de titre.
Foreground="Navy"
Indique la couleur du fond.
ResizeMode= "NoResize"
"NoResize" indique: pas de bouton minimize ni maximize ni bord modifiable. "CanResize" c'est l'inverse:bouton
minimize et maximize bord modifiable. "CanMinimize" n'affiche que le bouton minimize. "CanResizeWithGrip"
ajoute un petit indicateur de redimensionnement (un grip) qui apparaît en bas à droite de la fenêtre.
SizeToContent="Height"
Si "Manual", WPF utilise les propriétés "Height" et "Width" pour fixer la taille de la fenêtre. Si "Height"
WPF calculera la hauteur en fonction du contenu. Si "Width" WPF calculera la largeur en fonction
du contenu.
WindowStartupLocation="CenterScreen"
Position de départ de la fenêtre."CenterScreen" positionne au centre de l'écran.
Si "Manual" c'est les propriétés Left et Top qui sont utilisées pour positionner la fenêtre. Si "CenterOwner"
c'est centré sur la fenêtre appelante.
WindowStyle="None"
"SingleBorderWindow" correspond à une bordure simple. "ThreeDBorderWindow" bordure est légèrement plus
accentuée. "ToolWindow" bordure utilisée pour les boîtes à outils (petite bordure avec une petite
barre de titre). "None" pas de bordure et pas de barre de titre. Si vous avez autorisé le redimensionnement
(par "ResizeMode") il y aura quand même une petite bordure pour permettre le redimensionnement qui
reste possible.
Topmost="None"
Si "True" la fenêtre sera toujours au-dessus de toutes les autres, (jamais masquée)
ShowInTaskbar="False"
Si "False" nous désactivons la fenêtre dans la barre des tâches. Celle-ci ne sera donc pas visible à
côté du menu démarrer.
AllowsTransparency="True"
Opacity="0.75"
Pour la transparence des fenêtres. Pour rendre totalement transparentes vos fenêtres vous devez activer
la propriété "AllowsTransparency" à True et "WindowStyle" à None. Une fois la transparence activée,
vous verrez que la propriété "Opacity" modifie la transparence: un nombre entre 0 (transparent)
et 1 (totalement opaque).
Code VB, propriétés, évènements.
Comment ouvrir par code VB une seconde fenêtre qui a été crée en mode design (class Window2)?
Dim w As New Window2
w. Show ()
|
ou
pour ouvrir une fenêtre modale.
La fenêtre qui s'ouvre est activée en même temps.
Si on ajoute
avant d'utiliser Show, la fenêtre qui s'ouvre n'est pas activée.
Si on voulait créer une fenêtre de toute pièce avec du code VB( sans l'avoir dessinée en mode design
avant):
Dim w As New Window
w. Show ()
|
On rappelle que les propriétés de la fenêtre seront accessibles là où la fenêtre est visible en fonction
de sa portée.
Si la fenêtre est en arrière plan, pour l'activer et la mettre au premier plan:
Une fenêtre activée reçoit les évènements de la souris et du clavier.
Pour la fermer:
On peut modifier toutes les propriétés de la fenêtre par code VB:
Me. Background = New SolidColorBrush (Colors. Red )
|
' couleur du fond en rouge
'texte de la barre de titre
'hauteur de la fenêtre.
On remarque que dans la fenêtre on peut utiliser Me pour désigner celle-ci.
On peut utiliser BorderThickness pour l'épaisseur des bords; MaxHeight, MinHeight, MaxWidth,
MinWidth pour limiter la réduction et l'agrandissement de la fenêtre par l'utilisateur.
SizeToContent permet d'ajuster la fenêtre à la taille de son contenu, il peut prendre la valeur:
-
Manual (pas d'effet)
-
Width ajustement de la largeur au contenu
-
Height ajustement de la hauteur au contenu
-
WidthAndHeight ajustement de la largeur et de la hauteur au contenu
Quand la fenêtre s'ouvre surviennent les évènements suivants:
Initialized Survient avant le chargement. A utiliser si on n'a pas besoin d'avoir accès aux éléments
visuels qui ne sont par encore en place.
Private Sub Window1_Initialized (ByVal sender As Object, ByVal e As System. EventArgs ) Handles Me. Initialized
End Sub
|
Loaded Fenêtre chargée mais non visible, permet de modifier l'IU, les boutons..
Private Sub Window1_Loaded (ByVal sender As System. Object , ByVal e As System. Windows . RoutedEventArgs )
_Handles MyBase. Loaded
End Sub
|
ContentRendered
SourceInitialised
= A CE MOMENT LE FORMULAIRE EST AFFICHE=
Activated se produit lorsque la fenêtre est activée (l'utilisateur à cliqué dessus ou on a utilisé
la méthode Window1.Activate)
Desactivated lorsque la fenêtre passe en arrière plan.
Quand une fenêtre est fermée les évènements suivants se déclenchent:
Closing avant la fermeture, on peut annuler la fermeture de la fenêtre en donnant à l'argument
e de type CancelEventArgs la valeur True.
Private Sub Window1_Closing (ByVal sender As Object, ByVal e As System. ComponentModel . CancelEventArgs )
_Handles Window1. Closing
e. Cancel = True
End Sub
|
Closed quand la fenêtre se ferme.
Unloaded : déchargement de la fenêtre.
(Microsoft indique que Activated survient avant loaded et ContentRendered dans une ouverture initiale
de fenêtre, mais je n'ai pas vu cela!!!)
GotFocus et LostFocus surviennent lors de la prise ou de la perte du focus, il faut éviter
de les utiliser car en pratique le fonctionnement est curieux, Activated et Deactivated semblent
plus judicieux.
SizeChanged lorsque l'utilisateur modifie la taille de la fenêtre.
Remarque:
VB met automatiquement dans les formulaires une 'Grid' qui servira de conteneur pour tous les autres
contrôles.
XI-E-4. Positionnement, dimensions des contrôles
Le positionnement des contrôles est fondamental en WPF, il faut bien le comprendre.
Comparaisons WindowsForms-WPF.
En Windows Forms on pouvait mettre autant de contrôles que l'on voulait dans une form (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 re-agencement,
il fallait tout gérer soit même.
En WPF 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. WPF 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 un formulaire en fonction de son contenu, de son conteneur de disposition parent et de la dimension
d'écran disponible. Cela facilite la localisation en ajustant automatiquement
la taille et la position des éléments lorsque la longueur des chaînes qu'ils contiennent
est modifiée.
La disposition WPF est basée sur les grilles (Grid), l'empilement (Stack) et l'ancrage (Dock).
XI-E-4-a. Hiérarchie des contrôles
Contrôles conteneurs et non conteneurs.
Certains contrôles (dit 'non conteneur') ne peuvent contenir qu'un contrôle. Essayer de mettre 2 boutons
dans une fenêtre vide avec le designer ou une image et un texte dans un bouton: impossible!!
Certains contrôles (dit 'conteneur') 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.
Une fenêtre (Window) ou un bouton (dit 'non conteneur'), ont une propriété nommé 'Content' qui ne peut
donc contenir d'un seul élément.
Pour un bouton par exemple, en VB:
affiche le texte 'Ok' dans le bouton.
En XAML:
Content peut contenir un texte (comme en Windows Forms)mais pas seulement; en fait il peut contenir un
Objet (un seul): du texte comme dans l'exemple précédent mais aussi un objet conteneur comme une
Grid, un Canvas, un StackPanel, un DockPanel.. qui lui peut contenir plusieurs contrôles. Cela
revient à mettre autant de contrôles que l'on veut dans un contrôle.
C'est pour cela que quand on crée une fenêtre en VB, il est automatiquement ajouté une grid.
Les conteneurs comme une Grid par exemple, ont une propriété nommé Children qui permet d'ajouter plusieurs
contrôles.
En VB
MyGrid. Children . Add (Button1)
|
A titre d'exemple, on peut mettre une Grid dans un bouton et mettre dans les cellules de cette Grid 2
textes, une image, une video..
Voyons maintenant un exemple en xaml d'un conteneur, un StackPanel contenant un TextBlock et 3 boutons:
< StackPanel >
< TextBlock > Faire un choix:< / TextBlock >
< Button > Option 1< / Button >
< Button > Option 2< / Button >
< Button > Option 3< / Button >
< / StackPanel >
|
Modèle de contenu.
Pour être complet, pour tous les objets visuels WPF, il y a 4 modèles de contenu:
-ContentControl
Sa propriété Content contient un objet. Exemple Window, Button...
-HeaderContentControl
Sa propriété Content contient un objet. Sa propriété Header fournit un titre au contrôle. Exemple 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.
Exemple MenuItem, ToolBar, TreeViewItem.
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 verra qu'un évènement (un click 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).
Remarquons aussi qu'un contrôle WPF 'complexe' est composé de contrôles plus simples: Alors que dans les WindowsForms un ListBox est un ListBox, en WPF un ListBox est en fait Un StackPanel (pouvant contenir des TextBlocks, si on affiche du texte, mais aussi des CheckBox ou des Images..) plus un ScrollView. Cela explique que si on applique un style aux TextBlocks en général, ce style sera appliqué à chaque ligne du ListBox. Là aussi, un contrôle complexe, on peut être vu comme un graphe hiérarchisé de contrôles simples.
XI-E-4-b. Position et dimensions d'un contrôle
En WPF, les dimensions sont en "device independent pixels".
Un pixel indépendant du périphérique ou DIP (Device Independent Pixel) mesure 1/96 d'un pouce et est indépendant de la résolution du periphérique d'affichage ou d'impression.
Noter que les valeurs des dimensions et coordonnées, en WPF, sont des 'Double' (Nombre réel en virgule flottante double précision).
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. Dans une grille par exemple, dans chaque cellule on pourra positionner un contrôle comme indiqué ci-dessous.
Width="50" Height="30" indiquent les dimensions d'un objet (largeur, hauteur).
On peut aussi leur donner la valeur "Auto" afin que le contrôle occupe la totalité de la largeur ou de la hauteur restante.
Exemple 1:
Mettre un bouton dans un conteneur (la cellule d'une grille par exemple), on le positionne dans le Designer avec la souris.
Cela donne par exemple dans le code XAML:
< Button Height= " 39 " Margin= " 30,64,128,0 " Name= " Button1 " VerticalAlignment= " Top " Click= " OnClick5 " > Ok< / Button>
|
Voyons le détail de cette ligne:
crée un bouton.
Name="Button1" indique le nom du contrôle dans le designer.
On peut indiquer sur quoi le contrôle est aligné:
VerticalAlignment="Top" bouton aligné sur le bord supérieur. Si la fenêtre est redimentionnée, le bouton sera toujours aligné et à égale distance du bord supérieur.
Margin="30,64,128,0" indique les marges gauche, supérieure, droite, inférieure.
Exemple 2:
Si VerticalAlignment="Bottom" voici un exemple de margin.
Les grands principes:
VerticalAlignement et HorizontalAlignment définissent sur quoi est aligné le contrôle
(cela remplace la propriété 'Anchor' des Windows Forms).
Rappelons que si on modifie la taille de la fenêtre, 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 rempli verticalement).
valeur possible pour HorizontalAlignment:Left, Right, Center, Stretch(étire le contrôle et rempli horizontalement)..
Attention, si on renseigne Width et Height Stretch ne fonctionne pas!!
Margin définit les distances au conteneur.
Margin="30,64,128,0" indique les marges gauche, supérieure, droite, inférieure.
Margin="30" indique que toutes les marges sont à 30.
Paddding définit l'épaisseur d'une marge dans le contrôle. (valable dans certains contrôles seulement)
Largeur de bouton et texte:
Si on donne une valeur à Width=50, la largeur est imposée.
Si on specifie les Margin=20,20,20,20 et qu'on laisse Width= Auto, les marges imposent les dimensions du bouton.
Si on specifie les Margin=20,20,0,20 (marge de droite à 0) et qu'on a Width= Auto, le bouton se dimensionne pour afficher le texte qu'il contient.
Priorités des propriétés:
Dans les dimensions horizontales par exemple:
La propriété Width, si celle-ci à une valeur, est utilisée pour la largeur du composant, même si HorizontalAlignment=« Strech ».
Si Width = « Auto » ou rien, WPF regarde la valeur de HorizontalAlignment .
Si HorizontalAlignment= « Strech », la largeur sera tout l'espace disponible.
Sinon, la propriété « Width » étant sur « Auto », la largeur s'adaptera au contenu.
Pour résumer, voici un conteneur avec trois boutons:
Comment bien positionner des boutons?
On met 3 boutons dans un StackPanel (conteneur qui met les boutons les uns au dessus des autres).
On selectionne les 3 boutons (Ctrl+Click sur les 3 boutons), dans la fenêtre de propriété, on met:
Width=Auto
Height=Auto
VerticalAlignment=Strech
HorizontalAlignment=Strech
Cela met les 3 boutons à la même dimension et ils remplissent la totalité du StackPanel:
Et en VB cette fois:
Dim myButton As New Button ()
myButton. Height = " 33 "
myButton. Width = " 55 "
myButton. HorizontalAlignment = Windows. HorizontalAlignment . Stretch
myButton. Content = " Button Stretch "
Grid1. Children . Add (myButton)
|
Autre exemple:
Dim myButton As New Button ()
myButton. Margin = New Thickness (10, 20,0, 20)
myButton. Padding = New Thickness (10)
myButton. HorizontalAlignment = Windows. HorizontalAlignment . Center
GrosBouton. Content = myButton
|
On vient de voir comment sont positionnés les contrôles dans un conteneur.
On verra dans le chapitre sur les contrôles conteneurs les différents conteneurs.
- Les Grid.
- Les Panels
- Les DockPanel.
- Les StackPanel.
- ...
XI-E-5. Aspect des contrôles
Quand on dépose un bouton dans un formulaire, il a un aspect 'standard', on a besoin souvent 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 à tous les contrôles.
XI-E-5-a. 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 prendra l'exemple d'un bouton:
On veut avoir ce beau bouton (bof!).
Avec le designer, on prend un bouton dans la boite à outils, on le pose dans un conteneur (une grid par exemple).
Pour définir les dimensions, la couleur, le texte dans le bouton, on va modifier les propriétés dans la fenêtre de propriétés en bas à droite:
- Height et Width permettent de modifier les dimensions.
- Foreground et Background donnent la couleur du texte et du fond.
- BorderBrush et BorderThickness indique la couleur et l'épaisseur des bords
- FontFamily indique le nom de la font
- FontSize indique la hauteur de la font
- FontStyle permet de choisir entre Normal, Italic, Oblique.
- FontWeight permet de choisir entre Normal, Bold, Heavy, ...
- Name donne le nom du bouton en haut de la fenêtre.
- Content indique le texte à afficher.
En bas de l'écran dans la fenêtre XAML, apparaît le code xaml correspondant au bouton.
< Button
Height = " 100 " Width = " 200 "
Foreground = " White " Background = " Red "
BorderBrush = " Green " BorderThickness = " 100 "
FontFamily = " Algerian " FontSize = " 24 " FontStyle = " Italic " FontWeight = " Bold " Name = " mybutton " > Beau Bouton< / Button >
|
Dans une fenêtre vide, on aurait pu coller le code xaml entre les balises grid et voir apparaître le bouton.
On aurait pu créer le bouton avec du code Visual Basic:
Dim myButton As New Button ()
myButton. Height = " 100 "
myButton. Width = " 200 "
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
myButton. Content = " Beau Bouton "
Grid. Children . Add (myButton)
|
On a à notre disposition une énumération Colors qui énumère les couleurs:
Mais en vb, on ne peut pas affecter directement la couleur: myButton.Background = Colors.Red ne fonctionne pas.
Il faut utiliser une brush:
myButton. Background = New SolidColorBrush (Colors. Red )
|
On a aussi une énumérations de SolidColorBrush que l'on peut utiliser comme cela:
myButton. Background = Brushes. Red
|
On peut 'enrichir' une partie du texte:
On utilise dans ce cas des balises comme 'bold' ou 'italic' par exemple dans un TextBlock:
< TextBlock Width = " 150 " Height = " 50 "
TextAlignment = " Center " > < Bold > Un< / Bold > < Italic > bouton< / Italic > < / TextBlock >
|
Au lieu de mettre une couleur unie dans le fond d'un contrôle, on peut y mettre une 'Brush' ce qui produit un bel aspect:
Cela donne:
En XAML:
< Button Content = " Ok " >
< Button . Background >
< LinearGradientBrush StartPoint = " 0,0 " EndPoint = " 0,1 " >
< GradientStop Color = " White " Offset = " 0 " / >
< GradientStop Color = " LightGray " Offset = " 1 " / >
< / LinearGradientBrush >
< / Button . Background >
< / Button >
|
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 templates et contrôles templates (voir plus bas).
XI-E-5-b. 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. Le faire en tapant du code XAML (dans le designer VB c'est difficile de mettre un StackPanel dans un Button, il se met dessus et pas dedans, donc copier coller le code XAML). De plus l'image doit être dans les ressources: voir ce chapitre.
< Button Name = " Button1 " >
< StackPanel Name = " StackPanel " >
< TextBlock TextAlignment = " Center " >
Ok
< / TextBlock >
< Image Source = " oktransp.GIF " Height = " 25.704 " Width = " 127.092 " >
< / Image >
< / StackPanel >
< / Button >
|
Cela donne:
On se rend compte de la puissance des WPF: on peut mettre autant de contrôle que l'on veut dans un contrôle en utilisant des conteneurs. Dans notre bouton on aurait pu mettre 2 images, 2 textes...
XI-E-5-c. Aspect visuel des contrôles : Template visuel
Ici on modifie l'aspect profond des contrôles. Débutant s'abstenir.
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, coins..).
Ce template est entre les balises 'Button.Template' pour un bouton. On peut bien sur modifier ce Template ce qui modifie l'aspect du bouton: On peut ainsi obtenir des boutons ronds, elliptiques..
Dans le template du bouton, ici, on va définir la forme qui est rectangle mais aussi la forme des coins (radius).
On va donc faire un bouton rouge avec des coins ronds ( grâce à Rectangle RadiusX="9" RadiusY="9").
< Button Name = " Button2 " >
< Button . Template >
< ControlTemplate >
< Grid >
< Rectangle RadiusX = " 9 " RadiusY = " 9 " Fill = " Red " >
< / Rectangle >
< ContentPresenter HorizontalAlignment = " Center "
VerticalAlignment = " Center " Content = " BoutonTemplate " / >
< / Grid >
< / ControlTemplate >
< / Button . Template >
< / Button >
|
Cela donne:
On voit qu'a partir du moment ou on utilise le ControlTemplate, il faut tout refaire, la forme du contrôle (Rectangle), mais aussi le contenu grâce à ContentPresenteur.
Si au lieu de mettre 'Rectangle Radius..' on met 'Ellipse Fill="Green"', le bouton est vert en forme d'ellipse.
Cette exemple fonctionne mais est incomplet car comme dans le Template, on n'a pas défini l'aspect du bouton en cas de click ou quand il a le focus, le bouton ne change jamais d'aspect même quand on clique dessus!!
Ici on a modifié UN contrôle, il est possible de modifier TOUS les contrôles de même type: Voir le chapitre sur les ressources qui parle des modèle 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).
XI-E-5-d. Modification du Bitmap d'un contrôle
Ici on modifie le BitMap (les pixels) du dessin du contrôle en appliquant des effets spéciaux. Débutant s'abstenir.
BlurBitmapEffect permet d'appliquer un floue comme à travers un objectif non mis au point.
Appliquons cet effet à un bouton.
< Button Name = " Button1 " Width = " Auto " > ButtonEffect
< Button . BitmapEffect >
< BlurBitmapEffect Radius = " 1 " KernelType = " Box " / >
< / Button . BitmapEffect >
< / Button >
|
Voici ce que cela donne sur le bouton supérieur ( notez que l'aspect floue n'apparait pas dans le designer mais uniquement lors de l'exécution).
Il existe d'autres effets:
OuterGlowBitmapEffect crée un halo de couleur autour d'un objet.
DropShadowBitmapEffect crée une ombre derrière un objet.
BevelBitmapEffect crée un biseau qui déclenche la surface d'une image d'après une courbe spécifiée.
EmbossBitmapEffect crée un placage de relief pour un Visual.
XI-E-6. Remplissage de surface
On peut 'remplir' le fond d'un bouton, un rectangle, le même du texte....
Pour cela, on utilise des Brush.
XI-E-6-a. SolidColorBrush
C'est simple, c'est une couleur unie.
Exemple 1. On veut choisir la couleur du Background d'un bouton.
On peut le définir en mode design dans la fenêtre de propriétés.
En XAML:
< Button Background = " Red " / >
|
En VB:
myButton. Background = New SolidColorBrush (Colors. Red )
|
Comme en winforms, on ne peut pas affecter directement la couleur: (myButton.Background = Colors.Red ne fonctionne pas) on est obligé d'instancier une nouvelle SolodColorBrush et de lui donner la valeur Red qui appartient à la collection Colors.
Exemple 2: créer un rectangle rouge:
Dim Rect As new Rectangle ()
Rect. Width = 75
Rect. Height = 75
Dim myBrush As new SolidColorBrush (Colors. Red )
Rect. Fill = myBrush
Grid. Content = Rect
|
Remarquer qu'on a utilisé la méthode Fill pour remplir le rectangle avec la Brush.
En XAML:
< Rectangle Width = " 75 " Height = " 75 " >
< Rectangle . Fill >
< SolidColorBrush Color = " Red " / >
< / Rectangle . Fill >
< / Rectangle >
|
XI-E-6-b. LinearGradientBrush
C'est une couleur qui se transforme progressivement en une autre puis eventuellement 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 3 couleurs dans la diagonale.
En VB, on ne peut pas le faire dans la fenêtre designer, le plus simple est de coller le code XAML dans la fenêtre XAML.
Pour suivre la diagonale StartPoint="0,0" EndPoint="1,1".
Le rouge sera à 0% de la diagonale, le blanc à 50% le bleu à 100% de la diagonale.
En XAML:
< Grid >
< Grid . Background >
< LinearGradientBrush StartPoint = " 0,0 " EndPoint = " 1,1 " >
< GradientStop Color = " Red " Offset = " 0 " / >
< GradientStop Color = " White " Offset = " 0.5 " / >
< GradientStop Color = " Blue " Offset = " 1 " / >
< / LinearGradientBrush >
< / Grid . Background >
< / Grid >
|
En VB:
Dim myBrush As new LinearGradientBrush
myBrush. GradientStops . Add (new GradientStop (Colors. Red , 0. 0 )))
myBrush. GradientStops . Add (new GradientStop (Colors. White , 0. 5 ))
myBrush. GradientStops . Add (new GradientStop (Colors. Blue , 1. 0 ))
Grid. background = myBrush
|
Exemple 2 sur un bouton, on positionne 2 couleurs (blanc et gris) de haut en bas.
Pour que le gradient s'applique de haut en bas StartPoint="0,0" EndPoint="0,1".
le blanc sera à 0% de la verticale, le gris à 100% de la verticale.
En XAML:
< Button Content = " Ok " >
< Button . Background >
< LinearGradientBrush StartPoint = " 0,0 " EndPoint = " 0,1 " >
< GradientStop Color = " White " Offset = " 0 " / >
< GradientStop Color = " LightGray " Offset = " 1 " / >
< / LinearGradientBrush >
< / Button . Background >
< / Button >
|
Remarque: on aurait pu mettre un GradientStop en dehors de la zone ( à 2 par exemple pour le LightGray) ce qui permet s'estomper la couleur grise et de faire un bouton plus clair.
XI-E-6-c. RadialGradientBrush
Ici on applique 2 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 à 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 : cercle de blanc au centre; le bleu est à l'offset 0.5, cela donne un cercle de bleu autour du blanc; le vert est à l'offset 1 (100%): le cercle vert est autour du cercle bleu.
En XAML
< RadialGradientBrush GradientOrigin= " 0.50,0.50 " >
< GradientStop Color= " white " Offset= " 0 " / >
< GradientStop Color= " LightBlue " Offset= " 0.5 " / >
< GradientStop Color= " LightGreen " Offset= " 1 " / >
< / RadialGradientBrush>
|
Pour l'exemple suivant le centre du gradient est à 0.50 1 excentré en bas du cercle.
On a ajouté artificiellement, pour mieux comprendre, le cercle gris qui occupe la totalité du conteneur et le centre du gradient symbolisé par un point noir.
L'offset du blanc est 0.75: le blanc monte haut.
En XAML
< RadialGradientBrush GradientOrigin = " 0.50,1 " >
< GradientStop Color = " white " Offset = " 0.75 " / >
< GradientStop Color = " LightBlue " Offset = " 1.0 " / >
< / RadialGradientBrush >
|
Ici l'offset du blanc est 0.50: le blanc monte moins haut.
En XAML:
< RadialGradientBrush GradientOrigin = " 0.50,1 " >
< GradientStop Color = " white " Offset = " 0.50 " / >
< GradientStop Color = " LightBlue " Offset = " 1.0 " / >
< / RadialGradientBrush >
|
Exemple sur une grid. Centre du gradient excentré en haut à droite (0.75 0.25) et il y a 3 couleurs.
En XAML:
< Grid >
< Grid . Background >
< RadialGradientBrush GradientOrigin = " 0.75,0.25 " >
< GradientStop Color = " Yellow " Offset = " 0.0 " / >
< GradientStop Color = " Orange " Offset = " 0.5 " / >
< GradientStop Color = " Red " Offset = " 1.0 " / >
< / RadialGradientBrush >
< / Grid . Background >
< / Grid >
|
En VB:
Dim myBrush As New RadialGradientBrush
myBrush. GradientOrigin = New Point (0. 75 , 0. 25 )
myBrush. GradientStops . Add (New GradientStop (Colors. Yellow , 0. 0 ))
myBrush. GradientStops . Add (New GradientStop (Colors. Orange , 0. 5 ))
myBrush. GradientStops . Add (New GradientStop (Colors. Red , 1. 0 ))
grid. Background = myBrush
|
XI-E-6-d. ImageBrush
On peut mettre une image dans un rectangle par exemple:
En XAML:
< Rectangle Width = " 75 " Height = " 75 " >
< Rectangle . Fill >
< ImageBrush ImageSource = " c:\graphe.bmp " / >
< / Rectangle . Fill >
< / Rectangle >
|
En VB:
Dim Rect As New Rectangle ()
Rect. Width = 75
Rect. Height = 75
Dim myBrush = New ImageBrush
myBrush. ImageSource = New BitmapImage (New Uri (" c:\graphe.bmp " , UriKind. Relative ))
Rect. Fill = myBrush
grid. Children . Add (Rect)
|
XI-E-6-e. Autre
Il existe aussi les DrawingBrush qui permettent de dessiner des motifs géométriques que l'on peut même répéter.
Les VisualBrush, elles, permettent certains effets comme l'effet 'miroir'.
XI-E-7. Ressources
Les ressources sont un ensemble d'éléments,: images, icônes, textes, sons,(ressources contenues dans des fichiers externes) ou des couleurs, brush, style (ressources dites 'internes') qui sont utilisées par le programme.
Nous allons voir
1-Les ressources internes
2-Les fichiers de ressources.
XI-E-7-a. Ressources 'internes'
Ici on va voir comment créer des styles, des modèles de contrôles, des modèles de données pour les appliquer à tous les contrôles ou données .
XI-E-7-a-i. Ressources 'simples'
Plutôt que d'indiquer X fois quelle couleur ou Brush utiliser dans une fenêtre, il est plus simple de définir une ressource contenant la couleur ou la Brush puis X fois, indiquer quelle ressource utiliser.
Les ressources sont dans un dictionnaire de ressources. Dans les ressources d'une fenêtre par exemple ou les ressources d'un objet entre les balises de début et de fin de ressources.
< Window . Resources >
< / Window . Resources >
|
Remarquez: Resources avec un 's' et non Ressources.
Ici dans ses ressources simples, la ressource est une Brush, une couleur..
Chaque ressource doit avoir une clé unique. Il faut affecter la clé unique via l'x:Key, attribut.
En principe, la clé est une chaîne.
< SolidColorBrush x : Key = " MyBrush " Color = " Gold " / >
|
Ici la clé unique de la SolidColorBrush est "MyBrush", c'est son 'nom', son nom d'index.
Ensuite dans le code xaml de l'UI on utilise cette ressource grâce à {StaticResource MyBrush}:
< Button Background = " {StaticResource MyBrush} " / >
< Ellipse Fill = " {StaticResource MyBrush} " / >
|
En VB
Me. Background = Window1. FindResource (" MyBrush " )
|
Exemple complet:
Dans une grid, on crée une ressource de type LinearGradientBrush nommé BrushBizarre, ensuite on applique cette Brush au fond d'un bouton.
< Grid>
< Grid. Resources >
< LinearGradientBrush x:Key= " BrushBizarre " >
< GradientStop Color= " Yellow " Offset= " 0 " / >
< GradientStop Color= " Green " Offset= " 1 " / >
< / LinearGradientBrush>
< / Grid. Resources >
< Button Background= " {StaticResource BrushBizarre} " > Click Me< / Button>
< / Grid>
|
Ou mettre les ressources?
-Dans un objet. Dans une Grid comme dans l'exemple précédent.
-Dans une fenêtre, Entre les balises:
< Window . Resources >
< / Window . Resources >
|
-Dans le fichier Application avec la balise:
< Application . Resources >
< / Application . Resources >
|
- Dans un dictionnaire de ressources:
Menu 'Projet', puis 'Ajouter un dictionnaire de ressources. Nommer le Dictionnary1 puis Ok.
Y mettre une solidColorBrush.
< ResourceDictionary xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml " >
< SolidColorBrush x : Key = " MyBrush " Color = " Gold " / >
< / ResourceDictionary >
|
Pour que cela fonctionne, il faut ajouter dans le fichier Application la référence au dictionnaire.
< Application x : Class = " Application "
. . . .
< Application . Resources >
< ResourceDictionary Source = " Dictionary1.xaml " / >
< / Application . Resources >
< / Application >
|
L'emplacement où est déclaré la ressource affecte l'emplacement où la ressource peut s'appliquer.
Si vous déclarez la ressource dans l'élément racine de votre fichier de définition d'application 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.
Ressources 'Statique' et 'Dynamique'
Les ressources utilisées avec le mot clé StaticResource sont récupérées au chargement de l'application alors que pour celles utilisées avec le mot clé DynamicResource le chargement est différé au moment de l'exécution et la valeur est réévaluée à chaque accès à cette ressource. Dans ce mode WPF crée une expression qui est évaluée à l'utilisation et permet notamment de gérer le cas ou la valeur dépend d'information connue seulement au runtime.
La syntaxe est la même sauf que lors de l'utilisation de la ressource, on utilise DynamicResource.
< Window x : Class = " Window1 "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml "
Title = " Window1 " Height = " 300 " Width = " 300 " >
< Window . Resources >
< SolidColorBrush x : Key = " BackgroundBrush " Color = " Green " / >
< / Window . Resources >
< Button Margin = " 10 " Padding = " 10 " Background = " {DynamicResource
BackgroundBrush} " >
Mon Button
< / Button >
< / Window >
|
En VB:
< Window . Resources >
< SolidColorBrush x : Key = " Mybrush " Color = " Red " / >
< / Window . Resources >
< Button x : Name = " btn " Content = " Find Position " Click = " Button_Click " / >
|
btn. SetResourceReference (BackgroundProperty, " Mybrush " )
|
XI-E-7-a-ii. Les Styles
Le style est une ressource qui est appliquée à un 'Type' d'objet. On peut créer un style pour tous les Button, toutes les List..
Ici on va créer un style pour tous les TextBlock de la fenêtre.
TargetType="TextBlock" indique quel type cela concerne.
Dans ce style , grâce à 'Setter Property=' on affecte des valeurs aux propriétés des TextBlock.
< Window x : Class = " Window1 "
. . . . .
< Window . Resources >
< Style TargetType = " TextBlock " >
< Setter Property = " Background " Value = " Blue " / >
< Setter Property = " DockPanel.Dock " Value = " Top " / >
< Setter Property = " FontSize " Value = " 18 " / >
< Setter Property = " Foreground " Value = " #4E87D4 " / >
< Setter Property = " FontFamily " Value = " Trebuchet MS " / >
< Setter Property = " Margin " Value = " 0,40,10,10 " / >
< / Style >
< / Window . Resources >
|
Ensuite, si on utilise un textBlock la ressource est appliquée automatiquement:
< TextBlock > Titre< / TextBlock >
|
Attention,la premiere ligne est 'Style TargetType="TextBlock"', dans ce cas , le style est appliqué à tous les TextBlock.
Si on utilise x:Key= , la ressource n'est pas appliquée automatiquement à tous les TextBlock!!
Il faut dans ce cas utiliser l'attribut Style= dans le TextBlock sur lequel on veut appliquer le style.
Donc si on écrit:
< Style TargetType = " TextBlock " x : Key = " TitleText " >
|
Pour appliquer le style à un TextBlock particulier, il faut écrire ensuite:
< TextBlock Style = " {StaticResource TitleText} " > Titre< / TextBlock >
|
On peut modifier le Style quand un évènement se déclenche: Si le 'Target' est le survol d'un MenuItem dans un menu, mettre le texte en rouge:
< Window . Resources >
< Style TargetType = " {x:Type MenuItem} " >
< Style . Triggers >
< Trigger Property = " MenuItem.IsMouseOver " Value = " true " >
< Setter Property = " Foreground " Value = " Red " / >
< Setter Property = " FontSize " Value = " 12 " / >
< Setter Property = " FontStyle " Value = " Italic " / >
< / Trigger >
< / Style . Triggers >
< / Style >
< / Window . Resources >
|
En VB pour appliquer le style à un contrôle:
Button1.Style = Resources("TitleText")
|
On peut étendre un style existant:
< Style BasedOn = " {StaticResource {x:Type TextBlock}} "
TargetType = " TextBlock " x : Key = " TitleText " >
< Setter Property = " FontSize " Value = " 26 " / >
< / Style >
|
ATTENTION: si vous créez un style pour le type TextBlock, le style est appliqué à tous les TextBlock, y compris si le TextBlock fait partie d'un autre contrôle, tel qu'un ListBox.
XI-E-7-a-iii. Les modèles de Contrôle : Control Template'
Ce sont des Modèle de contrôles permettant de modifier tous les contrôles d'un même type.
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.
Comment avoir des boutons en forme d'ellipse?
Il faut créer un style, redéfinir le style par défaut et créer un nouveau modèle avec:
< Setter Property = " OverridesDefaultStyle " Value = " True " / >
< Setter Property = " Template " / >
|
puis définir une ellipse.
Cela donne:
< Window . Resources >
< Style TargetType = " Button " >
< Setter Property = " OverridesDefaultStyle " Value = " True " / >
< Setter Property = " Template " >
< Setter . Value >
< ControlTemplate TargetType = " Button " >
< Grid >
< Ellipse Fill = " Green " / >
< ContentPresenter HorizontalAlignment = " Center "
VerticalAlignment = " Center " / >
< / Grid >
< / ControlTemplate >
< / Setter . Value >
< / Setter >
< / Style >
< / Window . Resources >
|
Maintenant tous les boutons sont comme cela:
Cette exemple fonctionne mais est incomplet car comme dans le Template, on n'a pas défini l'aspect du bouton en cas de click ou quand il a le focus, le bouton ne change jamais d'aspect même quand on clique dessus!!
On peut faire plus fort et
écrire le Template pour gérer les évènements.
En haut de la Window, dans les ressources je mets un 'Template' de bouton':
< Window . Resources >
< Style TargetType = " Button " >
< Setter Property = " SnapsToDevicePixels " Value = " true " / >
< Setter Property = " OverridesDefaultStyle " Value = " true " / >
< Setter Property = " Template " >
< Setter . Value >
< ControlTemplate TargetType = " Button " >
< Border
x : Name = " Border "
CornerRadius = " 5 "
BorderThickness = " 1 "
BorderBrush = " Black "
Background = " AliceBlue " >
< ContentPresenter
Margin = " 2 "
HorizontalAlignment = " Center "
VerticalAlignment = " Center "
RecognizesAccessKey = " True " / >
< / Border >
< ControlTemplate . Triggers >
< Trigger Property = " IsMouseOver " Value = " true " >
< Setter TargetName = " Border " Property = " BorderThickness " Value = " 2 " / >
< / Trigger >
< Trigger Property = " IsPressed " Value = " true " >
< Setter TargetName = " Border " Property = " Background " Value = " LightBlue " / >
< Setter TargetName = " Border " Property = " BorderBrush " Value = " Black " / >
< / Trigger >
< Trigger Property = " IsEnabled " Value = " false " >
< Setter Property = " Foreground " Value = " White " / >
< / Trigger >
< / ControlTemplate . Triggers >
< / ControlTemplate >
< / Setter . Value >
< / Setter >
< / Style >
< / Window . Resources >
|
Le Trigger Property=""IsEnabled"" à False met du blanc comme couleur de fond si le contrôle n'est pas 'enabled'.
Le Trigger Property="IsMouseOver" permet d'épaissir les bords quand la souris est sur le contrôle.
Le Trigger Property="IsPressed" permet d'assombrir le fond quand on clicke sur le bouton.
Tous les boutons du formulaire auront cet aspect et ce comportement.
XI-E-7-a-iv. 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 aux Data Template (modèle de données); voir le chapitre sur les liaisons de données.
Ici par exemple plutôt que d'afficher bêtement une liste de nom, je vais les afficher dans un cadre coloré en fonction du sexe.
XI-E-7-b. Les fichiers de ressources externes
Exemple de fichier Image.
J'ai une image nommée 'noel.bmp', je veux la mettre dans les ressources pour la charger dans un contrôle image.
i- 'Charger' le fichier image dans les ressources.
Créons un répertoire nommé image sous le répertoire du source : (C:\Documents and Settings\phil\Mes documents\Visual Studio 2008\Projects\MyApplication\MyApplication\image)
Y mettre le fichier noel.bmp.
Puis incorporons le dans le projet:
Menu Projet=>Ajouter un élément existant.
Pointer C:\Documents and Settings\phil\Mes documents\Visual Studio 2008\Projects\MyApplication\MyApplication\image\noel.bmp
Puis cliquer sur 'Ajouter'
Noel.bmp apparaît dans l'explorateur de solution (en haut à droite).
Cliquer sur noel.bmp puis dans la fenêtre propriété indiquer 'Resource' comme Action de génération (Build Action); c'est souvent déjà le cas car c'est 'Resources' par défaut.
ii- Mettre la ressource dans l'image.
< Image Margin = " 34,36,48,46 " Name = " Image1 " Stretch = " Fill " Source = " noel.bmp " / >
|
Noter bien: pas de chemin.
L'image noel.bmp apparaît dans le contrôle image.
En VB:
Image1. Source = New BitmapImage (New Uri (" Noel.bmp " , 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:
o-Charger Noel.bmp dans le projet comme ci-dessus.
oo-Créer une ressource nommé "someImage" dans Application.xaml
< Application . Resources >
< ImageSource x : Key = " someImage " > noel.bmp< / ImageSource >
< / Application . Resources >
|
ooo-Mettre la ressource dans une image
< Image Margin = " 34,36,3,8 " Name = " Image1 " Stretch = " Fill " Source = " {StaticResource someImage} " / >
|
XI-E-8. Les liaisons de données ou Binding
"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'etablir une connexion entre un contrôle et une sources de données. Cela permet d'afficher automatiquement le contenu d'une base de données, d'une collection... dans une DataGrid, une ListBox...
Il faut donc un objet visuel, la cible, (ListBox, TextBox..) ayant une propriété de dépendante 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 uni (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 modif dans l'UI.
XI-E-8-a. Principes du Binding
Binding entre objets:
Pour qu'une propriété d'un objet (dit 'cible') soit liée à une source, il faut lui affecter un objet Binding ( Text="{Binding..) puis indiquer l'objet source avec ElementName et la propriété source avec Path:
< TextBox Text = " {Binding Path=Text, ElementName=txtsource} " / >
|
Ici la propriété Text du TextBox est liée à la propriété Text d'un autre contrôle. Si on tape un texte dans le contrôle txtsource, il apparaitra dans la propriété Text de notre TextBox.
DataContext
La propriété DataSource des contrôles WindowsForms n'existe plus; les contrôles WPF ont un DataContext. Il peut être renseigné enXaml ou dans le code VB. 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 heriterons 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ément donc). Chaque propriété à afficher sera indiquée par Path.
< DockPanel Name = " DockPanel1 " >
< DockPanel . DataContext >
< Binding Source = " {StaticResource myDataSource} " / >
< / DockPanel . DataContext >
< Button Background = " {Binding Path=ColorName} " > Ok< / Button >
< Button Background = " {Binding Path=ColorName} " > Ok< / Button >
< / DockPanel >
|
Ici on crée une liaison du DockPanel avec une collection nommée myDataSource faisant partie des ressources
Les boutons héritent du DataContext du DockPanel. Cela permet ensuite de lier la propriété BackGround de chaque bouton à différentes propriétés de myDataSource.
Vous pouvez indiquer le DataContext dans le code VB:
Public MyCollection As ObservableCollection (Of String )
DockPanel1. DataContext = MyCollection
|
Dans ce cas en Xaml, il faut simplement indiquer qu'il y a un Binding avec l'expression"{Binding}":
< ListBox Margin = " 14,16,8,2 " Name = " ListBox1 " ItemsSource = " {Binding } " / >
|
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 VB, vous devez définir la propriété DataContext dans le code VB (et pas en XAML).
Si on utilise pas le DataContext, on peut aussi utiliser la propriété Source du Binding:
< Button Background = " {Binding Source={StaticResource myDataSource}/>
|
Si on utilise un contrôle de type ItemsControl tel qu'un ListBox, ListView ou un TreeView pour afficher une collection de données, il faut utiliser ItemsSource pour indiquer la source:
< ListBox Name = " ListBox1 " ItemsSource = " {Binding Source={StaticResource ListData}} " / >
|
Mode indique le mode de mise a jour (OneWay, TwoWay, OneTime, OneWayToSource).
< TextBox Name = " txtcible " Margin = " 21,0,25,21 "
Text = " {Binding Path=Text, ElementName=txtsource, Mode=TwoWay} " / >
|
UpdateTrigger détermine le moment des mises à jour de la source de liaison
XI-E-8-b. Liaison entre contrôles
A - Créons deux TextBox; quand on tape un texte dans la première, il apparait dans la seconde:
< TextBox Name = " txtsource " Text = " " / >
< TextBox Name = " txtcible " Margin = " 21,0,25,21 "
Text = " {Binding Path=Text, ElementName=txtsource} " / >
|
Dans la cible, la propriété Text= 'Binding' entre accolades, ce qui permet d'indiquer qu'il y a une liaison, 'ElementName' indique la source de la liaison. Path la propriété de la source. Il y a une ',' entre chaque élément à partir du second.
La syntaxe suivante est aussi acceptée.
< TextBox Name = " txtcible "
Text = " {Binding Text, ElementName=txtsource} " / >
|
Par défaut le Mode est égal à OneWay (la liaison se fait uniquement dans le sens source=>cible).
On peut donner la valeur TwoWay au mode, ce qui permet de faire en plus la liaison cible=>source: quand on tape dans la cible, la source est modifiée (cela est visible dans notre exemple ci-dessous quand on quitte la cible):
< TextBox Name = " txtsource " Text = " " / >
< TextBox Name = " txtcible " Margin = " 21,0,25,21 "
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), et OneWayToSource (liaison cible=>source uniquement).
On a vu qu'en mode TwoWay, la mise à jour de la source, en cas de modification de la cible était effectuée quand on quittait la cible. C'est parce que la valeur par défaut de la propriété UpdateSourceTrigger a la valeur LostFocus (pour un TextBox). Si on veut que la modification se fasse quand on tape sur une touche, il faut écrire:
< TextBox Name = " txtcible "
Text = " {Binding Text,ElementName=txtName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged } " / >
|
Ainsi le Trigger qui déclenche la mise à jour dans la liaison est le changement de la propriété Text.
A noter que pour une ListBox PropertyChanged est la valeur par défaut de UpdateSourceTrigger. Il y a une autre valeur qui est 'Explicit'; la mise à jour se fait uniquement quand on fait UpdateSource.
B - Créons deux ListBox; quand on ajoute des éléments dans la première, ils apparaissent dans la seconde:
< Grid >
< ListBox Name = " listBox1 " SelectionMode = " Multiple " / >
< ListBox Name = " listBox2 " ItemsSource = " {Binding ElementName=listBox1, Path=Items, Mode=OneWay} "
< / Grid >
|
C - Créons maintenant un bouton et un Slider; quand on bouge le curseur du Slider cela modifie la hauteur du bouton: il faut donc comme source la propriété Value du slider et comme cible la propriété Height du bouton:
< Grid >
< Button Name = " Button1 " Width = " 97 " Height = " {Binding ElementName=Slider1, Path=Value} " > Button< / Button >
< Slider Height = " 20 " Name = " Slider1 " Value = " 5 " Maximum = " 200 " Minimum = " 0 " / >
< / Grid >
|
En résumé:
Dans le Binding, ElementName indique le contrôle source.
Si la source est un objet, il faut utiliser la propriété Path pour spécifier la propriété de l'objet.
XI-E-8-c. Liaison Collection-ListBox et Tableau-ListBox
On crée une collection List(Of), on la remplit avec des nombres, comment faire une liaison List() ListBox et afficher automatiquement les nombres dans la listbox?
Pour une ListBox, c'est ItemsSource qui indique la source.
Voici le code XAML de la ListBox: ItemsSource ="{Binding}" indique que la source est une liaison mais n'indique pas la source.
< ListBox Name = " listbox1 " ItemsSource = " {Binding} " >
< / ListBox >
|
Voici le code VB: ListBox1.DataContext = coll indique quelle est la source de la liaison:
Class Window1
Private Sub Window1_Loaded (ByVal sender As System. Object , ByVal e As System. Windows . RoutedEventArgs ) _
Handles Window1. Loaded
Dim coll As New List (Of String )
For i As Integer = 0 To 100
coll. Add (i. ToString )
Next
listbox1. DataContext = coll
End Sub
End Class
|
La liaison est ici par défaut: OneWay.
Cela marche aussi avec un tableau.
Class Window1
Private Sub Window1_Loaded (ByVal sender As System. Object , ByVal e As System. Windows . RoutedEventArgs ) _
Handles Window1. Loaded
Dim coll (100) As String
For i As Integer = 0 To 100
coll (i) = i. ToString
Next
listbox1. DataContext = coll
End Sub
End Class
|
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 VB, vous devez définir la propriété DataContext dans le code VB.
XI-E-8-d. Liaison avec une collection d'objets
On veut une collection contenant des personnes ayant un nom et un prénom et les afficher par liaison dans une liste.
On va créer une classe 'NomPerson' qui a 2 propriétés: 'Nom' et 'Prenom'.
On va aussi créer une classe nommée 'Names' qui hérite de la Classe ObservableCollection(Of).
Noter qu'au lieu d'utiliser une collection habituelle(List(Of)), on utilise une ObservableCollection, on verra plus tard pourquoi.
Remarquons que pour utiliser une ObservableCollection il faut ajouter 'Imports System.Collections.ObjectModel'
Imports System. Collections . ObjectModel
Public Class Names
Inherits ObservableCollection (Of NomPerson)
Public Sub New ()
MyBase. Add (New NomPerson (" Paul " , " Durand " ))
MyBase. Add (New NomPerson (" Emilie " , " Dupont " ))
MyBase. Add (New NomPerson (" Philippe " , " Lasserre " ))
MyBase. Add (New NomPerson (" Jules " , " Dubout " ))
End Sub
End Class
Public Class NomPerson
Public Sub New (ByVal first As String , ByVal last As String )
Me. _Prenom = first
Me. _Nom = last
End Sub
Public Property Prenom () As String
Get
Return Me. _Prenom
End Get
Set (ByVal value As String )
Me. _Prenom = value
End Set
End Property
Public Property Nom () As String
Get
Return Me. _Nom
End Get
Set (ByVal value As String )
Me. _Nom = value
End Set
End Property
Private _Prenom As String
Private _Nom As String
End Class
|
Dans le code Xaml, il faut avoir accès au namespace du code vb,(on lui donne le prefixe, on le nomme: 'm') on ajoute donc:
xmlns:m="clr-namespace:Wpfbindingcoll"
|
Il faut aussi mettre la collection dans les ressources :
< Window . Resources >
< m : Names x : Key = " ListData " / >
|
m:Names indique que dans l'espace de nom 'm', on utilise la classe 'Names' comme ressource;( la clé se nomme 'ListData')
Pas besoin d'instancier explicitement la classe Names.
Ensuite on fait la liaison ListBox1-ListData, pour cela on utilise ItemsSource qui permet de lier un contrôle de type ItemsControl (ListBox, ListWiev, TreeWiev) à une collection.
On indique dans ItemsSource qu'il s'agit d'un Binding dont la source est une ressource nommée ListData.
< ListBox Margin = " 14,16,8,2 " Name = " ListBox1 " ItemsSource = " {Binding Source={StaticResource ListData}} " / >
|
Enfin, il faut afficher correctement les données dans la listbox:
- Soit on se contente d'afficher uniquement le nom: on indique dans le code XAML de la ListBox, grâce à DisplayMemberPath, quelle propriété afficher:
< ListBox Margin = " 14,16,8,2 " Name = " ListBox1 " ItemsSource = " {Binding} " DisplayMemberPath = " Nom " / >
|
-Soit on crée un affichage plus élaboré: on met dans les ressources un DataTemplate:
< DataTemplate x : Key = " MyDataTemplate " >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
< / Grid . ColumnDefinitions >
< TextBlock Text = " {Binding Path=Nom} " Grid . Column = " 0 "
FontWeight = " Bold " / >
< TextBlock Text = " {Binding Path=Prenom} " Grid . Column = " 1 " / >
< / Grid >
< / DataTemplate >
|
Le DataTemplate (modèle de données) indique pour chaque élément d'afficher dans une grid 2 TextBox liés à la propriété nom et prenom.
Il faut, bien sur, indiquer à la listbox d'utiliser le datatemplate avec.
ItemTemplate="{StaticResource MyDataTemplate}"
|
On voit bien que comme il y a héritage du DataContext, il suffit de mettre le Path dans le Binding Des TextBlock.
Cela donne, au total, (version avec DataTemplate) le code XAML suivant:
|
< Window x : Class = " Window1 "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml "
xmlns : m = " clr-namespace:Wpfbindingcoll "
Title = " Window1 " Height = " 300 " Width = " 300 " >
< Window . Resources >
< m : Names x : Key = " ListData " / >
< DataTemplate x : Key = " MyDataTemplate " >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
< / Grid . ColumnDefinitions >
< TextBlock Text = " {Binding Path=Nom} " Grid . Column = " 0 "
FontWeight = " Bold " / >
< TextBlock Text = " {Binding Path=Prenom} " Grid . Column = " 1 " / >
< / Grid >
< / DataTemplate >
< / Window . Resources >
< Grid >
< Grid . RowDefinitions >
< RowDefinition Height = " 214* " / >
< RowDefinition Height = " 48* " / >
< / Grid . RowDefinitions >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 198* " / >
< ColumnDefinition Width = " 80* " / >
< / Grid . ColumnDefinitions >
< ListBox Margin = " 14,16,8,2 " Name = " ListBox1 " ItemsSource = " {Binding Source={StaticResource ListData}} "
ItemTemplate = " {StaticResource MyDataTemplate} " / >
< / Grid >
< / Window >
|
Et voila, la liaison avec la collection fonctionne et cela donne:
Création de la collection en VB:
Une autre manière de faire est (au lieu de créer la collection 'ListData' dans le code XAML) d'instancier un objet 'MyNames' avec la classe 'Names' dans le code VB et ensuite de le lier au contrôle ListBox1 grâce à la propriété 'DataContext' du ListBox en VB:
Class Window1
Public MyNames As New Names
Private Sub Window1_Loaded (ByVal sender As System. Object , ByVal e As System. Windows . RoutedEventArgs ) _
Handles MyBase. Loaded
ListBox1. DataContext = MyNames
End Sub
End Class
|
Dans ce cas en XAML le code de la ListBox est:
|
< ListBox Margin = " 14,16,8,2 " Name = " ListBox1 " ItemsSource = " {Binding } "
ItemTemplate = " {StaticResource MyDataTemplate} " / >
|
Il faut bien sur enlever dans les ressources la ligne: 'm:Names x:Key="ListData"'
Ajout d'élément à la collection en VB:
Dans le code vb, si j'ajoute une nouvelle personne à la collection, elles apparait automatiquement dans la liste.
MyNames. Add (New NomPerson (" toto " , " Zorro " ))
|
On note bien qu'il faut modifier la collection, ce qui entraine une mise à jour de la ListBox. On est en mode OneWay.
Ajouter un élement à la ListBox génère une erreur.
Necessité d'utiliser ObservableCollection(Of)
Si on avait utilisé dans la classe 'Names' une collection comme List(Of), la mise à jour n'aurait pas eu lieu dans la ListBox. Il faut donc bien utiliser ObservableCollection(Of) qui possède l'interface 'INotifyCollectionChanged' qui entraine, en cas de modification de la collection, une mise à jour de la ListBox.
Pour toute modification de la source soit immédiatement répercuté dans l'affichage dans l'affichage, on peut ajouter: UpdateSourceTrigger=PropertyChanged:
Exemple dans un TextBox:
< TextBox Grid . Column = " 1 " Grid . Row = " 0 " Margin = " 3 " Text = " {Binding Nom, UpdateSourceTrigger=PropertyChanged} " / >
|
XI-E-8-e. Liaison avec une base de données
Exemple:
Dans une base de données Accès nommé 'Nom.Mdb', j'ai une table 'NOMPATIENT' avec plusieurs colonnes (NOM, PRENOM...SEXE), je veux afficher la colonne des noms et prénoms dans une listBox:
Dans un formulaire, je vais mettre une ListBox1 et un Button1.
Ensuite, dans le code VB je vais créer un DataSet et le 'remplir' avec la BD.
Il faut importer les espaces de nom OLEDB.
Créer le DataSet, le remplir.
Indiquer la liaison ListBox1-DataSet grâce à 'ListBox1.DataContext'.
Imports System
Imports System. Data
Imports System. Data . OleDb
Class Window1
Private ObjetConnection As OleDbConnection
Private ObjetCommand As OleDbCommand
Private ObjetDataAdapter As OleDbDataAdapter
Private ObjetDataSet As New DataSet
Private strSql As String
Private ObjetDataTable As DataTable
Private strConn As String
Private Sub Button1_Click_1 (ByVal sender As System. Object , ByVal e As System. Windows . RoutedEventArgs ) _
Handles Button1. Click
strConn = " Provider=Microsoft.Jet.OLEDB.4.0; " & " Data Source= c:\nom.mdb; "
strSql = " SELECT FICHEPATIENT.* FROM FICHEPATIENT "
ObjetConnection = New OleDbConnection
ObjetConnection. ConnectionString = strConn
ObjetConnection. Open ()
ObjetCommand = New OleDbCommand (strSql)
ObjetDataAdapter = New OleDbDataAdapter (ObjetCommand)
ObjetCommand. Connection () = ObjetConnection
ObjetDataAdapter. Fill (ObjetDataSet, " FICHEPATIENT " )
ListBox1. DataContext = ObjetDataSet
End Sub
End Class
|
Ensuite dans le code XAML il faut indiquer dans 'ItemsSource' quelle table afficher grâce à Path :
< ListBox Name = " ListBox1 " ItemsSource = " {Binding Path=FICHEPATIENT} " / >
|
Maintenant cela devrait marcher car la liaison est correcte: on clique sur le bouton et on voit:
Cela n'est pas le résultat espéré!! Cela n'affiche qu'une représentation sous forme de chaîne du type de l'objet auquel il est lié.
Comment faire?
On pourrait dans la classe NomPerson Overrider la méthose ToString afin qu'elle retourne une String comportant le nom et le prénom mais c'est rigide. Il faut plutôt utiliser un MODELE DE DONNEES (DataTemplate) pour indiquer comment afficher les données. On peut mettre ce DataTemplate dans les ressources du formulaire par exemple en le nommant 'MyDataTemplate'.
Ici on va indiquer comme modèle, au sein d'une Grid, de mettre 2 TextBlocks liés au Champ NOM et PRENOM. Chaque ListItem de la ListBox sera composé de cette Grid Contenant les 2 TextBlock (le premier sera en gras).
< Window . Resources >
< DataTemplate x : Key = " MyDataTemplate " >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 250 " / >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
< / Grid . ColumnDefinitions >
< TextBlock Text = " {Binding Path=NOM} " Grid . Column = " 0 "
FontWeight = " Bold " / >
< TextBlock Text = " {Binding Path=PRENOM} " Grid . Column = " 1 " / >
< / Grid >
< / DataTemplate >
< / Window . Resources >
|
Enfin, il faut indiquer à la listBox d'utiliser le DataTemplate grâce à l'attribut ItemTemplate:
< ListBox Name = " ListBox1 " ItemsSource = " {Binding Path=FICHEPATIENT} "
ItemTemplate = " {StaticResource MyDataTemplate} " / >
|
Et là enfin, cela marche et donne:
Pour résumer, voila le code XAML complet:
< Window x : Class = " Window1 "
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns : x = " http://schemas.microsoft.com/winfx/2006/xaml "
Title = " Window1 " Height = " 280 " Width = " 655 " >
< Window . Resources >
< DataTemplate x : Key = " MyDataTemplate " >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 250 " / >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
< / Grid . ColumnDefinitions >
< TextBlock Text = " {Binding Path=NOM} " Grid . Column = " 0 "
FontWeight = " Bold " / >
< TextBlock Text = " {Binding Path=PRENOM} " Grid . Column = " 1 " / >
< / Grid >
< / DataTemplate >
< / Window . Resources >
< Grid >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 465* " / >
< ColumnDefinition Width = " 168* " / >
< / Grid . ColumnDefinitions >
< ListBox Margin = " 14,28,14,10 " Name = " ListBox1 " ItemsSource = " {Binding Path=FICHEPATIENT} "
ItemTemplate = " {StaticResource MyDataTemplate} " / >
< Button Height = " 37 " Margin = " 0,37,42,0 " Name = " Button1 " VerticalAlignment = " Top "
Grid . ColumnSpan = " 2 " HorizontalAlignment = " Right " Width = " 134 " > Button< / Button >
< / Grid >
< / Window >
|
Notez qu'on aurait pu mettre plus simplement le DataTemplate directement dans le ListBox: ici le DataTemplate comporte un StackPanel avec 3 TextBocks.
< ListBox Width = " 400 " Margin = " 10 "
ItemsSource = " {Binding Source=FICHEPATIENT} " >
< ListBox . ItemTemplate >
< DataTemplate >
< StackPanel >
< TextBlock Text = " {Binding Path=NOM} " / >
< TextBlock Text = " {Binding Path=PRENOM} " / >
< TextBlock Text = " {Binding Path=RUE} " / >
< / StackPanel >
< / DataTemplate >
< / ListBox . ItemTemplate >
< / ListBox >
|
Les Data Template ont une puissance extraordinaire: je fais modifier mon modèle, ajouter un bord bleu à la grid et mettre le NOM sur la ligne 1, et le prenom sur la ligne 2 (on remarque qu'on a ajouté au préalable des RowsDefinitions; voir le chapitre sur les grid).
Voici le Data Template:
< Window . Resources >
< DataTemplate x : Key = " MyDataTemplate " >
< Border Name = " border " BorderBrush = " Aqua " BorderThickness = " 1 "
Padding = " 5 " Margin = " 5 " >
< Grid >
< Grid . RowDefinitions >
< RowDefinition / >
< RowDefinition / >
< RowDefinition / >
< / Grid . RowDefinitions >
< Grid . ColumnDefinitions >
< ColumnDefinition Width = " 250 " / >
< ColumnDefinition Width = " 100 " / >
< ColumnDefinition Width = " * " / >
< / Grid . ColumnDefinitions >
< TextBlock Text = " {Binding Path=NOM} " Grid . Row = " 0 "
FontWeight = " Bold " / >
< TextBlock Text = " {Binding Path=PRENOM} " Grid . Row = " 1 " / >
< / Grid >
< / Border >
< / DataTemplate >
< / Window . Resources >
|
Cela donne:
De plus en plus fort, on peut modifier l'aspect des données suivant certaines conditions. Si dans la ListBox des noms il s'agit d'une femme, le cadre doit être rose:
On utilise le DataTemplate.Trigger, on le lie au champ SEXE; si sa valeur est égale à 'F', la couleur du bord devient égale à Pink.
< Window . Resources >
< DataTemplate x : Key = " MyDataTemplate " >
< Border Name = " border " BorderBrush = " Aqua " BorderThickness = " 1 "
Padding = " 5 " Margin = " 5 " >
....
< / Border >
< DataTemplate . Triggers >
< DataTrigger Binding = " {Binding Path=SEXE} " >
< DataTrigger . Value > F< / DataTrigger . Value >
< Setter TargetName = " border " Property = " BorderBrush " Value = " Pink " / >
< / DataTrigger >
< / DataTemplate . Triggers >
< / DataTemplate >
< / Window . Resources >
|
Cela donne:
XI-E-9. Les Triggers
Les sources présentés sur cette page sont libres de droits,
et vous pouvez les utiliser à votre convenance. Par contre cette page de présentation de ces sources constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright © .
Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts.
Cette page est déposée à la SACD.
|