Comment : gérer l'événement ContextMenuOpening

L’événement ContextMenuOpening peut être géré dans une application pour ajuster un menu contextuel existant avant l’affichage ou pour supprimer le menu qui serait autrement affiché en définissant la Handled propriété true sur les données d’événement. La raison classique de la définition Handledtrue dans les données d’événement consiste à remplacer entièrement le menu par un nouvel ContextMenu objet, ce qui nécessite parfois l’annulation de l’opération et le démarrage d’un nouvel ouverture. Si vous écrivez des gestionnaires pour l’événement, vous devez connaître les problèmes de minutage entre un ContextMenu contrôle et le service chargé de l’ouverture et du ContextMenuOpening positionnement des menus contextuels pour les contrôles en général. Cette rubrique illustre certaines des techniques de code pour différents scénarios d’ouverture de menu contextuel et illustre un cas où le problème de minutage entre en jeu.

Il existe plusieurs scénarios de gestion de l’événement ContextMenuOpening :

  • Ajustement des éléments de menu avant l’affichage.

  • Remplacement de l’intégralité du menu avant l’affichage.

  • Supprime complètement tout menu contextuel existant et n’affiche aucun menu contextuel.

Exemple

Ajustement des éléments de menu avant l’affichage

L’ajustement des éléments de menu existants est assez simple et est probablement le scénario le plus courant. Vous pouvez le faire pour ajouter ou soustraire des options de menu contextuel en réponse aux informations d’état actuelles de votre application ou des informations d’état particulières disponibles sous forme de propriété sur l’objet où le menu contextuel est demandé.

La technique générale consiste à obtenir la source de l’événement, qui est le contrôle spécifique qui a été cliqué avec le bouton droit et à obtenir la ContextMenu propriété à partir de celle-ci. Vous souhaitez généralement case activée la Items collection pour voir quels éléments de menu contextuel existent déjà dans le menu, puis ajouter ou supprimer les nouveaux MenuItem éléments appropriés dans ou dans la collection.

void AddItemToCM(object sender, ContextMenuEventArgs e)
{
    //check if Item4 is already there, this will probably run more than once
    FrameworkElement fe = e.Source as FrameworkElement;
    ContextMenu cm = fe.ContextMenu;
    foreach (MenuItem mi in cm.Items)
    {
        if ((String)mi.Header == "Item4") return;
    }
    MenuItem mi4 = new MenuItem();
    mi4.Header = "Item4";
    fe.ContextMenu.Items.Add(mi4);
}

Remplacement de l’intégralité du menu avant l’affichage

Un autre scénario consiste à remplacer l’intégralité du menu contextuel. Bien sûr, vous pouvez également utiliser une variante du code précédent, pour supprimer chaque élément d’un menu contextuel existant et en ajouter de nouveaux à partir de l’élément zéro. Mais l’approche la plus intuitive pour remplacer tous les éléments dans le menu contextuel consiste à créer un nouvel ContextMenuélément, à le remplir avec des éléments, puis à définir la FrameworkElement.ContextMenu propriété d’un contrôle comme étant la nouvelle ContextMenu.

Voici le code de gestionnaire simple pour remplacer un ContextMenu. Le code fait référence à une méthode personnalisée BuildMenu , qui est séparée, car elle est appelée par plusieurs des exemples de gestionnaires.

void HandlerForCMO(object sender, ContextMenuEventArgs e)
{
    FrameworkElement fe = e.Source as FrameworkElement;
    fe.ContextMenu = BuildMenu();
}
ContextMenu BuildMenu()
{
    ContextMenu theMenu = new ContextMenu();
    MenuItem mia = new MenuItem();
    mia.Header = "Item1";
    MenuItem mib = new MenuItem();
    mib.Header = "Item2";
    MenuItem mic = new MenuItem();
    mic.Header = "Item3";
    theMenu.Items.Add(mia);
    theMenu.Items.Add(mib);
    theMenu.Items.Add(mic);
    return theMenu;
}

Toutefois, si vous utilisez ce style de gestionnaire pour ContextMenuOpening, vous pouvez potentiellement exposer un problème de minutage si l’objet dans lequel vous définissez le ContextMenu n’a pas de menu contextuel préexistant. Lorsqu’un utilisateur clique avec le bouton droit sur un contrôle, ContextMenuOpening il est déclenché même si l’existant ContextMenu est vide ou null. Mais dans ce cas, quel que soit le nouveau ContextMenu que vous définissez sur l’élément source arrive trop tard pour être affiché. En outre, si l’utilisateur clique avec le bouton droit sur une seconde fois, cette fois que votre nouveau ContextMenu s’affiche, la valeur n’est pas null et votre gestionnaire remplace correctement et affiche le menu lorsque le gestionnaire s’exécute une deuxième fois. Cela suggère deux solutions de contournement possibles :

  1. Assurez-vous que ContextMenuOpening les gestionnaires s’exécutent toujours sur les contrôles qui ont au moins un espace réservé ContextMenu disponible, que vous envisagez de remplacer par le code du gestionnaire. Dans ce cas, vous pouvez toujours utiliser le gestionnaire indiqué dans l’exemple précédent, mais vous souhaitez généralement affecter un espace réservé ContextMenu dans le balisage initial :

    <StackPanel>
      <Rectangle Fill="Yellow" Width="200" Height="100" ContextMenuOpening="HandlerForCMO">
        <Rectangle.ContextMenu>
          <ContextMenu>
            <MenuItem>Initial menu; this will be replaced ...</MenuItem>
          </ContextMenu>
        </Rectangle.ContextMenu>
      </Rectangle>
      <TextBlock>Right-click the rectangle above, context menu gets replaced</TextBlock>
    </StackPanel>
    
  2. Supposons que la valeur initiale ContextMenu peut être null, en fonction d’une logique préliminaire. Vous pouvez soit case activée ContextMenu pour null, soit utiliser un indicateur dans votre code pour case activée si votre gestionnaire a été exécuté au moins une fois. Étant donné que vous supposez que le ContextMenu paramètre est sur le point d’être affiché, votre gestionnaire définit Handledtrue ensuite sur les données d’événement. Pour ce qui est responsable de l’affichage ContextMenuService du menu contextuel, une true valeur pour Handled les données d’événement représente une demande d’annulation de l’affichage pour la combinaison de menu contextuel/contrôle qui a déclenché l’événement.

Maintenant que vous avez supprimé le menu contextuel potentiellement suspect, l’étape suivante consiste à en fournir une nouvelle, puis à l’afficher. Définir le nouveau est essentiellement le même que le gestionnaire précédent : vous générez un nouveau ContextMenu et définissez la propriété de la source de FrameworkElement.ContextMenu contrôle avec celle-ci. L’étape supplémentaire est que vous devez maintenant forcer l’affichage du menu contextuel, car vous avez supprimé la première tentative. Pour forcer l’affichage, vous définissez la Popup.IsOpen propriété true sur dans le gestionnaire. Soyez prudent lorsque vous effectuez cette opération, car l’ouverture du menu contextuel dans le gestionnaire déclenche à nouveau l’événement ContextMenuOpening . Si vous reentez votre gestionnaire, il devient infiniment récursif. C’est pourquoi vous devez toujours case activée pour null ou utiliser un indicateur si vous ouvrez un menu contextuel à partir d’un gestionnaire d’événementsContextMenuOpening.

Suppression d’un menu contextuel existant et affichage d’aucun menu contextuel

Le scénario final, l’écriture d’un gestionnaire qui supprime totalement un menu est rare. Si un contrôle donné n’est pas censé afficher un menu contextuel, il existe probablement des moyens plus appropriés d’assurer cela qu’en supprimant le menu juste quand un utilisateur le demande. Toutefois, si vous souhaitez utiliser le gestionnaire pour supprimer un menu contextuel et afficher rien, votre gestionnaire doit simplement être défini Handledtrue sur les données d’événement. Celui-ci ContextMenuService est responsable de l’affichage d’un menu contextuel case activée les données d’événement de l’événement déclenché sur le contrôle. Si l’événement a été marqué Handled n’importe où le long de l’itinéraire, l’action d’ouverture du menu contextuel qui a lancé l’événement est supprimée.

    void HandlerForCMO2(object sender, ContextMenuEventArgs e)
    {
        if (!FlagForCustomContextMenu)
        {
            e.Handled = true; //need to suppress empty menu
            FrameworkElement fe = e.Source as FrameworkElement;
            fe.ContextMenu = BuildMenu();
            FlagForCustomContextMenu = true;
            fe.ContextMenu.IsOpen = true;
        }
    }
}

Voir aussi