Cours VB.NET

Image non disponible


précédentsommairesuivant

XI-E. Grands Principes

Ou trouvez de la documentation?

Msdn WPF: la bible

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

 
Sélectionnez

<Button>Ok</Button>

Cela crée un bouton sur lequel est affiché 'Ok'. Il apparaît dans le formulaire.

-Le créer dans le code VB:

 
Sélectionnez

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

 
Sélectionnez

<Button Background="Blue" >Ok</Button>

ou de manière plus complexe 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 VB:

 
Sélectionnez

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.

Image non disponible

Ajoutons notre bouton.

Image non disponible

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:

 
Sélectionnez

<Button>Ok</Button>

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:

 
Sélectionnez

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.

 
Sélectionnez

<Button Click="OnClick5">Ok</Button>

Dans ce cas si je double-clique sur le bouton, la routine suivante est créée:

 
Sélectionnez

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.

Image non disponible

MyProjet donne accès aux propriétés du projet.

Application.xaml est le fichier d'application:

 
Sélectionnez

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

 
Sélectionnez

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

<!-- Les ressources r?parties au niveau Application sont d?finies ici. -->

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

 
Sélectionnez

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

 
Sélectionnez

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:

 
Sélectionnez

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

' Insérez le code requis pour la création d'objet sous ce point.

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

Image non disponible

Cliquer sur Fenêtre WPF puis sur le bouton 'Ajouter'.

On a crée une seconde fenêtre.

Image non disponible

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.

Image non disponible

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.

 
Sélectionnez

Dim w As New Window2

Ensuite il faut l'afficher:

 
Sélectionnez

w.Show()

ou

 
Sélectionnez

w.ShowDialog()

pour ouvrir la fenêtre Window2 en mode 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:

 
Sélectionnez

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

 
Sélectionnez

<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">
Image non disponible

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

 
Sélectionnez

Dim w As New Window2
w.Show()

ou

 
Sélectionnez

w.ShowDialog() 

pour ouvrir une fenêtre modale.

La fenêtre qui s'ouvre est activée en même temps.

Si on ajoute

 
Sélectionnez

w.ShowActivated = False

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

 
Sélectionnez

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:

 
Sélectionnez

w.Activate

Une fenêtre activée reçoit les évènements de la souris et du clavier.

Pour la fermer:

 
Sélectionnez

w.Close 

On peut modifier toutes les propriétés de la fenêtre par code VB:

 
Sélectionnez

Me.Background = New SolidColorBrush(Colors.Red) 

' couleur du fond en rouge

 
Sélectionnez

Me.Title = "Ma fenetre"

'texte de la barre de titre

 
Sélectionnez

Me.Height = "200" 

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

 
Sélectionnez

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

 
Sélectionnez

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.

 
Sélectionnez

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.

Formulaire MDI:

Les formulaires MDI sont absents en WPF. Il ne faut plus les utiliser!! A la place , on utilise les onglets (TabControl).

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:

 
Sélectionnez

    MyButton.Content = "Ok" 

affiche le texte 'Ok' dans le bouton.

En XAML:

 
Sélectionnez

<Button Content="Ok"/>

Content.

Dans un controle non conteneur, la propriété 'Content' ne peut contenir d'un objet et un seul.
Pour un bouton par exemple, on a 2 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 (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

 
Sélectionnez

 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:

 
Sélectionnez

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

Image non disponible

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.

Image non disponible

Cela donne par exemple dans le code XAML:

 
Sélectionnez

<Button Height="39" Margin="30,64,128,0" Name="Button1" VerticalAlignment="Top" Click="OnClick5">Ok</Button>

Voyons le détail de cette ligne:

 
Sélectionnez

<Button>Ok</Button> 

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.

Image non disponible

Les grands principes:

VerticalAlignement et HorizontalAlignment définissent sur quoi est aligné le contrôle. Le contrôle y sera 'ancré', il on modifie les dimensions de la fenêtre, le contrôle suivra.

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

Image non disponible

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:

Image non disponible

Et en VB cette fois:

 
Sélectionnez

Dim myButton As New Button() 'création d'un bouton 
myButton.Height = "33"       'modification des dimensions du bouton
myButton.Width = "55"
myButton.HorizontalAlignment = Windows.HorizontalAlignment.Stretch  
myButton.Content = "Button  Stretch" 
Grid1.Children.Add(myButton) 'met le bouton dans le conteneur grid 

Autre exemple:

 
Sélectionnez

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 met myButtom dans un autre gros bouton, cela ne sert à rien, 
'c'est juste pour illustrer la propriété Content. 

Dernier exemple:
Positionnons un TabControl dans une fenêtre; il doit remplir la fenêtre mais laisser voir les boutons en haut.

HorizontalAlignement et VerticalAlignement auront la valeur Stretch pour remplir, Height et Width = Auto pour ne pas forcer une dimension. Pour laisser voir les boutons en haut, on joue sur la Margin supérieure.

Image non disponible

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

Image non disponible

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:

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

 
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 Visual Basic:

 
Sélectionnez

Dim myButton As New Button() 'on crée le bouton nommé 'MyButton'
myButton.Height = "100"      'modification des dimensions du bouton
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 met le bouton dans le conteneur grid

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:

 
Sélectionnez

myButton.Background = New SolidColorBrush(Colors.Red)

On a aussi une énumérations de SolidColorBrush que l'on peut utiliser comme cela:

 
Sélectionnez

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:

 
Sélectionnez

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

Image non disponible

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>

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.

 
Sélectionnez

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

Image non disponible

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, Style

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

 
Sélectionnez

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

Image non disponible

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 donc jamais d'aspect même quand on clique dessus!!

On pourrait créer un Style pour un bouton:

 
Sélectionnez

<Button  Height="51"  Margin="74,35,0,0" Name="Button1"  Width="213" >
           Ok
            <Button.Style>
                <Style>
                    <Setter Property="Button.Background" Value="Red"/>
                </Style>
            </Button.Style>
        </Button>

En fait c'est beaucoup plus simple d'utiliser une propriété (les Styles sont plutôt utilisés dans les ressources):

 
Sélectionnez

<Button 
Height=" 100" Width="200" 
Background="Red" 
Name="mybutton">Beau Bouton</Button> 

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.

 
Sélectionnez

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

Image non disponible

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:

 
Sélectionnez

<Button Background="Red"/> 

En VB:

 
Sélectionnez

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:

 
Sélectionnez

Dim Rect As new Rectangle() 
Rect.Width = 75 
Rect.Height = 75 
'Creation d'une SolidColorBrush  
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:

 
Sélectionnez

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

Image non disponible

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.

Image non disponible

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:

 
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> 

En VB:

 
Sélectionnez

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.

Image non disponible

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>

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:

Image non disponible

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.

Image non disponible

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

 
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.

Image non disponible

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.

Image non disponible

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 3 couleurs.

Image non disponible

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 > 

En VB:

 
Sélectionnez

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:

 
Sélectionnez

<Rectangle Width="75" Height="75">  
<Rectangle.Fill>  
<ImageBrush ImageSource="c:\graphe.bmp" />  
</Rectangle.Fill>  
</Rectangle> 

En VB:

 
Sélectionnez

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).
- Couleurs, brush, style, ControlTemplate(modification de l'aspect du contrôle), DataTemplate(modification de l'affichage des données) ressources dites 'internes'.

Nous allons donc 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.

 
Sélectionnez

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

 
Sélectionnez

<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}.
Seul le contrôle utilisant la ressource la prendra en compte!!

 
Sélectionnez

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

En VB

 
Sélectionnez

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.

 
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> 

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

-Dans une fenêtre, Entre les balises:

 
Sélectionnez

  <Window.Resources>

  </Window.Resources>

-Dans le fichier Application avec la balise:

 
Sélectionnez

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

 
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 Application la référence au dictionnaire.

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

<Window.Resources>
    <SolidColorBrush x:Key="Mybrush" Color="Red" />
</Window.Resources>
 

<Button x:Name="btn"Content="Find Position" Click="Button_Click" />
 
Sélectionnez

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.

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

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

 
Sélectionnez

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

 
Sélectionnez

<Style TargetType="TextBlock" x:Key="TitleText">

Pour appliquer le style à un TextBlock particulier, il faut écrire ensuite:

 
Sélectionnez

<TextBlock Style="{StaticResource TitleText}">Titre</TextBlock>

On vient d'utiliser un Setter pour modifier une propriété, mais il est possible dans un Style de mettre un DataTemplate, un ControlTemplate, un EventTrigger ou un Trigger.
Voyons un exemple avec un Trigger.

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:

 
Sélectionnez

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

 
Sélectionnez

Button1.Style = Resources("TitleText")
ou
textblock1.Style = CType(Me.Resources("TitleText"), Style)
'Option Explicit= On  obligeant un Casr de Objet à Style)

On peut étendre un style existant:

 
Sélectionnez

<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

On a vu plus haut qu'on pouvait mettre un Template dans un contrôle:

 
Sélectionnez

<Button>
  <Button.Template>
  <ControlTemplate>
  
  <ControlTemplate>
  </Button.Template>
</Button>

Mais on peut aussi créer des Modèle de contrôles 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.

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:

 
Sélectionnez

<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template"/>

puis définir une ellipse.

Cela donne:

 
Sélectionnez

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

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

 
Sélectionnez

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

Image non disponible

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 pour le détail.

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 (pour les données, on utilise un Binding; voir plus loin).

 
Sélectionnez

<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.Triggers>
                <DataTrigger Binding="{Binding Path=SEXE}">
                    <DataTrigger.Value>F</DataTrigger.Value>
                    <Setter TargetName="border" Property="BorderBrush" Value="Pink"/>
                </DataTrigger>
            </DataTemplate.Triggers>

        </DataTemplate>
    </Window.Resources>
Image non disponible

Comment afficher une liste d'images?
(Mesphotos étant une liste de noms d'image; on montre ici le principe simplement).

 
Sélectionnez

<ListBox ItemsSource="{Binding Source={StaticResource MesPhotos}}"
         Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>

Ici on affiche la liste des noms d'image.
Comment afficher les images elle mêmes? en créant un DataTemplate:

 
Sélectionnez

<Window.Resources>

...

<!--DataTemplate to display Photos as images
    instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
  <Border Margin="3">
    <Image Source="{Binding Source}"/>
  </Border>
</DataTemplate>

...

</Window.Resources>

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'

Image non disponible

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.

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

<Application.Resources>
<ImageSource x:Key= "someImage">noel.bmp</ImageSource>
</Application.Resources>

ooo-Mettre la ressource dans une image

 
Sélectionnez

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

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

 
Sélectionnez

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

 
Sélectionnez

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}":

 
Sélectionnez

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

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

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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

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

 
Sélectionnez

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

 
Sélectionnez

<ListBox Name="listbox1" ItemsSource ="{Binding}">            
</ListBox>

Voici le code VB: ListBox1.DataContext = coll indique quelle est la source de la liaison:

 
Sélectionnez

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.

 
Sélectionnez

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'

 
Sélectionnez

Imports System.Collections.ObjectModel


Public Class Names
    Inherits ObservableCollection(Of NomPerson)

    ' Methods
    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
    ' Methods
    Public Sub New(ByVal first As String, ByVal last As String)
        Me._Prenom = first
        Me._Nom = last
    End Sub


    ' Properties
    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


    ' Fields
    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:

 
Sélectionnez

xmlns:m="clr-namespace:Wpfbindingcoll"

Il faut aussi mettre la collection dans les ressources :

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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:

 
Sélectionnez

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

Image non disponible

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:

 
Sélectionnez

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:

 
Sélectionnez

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

 
Sélectionnez

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:

 
Sélectionnez

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

 
Sélectionnez

Imports System
Imports System.Data
Imports System.Data.OleDb

    Class Window1
    'Déclarer la connexion
    Private ObjetConnection As OleDbConnection
    ' Déclaration l'Objet Commande 
    Private ObjetCommand As OleDbCommand
    ' Déclaration Objet DataAdapter
    Private ObjetDataAdapter As OleDbDataAdapter
    ' Déclaration Objet DataSet
    Private ObjetDataSet As New DataSet
    'String contenant la 'Requête SQL' 
    Private strSql As String
    ' Déclaration Objet DataTable
    Private ObjetDataTable As DataTable
    'Paramètres de connexion à la DB
    Private strConn As String


    Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) _
    Handles Button1.Click

    'Initialisation de la chaine de paramètres pour la connexion
    strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source= c:\nom.mdb;"
    'Initialisation de la chaîne contenant l'instruction SQL
    strSql = "SELECT FICHEPATIENT.* FROM FICHEPATIENT"
    'Instanciation d'un Objet Connexion
     ObjetConnection = New OleDbConnection
    'Donner à la propriété ConnectionString les paramètres de connexion
    ObjetConnection.ConnectionString = strConn
    'Ouvrir la connexion
     ObjetConnection.Open()
    'Instancier un objet Commande
    ObjetCommand = New OleDbCommand(strSql)
    'Instancier un objet Adapter
    ObjetDataAdapter = New OleDbDataAdapter(ObjetCommand)
    'initialiser l'objet Command
    ObjetCommand.Connection() = ObjetConnection
    'Avec l'aide de la propriété Fill du DataAdapter charger le DataSet
    ObjetDataAdapter.Fill(ObjetDataSet, "FICHEPATIENT")

    'Indiquer au ListBox d'afficher le DataSet 

        ListBox1.DataContext = ObjetDataSet 

     End Sub
End Class

Ensuite dans le code XAML il faut indiquer dans 'ItemsSource' quelle table afficher grâce à Path :

 
Sélectionnez

<ListBox  Name="ListBox1" ItemsSource="{Binding Path=FICHEPATIENT}"  />

Maintenant cela devrait marcher car la liaison est correcte: on clique sur le bouton et on voit:

Image non disponible

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

 
Sélectionnez

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

 
Sélectionnez

  <ListBox  Name="ListBox1" ItemsSource="{Binding Path=FICHEPATIENT}"  
  ItemTemplate  ="{StaticResource MyDataTemplate}"  />

Et là enfin, cela marche et donne:

Image non disponible

Pour résumer, voila le code XAML complet:

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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

Image non disponible

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.

 
Sélectionnez

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

Image non disponible

XI-E-9. Les Triggers, les StoryBoard

On peut déclencher une action quand un évènement se produit.
Pour cela on va utiliser un Trigger (gachette, détente) dans un Style.
Exemple:
On veut modifier le Style des boutons (ici la couleur de l'arrière plan, la taille et le style des caractères) quand un évènement se déclenche ( Ici le survol d'un bouton):

 
Sélectionnez

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style  TargetType="Button">
            <Style.Triggers>
                <Trigger Property="Button.IsMouseOver" Value="true">
                    <Setter Property = "Foreground" Value="Red"/>
                    <Setter Property = "FontSize" Value="20"/>
                    <Setter Property = "FontStyle" Value="Italic"/>
                </Trigger>
            </Style.Triggers>
           
        </Style>

    </Window.Resources>

    <Button  >
       Ok 
    </Button>
</Window>

Ainsi quand la souris passe sur le bouton, les caractères deviennent rouge en italique et augmentent de taille.

Attention: Quand la condition n'est plus remplie, les Setter n'agissent plus et les caractères redeviennent comme avant.

S'il y a plusieurs conditions il faut utiliser les multiTriggers:

 
Sélectionnez

<MultiTrigger>
<MultiTrigger.Conditions>
  <Condition Property="IsMouseOver" Value="True" />
  <Condition Property="IsFocused" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Button.Foreground" Value="Red" />
</MultiTrigger>

On peut aussi mettre un Trigger sur la valeur d'une donnée, on parle de DataTrigger:

 
Sélectionnez

<DataTrigger Binding="{Binding Path=Sexe}" Value="F">
<Setter Property="Button.Foreground" Value="Pink" />
</DataTrigger>

On met le DataTrigger sur le champ 'Sexe'; si celui-ci est égalà 'F', on affiche en Rose!!

On peut mettre les triggers dans un Style mais aussi dans un Template; on ne peut pas le mettre directement dans un contrôle (du moins je n'y suis pas arrivé).

On peut aussi mettre un Trigger sur un évènement (eventTrigger) mais comme action, on utilise un StoryBoard (scénarimage).
(Quand l'évènement se produit, il n'y a pas de retour à l'état antérieur comme avec le Trigger simple; le StoryBoard permet lui un changement avec un temps donné).

Ici on va créer dans les ressources de la fenêtre un style pour les boutons; il contient un EventTrigger déclenché par le Click et qui exécute un StoryBoard effectuant une animation qui augmente la largeur du bouton puis une animation qui la réduit:

 
Sélectionnez

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style  TargetType="Button">
            <Style.Triggers>            
                        <EventTrigger RoutedEvent="Click">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation To="150" Duration="0:0:1.5" 
                                    AccelerationRatio="0.10" DecelerationRatio="0.25" 
                                    Storyboard.TargetProperty="(Button.Width)" />
                                    <DoubleAnimation To="100" Duration="0:0:1.5" 
                                    AccelerationRatio="0.10" DecelerationRatio="0.25" 
                                    Storyboard.TargetProperty="(Button.Width)" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>       
            </Style.Triggers>
        </Style>
    </Window.Resources>

<Grid>
 <Button  HorizontalAlignment="Left" Margin="4,0,0,0" Name="Button1" VerticalAlignment="Top" Width="100"  Height="100">
 </Button>
</Grid>
</Window>

Détaillons la notion de StoryBoard:

Dans le StoryBoard suivant DoubleAnimation permet d'animer la valeur d'une propriété de type 'Double' sur un Duration spécifié. Il existe aussi ColorAnimation, PointAnimation.
On peut utiliser dans le storyBoard From (Valeur de départ), To (valeur d'arrivée), Duration="H:M:S" (durée) des AcceleratorRatio ou DeceleratorRatio.
Pour spécifier une valeur finale par rapport à la valeur de départ, définissez la propriété By(au lieu de la propriété To).
On peut ajouter AutoReverse= True pour un retour à l'état initial et RepeatBehavior= "Forever" pour que l'animation se répete sans fin.

 
Sélectionnez

<Storyboard>
  <DoubleAnimation From="1.0" To="0.0" Duration="0:0:1" 
    AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>

On peut ajouter un BeginTime pour démarrer le StoryBoard au bout d'un certain temps.

 
Sélectionnez

<Storyboard BeginTime="0:0:2" x:Name="myStoryboard">
</StoryBoard>

TargetName indique l'objet à animer.
TargetProperty indique sur quelle propriété agir.
Ici on va agir sur un rectangle nommé 'MonRectangle' et sur sa propriété 'Opacity'.

 
Sélectionnez

<Storyboard>
  <DoubleAnimation
    Storyboard.TargetName="MonRectangle" 
    Storyboard.TargetProperty="Opacity"
    From="1.0" To="0.0" Duration="0:0:1" 
    AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>

Pour que le StoryBoard démarre il faut utiliser Begin.
- Soit dans le code XAML:

 
Sélectionnez

<BeginStoryboard>
   <Storyboard>
   </Storyboard>
</BeginStoryboard>

- Soit avec du code vb.

 
Sélectionnez

myStoryboard.Begin()

Dans ce cas le StoryBoard doit avoir un x:Name:

 
Sélectionnez

<Storyboard x:Name="myStoryboard">
</StoryBoard>

En plus de Begin , il existe Stop, Resume, Pause.

On peut aussi effectuer des 'Transformation': Un exemple de Microsoft sur la rotation d'un rectangle:

 
Sélectionnez

<StackPanel Margin="15">
  <StackPanel.Resources>
    <Storyboard x:Name="myStoryboard">
      <DoubleAnimation
       Storyboard.TargetName="myTransform"
       Storyboard.TargetProperty="Angle"
       From="0" To="360" Duration="0:0:5" 
       RepeatBehavior="Forever" />
    </Storyboard>
  </StackPanel.Resources>
  <Rectangle Width="50" Height="50" Fill="RoyalBlue"
   MouseLeftButtonDown="StartAnimation">
    <Rectangle.RenderTransform>
      <RotateTransform x:Name="myTransform" Angle="45" CenterX="25" CenterY="25" />
    </Rectangle.RenderTransform>
  </Rectangle>
</StackPanel>

On peut ajouter des effets d'accélération: Ici un effet rebondissant avec BouceEase.

 
Sélectionnez

<Storyboard x:Name="myStoryboard">
            <DoubleAnimation From="30" To="200" Duration="00:00:3" 
                Storyboard.TargetName="myRectangle" 
                Storyboard.TargetProperty="Height">
                <DoubleAnimation.EasingFunction>
                    <BounceEase Bounces="2" EasingMode="EaseOut" 
                                Bounciness="2" />
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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 © . 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'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.