À la découverte d'Avalon Partie III : Les contrôles usuels dans Avalon

Jean-Alain Baeyens

Après avoir découvert dans les articles précédents comment installer et utiliser WinFX avec XAML et .NET, nous allons explorer les contrôles usuels sous « Windows Presentation Foundation » (Avalon). Dans cette partie, nous entrons de plain-pied dans le monde des API WINFX d'Avalon et dans le concept XAML. Au terme de ce tutoriel, excepté les menus qui seront abordés ultérieurement, vous devriez être capable de créer vous-même les interfaces utilisateur les plus courantes. ♪

- Titre : À la découverte d'Avalon Partie III : Les contrôles usuels dans Avalon

- Auteur : Jean-Alain Baeyens

- Parution : 30 septembre 2005

- Licence : 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 ® 2005 Jean-Alain Baeyens. 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.

Sommaire

- I. Avertissement

- II. Remerciements

- III. Introduction

- IV. Le contrôle de type « Label »

- V. Le contrôle de type « TextBox »

- VI. Le contrôle de type « TextBlock »

- VII. Le contrôle de type « Button »

- VIII. Le contrôle de type « RadioButton »

- IX. Le contrôle de type « CheckBox »

- X. Le contrôle de type « ListBox »

- XI. Le contrôle de type « ComboBox »

- XII. Le contrôle de type « Image »

- XIII. Le binding

- XIII-A. Dans le code C#

- XIII-B. Dans le code XAML

- XIV. Conclusion

I - Avertissement

Cet article fait partie d'un ensemble sur Avalon (« Windows Presentation Foundation »). Ceux-ci, bien qu'écrits séparément, forment un tout. À la manière des chapitres d'un livre, vous pouvez accéder directement au sujet qui vous intéresse, mais pour un aperçu complet, il est préférable de commencer la lecture au premier article.

1re partie : Installation
2epartie : Ma première fenêtre avec Avalon
3e partie : Les contrôles courants
4epartie : Le menu
5epartie : Les modèles « Avalon Express Application » et « Navigation Application »

II - Remerciements

Je remercie la rédaction .NET de developpez.com pour son soutien dans la réalisation de cet article.

III - Introduction

Il ne s'agit pas ici de découvrir toutes les possibilités des différents contrôles possibles, mais bien de parcourir les contrôles courants dans leur utilisation la plus usuelle.

Si vous souhaitez reproduire l'exemple du chapitre, commencez par créer un nouveau projet de type « Avalon Application ».

IV - Le contrôle de type « Label »

Nous avons déjà vu dans la deuxième partie, l'utilisation du contrôle Label. Dans les exemples qui vont suivre en lieu et place du positionnement avec un objet de type Grid, nous allons utiliser un objet de type Canvas. Cela nous permettra de définir la position exacte du contrôle.

Utilisation d'un label avec un canvas

<Window x:Class="AvalonApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les contrôles standard en XAML"
Width="500"
Height="300"
>
<Canvas>
<Label Name="lblNom" Canvas.Top="10" Canvas.Left="10">
Nom:
</Label>
</Canvas>
</Window>

Pour positionner un contrôle avec un Canvas, nous pouvons utiliser les propriétés Canvas.Top, Canvas.Left, Canvas.Right et Canvas.Bottom. Pour le dimensionner, nous utiliserons les propriétés Width et Height.

V - Le contrôle de type « TextBox »

Une « TextBox » se réalise au moyen de l'élément TextBox. Deux façons différentes d'initialiser le contenu directement dans le fichier XAML vous sont présentées dans l'exemple qui suit.

Ajout d'une TextBox à notre fenêtre

<Window x:Class="AvalonApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les contrôles standards en XAML"
Width="500"
Height="300"
>
<Canvas>
<Label Name="lblNom" Canvas.Top="10" Canvas.Left="10">
Nom:
</Label>
<TextBox Name="txtNom" Text="texte par défaut" Canvas.Top="10"
Canvas.Left="100" Width="150" Height="24"/>
<Label Name="lblPrenom" Canvas.Top="40"
Canvas.Left="10">Prénom:</Label>
<TextBox Name="txtPrenom" Canvas.Top="40" Canvas.Left="100"
Width="150" Height="24">texte par défaut</TextBox>
</Canvas>
</Window>

Vous devrez probablement initialiser la valeur des TextBox depuis votre code. Cela peut se faire dans la méthode nommée dans l'exemple « windowLoaded » et associée à l'évènement Loaded de la fenêtre. Cette association se fait dans le nœud Window.

Le noeud Window modifié

<Window x:Class="AvalonApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les contrôles standards en XAML"
Width="500"
Height="300"
Loaded="windowLoaded"
>

La méthode associée à l'évènement Loaded de la fenêtre

private void windowLoaded(object sender, RoutedEventArgs e)
{
this.txtNom.Text = "Dupont";
this.txtPrenom.Text = "Louis";
}

VI - Le contrôle de type « TextBlock »

L'origine du TextBlock est plutôt Web que WinForm. Il fait pourtant son entrée dans l'API disponible pour les applications Windows classiques.

Contrairement à Label, TextBlock n'hérite pas de la classe controle. Texblock est optimisée pour l'affichage de longs textes.

Utilisation d'un TextBlock

<Window x:Class="AvalonApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les contrôles standards en XAML"
Width="500"
Height="300"
>
<Canvas>
<Label Name="lblNom" Canvas.Top="10" Canvas.Left="10">
Nom:
</Label>
<TextBox Name="txtNom" Text="texte par défaut" Canvas.Top="10"
Canvas.Left="100" Width="150" Height="24"/>
<Label Name="lblPrenom" Canvas.Top="40" Canvas.Left="10">
Prénom:
</Label>
<TextBox Name="txtPrenom" Canvas.Top="40" Canvas.Left="100" Width="150"
Height="24">texte par défaut</TextBox>
<TextBlock Name="txtLogo" Background="Gray" Canvas.Top="5"
Canvas.Left="300" Width="150" Height="100"
TextWrap="Wrap" TextAlignment="center" Foreground="blue"
FontWeight="Bold">
www.developpez.com
Section dotnet
</TextBlock>
</Canvas>
</Window>

Si vous désirez initialiser le contenu du TextBlock dans votre code C#, par exemple pour afficher un texte variable, vous devrez utiliser une instruction de ce type :

this.txtLogo.TextContent =
"www.developpez.com  Le meilleur site pour les développereurs
francophones: Section .NET";

Dans l'exemple, je fais cette initialisation dans la méthode « windowLoaded ».

VII - Le contrôle de type « Button »

Comme nous avons déjà abordé les boutons dans le chapitre précédent et que leur utilisation est des plus simples, nous allons en profiter pour voir comment les centrer sans effort malgré que nous ayons choisi de travailler avec un Canvas.

La solution la plus simple est de définir une grille. Il est en effet tout à fait possible de mixer les deux techniques. Nous allons donc définir une grille de deux lignes et deux colonnes. La ligne du bas est fixée à une hauteur de 60 pixels. Nous positionnons le Canvas dans la première ligne et les boutons dans la deuxième, respectivement dans la première et la deuxième colonne. La grille fait le reste.

Positionnement des boutons avec une grille.

<Window x:Class="AvalonApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les contrôles standard en XAML"
Width="500"
Height="300"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<Canvas Grid.Row="0">
<Label Name="lblNom" Canvas.Top="10" Canvas.Left="25">
Nom:
</Label>
<TextBox Name="txtNom" Text="texte par défaut" Canvas.Top="10"
Canvas.Left="100" Width="150" Height="24"/>
<Label Name="lblPrenom" Canvas.Top="40" Canvas.Left="25">
Prénom:
</Label>
<TextBox Name="txtPrenom" Canvas.Top="40" Canvas.Left="100"
Width="150" Height="24">texte par défaut</TextBox>
<TextBlock Name="txtLogo" Background="Gray" Canvas.Top="5"
Canvas.Left="300" Width="150" Height="100"
TextWrap="Wrap" TextAlignment="center" Foreground="blue"
FontWeight="Bold">
www.developpez.com
Section dotnet
</TextBlock>
</Canvas>
<Button Name="btnOk" Grid.Row="1" Grid.Column="0" Height="20"
Width="80">Ok</Button>
<Button Name="btnCancel" Grid.Row="1" Grid.Column="1" Height="20"
Width="80">Annuler</Button>
</Grid>
</Window>

Le code C# ci-après est associé aux évènements Click des boutons. Remarquez que pour permettre d'assigner une valeur à DialogResult, nous devons afficher la fenêtre au moyen de la méthode ShowDialog et non plus Show. Vous trouverez l'instruction à changer dans la méthode « AppStartingUp » de la classe « MyApp » dans le fichier « MyApp.xaml.cs ». Dans la pratique, vous appellerez rarement une fenêtre de dialogue directement lors du chargement de l'application.

Le code C# associé

private void clickOk(object sender, RoutedEventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}

private void clickCancel(object sender, RoutedEventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}

Il est temps pour nous de découvrir ce que nous avons créé.

VIII - Le contrôle de type « RadioButton »

Les RadioButton sont généralement groupés pour offrir un choix. Nous devons donc les inclure dans un ensemble plus grand le RadioButtonList. Les RadioButton sont alors des nœuds fils.

<RadioButtonList Canvas.Top="100" Canvas.Left="30">
<RadioButton Name="rbFemme" Height="20" FontSize="10"
IsChecked="True">Madame</RadioButton>
<RadioButton Name="rbHomme" FontSize="10">Monsieur</RadioButton>
</RadioButtonList>

IsChecked est optionnel, mais permet de définir une valeur par défaut. À la place, vous pouvez utiliser la propriété SelectedIndex du RadioButtonList pour initialiser la valeur. Dans cet exemple, nous l'aurions initialisé à 0.

Si vous ne placez pas les RadioButton dans un RadioButtonList, ils fonctionnent indépendamment les uns des autres à la manière des CheckBox.

Vous pouvez également initialiser un RadioButtonList depuis le code C#.

En utilisant IsChecked

rbFemme.IsChecked = true;

En utilisant SelectedIndex

rblSexe.SelectedIndex = 0;

IX - Le contrôle de type « CheckBox »

L'utilisation d'une CheckBox se fait très simplement depuis le code XAML.

<CheckBox Name="chkNewsLetter" Canvas.Top="170" Canvas.Left="30"
Height="24">S'abonner à la lettre d'information</CheckBox>

Comme pour le RadioButton, vous pouvez utiliser la propriété IsChecked pour initialiser votre contrôle que ce soit dans le code XAML ou le code C#.

X - Le contrôle de type « ListBox »

Pour créer une ListBox, on utilise le tag du même nom. Les différents éléments de la ListeBox, sont définis par des nœuds fils de type ListBoxItem.

<ListBox Name="lstLangue" Canvas.Top="100" Canvas.Left="200">
<ListBoxItem>Français</ListBoxItem>
<ListBoxItem>Anglais</ListBoxItem>
<ListBoxItem>Allemand</ListBoxItem>
<ListBoxItem>Néerlandais</ListBoxItem>
</ListBox>

Pour initialiser une valeur par défaut, vous pouvez utiliser la propriété SelectedIndex soit dans le code XAML, soit dans le code C#.

Si vous désirez charger la liste des valeurs de la ListBox dans l'évènement Loaded (dans l'exemple, la méthode « windowLoaded »), vous pouvez remplacer le code XAML :

<ListBoxItem>Français</ListBoxItem>
<ListBoxItem>Anglais</ListBoxItem>
<ListBoxItem>Allemand</ListBoxItem>
<ListBoxItem>Néerlandais</ListBoxItem>

par le code C# suivant :

lstLangue.Items.Add("Français");
lstLangue.Items.Add("Anglais");
lstLangue.Items.Add("Allemand");
lstLangue.Items.Add("Néerlandais");

Si les valeurs possibles sont déjà contenues dans une collection, par exemple un ArrayList nommé « langues », vous pouvez également utiliser cette syntaxe :

lstLangue.ItemsSource = langues;

XI - Le contrôle de type « ComboBox »

Une ComboBox fonctionne sur le même principe qu'une ListBox excepté le fait que l'on utilise les classes ComboBox et ComboBoxItem.

<Label Name="lblType" Canvas.Top="190" Canvas.Left="25">
Activité:
</Label>
<ComboBox Name="cboType" Canvas.Top="190" Canvas.Left="100">
<ComboBoxItem>Développement</ComboBoxItem>
<ComboBoxItem>Systèmes d'exploitation</ComboBoxItem>
<ComboBoxItem>Réseaux</ComboBoxItem>
<ComboBoxItem>Bases de données</ComboBoxItem>
<ComboBoxItem>Autres</ComboBoxItem>
</ComboBox>

XII - Le contrôle de type « Image »

Il est possible d'inclure une image dans votre fenêtre. Pour ce faire, utilisez la classe Image.

<Image Name="imgLogo" Canvas.Top="110" Canvas.Left="310">
<Image.Source>
<Binding Source="logodvp.gif"/>
</Image.Source>
</Image>

Il est également possible de charger l'image depuis le code C#.

Charger l'image dans le code C#

private void windowLoaded(object sender, RoutedEventArgs e)
{
BitmapImage logo = new BitmapImage(new Uri(@"logodvp.gif"));
this.imgLogo.Source = logo;
}

Découvrons notre exemple ainsi complété.

XIII - Le binding

Plutôt que de charger vous-même les valeurs dans les contrôles de votre fenêtre, vous désirerez surement utiliser le binding. Pour l'exemple, nous allons effectuer le binding des contrôles écrans avec un objet contenant les données. Il est, dans le cadre de l'exemple, extrêmement simplifié.

Classe pour l' objet contenant les données.

class DataClass
{
private string nom = "Mon nom";
public string Nom
{
get { return nom; }
set { nom = value; }
}

private string prenom = "Mon prenom";
public string Prenom
{
get { return prenom; }
set { prenom = value; }
}

private int sexe = 1;
public int Sexe
{
get { return sexe; }
set { sexe = value; }
}

private int langue = 3;
public int Langue
{
get { return langue; }
set { langue = value; }
}

private int sujet = 3;
public int Sujet
{
get { return sujet; }
set { sujet = value; }
}

private bool news = true;
public bool News
{
get { return news; }
set { news = value; }
}
}

XIII-A - Dans le code C#

Il est possible de réaliser le binding de vos données en utilisant la méthode SetBinding.

Vous pouvez constater qu'il est possible de faire un bind sur n'importe quelle propriété de votre contrôle. On peut parfaitement grâce au binding, changer la couleur du font sur base de vos données.

Méthode associée à l'évènement Loaded

private void windowLoaded(object sender, RoutedEventArgs e)
{
Binding bindNom = new Binding("Nom");
bindNom.Source = dataObject;
this.txtNom.SetBinding(TextBox.TextProperty, bindNom);

Binding bindPrenom = new Binding("Prenom");
bindPrenom.Source = dataObject;
this.txtPrenom.SetBinding(TextBox.TextProperty, bindPrenom);

Binding bindLangue = new Binding("Langue");
bindLangue.Source = dataObject;
this.lstLangue.SetBinding(ListBox.SelectedIndexProperty, bindLangue);

Binding bindType = new Binding("Sujet");
bindType.Source = dataObject;
this.cboType.SetBinding(ComboBox.SelectedIndexProperty, bindType);

Binding bindSexe = new Binding("Sexe");
bindSexe.Source = dataObject;
this.rblSexe.SetBinding(RadioButtonList.SelectedIndexProperty, bindSexe);

Binding bindNews = new Binding("News");
bindNews.Source = dataObject;
this.chkNewsLetter.SetBinding(CheckBox.IsCheckedProperty, bindNews);

this.txtLogo.TextContent =
"www.developpez.com  Le meilleur site pour les développereurs
francophones: Section .NET";
}

Il est également possible de définir une source pour un ensemble de contrôles. Nous pouvons par exemple définir la source pour le Canvas. Pour cela nous devons en premier lieu le nommer.

Nommer le Canvas

<Canvas Grid.Row="0" Name="dataZone">

Une fois cette opération réalisée, il ne reste plus qu'à utiliser la propriété DataContext de l'objet Canvas. Grâce à cela, il n'est plus nécessaire d'assigner la propriété Source des objets Binding.

Renseigner la source pour l'ensemble du Canvas

private void windowLoaded(object sender, RoutedEventArgs e)
{
dataZone.DataContext = dataObject;
Binding bindNom = new Binding("Nom");
this.txtNom.SetBinding(TextBox.TextProperty, bindNom);

Binding bindPrenom = new Binding("Prenom");
this.txtPrenom.SetBinding(TextBox.TextProperty, bindPrenom);

Binding bindLangue = new Binding("Langue");
this.lstLangue.SetBinding(ListBox.SelectedIndexProperty, bindLangue);

Binding bindType = new Binding("Sujet");
this.cboType.SetBinding(ComboBox.SelectedIndexProperty, bindType);

Binding bindSexe = new Binding("Sexe");
this.rblSexe.SetBinding(RadioButtonList.SelectedIndexProperty, bindSexe);

Binding bindNews = new Binding("News");
this.chkNewsLetter.SetBinding(CheckBox.IsCheckedProperty, bindNews);

this.txtLogo.TextContent =
"www.developpez.com  Le meilleur site pour les développeurs
francophones : Section .NET";
}

XIII-B - Dans le code XAML

Plutôt que de réaliser le Binding dans le code dotnet, nous pouvons le réaliser au niveau du code XAML. Vous pouvez le faire en ajoutant des nœuds fils à votre contrôle (voir la TextBox « Nom ») soit par inclusion dans les attributs du nœud existant (les autres contrôles).

<Window x:Class="AvalonApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les contrôles standard en XAML"
Width="500"
Height="300"
Loaded="windowLoaded"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<Canvas Grid.Row="0" Name="dataZone">
<Label Name="lblNom" Canvas.Top="10" Canvas.Left="25">
Nom:
</Label>
<TextBox Name="txtNom" Canvas.Top="10" Canvas.Left="100"
Width="150" Height="26">
<TextBox.Text>
<Binding Path="Nom"/>
</TextBox.Text>
</TextBox>
<Label Name="lblPrenom" Canvas.Top="40" Canvas.Left="25">
Prénom:
</Label>
<TextBox Name="txtPrenom" Text="{Binding Path=Prenom}"
Canvas.Top="40" Canvas.Left="100" Width="150" Height="24">
texte par défaut
</TextBox>
<TextBlock Name="txtLogo" Background="Gray" Canvas.Top="5"
Canvas.Left="300" Width="150" Height="100"
TextWrap="Wrap" TextAlignment="Center"
Foreground="blue" FontWeight="Bold">
www.developpez.com
Section dotnet
</TextBlock>
<RadioButtonList Name="rblSexe" SelectedIndex="{Binding Path=Sexe}"
Canvas.Top="100" Canvas.Left="30">
<RadioButton Name="rbFemme" Height="20" FontSize="10"
IsChecked="True">
Madame
</RadioButton>
<RadioButton Name="rbHomme" FontSize="10">
Monsieur
</RadioButton>
</RadioButtonList>
<Label Name="lblLangue" Canvas.Top="95" Canvas.Left="140">
Langue:
</Label>
<ListBox Name="lstLangue"  SelectedIndex="{Binding Path=Langue}"
Canvas.Top="100" Canvas.Left="200" Loaded="lstLangueLoaded">
<ListBoxItem>Français</ListBoxItem>
<ListBoxItem>Anglais</ListBoxItem>
<ListBoxItem>Allemand</ListBoxItem>
<ListBoxItem>Néerlandais</ListBoxItem>
</ListBox>
<CheckBox Name="chkNewsLetter" IsChecked="{Binding Path=News}"
Canvas.Top="170" Canvas.Left="30" Height="24">
S'abonner Ã  la lettre d'information
</CheckBox>
<Label Name="lblType" Canvas.Top="190" Canvas.Left="25">
Activité:
</Label>
<ComboBox Name="cboType" SelectedIndex="{Binding Path=Sujet}"
Canvas.Top="190" Canvas.Left="100">
<ComboBoxItem>Développement</ComboBoxItem>
<ComboBoxItem>Systèmes d'exploitation</ComboBoxItem>
<ComboBoxItem>Réseaux</ComboBoxItem>
<ComboBoxItem>Bases de données</ComboBoxItem>
<ComboBoxItem>Autres</ComboBoxItem>
</ComboBox>
<Image Name="imgLogo" Canvas.Top="110" Canvas.Left="310">
<Image.Source>
<Binding Source="logodvp.gif"/>
</Image.Source>
</Image>
</Canvas>
<Button Name="btnOk" Grid.Row="1" Grid.Column="0" Height="20"
Width="80" Click="clickOk">Ok</Button>
<Button Name="btnCancel" Grid.Row="1" Grid.Column="1" Height="20"
Width="80" Click="clickCancel">Annuler</Button>
</Grid>
</Window>

Notre méthode exécutée au chargement de la fenêtre s'en trouve fortement simplifiée.

La méthode windowLoad déchargée de la gestion du Binding

private void windowLoaded(object sender, RoutedEventArgs e)
{
dataZone.DataContext = dataObject;
this.txtLogo.TextContent =
"www.developpez.com  Le meilleur site pour les développereurs
francophones: Section .NET";
}

Remarquez que l'assignation du texte à la propriété TextContent aurait aussi pu se faire via le binding.

XIV - Conclusion

Bien sûr, nous n'avons vu qu'un aperçu du potentiel de ces différentes classes et des nouvelles techniques associées à XAML. Toutefois, excepté les menus que nous découvrirons dans le prochain chapitre, cette partie permet déjà de réaliser un bon nombre des interfaces utilisateurs et je ne doute pas qu'avec un peu de perspicacité, vous découvrirez comment utiliser les nombreuses autres propriétés et méthodes de ces classes.

J'espère vous compter encore parmi les lecteurs pour la suite à paraitre très prochainement.