I. Avertissement▲
Cet article fait partie d'un ensemble sur Avalon (« Windows Presentation Foundation »). Ceux-ci, bien qu'écrits séparément, forment un tout. À la manière des chapitres d'un livre, vous pouvez accéder directement au sujet qui vous intéresse, mais pour un aperçu complet, il est préférable de commencer la lecture au premier article.
1re partie : Installation
2epartie : Ma première fenêtre avec Avalon
3epartie : Les contrôles courants
4e partie : Le menu
5epartie : Les modèles « Avalon Express Application » et « Navigation Application »
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 base.
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▲
IV-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é.
<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.
IV-B. Les sous-menus▲
Pour créer un sous-menu, il suffit de définir un ou des MenuItem dans le nœud MenuItem père. Chaque nouveau MenuItem peut, à son tour, contenir d'autres MenuItem fils.
<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.
IV-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.
<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>
IV-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.
<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 ».
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
;
}
IV-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.
<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.
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.
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ésents. Il ne reste ensuite qu'à les insérer et les retirer aux moments opportuns.
<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>
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 dû 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.
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;
}
IV-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.
<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é.
<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)▲
VI-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 nœud 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 trois boutons. Les boutons incluent eux-mêmes 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.
...
<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>
VI-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 rendus accessibles 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.
<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 de 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 ».