Plan du site  
français  English
pixel
pixel

Articles - Étudiants SUPINFO

Introduction

Après avoir fait un tour d'horizon de ce que sont les applications universelles, nous allons maintenant découvrir toutes sortes de contrôles utilisés dans une application.

Un contrôle est un élément de page, qu'il serve pour la mise en page au sein de votre application (par exemple une grille sur laquelle placer d'autres éléments) ou en tant qu'élément atomique, tels qu'un bouton ou une zone de texte.

Cependant, avant de débuter une longue liste d'éléments intégrables dans une page, il est nécessaire de revenir sur l'anatomie d'une page. Pour rappel, nous allons dans le cadre de ce cours utiliser le XAML, bien que le HTML soit également une possibilité.

Rappels syntaxiques concernant le XML

Le XAML est un dérivé du langage XML et est à ce titre une manière de définir une structure.

Le language XML est basé sur un ensemble de balises reconnaissables à leur chevrons, les caractères < et >, pouvant chacune comporter soit d'autres balises, soit un élément textuel.

[Note]

La première ligne d'un document XML est toujours la suivante :

<?xml version="1.0" encoding="UTF-8"?>

Elle indique la version de la normalisation XML utilisée (ici 1.0), ainsi que l'encodage utilisé (ici UTF-8). Si ces valeurs peuvent changer selon les documents, la présence de cette ligne est obligatoire.

Pour simplifier la lecture de ces rappels syntaxiques, elle demeurera absente des exemples.

Voici un exemple simple de XML :

<BaliseA>
  <BaliseB>Texte</BaliseB>
</BaliseA>

Dans cet exemple, la Balise A est ouverte à la ligne 1 et fermée à la ligne 3. A l'intérieur, on retrouve une balise B, ouverte comme fermée à la ligne 2 et contenant l'élément textuel "Texte".

Les balises peuvent être "double" (comporter un élément d'ouverture et un élément de fermeture) comme c'est le cas ici, mais également être "simples", parfois également appelées balises orphelines ou auto-fermantes. Pour symboliser une balise simple, on utilise le nom de la balise avec à son début le caractère < et à sa fin le duo />.

<BaliseA>
  <BaliseC />
</BaliseA>

Dans cet exemple, la BaliseC est simple et n'a d'autre intérêt dans la structure que d'être présente. Elle ne contient ni d'autres balise, ni d'élément textuel. Une balise simple n'est d'ailleurs que la contraction d'une balise double sans contenu en son sein, comme présenté dans l'exemple suivant :

<BaliseA>
  <BaliseC />
  <BaliseC></BaliseC>
</BaliseA>

Ici, les deux balises C sont équivalentes.

On peut également utiliser dans ces balises des attributs. Ils s'intègrent dans la balise ouvrante (et non fermante) d'une balise double, ou au sein d'une balise simple. La syntaxe pour les attributs est le nom de celui-ci, puis le symbole =, puis la valeur associée à cet attribut placée entre guillemets.

<BaliseA monattribut="mavaleur">
  <BaliseC attribut1="valeur1" attribut2="valeur2" attribut3="valeur3" />
</BaliseA>

Dans cet exemple, on remarque la balise A possède l'attribut monattribut égal à la valeur mavaleur. On note également que la balise C possède trois attributs avec chacun possédant leur valeur.

Dernier élément concernant la syntaxe du XML, les commentaires. Ces éléments n'impacteront en aucun cas la traitement du fichier. Les commentaires permettent dans chaque langage de programmation d'offrir des informations supplémentaires au lecteur du fichier. Pour cela, on débute le commentaire par les caractères <!--, et on le termine par les caractères -->.

Si leur syntaxe peut paraître complexe au premier abord, elle se retient et s'utilise aisément après l'avoir pratiquée.

<!-- Exemple de structure XML commentée -->
<BaliseA> <!-- Voici l'ouverture de la balise A -->
  <BaliseB>
    <!-- 
      On trouve au sein de la balise B un simple élément textuel écrit ci-dessous. 
      Ce commentaire ne sera pas lu par un ordinateur et donc ne fera pas partie
      du contenu de la balise. Notez qu'un commentaire peut s'écrire indifféremment
      sur plusieurs lignes.
    -->
    Contenu de la balise B
  </BaliseB>
</BaliseA> <!-- Voici la fermeture de la balise A -->

Pour compléter cette description sur la structure XML, il est nécessaire d'ajouter quelques règles.

Il ne peut y avoir qu'un seul élément au "premier niveau" de la structure

Dans nos exemples précédents, l'élément de premier niveau était la balise A ; celle-ci contient tous les autres éléments. Une structure XML ne peut contenir plusieurs éléments de premier niveau. Voici donc ci-dessous un exemple non-fonctionnel :

<!-- Note: Cette structure montre un exemple incorrect, ne pas copier/coller -->
<BaliseA>
  <BaliseC />
</BaliseA>
<BaliseB />

Chaque attribut d'une balise doit être unique

Dans la présentation des attributs, nous avons utilisé pour la balise C les attributs "attribut1", "attribut2" et "attribut3". Ceux-ci sont différenciables par leur nom et de fait ne permettent aucune ambiguité pour le lecteur ou le programme utilisant cette structure.

Ci-dessous, un exemple non-fonctionnel illustrant cette règle :

<!-- Note: Cette structure montre un exemple incorrect, ne pas coller/coller -->
<BaliseA>
  <BaliseB attribut="valeur" attribut="valeur" />
</BaliseA>

L'ordre d'ouverture des balises doit correspondre à l'ordre de fermeture

La structure XML peut être représentée comme des poupées russes.

Figure 1.1. Les poupées russes (ou poupées matryoshka)

Les poupées russes (ou poupées matryoshka)

Ainsi, si au sein d'une balise A on place une balise B, cette dernière devra être fermée avant de fermer la balise A.

Voici ci-dessous un exemple non-fonctionnel illustrant cette règle :

<!-- Note: Cette structure montre un exemple incorrect, ne pas coller/coller -->
<BaliseA>
  <BaliseB>
    <BaliseC />
</BaliseA> <!-- La balise A se ferme alors que la balise B contenue en elle 
                n'est pas fermée. Ceci est incorrect -->
  </BaliseB>

A quoi sert le XML ?

Le langage XML permet de définir une structure de données. Elle représente très simplement l'appartenance et la hiérarchie au sein de celle-ci, et présente d'une manière élégante et visuelle ce que l'on souhaite.

<!-- Structure représentant une personne -->
<Personne>
  <Prenom>Jean</Prenom>
  <Nom>Dupont</Nom>
  <DateDeNaissance Jour="1" Mois="3" Annee="1972" Heure="23" Minute="16" Seconde="12" />
  <Loisirs>
    <Loisir Nom="Planche à voile">
      <Niveau>Expert</Niveau>
      <Experience>19 ans</Experience>
    </Loisir>
    <Loisir Nom="Handball">
      <Niveau>Initié</Niveau>
      <Experience>3 ans</Experience>
    </Loisir>
  </Loisirs>
  <Description>
    Lorem ipsum dolor sit amet
  </Description>
</Personne>

Le XML au sens général est donc utilisé dans de nombreux cas de transfert de données structurées, comme les flux RSS ou les échanges SOAP. Mais cette syntaxe a eu également de nombreux dérivés tels que le HTML utilisé pour la structure des pages Web, les documents Microsoft Office basés sur le format OOXML ou le XAML.

Des outils en ligne permettent d'ailleurs de valider ou d'indenter sa structure XML afin de s'assurer tant de leur syntaxe que de leur contenu.

XAML et anatomie d'une page

Au sein d'un projet d'application universelle, toutes les pages de celle-ci utilisent du XAML, un dérivé du XML respectant sa syntaxe. Chaque balise représente ainsi un contrôle de votre page ; on peut par exemple présenter les éléments TextBlock et Button représentant respectivement un texte non-modifiable et un bouton.

<!-- Exemple de contrôles XAML. La structure complète est absente pour faciliter la lecture -->
<TextBlock Text="Bloc de texte" /> <!-- L'attribut "Text" définit le contenu du bloc -->
<Button Content="Cliquez ici" />   <!-- L'attribut "Content" définit le contenu du bouton -->
[Note]

On peut aussi bien écrire la balise TextBlock de manière simple comme de manière double. Ainsi :

<TextBlock Text="Bloc de texte" />

et

<TextBlock>Bloc de texte</TextBlock>

sont équivalents. Attention cependant à ne pas mélanger les deux méthodes pour une même balise.

Au sein d'une page, on retrouvera un contrôle "Page" au premier niveau, contenant l'ensemble des contenus visibles. La balise Page contient également de nombreux attributs utilisés pour permettre la disponibilité des autres contrôles ainsi que le lien avec le code-behind que nous détaillerons plus tard dans ce chapitre.

<Page
  x:Class="HelloWorldApp.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:HelloWorldApp"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d">
    <!-- Contenu de la page -->
</Page>

Contrôles de mise en page

Dans cette section, on présentera quelques éléments servant la structure de la page, utilisés pour grouper d'autres contrôles, de mise en page ou actionnables par l'utilisateur.

Grid

L'élément Grid symbolise une grille dans laquelle placer nos éléments. A ce titre, on peut y définir des colonnes comme des lignes qui seront ensuite référencés par les contrôles inclus dans cette grille.

<Page> <!-- Attributs absents pour faciliter la lecture -->
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="40" /> <!-- Ligne de hauteur 40 -->
      <RowDefinition Height="30" /> <!-- Ligne de hauteur 30 -->
    </Grid.RowDefinitions>
    <!-- L'attribut Grid.Row permet de spécifier dans quelle ligne de la grille
         l'élément sera placé. Le compteur démarrant à 0, spécifier Grid.Row="0"
         entend qu'on place cet élément dans la première ligne -->
    <TextBlock Text="Présent dans la première ligne" Grid.Row="0" /> 
    <TextBlock Text="Présent dans la seconde ligne" Grid.Row="1" />
    <TextBlock Text="Dans la grille" /> <!-- Si l'attribut Grid.Row n'est pas spécifié,
                                             l'élément sera dans la première ligne. -->
  </Grid>
</Page>

S'il est possible de définir des lignes avec une certaine hauteur via l'attribut "Height", il est également possible de définir des colonnes avec une certaine largeur via l'attribut "Width". L'intérêt est de mixer la définition des colonnes et des lignes pour construire une grille adaptée aux contrôles présents.

<Page> <!-- Attributs absents pour faciliter la lecture -->
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="40" /> <!-- Ligne de hauteur 40 -->
      <RowDefinition Height="30" /> <!-- Ligne de hauteur 30 -->
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="250" />  <!-- Colonne de largeur 250 -->
      <ColumnDefinition Width="350" />  <!-- Colonne de largeur 350 -->
    </Grid.ColumnDefinitions>
    <TextBlock Text="Première ligne, première colonne" Grid.Row="0" Grid.Column="0" /> 
    <TextBlock Text="Première ligne, deuxième colonne" Grid.Row="0" Grid.Column="1" />
    <TextBlock Text="Deuxième ligne, première colonne" Grid.Row="1" Grid.Column="0" />
    <TextBlock Text="Deuxième ligne, deuxième colonne" Grid.Row="1" Grid.Column="1" />
  </Grid>
</Page>

Figure 1.2. Résultat du précédent exemple

Résultat du précédent exemple

Si l'utilisation de nombres pour les hauteurs et largeurs peut sembler efficace, elle est cependant délicate dans le cas d'une application universelle : dans ce cas, quelle est la largeur de notre page ? Et quelle sera-t-elle sur un autre équipement ?

Pour adapter sa mise en page, il est important d'utiliser tant que possible les valeurs * et auto.

  • L'astérisque (*) signifie "autant que possible". Ainsi, on peut définir une colonne avec une valeur fixe de 40, et une autre colonne prenant le reste de la place. L'astérisque ne sera jamais prioritaire et prendra donc la place restante. Dans le cas de deux colonnes ou lignes définies avec un astérisque, elles se partageront la place.

  • La valeur "auto" signifie "aussi peu que possible". Elle s'adapte donc au contenu en son sein. Une colonne ayant pour largeur "auto" sans aucun élément ne sera donc pas visible.

<Page> <!-- Attributs absents pour faciliter la lecture -->
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*" />    <!-- Autant que possible    -->
      <RowDefinition Height="auto" /> <!-- Aussi peu que possible -->
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />  <!-- Autant que possible, donc la moitié -->
      <ColumnDefinition Width="*" />  <!-- Autant que possible, donc la moitié -->
    </Grid.ColumnDefinitions>
    <TextBlock Text="Première ligne, première colonne" Grid.Row="0" Grid.Column="0" /> 
    <TextBlock Text="Première ligne, deuxième colonne" Grid.Row="0" Grid.Column="1" />
    <TextBlock Text="Deuxième ligne, première colonne" Grid.Row="1" Grid.Column="0" />
    <TextBlock Text="Deuxième ligne, deuxième colonne" Grid.Row="1" Grid.Column="1" />
  </Grid>
</Page>

Figure 1.3. Résultat du précédent exemple (* et auto)

Résultat du précédent exemple (* et auto)

Dans ce précédent exemple, la seconde ligne s'adapte à la taille du texte en son sein, donc la hauteur des textes. La première ligne prenant autant de hauteur que possible, elle "pousse" la seconde ligne tout en bas de l'interface.

On peut remarquer dans l'image qu'au lieu d'un simple astérisque, les éléments sont notés "1*". L'astérisque est une unité de mesure et il est possible d'y associer une valeur. Celle-ci symbolisant un poids. Si l'interface est séparée en deux colonnes, la première valant "2*" et le seconde "3*", la première s'arrogera deux cinquièmes de l'écran (soit 40%) quand la seconde emplira trois cinquièmes de l'écran (soit 60%).

<Page> <!-- Attributs absents pour faciliter la lecture -->
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="1*" /> <!-- Représente 1/(1+2+3+4) soit un dixième -->
      <ColumnDefinition Width="2*" /> <!-- Représente 2/(1+2+3+4) soit deux dixièmes -->
      <ColumnDefinition Width="3*" /> <!-- Représente 3/(1+2+3+4) soit trois dixièmes -->
      <ColumnDefinition Width="4*" /> <!-- Représente 4/(1+2+3+4) soit quatre dixièmes -->
    </Grid.ColumnDefinitions>
    <TextBlock Text="Première colonne" Grid.Column="0" />
    <TextBlock Text="Deuxième colonne" Grid.Column="1" />
    <TextBlock Text="Troisième colonne" Grid.Column="2" />
    <TextBlock Text="Quatrième colonne" Grid.Column="3" />
  </Grid>
</Page>

Figure 1.4. Résultat du précédent exemple (utilisation des poids)

Résultat du précédent exemple (utilisation des poids)

[Note]

L'utilisation des différentes unités de mesure dépendra du contexte : pour des éléments ayant une taille fixe quelque soit la taille de l'équipement ciblé, on utilisera des valeurs numériques. Pour une mise en page adaptable en fonction de l'équipement, on utilisera les autres méthodes, plus flexibles et compatibles.

StackPanel

Ce contrôle permet de définir une zone dans laquelle les éléments vont s'ajouter les uns après les autres, en fonction d'une orientation, verticale par défaut. L'orientation verticale place les éléments ligne par ligne, les uns en dessous des autres. A l'inverse, l'orientation horizontale les place sur une même ligne tant que c'est possible et passe à une nouvelle ligne si nécessaire.

<!-- Reste de la page omis -->
<StackPanel>
  <TextBlock Text="Element A" />
  <TextBlock Text="Element B" />
  <TextBlock Text="Element C" />
</StackPanel>

Figure 1.5. Un StackPanel simple

Un StackPanel simple

Les contrôles de mise en page pouvant eux-même être inclus dans d'autres contrôles de mise en page, on peut visualiser la différence d'orientation des StackPanels à travers cet exemple combinant ceux-ci et un contrôle Grid vu précédemment.

<Page> <!-- Attributs absents pour faciliter la lecture -->
  <Grid>
    <Grid.RowDefinitions>
      <ColumnDefinition Width="auto" />
      <ColumnDefinition Width="auto" />
      <ColumnDefinition Width="auto" />
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Column="0">
      <TextBlock Text="Element A" />
      <TextBlock Text="Element B" />
      <TextBlock Text="Element C" />
    </StackPanel>
    <StackPanel Grid.Column="1" Orientation="Vertical">
      <TextBlock Text="Element A" />
      <TextBlock Text="Element B" />
      <TextBlock Text="Element C" />
    </StackPanel>
    <StackPanel Grid.Column="2" Orientation="Horizontal">
      <TextBlock Text="Element A" />
      <TextBlock Text="Element B" />
      <TextBlock Text="Element C" />
    </StackPanel>
  </Grid>
</Page>

Figure 1.6. Visualisation de l'orientation d'un StackPanel

Visualisation de l'orientation d'un StackPanel

RelativePanel

Cet élément de mise en page est en soi neutre, ne disposant ni de colonnes, ni de lignes, ni d'orientation. L'intérêt de celui-ci est d'en placer les contrôles enfants relativement les uns par rapport aux autres.

<!-- Reste de la page omis -->
<RelativePanel>
  <TextBlock x:Name="ElementA" Text="Element A" />
  <TextBlock x:Name="ElementB" Text="Element B" RelativePanel.RightOf="ElementA" />
  <TextBlock x:Name="ElementC" Text="Element C" RelativePanel.Below="ElementB" 
                                                RelativePanel.AlignRightWith="ElementB" />
</RelativePanel>

Figure 1.7. Utilisation d'un RelativePanel

Utilisation d'un RelativePanel

On remarque l'utilisation du nouvel attribut x:Name. Celui-ci définit un nom à l'élément, utilisable ensuite dans les attributs RightOf, Below et AlignRightWith. Il est également possible d'aligner les éléments par rapport au RelativePanel lui même. Dans ce cas, donner un nom au RelativePanel n'est pas nécessaire.

Le prochain exemple tiré de la documentation officielle présente ces possibilités :

<RelativePanel BorderBrush="Gray" BorderThickness="1">
    <Rectangle x:Name="RedRect" Fill="Red" Height="44" Width="44"/>
    <Rectangle x:Name="BlueRect" Fill="Blue"
               Height="44" Width="88"
               RelativePanel.RightOf="RedRect" />

    <Rectangle x:Name="GreenRect" Fill="Green" 
               Height="44"
               RelativePanel.Below="RedRect" 
               RelativePanel.AlignLeftWith="RedRect" 
               RelativePanel.AlignRightWith="BlueRect"/>
    <Rectangle Fill="Yellow"
               RelativePanel.Below="GreenRect" 
               RelativePanel.AlignLeftWith="BlueRect" 
               RelativePanel.AlignRightWithPanel="True"
               RelativePanel.AlignBottomWithPanel="True"/>
</RelativePanel>

Figure 1.8. RelativePanel et alignements

RelativePanel et alignements

Explications :

  • L'élément Rectangle construit un rectangle et possède différents attributs comme la hauteur ou la largeur, mais également Fill qui colorera l'élément avec la couleur précisée en valeur.

  • Si les rectangles bleus et rouges possèdent bien une largeur et une hauteur, le rectangle vert ne possède pas d'attribut spécifiant sa largeur. Celle-ci est calculée via l'alignement à gauche avec le rectangle rouge, et l'alignement à droite avec le rectangle bleu. Conséquemment, sa largeur sera la somme des largeurs des rectangles rouge et bleu.

  • Le rectangle jaune ne possède ni largeur ni hauteur. Cependant, il est aligné à gauche avec le rectangle bleu et à droite avec le RelativePanel. De la même manière, il est placé en dessous du rectangle vert et est aligné en bas avec le RelativePanel, définissant de facto sa hauteur.

ScrollViewer

Le dernier élément de mise en page est particulier car il ne permet pas réellement de placer les éléments à l'intérieur. Néanmoins, il permet en fixant ses dimensions d'autoriser un défilement par la molette ou le doigt si son contenu est plus volumineux.

<Page> <!-- Attributs absents pour faciliter la lecture -->
  <ScrollViewer HorizontalScrollBarVisibility="Auto"> 
  <!-- L'attribut HorizontalScrollBarVisibility autorise le défilement horizontal si nécessaire.
       En effet, par défaut le contrôle ScrollViewer ne le permet pas, contrairement au défilement
       vertical solidement ancré dans les habitudes des utilisateurs. -->
    <StackPanel>
      <TextBlock>Le défilement est activé quand nécessaire. Vous pouvez redimensionner la
        fenêtre de l'application sur un ordinateur pour constater le défilement.</TextBlock>
      <Rectangle Width="500" Height="600" Fill="Green" ></Rectangle>
    </StackPanel>
  </ScrollViewer>
</Page>

Figure 1.9. Rendu d'un ScrollViewer

Rendu d'un ScrollViewer

Conclusion

Au travers de ces différents éléments, nous pouvons facilement présenter n'importe quel type de mise en page, d'une barre d'outils à une liseuse, d'un jeu de Memory à une application présentant des vidéos. Il manque un dernier élément de mise en page, SplitView, que nous verrons dans les chapitres suivants.

Contrôles passifs

Dans cette partie, nous détaillerons les contrôles avec lesquels l'utilisateur ne peut modifier visuellement, par exemple les textes et les images, par opposition aux contrôles actifs comme les zones de textes modifiables ou les curseurs.

TextBlock

Plusieurs fois utilisé dans les exemples précédents, le TextBlock est un contrôle permettant d'afficher un texte non modifiable. Sans présenter ici tous les attributs disponibles pour ce contrôle, voici un exemple illustrant ses possibilités.

<!-- Reste de la page omis -->
<Grid Background="White">
  <TextBlock FontFamily="Verdana" FontSize="30" FontStyle="Italic" FontWeight="Bold"
             Foreground="Purple" TextAlignment="Center" Width="450"  
             Opacity=".6" TextWrapping="Wrap" MaxLines="6">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vulputate 
      semper lorem, eget pharetra orci consectetur sit amet. Integer nibh mauris, interdum 
      eget lacus a, ultricies consectetur erat. Vestibulum id pulvinar urna. Interdum et 
      malesuada fames ac ante ipsum primis in faucibus. Fusce ornare interdum libero sit 
      amet ultrices.
  </TextBlock>
</Grid>

Figure 1.10. Rendu d'un TextBlock

Rendu d'un TextBlock

Explications :

  • FontFamily : Définit la police utilisée. Il est préférable autant que possible de ne pas changer cet attribut pour respecter les conventions UWP. Cependant, c'est un attribut très convoité pour utiliser une police ne représentant pas des lettres, mais des icônes, comme "Segoe MDL2 Assets". Bien que présentée par l'exemple plus tard dans ce cours, vous pouvez trouver dès à présent la liste des possibilités de cette police de caractères sur cette page.

  • FontSize : La taille du texte.

  • FontStyle : Normal, italique ou oblique. La différence entre italique et oblique est ténue : l'italique utilise un dérivé de la police de caractères spécialement prévu à cet effet. Oblique "tord" la police de caractères sans utiliser ce dérivé, expliquant une différence légèrement perceptible.

  • FontWeight : Normal, Gras, Léger, et ses déclinaisons plus ou moins prononcées.

  • Foreground : La couleur avec laquelle est écrite le texte parmi une liste de couleurs fournies par UWP.

  • TextAlignement : Le texte peut être aligné à gauche, à droite, justifié ou centré.

  • Width : La largeur que l'on donne à notre texte

  • Opacity : Un facteur d'opacité, de 0 à 1. Si cette valeur est à 0, le texte est totalement transparent. A 1, le texte est totalement opaque.

  • TextWrapping : Le texte doit-il ouvrir une nouvelle ligne s'il ne tient pas dans la largeur convenue ?

  • MaxLines : Le nombre de lignes maximum que ce texte prendra au maximum.

[Note]

Ces éléments sont tous bien sûr facultatifs. Nous avons pu observer dans les exemples précédents des éléments TextBlock sans aucun de ces attributs.

Beaucoup de ces attributs sont partagés avec d'autres éléments : FontFamily, FontSize, FontStyle et FontWeight pour beaucoup d'éléments textuels, Opacity et Foreground pour beaucoup d'éléments visuels. On pourrait également citer Margin et Padding pour les marges externes et internes, non-représentées dans cet exemple.

Button

Un bouton se construit comme un contrôle TextBlock. Conséquemment, de nombreux attributs sont partagés entre les deux éléments et ne seront pas repris dans cette section. Un bouton se présente donc comme ceci :

<!-- Reste de la page omis -->
<Button Content="Ceci est un bouton" Margin="20" />

Figure 1.11. Rendu d'un Button

Rendu d'un Button

[Note]

Comme l'élément TextBlock, le contenu du bouton peut être placé entre la balise ouvrante et fermante. L'attribut Margin place 20 unités de marge autour de l'élément dans toutes les directions.

HyperlinkButton

Dans une application universelle, un lien est considéré comme un bouton : seul le nom du contrôle change.

<!-- Reste de la page omis -->
<HyperlinkButton Content="Ceci est un bouton hyperlien" Margin="20" />

Figure 1.12. Rendu d'un HyperlinkButton

Rendu d'un HyperlinkButton

[Note]

Button comme HyperlinkButton, sans être des éléments actifs, sont amenés à être manipulés par les utilisateurs. Nous verrons dans la suite de ce cours comment leur associer des actions.

Image

Il est également possible d'afficher une image grâce au contrôle éponyme, dans de nombreux formats comme PNG ou JPG. Pour plus d'informations sur les formats utilisés, n'hésitez pas à consulter cette liste.

<!-- Reste de la page omis -->
<!-- Affiche l'image stockée à ce chemin. Spécifier la hauteur ou la largeur 
     redimensionnera l'image sans nuire aux proportions. -->
<Image Source="/Assets/StoreLogo.png" Width="50" />
<!-- Pour stocker l'image en cache et pouvoir la proposer plusieurs fois dans
     l'application, il est recommandé d'utiliser plutôt ce balisage -->
<Image>
  <Image.Source>
    <BitmapImage UriSource="/Assets/StoreLogo.png" />
  </Image.Source>
</Image>

Figure 1.13. Rendu d'un élément Image

Rendu d'un élément Image

[Note]

Le chemin commence pour des raisons de bonnes pratiques toujours par un /. En effet, celui-ci indique que le chemin est fait par rapport à la racine du projet appelé aussi "chemin absolu". Sachez cependant qu'en omettant ce caractère, l'image sera cherchée par rapport au fichier courant, ce sera dans ce cas un "chemin relatif". Pour plus d'informations à ce sujet, consultez ce lien.

Il n'est pas conseillé d'afficher une image présente en dehors du dossier de l'application pour des raisons de portabilité. Pour ajouter une image dans le projet, effectuez un clic droit sur le dossier "Assets", sélectionnez "Ajouter" puis "Elément existant", et naviguez vers le ou les fichiers à inclure dans votre projet.

Eléments actifs

Les éléments actifs prenant sens majoritairement avec des événements relatifs à leur activation (un curseur déplacé, un texte modifié, ...), leur syntaxe minimale est simple, mais ces contrôles prennent tout leur sens quand on les associe à des événements que l'on présentera dans la partie suivante.

TextBox et PasswordBox

A ne pas confondre avec TextBlock, cet élément présente une zone de texte à modifier, avec ou sans valeur par défaut. L'attribut PlaceholderText vous permet de définir un texte légèrement transparent pour aiguiller l'utilisateur, qui ne s'affichera que si la zone de texte est vide.

<!-- Reste de la page omis -->
<StackPanel Height="80" Margin="20" Orientation="Vertical">
  <TextBlock>Présentez-vous</TextBlock>
  <TextBox Width="200" Height="60" PlaceholderText="Entrez un texte ici." TextWrapping="Wrap" />
  <TextBlock>Validez votre mot de passe</TextBlock>
  <PasswordBox PlaceholderText="Mot de passe" Password="non" />
  <!-- Ne pas spécifier l'attribut Password d'un PasswordBox : l'utilisateur le fera -->
  <Button Margin="0,20">C'est parti !</Button>
</StackPanel>

Figure 1.14. Rendu d'un TextBox

Rendu d'un TextBox

L'élément PasswordBox agit exactement comme un élément TextBox. Cependant les caractères sont remplaçés visuellement par des points.

CheckBox et RadioButton

CheckBox et RadioButton sont des éléments classiques de la saisie de données. CheckBox représente une case à cocher et RadioButton un cercle sélectionné. L'un comme l'autre sont groupés entre elles ; on trouvera ainsi plusieurs cases à cocher ou plusieurs cercles pour une seule question, laissant le choix à l'utilisateur. La différence entre ces deux éléments résident dans l'unicité ou non de leurs choix. En effet, une question peut être répondue par plusieurs cases cochées, mais par un seul cercle sélectionné.

<!-- Reste de la page omis -->
<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="auto" />
    <ColumnDefinition Width="auto" />
  </Grid.ColumnDefinitions>
  <StackPanel Orientation="Vertical" Grid.Column="0" Margin="20">
    <TextBlock>Quel continent avez-vous déjà visité ?</TextBlock>
    <CheckBox Content="Afrique" />
    <CheckBox Content="Amérique" />
    <CheckBox Content="Europe" />
    <CheckBox Content="Asie" />
  </StackPanel>
  <StackPanel Orientation="Vertical" Grid.Column="1" Margin="20">
    <TextBlock>Sélectionnez votre tranche d'âge :</TextBlock>
    <RadioButton Content="Moins de 20 ans" />
    <RadioButton Content="De 21 à 40 ans"/>
    <RadioButton Content="De 41 à 60 ans"/>
    <RadioButton Content="60 ans et plus"/>
  </StackPanel>
</Grid>

Figure 1.15. Rendu des éléments CheckBox et RadioButton

Rendu des éléments CheckBox et RadioButton

Utilisez l'attribut IsChecked sur un de ces éléments pour le cocher ou décocher par défaut. Par défaut, ces éléments sont décochés.

[Note]

Comment UWP sait quels boutons radio ou cases à cocher sont liés entre eux ? Pour cela, deux possibilités :

  • Les éléments sont tous au même niveau de hiérarchie, dans le même contrôle. C'est notre cas ici, car nos éléments sont regroupés chacun dans un contrôle StackPanel.

  • Les éléments partagent une même valeur pour leur attribut GroupName. La valeur de cet attribut est laissée à votre libre choix, mais une valeur commune entre deux éléments signifiera pour l'application une relation.

Pour plus d'informations à propos des éléments CheckBox et RadioButton, n'hésitez pas à vous rendre sur cette page de la documentation officielle.

ToggleSwitch et ToggleButton

Ces éléments vous permettent de présenter un choix binaire à l'utilisateur : oui ou non.

Pour ToggleSwitch, l'attribut IsOn permet de définir si l'élément est coché ou décoché. Pour ToggleButton, c'est l'attribut isChecked qui en définit l'état. Cette différence s'explique par le fait que ToggleButton est en réalité issu de la même catégorie que les contrôles CheckBox et RadioButton. Bien que les deux solutions existent, ToggleSwitch est utilisé plus fréquemment grâce à son ergonomie.

<!-- Reste de la page omis -->
<StackPanel Orientation="Vertical">
  <ToggleSwitch IsOn="False" Header="Mode nuit :" />
  <ToggleSwitch IsOn="True" Header="Mode silencieux :" />
  <ToggleButton IsChecked="False" Content="ToggleButton Off" Width="140" />
  <ToggleButton IsChecked="True" Content="ToggleButton On" Width="140" />
</StackPanel>

Figure 1.16. Rendu des éléments ToggleSwitch et ToggleButton

Rendu des éléments ToggleSwitch et ToggleButton

DatePicker, TimePicker et CalendarDatePicker

Si pour les besoins de votre application vous souhaitez demander une date ou une heure à l'utilisateur, n'utilisez pas de champ TextBox : ceux-ci vous forceront à utiliser un format prédéfini (souvent jj/mm/aaaa pour une date) et ajouteront de la complexité pour l'utilisateur comme pour le créateur de l'application. Les contrôles DatePicker, TimePicker et CalendarDatePicker offrent une possibilité simple et ergonomique de gérer des éléments datés.

<!-- Reste de la page omis -->
<StackPanel Margin="20" Orientation="Vertical">
  <DatePicker Header="Sélectionnez une date" />
  <TimePicker Header="Sélectionnez une heure" />
  <CalendarDatePicker Header="Vous préférez une vue de calendrier ?" />
</StackPanel>

Figure 1.17. Rendu des éléments DatePicker, TimePicker et CalendarDatePicker

Rendu des éléments DatePicker, TimePicker et CalendarDatePicker

Au clic ou au toucher, ces contrôles s'étendent pour permettre de sélectionner facilement la date ou l'heure. Ces contrôles, de par la place qu'ils occupent lorsqu'ils sont dépliés, ne peuvent s'ouvrir qu'un par un.

Figure 1.18. Rendu des éléments précédents, cette fois dépliés

Rendu des éléments précédents, cette fois dépliés

Slider

Le dernier contrôle que nous verrons dans cette partie est le curseur, permettant de sélectionner une valeur parmi une échelle comportant un minimum et un maximum. Ce contrôle prenant par défaut toute la largeur possible, il est conseillé de lui appliquer l'attribut Width.

<!-- Reste de la page omis -->
<StackPanel Margin="20" Orientation="Vertical">
  <Slider Minimum="1" Maximum="10" Width="200" Header="Niveau de zoom" />
</StackPanel>

Figure 1.19. Rendu d'un Slider

Rendu d'un Slider

Le code-behind, au-delà du visible

Le panorama des contrôles présentés ci-dessus, bien que non-exhaustif, vous permettra de réaliser la majorité de vos mises en page. Si nous découvrirons dans le chapitre suivant les contrôles permettant d'afficher une liste d'éléments, nous allons dans cette partie nous attarder sur les événements liés à ces contrôles, et par cette occasion découvrir le code-behind.

Comme présenté dans l'introduction à ce cours, le code-behind est rédigé en C# (prononcez "C Sharp"). Si les fichiers contenant la structure XAML possède l'extension .xaml, le code-behind possède l'extension .xaml.cs, l'ajout "cs" correspondant à "C Sharp". Le XAML et le C# constituent ensemble et de manière indivisible une page d'une application universelle. Ainsi, la page principe de votre application nommée MainPage est constituée de deux fichiers, MainPage.xaml et MainPage.xaml.cs.

Pour afficher le code-behind d'une page dans Visual Studio, il vous faudra déplier l'arborescence comme présenté ci-dessous ; les deux fichiers constituant la page MainPage sont encadrés en rouge.

Figure 1.20. Arborescence de Visual Studio : Vue et Code-behind

Arborescence de Visual Studio : Vue et Code-behind

Anatomie d'un fichier de code-behind

Si nous ne pourrons pas dans ce cours revisiter l'ensemble des notions relatives au C# (traitées dans le cours 2NET), nous allons prendre quelques minutes pour détailler le fichier MainPage.xaml.cs.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace HelloWorldApp
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
    }
}

En début de fichier, les lignes commençant par using décrivent les namespaces inclus dans notre page ; nous pourrons donc utiliser toutes les fonctionnalités de ces ensembles. Pour rappel, les commentaires commencent par deux caractères /, la documentation commence par trois caractères /. Bien que non présents, on notera que les commentaires s'étalant sur plusieurs lignes commencent par les caractères /* et se terminent par les caractères */ comme dans de nombreux autres langages de programmation.

Au sein du namespace HelloWorldApp qui contient l'ensemble des pages de notre application, la classe MainPage (héritant des fonctionnalités de la classe Page) est déclarée : il s'agit de la classe reliée à notre page MainPage.xaml.

Dans notre classe MainPage se situe le constructeur, méthode du même nom que la classe. A l'intérieur un simple appel :

this.InitializeComponent();

Cette fonction initialise les contrôles XAML de la page et construit son rendu visuel. Conséquence, les fonctions appelées avant celui-ci ne pourront pas accéder aux contrôles.

Interaction XAML / code-behind

Pour accéder à un élément XAML dans le code-behind, il est nécessaire de lui attribuer un nom, qui agira comme un identifiant, comme ceci :

<TextBlock x:Name="MonTextBlock" Margin="20" />

L'élément sera donc accessible dans le code-behind sous le nom "MonTextBlock". On peut à partir de ce moment lui définir une valeur.

// Reste du fichier omis
public MainPage()
{
  this.InitializeComponent();
  MonTextBlock.Text = "Hello World";
}

Figure 1.21. Interaction Vue et Code-behind

Interaction Vue et Code-behind

On voit dans cet exemple que l'attribut Text de notre TextBlock peut également être défini dans le code-behind. Cependant, il est conseillé de mettre le maximum d'attributs par défaut et statiques (ne changeant pas au cours de la vie de l'application) dans la page XAML. Cependant, c'est une étape indispensable pour comprendre les possibilités des événements.

Evénements

Les événements sont une part importante de l'utilisation d'une application : ils permettent d'exécuter du code C# quand quelque chose de spécifique se produit. Ce "quelque chose" peut être un défilement, un chargement, un clic, un déplacement... Evidemment, ces événements peuvent dépendre du contrôle concerné : une liste possédera par exemple un événement au clic sur un élément de celle-ci.

Pour illustrer cette notion, nous allons créer un bouton qui changera le contenu de notre TextBlock "MonTextBlock".

<StackPanel Orientation="Horizontal" Height="60">
  <TextBlock x:Name="MonTextBlock" Margin="20" />
  <Button x:Name="ChangementTexte" Content="Changer le texte" />
</StackPanel>

Pour créer un événement sur notre bouton, il est possible de le faire de différentes manières :

  • Double-cliquer sur le bouton dans l'onglet "Création" représentant la fenêtre. L'événement ciblé sera celui d'un utilisateur cliquant sur le bouton.

  • Sélectionner le bouton dans l'onglet "Création" et double-cliquer dans la zone de texte en face du mot "Click"

Figure 1.22. Ajouter un événement, deux possibilités

Ajouter un événement, deux possibilités

Ajouter l'événement Click sur notre bouton aura deux effets. Premièrement, ajouter l'attribut Click dans notre bouton :

<Button x:Name="ChangementTexte" Content="Changer le texte" Click="ChangementTexte_Click" />

Deuxièmement, ajouter une méthode dans le code-behind :

private void ChangementTexte_Click(object sender, RoutedEventArgs e)
{
  
}

Cette méthode prend deux paramètres, comme l'ensemble des événements liés à une vue :

  • L'élément ayant déclenché l'événement, ici le bouton. Si vous voulez agir ou récupérer des informations sur ce bouton, il vous faudra le caster en Button.

  • Les propriétés de l'événement héritant de la classe EventArgs, ici des RoutedEventArgs. Selon le contexte, vous pourrez récupérer des informations supplémentaires.

Il suffit dans le code-behind de changer la propriété Text de MonTextBlock.

private void ChangementTexte_Click(object sender, RoutedEventArgs e)
{
  MonTextBlock.Text = "Goodbye World";
}

Figure 1.23. Evénement Click, avant et après

Evénement Click, avant et après

Pour finir cet exemple sur les événements, on peut imaginer stocker ces différents éléments comme propriétés de notre Page.

public sealed partial class MainPage : Page
{
  private string TexteA = "Hello World";
  private string TexteB = "Goodbye World";
  private bool TestBouton = false;

  public MainPage()
  {
    this.InitializeComponent();
    MonTextBlock.Text = TexteA;
  }

  private void ChangementTexte_Click(object sender, RoutedEventArgs e)
  {
    if (!TestBouton)
    {
      MonTextBlock.Text = TexteB;
    }
    else
    {
      MonTextBlock.Text = TexteA;
    }
    TestBouton = !TestBouton;
  }
}

Nous avons ajouté trois propriétés : TexteA qui contient notre texte par défaut "Hello World", TexteB qui contient le nouveau texte "Goodbye World", et un booléen TestBouton qui nous permettra de passer d'un texte à l'autre.

Au lancement de l'application, la propriété TestBouton est fausse, et le texte de l'élément TextBlock est bien le texte A, celui par défaut.

Au clic sur le bouton, si la propriété TestBouton est fausse, alors on change le texte de l'élément TextBlock pour TexteB, sinon on revient au texte par défaut. La propriété TestBouton est enfin inversée pour permettre de passer d'un texte à l'autre à chaque nouveau clic du bouton.

About SUPINFO | Contacts & addresses | Teachers | Press | INVESTOR | Conditions of Use & Copyright | Respect of Privacy
Logo de la société Cisco Logo de la société IBM Logo de la société Sun-Oracle Logo de la société Apple Logo de la société Sybase Logo de la société Novell Logo de la société Intel Logo de la société Accenture Logo de la société SAP Logo de la société Prometric Logo du IT Academy Program par Microsoft

SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels
and is accredited in France by Association Ecole Supérieure d'Informatique de Paris (ESI SUPINFO)