A la découverte d'Avalon.
Partie IV : Les menus dans Avalon.
Date de publication : 07/11/2005
Par
Jean-Alain Baeyens (autres articles)
Après avoir découvert dans le tutoriel précédent comment utiliser
les contrôles WinFX usuels avec XAML et .NET, nous allons poursuivre
la découverte de "Windows Presentation Foundation" avec les menus.
Nous aurons ainsi fait le tour d'une application simple avec les API d'Avalon.
I. Avertissement
II. Remerciements
III. Introduction
IV. Le menu d'une fenêtre
A. Le menu principal
B. Les sous-menus
C. Les éléments inactifs ou cochés du menu
D. Associer une action à un menu
E. Le menu dynamique
F. Séparer le menu et le reste de la fenêtre.
V. Le menu contextuel
VI. La barre d'outils (Toolbar)
A. Une barre d'outils statique
B. Des toolbars mobiles dans un ensemble
VII. 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. A 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.
II. Remerciements
Je remercie mon épouse ainsi que "pedro204" pour la relecture de cet article.
III. Introduction
Ce tutoriel nous permettra d'étudier le fonctionnement des menus.
Dans le cadre de cet article découverte, nous nous limiterons aux
fonctionnalités de bases.
Si vous souhaitez reproduire l'exemple du chapitre, commencez par créer
un nouveau projet de type "Avalon Application".
IV. Le menu d'une fenêtre
A. Le menu principal
Traditionnellement, le menu principal se présente horizontalement
en haut de fenêtre sur un fond gris clair. Pour créer notre menu,
nous allons utiliser les classes Menu et MenuItem.
La propriété VerticalAlignment permet de placer le menu
sous la barre de titre comme souhaité.
| Code XAML pour un menu principal |
<Window x:Class="AvalonMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les menus avec Avalon"
>
<Menu VerticalAlignment="Top" Height="20">
<MenuItem Header="Fichier"/>
<MenuItem Header="Edition"/>
<MenuItem Header="Aide"/>
</Menu>
</Window> |
 |
La propriété Height
est obligatoire sinon le menu occupera toute la hauteur disponible
dans notre fenêtre.
|
B. Les sous-menus
Pour créer un sous menu, il suffit de définir un ou des MenuItem
dans le noeud MenuItem père. Chaque nouveau MenuItem
peut, à son tour, contenir d'autres MenuItem fils.
| Code XAML pour un menu avec sous-menu |
<Window x:Class="AvalonMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les menus avec Avalon"
>
<Menu VerticalAlignment="Top" Height="20">
<MenuItem Header="Fichier">
<MenuItem Header="Ouvrir"/>
<MenuItem Header="Fermer"/>
<MenuItem Mode="Separator"/>
<MenuItem Header="Vérificateur">
<MenuItem Header="1.0"/>
<MenuItem Header="2.0"/>
</MenuItem>
</MenuItem>
<MenuItem Header="Edition">
<MenuItem Header="Copier"/>
<MenuItem Header="Coller"/>
</MenuItem>
<MenuItem Header="Aide"/>
</Menu>
</Window> |
 |
Notez l'utilisation de MenuItem Mode="Separator" pour créer un séparateur
|
C. Les éléments inactifs ou cochés du menu
Pour rendre un élément d'un menu inactif (grisé), il suffit
d'utiliser la propriété IsEnabled. Pour cocher un élément
d'un menu, il suffit d'utiliser la propriété IsChecked.
| Code XAML pour rendre une option du menu Disabled ou Checked |
<MenuItem Header="Fichier">
<MenuItem Header="Ouvrir"/>
<MenuItem Header="Fermer" IsEnabled="False"/>
<MenuItem Mode="Separator"/>
<MenuItem Header="Vérificateur">
<MenuItem Header="1.0" IsChecked="True"/>
<MenuItem Header="2.0"/>
</MenuItem>
</MenuItem> |
D. 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. Dans l'exemple, nous allons changer
le statut (Enabled, Checked) de certains éléments du menu
dans les actions déclenchées.
| Code XAML pour associer un élément du menu à une méthode |
<Window x:Class="AvalonMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les menus avec Avalon"
>
<Menu VerticalAlignment="Top" Height="20">
<MenuItem Header="Fichier">
<MenuItem Name="Ouvre" Header="Ouvrir" Click="OpenClick"/>
<MenuItem Name="Ferme" Header="Fermer" IsEnabled="False"
Click="CloseClick"/>
<MenuItem Mode="Separator"/>
<MenuItem Header="Vérificateur">
<MenuItem Name="V1" Header="1.0" IsChecked="True"
Click="V1Click"/>
<MenuItem Name="V2" Header="2.0" Click="V2Click"/>
</MenuItem>
</MenuItem>
<MenuItem Header="Edition">
<MenuItem Header="Copier"/>
<MenuItem Header="Coller"/>
</MenuItem>
<MenuItem Header="Aide"/>
</Menu>
</Window> |
 |
Pour pouvoir agir directement sur un élément du menu, le plus
simple est de lui donner un nom en utilisant l'attribut "Name".
|
| Code C# associéà notre menu |
private void OpenClick(object sender, RoutedEventArgs e)
{
this.Ouvre.IsEnabled = false;
this.Ferme.IsEnabled = true;
}
private void CloseClick(object sender, RoutedEventArgs e)
{
this.Ouvre.IsEnabled = true;
this.Ferme.IsEnabled = false;
}
private void V1Click(object sender, RoutedEventArgs e)
{
this.V1.IsChecked = true;
this.V2.IsChecked = false;
}
private void V2Click(object sender, RoutedEventArgs e)
{
this.V1.IsChecked = false;
this.V2.IsChecked = true;
} |
E. Le menu dynamique
Pour pouvoir modifier dynamiquement un menu depuis le code C#,
il suffit de manipuler notre objet de la classe Menu.
Nous devons commencer par le nommer.
| Nommer le menu |
<Menu Name="MainMenu" VerticalAlignment="Top" Height="20"> |
Une fois cette opération réalisée, nous pouvons atteindre la
propriété Items de notre menu. Pour l'exemple, nous
allons ajouter l'élément "Traiter" dans notre menu lorsque l'on
effectue "Ouvre" et le retirer lorsque l'on
effectue "Ferme". Le menu "Traiter" contient deux éléments.
En premier, nous devons créer nos trois MenuItem. Nous
devons remplir les différentes propriétés utiles et associer les
différents évènements, au minimum la propriété Header
et vraisemblablement l'évènement Click. Ici, je me limiterai
à Header. Nous ajoutons avec la méthode Add les deux
options à "Traiter". Il ne reste alors qu'à insérer "Traiter" dans
le menu principal en utilisant la méthode Insert.
Le premier paramètre de la méthode Insert indique la
position où doit être inséré le menu. N'oubliez pas que le
premier élément est en position 0.
| Ajout dynamique dans le menu |
private void OpenClick(object sender, RoutedEventArgs e)
{
MenuItem mnuTraiter = new MenuItem();
MenuItem mnuTraiter1 = new MenuItem();
MenuItem mnuTraiter2 = new MenuItem();
mnuTraiter.Header = "Traiter";
mnuTraiter1.Header = "Traitement principal";
mnuTraiter2.Header = "Traitement secondaire";
mnuTraiter.Items.Add(mnuTraiter1);
mnuTraiter.Items.Add(mnuTraiter2);
this.Ouvre.IsEnabled = false;
this.Ferme.IsEnabled = true;
this.MainMenu.Items.Insert(1, mnuTraiter);
} |
Pour retirer un élément, il suffit d'utiliser la
méthode RemoveAt du menu contenant l'élément tout en
précisant la position de l'élément à supprimer.
| Suppression dynamique dans le menu |
private void CloseClick(object sender, RoutedEventArgs e)
{
this.Ouvre.IsEnabled = true;
this.Ferme.IsEnabled = false;
this.MainMenu.Items.RemoveAt(1);
} |
Une alternative est de créer dans XAML le menu complet avec tous
les éléments. Ensuite le code C#, dans le constructeur
ou dans la méthode associée à l'évènement loaded de votre
fenêtre, vous retirez les éléments qui ne doivent pas être
présent. Il ne reste ensuite qu'à les insérer et les retirer
aux moments opportuns.
| Code XAML modifié pour disposer des éléments complémentaires |
<Window x:Class="AvalonMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les menus avec Avalon"
>
<Menu Name="MainMenu" VerticalAlignment="Top" Height="20">
<MenuItem Header="Fichier">
<MenuItem Name="Ouvre" Header="Ouvrir" Click="OpenClick"/>
<MenuItem Name="Ferme" Header="Fermer" IsEnabled="False"
Click="CloseClick"/>
<MenuItem Mode="Separator"/>
<MenuItem Header="Vérificateur">
<MenuItem Name="V1" Header="1.0" IsChecked="True"
Click="V1Click"/>
<MenuItem Name="V2" Header="2.0" Click="V2Click"/>
</MenuItem>
</MenuItem>
<MenuItem Name="Traiter" Header="Traiter">
<MenuItem Header="Traitement principal"/>
<MenuItem Header="Traitement secondaire"/>
</MenuItem>
<MenuItem Name="Editer" Header="Edition">
<MenuItem Name="Copie" Header="Copier"/>
<MenuItem Name="Coller" Header="Coller"/>
</MenuItem>
<MenuItem Header="Aide"/>
</Menu>
</Window> |
| Le code C# qui adapte le menu |
public Window1()
{
InitializeComponent();
this.MainMenu.Items.Remove(Traiter);
}
private void OpenClick(object sender, RoutedEventArgs e)
{
this.Ouvre.IsEnabled = false;
this.Ferme.IsEnabled = true;
this.MainMenu.Items.Insert(1,Traiter);
}
private void CloseClick(object sender, RoutedEventArgs e)
{
this.Ouvre.IsEnabled = true;
this.Ferme.IsEnabled = false;
this.MainMenu.Items.Remove(Traiter);
} |
 |
Notez au passage que j'ai utilisé la méthode Remove au
lieu de RemoveAt. Cela est rendu possible par le fait
que l'objet MenuItem est nommé. Cela aurait également
pu être possible avec la technique précédente mais nous aurions
du définir notre objet au niveau de la classe et non dans la
méthode.
|
Au lieu d'utiliser les méthodes Insert et Remove,
nous pouvons utiliser la propriété Visibility.
| Modifier le menu en utilisant Visibility |
public Window1()
{
InitializeComponent();
this.Traiter.Visibility = Visibility.Collapsed;
}
private void OpenClick(object sender, RoutedEventArgs e)
{
this.Ouvre.IsEnabled = false;
this.Ferme.IsEnabled = true;
this.Traiter.Visibility = Visibility.Visible;
}
private void CloseClick(object sender, RoutedEventArgs e)
{
this.Ouvre.IsEnabled = true;
this.Ferme.IsEnabled = false;
this.Traiter.Visibility = Visibility.Collapsed;
} |
F. Séparer le menu et le reste de la fenêtre.
Bien que notre menu se présente parfaitement, il est judicieux
de séparer celui-ci du reste de la fenêtre. Si votre
fenêtre ne contient qu'un menu qui lui-même ouvrira des fenêtres
filles, alors il n'y a pas de problème. Par contre, si votre fenêtre
contient d'autres contrôles, l'aspect du menu va s'en trouver altéré.
Je ne saurais donc trop vous conseiller d'utiliser une grille
dont la première ligne contiendra le menu.
| Le menu inclus dans une grille |
<Window x:Class="AvalonMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les menus avec Avalon"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="MainMenu" VerticalAlignment="Top" Height="20">
</Menu>
</Grid>
</Window> |
 |
En utilisant un Grid,
vous avez une entière liberté pour positionner votre menu. Dans
le même esprit, rien ne vous empêche de créer plusieurs menus
dans la même fenêtre.
|
V. Le menu contextuel
Pour illustrer le menu contextuel, nous allons ajouter un contrôle de type TextBox
dans notre fenêtre. Nous devons ensuite utiliser la propriété ContextMenu
du contrôle et y assigner un objet de type ContextMenu. Celui-ci reçoit
les éléments du menu souhaité.
| Création d'un menu contextuel en XAML |
<Window x:Class="AvalonMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les menus avec Avalon"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="MainMenu" VerticalAlignment="Top" Height="20">
</Menu>
<TextBox Grid.Row="1" Name= "Nom" Width="100" Height="30">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Copier"/>
<MenuItem Header="Coller"/>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Window> |
 |
Le menu contextuel peut également être réalisé dans le code .NET.
Pour cela, il suffit d'appliquer les techniques vues dans le paragraphe
Le menu dynamique
mais en assignant la propriété ContextMenu du contrôle.
Cette technique est particulièrement efficace pour assigner un même
menu à différents contrôles.
|
VI. La barre d'outils (Toolbar)
A. Une barre d'outils statique
Pour réaliser une barre d'outils statique sous le menu, nous devons
commencer par ajouter une ligne dans notre Grid. Dans
l'exemple, j'ai fixé la hauteur de la ligne à 30 points ce qui
limitera du même coup la hauteur de notre barre d'outils.
La barre d'outils elle même 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 noeud ToolBar les contrôles
que vous souhaitez voir apparaître. Dans l'exemple, j'ai placé en
premier une image qui permet une identification visuelle de la barre
d'outils. Cette image est suivie de 3 boutons. Les boutons incluent
eux même une icône au lieu d'un texte. N'oubliez pas d'assigner la
propriété ToolTip de votre bouton pour permettre une identification
facile par l'utilisateur de la fonction de chaque bouton. Pour associer
une action à notre bouton, il ne nous reste qu'à assigner la propriété
Click.
| Code à ajouter pour une barre d'outils statique. |
...
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
...
<ToolBar Grid.Row="1" VerticalContentAlignment="Center" >
<Image>
<Image.Source>
<Binding Source="I1.ico"/>
</Image.Source>
</Image>
<Button ToolTip="Ouvre" Click="OpenClick">
<Image>
<Image.Source>
<Binding Source="I2.ico"/>
</Image.Source>
</Image>
</Button>
<Button ToolTip="Ferme" Click="CloseClick">
<Image>
<Image.Source>
<Binding Source="I3.ico"/>
</Image.Source>
</Image>
</Button>
<Button ToolTip="Change de vérificateur">
<Image>
<Image.Source>
<Binding Source="I4.ico"/>
</Image.Source>
</Image>
</Button>
</ToolBar> |
B. Des toolbars mobiles dans un ensemble
Les applications modernes ne se contentent pas d'afficher une seule
barre d'outils mais bien un ensemble que vous pouvez déplacer
ou redimensionner à votre guise. Avec Avalon, nous pouvons
créer un container 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 container. 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 rendu accessible via la petite flèche
qui marque la fin de la barre d'outils. Le container est créé en
utilisant un objet de la classe ToolBarTray. La propriété
IsLocked permet de spécifier si les barres d'outils contenues
sont mobiles ou non.
Pour l'exemple, j'ai doublé la hauteur de la ligne de la grille
qui contient la barre d'outils dont j'ai fixé la hauteur avec
l'incontournable propriété Height. J'ai également ajouté une
seconde barre d'outils qui contient une image et une ComboBox.
| Utilisation du ToolBarTray |
<Window x:Class="AvalonMenu.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Les menus avec Avalon"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
...
<ToolBarTray Grid.Row="1" IsLocked="False">
<ToolBar VerticalContentAlignment="Center" Height="30">
<Image>
<Image.Source>
<Binding Source="I1.ico"/>
</Image.Source>
</Image>
<Button ToolTip="Ouvre" Click="OpenClick">
<Image>
<Image.Source>
<Binding Source="I2.ico"/>
</Image.Source>
</Image>
</Button>
<Button ToolTip="Ferme" Click="CloseClick">
<Image>
<Image.Source>
<Binding Source="I3.ico"/>
</Image.Source>
</Image>
</Button>
</ToolBar>
<ToolBar VerticalContentAlignment="Center" Height="30">
<Image >
<Image.Source>
<Binding Source="logo.gif"/>
</Image.Source>
</Image>
<ComboBox>
<ComboBoxItem IsSelected="true">Express</ComboBoxItem>
<ComboBoxItem>Complet</ComboBoxItem>
</ComboBox>
</ToolBar>
</ToolBarTray>
...
</Grid>
</Window> |

 |
Vous pouvez utiliser la propriété Band pour placer les barres
d'outils sur des lignes différentes.
|
VII. Conclusion
Avec Avalon, la gestion des menus est vraiment très simple et permet
très facilement des réaliser des menus et des barres d'outils déjà très
complexes.
Avec ce tutoriel se termine notre tour d'horizon du modèle d'application
Windows "classique". Dans le prochain article, nous verrons qu'il
est possible avec Avalon d'avoir des applications dans un autre modèle
de développement que le traditionnel "WinForm".


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 oeuvre intellectuelle protégée par les droits d'auteurs. 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'à 3 ans de prison et jusqu'à 300 000 E
de dommages et intérêts.
Cette page est déposée à la
SACD.