• Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 29/04/2014

Les conteneurs et le placement

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Dans notre Hello World, lorsque nous avons parlé du contrôle TextBlock, nous avons dit qu’il faisait partie du contrôle StackPanel qui lui-même faisait partie du contrôle Grid.
Ces deux contrôles sont en fait des conteneurs de contrôles dont l’objectif est de regrouper des contrôles de différentes façons.

Les contrôles conteneurs vont être très utiles pour organiser le look et l'agencement de nos pages. Il y en a quelques uns indispensables à découvrir qui vont constamment vous servir lors des vos développements. Nous allons à présent les découvrir et voir comment nous en servir.

StackPanel

Le StackPanel par exemple, comme son nom peut le suggérer, permet d’empiler les contrôles les uns à la suite des autres. Dans l’exemple suivant, les contrôles sont ajoutés les uns en-dessous des autres :

<StackPanel>
    <TextBlock Text="Bonjour à tous" />
    <Button Content="Cliquez-moi" />
    <Image Source="http://www.siteduzero.com/images/designs/2/logo_sdz_fr.png?normal" />
</StackPanel>

Ce qui donne la figure suivante.

Empilement vertical des contrôles grâce au StackPanel
Empilement vertical des contrôles grâce au StackPanel

Où nous affichons un texte, un bouton et une image. Nous verrons un peu plus précisément le contrôle Image dans le chapitre suivant.

Notons au passage que Visual Studio et l’émulateur peuvent très facilement récupérer des contenus sur internet, sauf si vous utilisez un proxy. Ici par exemple, en utilisant l’URL d’une image, je peux l’afficher sans problème dans mon application, si celle-ci est connectée à internet bien sûr.

Le contrôle StackPanel peut aussi empiler les contrôles horizontalement. Pour cela, il suffit de changer la valeur d’une de ses propriétés :

<StackPanel Orientation="Horizontal">
    <TextBlock Text="Bonjour à tous" />
    <Button Content="Cliquez-moi" />
    <Image Source="http://www.siteduzero.com/images/designs/2/logo_sdz_fr.png?normal" />
</StackPanel>

Ici, nous avons changé l’orientation de l’empilement pour la mettre en horizontal. Ce qui donne la figure suivante.

Empilement horizontal des contrôles
Empilement horizontal des contrôles

Ce qui ne rend pas très bien ici… Pour l’améliorer, nous pouvons ajouter d’autres propriétés à nos contrôles, notamment les réduire en hauteur ou en largeur grâce aux propriétés Height ou Width. Par exemple :

<StackPanel Orientation="Horizontal" Height="40">
    <TextBlock Text="Bonjour à tous" />
    <Button Content="Cliquez-moi" />
    <Image Source="http://www.siteduzero.com/images/designs/2/logo_sdz_fr.png?normal" />
</StackPanel>

Ici, j’ai rajouté une hauteur pour le contrôle StackPanel, en fixant la propriété Height à 40 pixels, ce qui fait que tous les sous-contrôles se sont adaptés à cette hauteur. Nous aurons donc la figure suivante.

Les contrôles ont la taille fixée à 40 pixels
Les contrôles ont la taille fixée à 40 pixels

Par défaut, le contrôle StackPanel essaie d’occuper le maximum de place disponible dans la grille dont il fait partie. Comme nous avons forcé la hauteur, le StackPanel va alors se centrer. Il est possible d’aligner le StackPanel en haut grâce à la propriété VerticalAlignment :

<StackPanel Orientation="Horizontal" Height="40" VerticalAlignment="Top">
    <TextBlock Text="Bonjour à tous" />
    <Button Content="Cliquez-moi" />
    <Image Source="http://www.siteduzero.com/images/designs/2/logo_sdz_fr.png?normal" />
</StackPanel>

Ce qui donne la figure suivante.

Alignement vertical en haut du StackPanel
Alignement vertical en haut du StackPanel

Nous allons revenir sur l’alignement un peu plus loin. Voilà pour ce petit tour du StackPanel !

ScrollViewer

Il existe d’autres conteneurs, voyons par exemple le ScrollViewer.
Il nous sert à rendre accessible des contrôles qui pourraient être masqués par un écran trop petit. Prenons par exemple ce code XAML :

<ScrollViewer>
    <StackPanel>
        <TextBlock Text="Bonjour à tous 1" Margin="40" />
        <TextBlock Text="Bonjour à tous 2" Margin="40" />
        <TextBlock Text="Bonjour à tous 3" Margin="40" />
        <TextBlock Text="Bonjour à tous 4" Margin="40" />
        <TextBlock Text="Bonjour à tous 5" Margin="40" />
        <TextBlock Text="Bonjour à tous 6" Margin="40" />
        <TextBlock Text="Bonjour à tous 7" Margin="40" />
    </StackPanel>
</ScrollViewer>

Nous créons 7 contrôles TextBlock, contenant une petite phrase, qui doivent se mettre les uns en-dessous des autres. Vous aurez deviné que la propriété Margin permet de définir une marge autour du contrôle, j’y reviendrai.
Si nous regardons le résultat, nous pouvons constater qu’il nous manque un TextBlock (voir la figure suivante).

Il manque un contrôle à l'écran
Il manque un contrôle à l'écran

Vous vous en doutez, il s’affiche trop bas et nous ne pouvons pas le voir sur l’écran car il y a trop de choses.
Le ScrollViewer va nous permettre de résoudre ce problème. Ce contrôle gère une espèce de défilement, comme lorsque nous avons un ascenseur dans nos pages web. Ce qui fait qu’il sera possible de naviguer de haut en bas sur notre émulateur en cliquant sur l’écran et en maintenant le clic tout en bougeant la souris de haut en bas (voir la figure suivante).

Le ScrollViewer permet de faire défiler l'écran
Le ScrollViewer permet de faire défiler l'écran

Vous pouvez également vous amuser à faire défiler le ScrollViewer horizontalement, mais il vous faudra changer une propriété :

<ScrollViewer HorizontalScrollBarVisibility="Auto">
    <StackPanel Orientation="Horizontal">
        ...

Grid

Parlons à présent du contrôle Grid. C’est un contrôle très utilisé qui va permettre de positionner d’autres contrôles dans une grille. Une grille peut être définie par des colonnes et des lignes. Il sera alors possible d’indiquer dans quelle colonne ou à quelle ligne se positionne un contrôle. Par exemple, avec le code suivant :

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="O" FontSize="50" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="O" FontSize="50" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="O" FontSize="50" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="X" FontSize="50" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="O" FontSize="50" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="X" FontSize="50" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="O" FontSize="50" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="X" FontSize="50" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="X" FontSize="50" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>

Je définis une grille composée de 3 lignes sur 3 colonnes. Dans chaque case je pose un TextBlock avec une valeur qui me simule un jeu de morpion. Ce qu’il est important de remarquer ici c’est que je définis le nombre de colonnes grâce à ColumnDefinition :

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

De la même façon, je définis le nombre de lignes grâce à RowDefinition :

<Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="*" />
    <RowDefinition Height="*" />
</Grid.RowDefinitions>

Il y a donc 3 colonnes et 3 lignes. Chaque colonne a une largeur d’un tiers de l’écran. Chaque ligne a une hauteur d’un tiers de l’écran. Je vais y revenir juste après.
Pour indiquer qu’un contrôle est à la ligne 1 de la colonne 2, j’utiliserai les propriétés Grid.Row et Grid.Column avec les valeurs 1 et 2. (À noter qu’on commence à numéroter à partir de 0, classiquement).
Ce qui donnera la figure suivante.

Une grille de 3x3
Une grille de 3x3

Pratique non ?
Nous pouvons voir aussi que dans la définition d’une ligne, nous positionnons la propriété Height. C’est ici que nous indiquerons la hauteur de chaque ligne. C’est la même chose pour la largeur des colonnes, cela se fait avec la propriété Width sur chaque ColomnDefinition.

Ainsi, en utilisant l’étoile, nous avons dit que nous voulions que le XAML s’occupe de répartir toute la place disponible. Il y a trois étoiles, chaque ligne et colonne a donc un tiers de la place pour s’afficher.
D’autres valeurs sont possibles. Il est par exemple possible de forcer la taille à une valeur précise. Par exemple, si je modifie l’exemple précédent pour avoir :

<Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="200" />
    <RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="100" />
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>

J’indiquerai ici que la première colonne aura une taille fixe de 100, la troisième une taille fixe de 50 et la deuxième prendra la taille restante.
De la même façon, pour les lignes, la deuxième est forcée à 200 et les deux autres se répartiront la taille restante, à savoir la moitié chacune.

J’en profite pour vous rappeler qu’un téléphone Windows Phone 7.5 a une résolution de 480 en largeur et de 800 en hauteur et qu’un téléphone Windows Phone 8 possède trois résolutions :

  • WVGA (800x480 pixels), comme Windows Phone 7.5

  • WXVGA (1280x768)

  • "True 720p" (1280x720)

Ainsi, sur un téléphone en WVGA la deuxième colonne aura une taille de 480 – 100 – 50 = 330. Ce qui donne une grille plutôt disgracieuse, mais étant donné que chaque contrôle est aligné au centre, cela ne se verra pas trop. Pour bien le mettre en valeur, il est possible de rajouter une propriété à la grille lui indiquant que nous souhaitons voir les lignes. Bien souvent, cette propriété ne servira qu’à des fins de débogages. Il suffit d’indiquer :

<Grid ShowGridLines="True">

Par contre, les lignes s’affichent uniquement dans l’émulateur car le designer montre déjà ce que ça donne (voir la figure suivante).

Affichage des lignes de la grille
Affichage des lignes de la grille

Il est bien sûr possible de faire en sorte qu’un contrôle s’étende sur plusieurs colonnes ou sur plusieurs lignes, à ce moment-là, on utilisera la propriété Grid.ColumnSpan ou Grid.RowSpan pour indiquer le nombre de colonnes ou lignes que l’on doit fusionner. Par exemple :

<TextBlock Text="X" FontSize="50" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.ColumnSpan="3" />

à la place de :

<TextBlock Text="X" FontSize="50" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Text="O" FontSize="50" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Text="X" FontSize="50" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" />

Et nous avons donc la figure suivante.

Une grille avec une case s'étirant sur 3 colonnes
Une grille avec une case s'étirant sur 3 colonnes

Avant de terminer sur les lignes et les colonnes, il est important de savoir qu’il existe une autre valeur pour définir la largeur ou la hauteur, à savoir la valeur Auto. Elle permet de dire que c’est la largeur ou la hauteur des contrôles qui vont définir la hauteur d’une ligne ou d’une colonne.
Remarquez que par défaut, un contrôle s’affichera à la ligne 0 et à la colonne 0 tant que son Grid.Row ou son Grid.Column n’est pas défini. Ainsi la ligne suivante :

<TextBlock Text="X" FontSize="50" Grid.Row="0" Grid.Column="0" />

est équivalente à celle-ci :

<TextBlock Text="X" FontSize="50" />

Voilà pour ce petit tour de ce contrôle si pratique qu’est la grille.

Canvas

Nous finirons notre aperçu des conteneurs avec le Canvas. Au contraire des autres conteneurs qui calculent eux même la position des contrôles, ici c’est le développeur qui indique l’emplacement d’un contrôle, de manière relative à la position du Canvas. De plus le Canvas ne calculera pas automatiquement sa hauteur et sa largeur en analysant ses enfants, contrairement aux autres conteneurs. Ainsi si on met dans un StackPanel un Canvas suivi d’un bouton, le bouton sera affiché par-dessus le Canvas, car ce dernier aura une hauteur de 0 bien qu’il possède des enfants.
Ainsi, l’exemple suivant :

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Canvas>
        <TextBlock Text="Je suis en bas à gauche" Canvas.Top="500" />
        <TextBlock Text="Je suis en haut à droite" Canvas.Left="250" Canvas.Top="10"/>
    </Canvas>
</Grid>

affichera la figure suivante.

Positionnement absolu avec le Canvas
Positionnement absolu avec le Canvas

Nous nous servons des propriétés Canvas.Top et Canvas.Left pour indiquer la position du contrôle relativement au Canvas.

C’est sans doute le conteneur qui permet le placement le plus simple à comprendre, par contre ce n’est pas forcément le plus efficace, surtout pour s’adapter à plusieurs résolutions ou lorsque nous retournerons l’écran. J’en parlerai un peu plus loin.
Remarquons qu’une page doit absolument commencer par avoir un conteneur comme contrôle racine de tous les autres contrôles. C’est ce que génère par défaut Visual Studio lorsqu’on crée une nouvelle page. Il y met en l’occurrence un contrôle Grid.

Alignement

L’alignement permet de définir comment est aligné un contrôle par rapport à son contenant, en général un panneau. Il existe plusieurs valeurs pour cette propriété :

  • Stretch (étiré) qui est la valeur par défaut

  • Left (gauche)

  • Right (droite)

  • Center (centre)

Ainsi le code XAML suivant :

<Grid>
    <TextBlock Text="Gauche-Haut" HorizontalAlignment="Left" VerticalAlignment="Top" />
    <TextBlock Text="Centre-Haut" HorizontalAlignment="Center" VerticalAlignment="Top" />
    <TextBlock Text="Droite-Haut" HorizontalAlignment="Right" VerticalAlignment="Top" />
    <TextBlock Text="Gauche-Centre" HorizontalAlignment="Left" VerticalAlignment="Center" />
    <TextBlock Text="Centre-Centre" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <TextBlock Text="Droite-Centre" HorizontalAlignment="Right" VerticalAlignment="Center" />
    <TextBlock Text="Gauche-Bas" HorizontalAlignment="Left" VerticalAlignment="Bottom" />
    <TextBlock Text="Centre-Bas" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
    <TextBlock Text="Droite-Bas" HorizontalAlignment="Right" VerticalAlignment="Bottom" />
</Grid>

produira le résultat que vous pouvez voir à la figure suivante.

Les différents alignements
Les différents alignements

Lorsqu’on utilise la valeur Stretch, les valeurs des propriétés Width et Height peuvent annuler l’effet de l’étirement.
On peut voir cet effet avec le code suivant :

<Grid>
    <Button Content="Etiré en largeur" Height="100" VerticalAlignment="Top" />
    <Button Content="Etiré en hauteur" Width="300" HorizontalAlignment="Left" />
</Grid>

Qui nous donne la figure suivante.

L'étirement est annulé par les propriétés Height et Width
L'étirement est annulé par les propriétés Height et Width

Bien sûr, un bouton avec que du Stretch remplirait ici tout l’écran.

Marges et espacement

Avant de terminer, je vais revenir rapidement sur les marges. Je les ai rapidement évoquées tout à l’heure. Pour mieux les comprendre, regardons cet exemple :

<StackPanel>
    <Rectangle Height="40" Fill="Yellow" />
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="Mon texte" />
        <Rectangle Width="100" Fill="Yellow" />
    </StackPanel>
    <Rectangle Height="40" Fill="Yellow" />
</StackPanel>

Il donne la figure suivante.

Un TextBlock sans marge
Un TextBlock sans marge

En rajoutant une marge au TextBlock, nous pouvons voir concrètement se décaler le texte :

<TextBlock Text="Mon texte" Margin="50"/>

Qui donne la figure suivante.

Une marge de 50 autour du TextBlock
Une marge de 50 autour du TextBlock

En fait, la marge précédente rajoute une marge de 50 à gauche, en haut, à droite et en bas. Ce qui est l’équivalent de :

<TextBlock Text="Mon texte" Margin="50 50 50 50"/>

Il est tout à fait possible de choisir de mettre des marges différentes pour, respectivement, la marge à gauche, en haut, à droite et en bas :

<TextBlock Text="Mon texte" Margin="0 10 270 100"/>

Qui donne la figure suivante.

La marge peut être de taille différente en haut, en bas, à gauche ou à droite du contrôle
La marge peut être de taille différente en haut, en bas, à gauche ou à droite du contrôle

Bref, les marges aident à positionner le contrôle à l’emplacement voulu, très utile pour avoir un peu d’espace dans un StackPanel.
Remarquez que nous avons aperçu dans ces exemples le contrôle Rectangle qui permet, vous vous en doutez, de dessiner un rectangle. Nous l’étudierons un peu plus loin.

  • Les conteurs contiennent des contrôles et nous sont utiles pour les positionner sur la page.

  • Chaque page doit posséder un unique conteneur racine.

  • Les propriétés d’alignement, de marge et d’espacement nous permettent d’affiner nos positionnements dans les conteneurs.

Exemple de certificat de réussite
Exemple de certificat de réussite