Cet extrait de l'ouvrage XAML vous est offert par Micro Application

Retrouvez-en d'autres sur www.microapp.com

6

Les menus

6.1   ...  170
6.2  Créer un menu contextuel ...  178
6.3  Créer une barre d’outils ...  183
6.4  Checklist ...  189

6.1  Créer un menu

Le menu principal

<Window x:Class="AvalonMenu.Window1"
 xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Les menus avec Avalon">
   <Menu VerticalAlignment="Top" Height="20">
      <MenuItem Header="Fichier"/>
      <MenuItem Header="Edition"/>
      <MenuItem Header="Aide"/>
   </Menu>
</Window>

Hauteur du menu

La propriété Height est obligatoire sinon le menu occupera toute la hauteur disponible dans notre fenêtre.

Fig. 6-1 : Le menu principal

<Window
 xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Les menus avec XAML"
 >
   <DockPanel>
      <Menu VerticalAlignment="Top" Height="20"
       DockPanel.Dock="Top">
         <MenuItem Header="Fichier"/>
         <MenuItem Header="Edition"/>
         <MenuItem Header="Aide"/>
      </Menu>
   </DockPanel>
</Window>

Le résultat sera identique, à la différence près qu’il est maintenant possible de placer d'autres contrôles ou d'autres conteneurs dans la fenêtre.

Les sous-menus

<Window
 xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Les menus avec XAML"
 >
   <DockPanel>
      <Menu VerticalAlignment="Top" Height="20"
       DockPanel.Dock="Top">
         <MenuItem Header="Fichier">
            <MenuItem Header="Ouvrir"/>
            <MenuItem Header="Fermer"/>
         </MenuItem>
         <MenuItem Header="Edition">
            <MenuItem Header="Copier"/>
            <MenuItem Header="Coller"/>
            <Separator />
            <MenuItem Header="Livre">
               <MenuItem Header="Précédent"/>
               <MenuItem Header="Suivant"/>
            </MenuItem>
         </MenuItem>
         <MenuItem Header="Aide"/>
      </Menu>
   </DockPanel>
</Window>

Fig. 6-2 : Les sous-menus

Placer des séparations dans le menu

Notez l'utilisation de la balise Separator pour créer un séparateur.

Rendre un élément du menu inactif

<MenuItem Header="Fichier">
   <MenuItem Header="Ouvrir"/>
   <MenuItem Header="Fermer" IsEnabled="False"/>
</MenuItem>

Fig. 6-3 : Les sous-menus

Cocher un élément du menu

<MenuItem Header="Aide">
   <MenuItem Header="En ligne" IsChecked="True"/>
</MenuItem>

Fig. 6-4 : Les sous-menus

Associer une action à un menu

Pour associer une méthode au clic sur un élément du menu, il suffit d'assigner le nom de la méthode à l'attribut Click de l'élément correspondant. Reprenons l'exemple précédent complet et adaptons-le pour rendre le menu Fermer disponible quand on clique sur Ouvrir et rendre en revanche ce dernier indisponible. Nous ferons l'inverse avec Fermer. Dans le même ordre d’idée, nous supprimerons ou activerons la coche lors du clic sur le menu en ligne.

Réaliser soi-même l’exemple

Si vous souhaitez reproduire cet exemple, vous devrez créer un projet dans Visual Studio.

<Window x:Class="Window1"
 xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Les menus avec XAML"
 >
   <DockPanel>
      <Menu VerticalAlignment="Top" Height="20"
       DockPanel.Dock="Top">
         <MenuItem Header="Fichier">
            <MenuItem Name="mnuOuvrir" Header="Ouvrir"
             Click="Click_Ouvrir"/>
            <MenuItem Name="mnuFermer" Header="Fermer"
             IsEnabled="False" Click="Click_Fermer"/>
         </MenuItem>
         <MenuItem Header="Edition">
            <MenuItem Header="Copier"/>
            <MenuItem Header="Coller"/>
            <Separator />
            <MenuItem Header="Livre">
               <MenuItem Header="Précédent"/>
               <MenuItem Header="Suivant"/>
            </MenuItem>
         </MenuItem>
         <MenuItem Header="Aide">
            <MenuItem Name="mnuEnLigne" Header="En ligne"
             IsChecked="True" Click="Click_EnLigne"/>
         </MenuItem>
      </Menu>
   </DockPanel>
</Window>

Importance des noms

Jusqu'à maintenant, dans les menus, je n'avais pas pris la peine de donner des noms. Comme vous pouvez le constater, cela se révèle rapidement nécessaire.

Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports System.Windows.Documents
Imports System.Windows.Media
Imports System.Windows.Media.Imaging
Imports System.Windows.Shapes
 
' Interaction logic for Window1.xaml
Partial Public Class Window1
    Inherits Window
 
    Public Sub New()
        InitializeComponent()
    End Sub
 
    Public Sub Click_Ouvrir(ByVal sender As Object _
, ByVal e As RoutedEventArgs)
        Me.mnuFermer.IsEnabled = True
        Me.mnuOuvrir.IsEnabled = False
    End Sub
 
    Public Sub Click_Fermer(ByVal sender As Object _
, ByVal e As RoutedEventArgs)
        Me.mnuFermer.IsEnabled = False
        Me.mnuOuvrir.IsEnabled = True
    End Sub
 
    Public Sub Click_EnLigne(ByVal sender As Object _
, ByVal e As RoutedEventArgs)
        Me.mnuEnLigne.IsChecked = _
                         Not Me.mnuEnLigne.IsChecked
    End Sub
 
End Class

La syntaxe d’écriture des méthodes appelées par les événements est tout à fait conforme à ce que nous avons vu dans le chapitre réservé à ce sujet.

Problème avec l’IntelliSense

Actuellement, l’IntelliSense ne reconnaît pas directement les noms que nous avons ajoutés. Recompilez votre projet et ceux-ci apparaîtront.

Pour changer notre menu, nous devons simplement modifier les propriétés IsChecked et IsEnabled qui correspondent aux attributs du même nom.

Inverser une valeur

Nous ne devons pas connaître l’état de IsChecked pour cocher ou décocher l’élément En ligne du menu. Il suffit d’inverser la valeur pour obtenir le résultat souhaité, ce qui se fait simplement en réassignant la valeur à elle-même et en la précédant de Not.

Rendre le menu dynamique

Plutôt qu’activer ou désactiver certaines options du menu, vous souhaiterez certainement rendre ce menu dynamique en fonction des événements déclenchés. Par exemple, le menu Edition est superflu si le fichier n’a pas été ouvert.

<Window x:Class="Window1"
 xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Les menus avec XAML"
 >
   <DockPanel>
      <Menu Name="mnuMain" VerticalAlignment="Top"
       Height="20" DockPanel.Dock="Top">
         <MenuItem Name="mnuFichier" Header="Fichier">
            <MenuItem Name="mnuOuvrir" Header="Ouvrir"
             Click="Click_Ouvrir"/>
            <MenuItem Name="mnuFermer" Header="Fermer"
             IsEnabled="False" Click="Click_Fermer"/>
         </MenuItem>
         <MenuItem Name="mnuEdition" Header="Edition"
          Visibility="Collapsed">
            <MenuItem Name="mnuCopier" Header="Copier"/>
            <MenuItem Name="mnuColler" Header="Coller"/>
            <Separator />
            <MenuItem Name="mnuLivre" Header="Livre">
               <MenuItem Name="mnuPrec" Header="Précédent"/>
               <MenuItem Name="mnuSuiv" Header="Suivant"/>
            </MenuItem>
         </MenuItem>
         <MenuItem Header="Aide">
            <MenuItem Name="mnuEnLigne" Header="En ligne"
             IsChecked="True" Click="Click_EnLigne"/>
         </MenuItem>
      </Menu>
   </DockPanel>
</Window>

Comme vous pouvez le constater, tous les éléments du menu sont maintenant nommés, ce qui nous permettra d’accéder à n’importe lequel des éléments.

Un autre attribut important a également été ajouté. L’attribut Visibility permet de cacher ou d’afficher un élément. Il peut prendre trois valeurs différentes.

   visible : l’élément est affiché.

   hidden : l’élément n’est pas affiché mais sa place est réservée.

   collapsed : l’élément n’est pas affiché et sa place n’est pas réservée, ce qui provoque un déplacement des autres éléments du menu.

Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports System.Windows.Documents
Imports System.Windows.Media
Imports System.Windows.Media.Imaging
Imports System.Windows.Shapes
 
' Interaction logic for Window1.xaml
Partial Public Class Window1
    Inherits Window
 
    Public Sub New()
        InitializeComponent()
    End Sub
 
    Public Sub Click_Ouvrir(ByVal sender As Object _
, ByVal e As RoutedEventArgs)
        Me.mnuFermer.IsEnabled = True
        Me.mnuOuvrir.IsEnabled = False
        Me.mnuEdition.Visibility = Windows.Visibility.Visible
    End Sub
 
    Public Sub Click_Fermer(ByVal sender As Object _
, ByVal e As RoutedEventArgs)
        Me.mnuFermer.IsEnabled = False
        Me.mnuOuvrir.IsEnabled = True
        Me.mnuEdition.Visibility = _
                            Windows.Visibility.Collapsed
    End Sub
 
    Public Sub Click_EnLigne(ByVal sender As Object _
, ByVal e As RoutedEventArgs)
        Me.mnuEnLigne.IsChecked = Not Me.mnuEnLigne.IsChecked
    End Sub
 
End Class

L’écran au démarrage se présente comme ceci.

Fig. 6-5 : Menu dynamique

Mais, après avoir cliqué sur Ouvrir, le menu Edition est ajouté.

Fig. 6-6 : Menu dynamique

Grâce à l’utilisation de la propriété Visibility, le code .NET est très simple. Cela nécessite de penser complètement le menu dans le module de base. Si vous êtes amené à ajouter un menu de manière totalement dynamique, c'est-à-dire sans qu’il soit prévu initialement, vous devrez le faire uniquement au moyen de code .NET.

6.2  Créer un menu contextuel

Comme son nom l’indique, le menu contextuel est destiné à offrir un menu spécifique à l’élément auquel il est attaché. L’accès à ce menu se fait grâce au clic droit de la souris. Il est de plus en plus couramment utilisé dans toutes les applications et plus seulement les applications professionnelles. D’autant que sa programmation est en définitive relativement aisée.

<Window x:Class="Window1"
 xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="Les menus avec XAML"
 >
   <DockPanel>
      <Menu Name="mnuMain" VerticalAlignment="Top"
        Height="20" DockPanel.Dock="Top">
         <MenuItem Name="mnuFichier" Header="Fichier">
            <MenuItem Name="mnuOuvrir" Header="Ouvrir"
              Click="Click_Ouvrir"/>
            <MenuItem Name="mnuFermer" Header="Fermer"
              IsEnabled="False" Click="Click_Fermer"/>
         </MenuItem>
         <MenuItem Name="mnuEdition" Header="Edition"
           Visibility="Collapsed">
            <MenuItem Name="mnuCopier" Header="Copier"/>
            <MenuItem Name="mnuColler" Header="Coller"/>
            <Separator />
            <MenuItem Name="mnuLivre" Header="Livre">
               <MenuItem Name="mnuPrec"
                Header="Précédent"
                Click="Click_Prev"/>
               <MenuItem Name="mnuSuiv"
                Header="Suivant"
                Click="Click_Next"/>
            </MenuItem>
         </MenuItem>
         <MenuItem Header="Aide">
            <MenuItem Name="mnuEnLigne" Header="En ligne"
             IsChecked="True" Click="Click_EnLigne"/>
         </MenuItem>
      </Menu>
      <Canvas Name="frmData" IsEnabled="False"
       Background="LightBlue" DockPanel.Dock="Bottom">
         <Canvas.ContextMenu>
            <ContextMenu>
               <MenuItem Header="Précédent"
                Click="Click_Prev"/>
               <MenuItem Header="Suivant"
                Click="Click_Next"/>
            </ContextMenu>
         </Canvas.ContextMenu>
         <Label Name="lblNom" Canvas.Top="10"
          Canvas.Left="10">
            Nom
         </Label>
         <TextBox Name="txtNom"  Canvas.Top="10"
          Canvas.Left="80" Width="150" MaxLength="30"
          CharacterCasing="Upper" />
         <Label Name="lblPrenom" Canvas.Top="10"
          Canvas.Left="240">
            Prénom
         </Label>
         <TextBox Name="txtPrenom"  Canvas.Top="10"
          Canvas.Left="300" Width="130" MaxLength="30"/>
         <Label Name="lblAdr" Canvas.Top="40"
          Canvas.Left="10">
            Rue
         </Label>
         <TextBox Name="txtAdr"  Canvas.Top="40"
          Canvas.Left="80" Width="350"
          MaxLength="80"/>
         <Label Name="lblCP" Canvas.Top="70"
          Canvas.Left="10">
            Code postal
         </Label>
         <TextBox Name="txtCP"  Canvas.Top="70"
          Canvas.Left="80"
          Width="50"  MaxLength="5"/>
         <Label Name="lblLocalite" Canvas.Top="70"
          Canvas.Left="150">
            Localité
         </Label>
         <TextBox Name="txtLocalite"  Canvas.Top="70"
          Canvas.Left="200" Width="230"  MaxLength="50"/>
         <Border Width="100" Height="120"
          BorderThickness="1"
          Background="White" BorderBrush="Black"
          Canvas.Top="10" Canvas.Right="10">
            <TextBlock Name="blkPhoto"
             VerticalAlignment="Center"
             HorizontalAlignment="Center" FontSize="20">
               Photo
            </TextBlock>
         </Border>
         <Label Name="lblCopyright" Canvas.Bottom="10"
          Canvas.Right="10"
          Content="Micro Application 2006" />
      </Canvas>
   </DockPanel>
</Window>

' Interaction logic for Window1.xaml
Partial Public Class Window1
    Inherits Window
 
    Public Sub New()
        InitializeComponent()
    End Sub
 
    Public Sub Click_Ouvrir(ByVal sender As Object _
                       , ByVal e As RoutedEventArgs)
        Me.mnuFermer.IsEnabled = True
        Me.mnuOuvrir.IsEnabled = False
        Me.mnuEdition.Visibility = Windows.Visibility.Visible
        Me.frmData.IsEnabled = True
    End Sub
 
    Public Sub Click_Fermer(ByVal sender As Object _
                       , ByVal e As RoutedEventArgs)
        Me.mnuFermer.IsEnabled = False
        Me.mnuOuvrir.IsEnabled = True
        Me.mnuEdition.Visibility = Windows.Visibility.Collapsed
        Me.frmData.IsEnabled = False
    End Sub
 
    Public Sub Click_EnLigne(ByVal sender As Object _
                        , ByVal e As RoutedEventArgs)
        Me.mnuEnLigne.IsChecked = Not Me.mnuEnLigne.IsChecked
    End Sub
 
    Public Sub Click_Prev(ByVal sender As Object _
                     , ByVal e As RoutedEventArgs)
        ' Code pour précedent
    End Sub
 
    Public Sub Click_Next(ByVal sender As Object _
                     , ByVal e As RoutedEventArgs)
        ' Code pour suivant
    End Sub
 
End Class

Fig. 6-7 : Le menu contextuel dans un environnement inactif

Si vous essayez d’activer le menu contextuel, rien ne se passe. Le menu n’est pas accessible car le canevas est désactivé. L’activation du canevas est réalisée dans la méthode Click_Ouvrir. Après avoir effectué Fichier, Ouvrir, le menu contextuel est maintenant actif.

Fig. 6-8 : Le menu contextuel

Toutefois, les boîtes de texte conservent leur menu contextuel par défaut.

Fig. 6-9 : Le menu contextuel par défaut

Si vous désirez les remplacer, il faudra créer un autre menu contextuel.

<TextBox Name="txtNom"  Canvas.Top="10" Canvas.Left="80"
   Width="150" MaxLength="30" CharacterCasing="Upper">
   <TextBox.ContextMenu>
      <ContextMenu>
         <MenuItem Header="Copier"/>
         <MenuItem Header="Coller"/>
      </ContextMenu>
   </TextBox.ContextMenu>
</TextBox>

Fig. 6-10 : Un menu contextuel dans une boîte de texte

Vous trouverez page 132 comment partager un menu contextuel entre plusieurs éléments de l’écran.

6.3  Créer une barre d’outils

Une barre d'outils statique

La barre d'outils est créée grâce à un objet de la classe ToolBar. Pour placer les éléments dans la barre d'outils, il n'y a qu'à ajouter au sein du nœud ToolBar les contrôles que vous souhaitez voir apparaître. Généralement, une barre d’outils est principalement composée de boutons.

<ToolBar DockPanel.Dock="Top" Height="24">
   <Button ToolTip="Ouvrir" Click="Click_Ouvrir">
      <Image>
         <Image.Source>
            <Binding Source="open.bmp"/>
         </Image.Source>
      </Image>
   </Button>
   <Button ToolTip="Fermer" Click="Click_Fermer">
      <Image>
         <Image.Source>
            <Binding Source="close.bmp"/>
         </Image.Source>
      </Image>
   </Button>
   <Button ToolTip="Precedent" Click="Click_Prev">
      <Image>
         <Image.Source>
            <Binding Source="precedent.bmp"/>
         </Image.Source>
      </Image>
   </Button>
   <Button ToolTip="Suivant" Click="Click_Next">
      <Image>
         <Image.Source>
            <Binding Source="suivant.bmp"/>
         </Image.Source>
      </Image>
   </Button>
</ToolBar>

Fig. 6-11 : Une simple barre d’outils

Positionnement de la barre d’outils

Nous avons ici utilisé un DockPanel pour placer la barre d’outils et le menu, mais nous aurions tout aussi bien pu utiliser une grille, un StackPanel ou même un WrapPanel. Le canevas est quant à lui moins approprié, mais pourquoi pas !

<ToolBar DockPanel.Dock="Top" Height="24">
   <Button ToolTip="Ouvrir" Click="Click_Ouvrir">
      <Image>
         <Image.Source>
            <Binding Source="open.bmp"/>
         </Image.Source>
      </Image>
   </Button>
   <Button ToolTip="Fermer" Click="Click_Fermer">
      <Image>
         <Image.Source>
            <Binding Source="close.bmp"/>
         </Image.Source>
      </Image>
   </Button>
   <Button ToolTip="Precedent">
      <Image>
         <Image.Source>
            <Binding Source="precedent.bmp"/>
         </Image.Source>
      </Image>
   </Button>
   <Button ToolTip="Suivant">
      <Image>
         <Image.Source>
            <Binding Source="suivant.bmp"/>
         </Image.Source>
      </Image>
   </Button>
   <Separator/>
   <ComboBox Width="80">
      <ComboBoxItem IsSelected="True">
         Par nom
      </ComboBoxItem>
      <ComboBoxItem>
         Par localité
      </ComboBoxItem>
   </ComboBox>
</ToolBar>

Fig. 6-12 : Barre d’outils avec ComboBox

Séparateur

La ComboBox est précédée d’un séparateur. Il ne s’agit absolument pas d’une obligation. Vous pouvez placer des séparateurs où bon vous semble simplement en y plaçant la balise Separator comme dans le code ci-dessus.

Un ensemble de barres d’outils

Les applications modernes ne se contentent pas d'afficher une seule barre d'outils mais affichent bien un ensemble dans lequel vous pouvez déplacer ou redimensionner à votre guise les différentes barres d’outils. Avec XAML, nous pouvons créer un conteneur dans lequel nous allons placer nos barres d'outils. Elles pourront être alors déplacées ou redimensionnées par l'utilisateur dans l'espace déterminé par le conteneur. Si l'utilisateur réduit trop la taille d'une barre d'outils, les éléments qui ne sont plus affichables sont automatiquement ajoutés dans une zone de dépassement et rendus accessibles via la petite flèche qui marque la fin de la barre d'outils.

<ToolBarTray DockPanel.Dock="Top" IsLocked="False"
 Background="LightGray">
   <ToolBar Band="0" Height="24">
      <Button ToolTip="Ouvrir" Click="Click_Ouvrir">
         <Image>
            <Image.Source>
               <Binding Source="open.bmp"/>
            </Image.Source>
         </Image>
      </Button>
      <Button ToolTip="Fermer" Click="Click_Fermer">
         <Image>
            <Image.Source>
               <Binding Source="close.bmp"/>
            </Image.Source>
         </Image>
      </Button>
      <Button ToolTip="Precedent">
         <Image>
            <Image.Source>
               <Binding Source="precedent.bmp"/>
            </Image.Source>
         </Image>
      </Button>
      <Button ToolTip="Suivant">
         <Image>
            <Image.Source>
               <Binding Source="suivant.bmp"/>
            </Image.Source>
         </Image>
      </Button>
      </ToolBar>
      <ToolBar Band="1" Height="24">
      <ComboBox Width="80">
         <ComboBoxItem IsSelected="True">
            Par nom
         </ComboBoxItem>
         <ComboBoxItem>
            Par localité
         </ComboBoxItem>
      </ComboBox>
   </ToolBar>
</ToolBarTray>

Vous pouvez constater que la barre d’outils précédemment créée a été scindée en deux au niveau du séparateur. C’est maintenant la ToolBarTray qui reçoit l’attribut DockPanel.Dock. Pour éviter un fond blanc, l’attribut Background reçoit la valeur LightGray.

L’attribut Band dans la balise ToolBar permet de fixer le numéro de ligne dans lequel vous désirez voir apparaître la barre d’outils.

Fig. 6-13 : Des barres d’outils mobiles

L’utilisateur peut déplacer les barres d’outils, soit pour les rassembler sur une même ligne, soit pour les inverser. Il ne pourra en revanche pas les décaler vers la gauche en laissant un espace entre les barres d’outils.

Fig. 6-14 : Des barres d’outils mobiles

Bloquer les barres d’outils

La propriété IsLocked permet de spécifier si les barres d'outils contenues sont mobiles ou non. Il est possible de le spécifier individuellement pour chaque barre en introduisant dans la balise de la barre concernée l’attribut ToolBarTray.IsLocked.

<ToolBarTray DockPanel.Dock="Left" IsLocked="False"
 Background="LightGray" Orientation="Vertical">

En effet, il est également judicieux de changer le docking, sans quoi le résultat ne sera pas celui espéré.

Ordre des lignes

Vu le changement d’orientation, les lignes définies par l’attribut Band deviennent des colonnes. C’est pourquoi il est préférable dans notre exemple de fixer la valeur à 0 pour les deux barres d’outils.

Hauteur des lignes

Pour les mêmes raisons, la hauteur qui était fixée à 24 points doit être adaptée pour par exemple devenir automatique. Pour y arriver, retirez l’attribut Height de chaque balise ToolBar.

Fig. 6-15 : Des barres d’outils à gauche

<Image MaxHeight="24" MaxWidth="24">

Fig. 6-16 : Zone de débordement de la barre d’outils

ToolBar Band="0" BandIndex="1">

Fig. 6-17 : Ordre des barres d’outils

Numérotation

Pour rappel, la numérotation commence à 0.

6.4  Checklist

Dans ce chapitre, nous avons vu comment créer et manipuler les menus et particulièrement comment :

   créer un menu d'application complet avec sous-menu et menus imbriqués ;

   rendre un menu dynamique ;

   créer un menu contextuel ;

   créer et manipuler une barre d'outils.