Gewusst wie: Behandeln des ContextMenuOpening -Ereignisses

Das ContextMenuOpening-Ereignis kann in einer Anwendung behandelt werden, um entweder ein vorhandenes Kontextmenü vor der Anzeige anzupassen oder um das Menü zu unterdrücken, das andernfalls angezeigt würde, indem die Handled-Eigenschaft in den Ereignisdaten auf true festgelegt wird. Der typische Grund dafür, die Einstellung Handled in den Ereignisdaten besteht auf true festzulegen besteht darin, das Menü vollständig durch ein neues ContextMenu-Objekt zu ersetzen, was manchmal das Abbrechen des Vorgangs und das Starten eines neuen Öffnenvorgangs erfordert. Wenn Sie Handler für das ContextMenuOpening-Ereignis schreiben, sollten Sie sich der Timingprobleme zwischen einem ContextMenu-Steuerelement und dem Dienst bewusst sein, der für das Öffnen und Positionieren von Kontextmenüs für Steuerelemente im Allgemeinen verantwortlich ist. In diesem Thema werden einige der Codetechniken für verschiedene Szenarien zum Öffnen von Kontextmenüs sowie ein Fall veranschaulicht, bei dem das Timingproblem eine Rolle spielt.

Es gibt mehrere Szenarien für die Behandlung des ContextMenuOpening-Ereignisses:

  • Anpassen der Menüelemente vor der Anzeige.

  • Ersetzen des ganzen Menüs vor der Anzeige.

  • Vollständiges Unterdrücken aller vorhandenen Kontextmenüs und Anzeigen keines Kontextmenüs.

Beispiel

Anpassen der Menüelemente vor der Anzeige

Das Anpassen der vorhandenen Menüelemente ist ziemlich einfach und wahrscheinlich das gängigste Szenario. Sie können dies anwenden, um Kontextmenüoptionen als Reaktion auf aktuelle Zustandsinformationen in Ihrer Anwendung oder auf bestimmte Zustandsinformationen hinzuzufügen oder zu entfernen, die als Eigenschaft in dem Objekt verfügbar sind, von dem das Kontextmenü angefordert wird.

Die generelle Methode besteht darin, die Quelle des Ereignisses abzurufen, wobei es sich um das bestimmte Steuerelement handelt, auf das mit der rechten Maustaste geklickt wurde, und seine ContextMenu-Eigenschaft abzurufen. In der Regel sollten Sie die Items-Auflistung überprüfen, um festzustellen, welche Kontextmenüelemente bereits im Menü vorhanden sind, und dann geeignete neue MenuItem-Elemente in der Auflistung hinzuzufügen oder daraus zu entfernen.

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);
}

Ersetzen des ganzen Menüs vor der Anzeige

Ein Alternativszenario ist, wenn Sie das gesamte Kontextmenü ersetzen möchten. Sie können natürlich auch eine Variante des voranstehenden Codes verwenden, um jedes Element eines vorhandenen Kontextmenüs zu entfernen und neue Elemente hinzuzufügen, beginnend mit Element Null. Der intuitivere Ansatz zum Ersetzen aller Elemente im Kontextmenü besteht jedoch darin, ein neues ContextMenu-Element zu erstellen, es mit Elementen aufzufüllen und dann die FrameworkElement.ContextMenu-Eigenschaft eines Steuerelements als neues ContextMenu-Element festzulegen.

Im Folgenden sehen Sie den einfachen Handlercode zum Ersetzen eines ContextMenu-Elements. Der Code verweist auf eine benutzerdefinierte BuildMenu-Methode, die gesondert aufgeführt ist, da sie von mehr als einem der Beispielhandler aufgerufen wird.

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;
}

Wenn Sie diese Art von Handler jedoch für ContextMenuOpening verwenden, können Sie möglicherweise ein Timingproblem einführen, wenn das Objekt, in dem Sie ContextMenu festlegen, kein zuvor vorhandenes Kontextmenü besitzt. Wenn ein Benutzer mit der rechten Maustaste auf ein Steuerelement klickt, wird ContextMenuOpening selbst dann ausgelöst, wenn das vorhandene ContextMenu leer oder null ist. In diesem Fall aber kommt, egal welches neue ContextMenu Sie für das Quellelement festlegen, das Kontextmenü zu spät an, um angezeigt zu werden. Außerdem wird, wenn der Benutzer ein zweites Mal mit der rechten Maustaste klicken sollte, diesmal Ihr neues ContextMenu angezeigt, der Wert ist nicht Null, und Ihr Handler wird das Menü ordnungsgemäß ersetzen und anzeigen, wenn der Handler ein zweites Mal ausgeführt wird. Dies legt zwei mögliche Problemumgehungen nahe:

  1. Sicherstellen, dass ContextMenuOpening-Handler immer für Steuerelemente ausgeführt werden, bei denen mindestens ein Platzhalter-ContextMenu verfügbar ist, das vom Handlercode ersetzt werden soll. In diesem Fall können Sie weiterhin den im vorherigen Beispiel gezeigten Handler verwenden, doch sie sollten in der Regel im anfänglichen Markup ein Platzhalter-ContextMenu zuweisen:

    <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. Gehen Sie davon aus, dass der anfängliche ContextMenu-Wert Null ist, basierend auf einer vorläufigen Logik. Sie können entweder ContextMenu auf NULL überprüfen oder ein Flag in Ihrem Code verwenden, um zu überprüfen, ob Ihr Handler mindestens einmal ausgeführt wurde. Da Sie davon ausgehen, dass das ContextMenu dabei ist, angezeigt zu werden, legt ihr Handler dann Handled in den Ereignisdaten auf true fest. Für das ContextMenuService, das für die Anzeige des Kontextmenüs verantwortlich ist, stellt ein true-Wert für Handled in den Ereignisdaten eine Anforderung zum Abbrechen der Anzeige für die Kombination aus Kontextmenü und Steuerelement dar, die das Ereignis ausgelöst hat.

Nachdem Sie das potenziell verdächtige Kontextmenü unterdrückt haben, besteht der nächste Schritt darin, ein neues bereitzustellen und dann anzuzeigen. Das Festlegen des neuen entspricht grundsätzlich dem vorherigen Handler: Sie erstellen ein neues ContextMenu und legen die FrameworkElement.ContextMenu-Eigenschaft der Steuerelementquelle darauf fest. Der zusätzliche Schritt ist, dass Sie nun die Anzeige des Kontextmenüs erzwingen müssen, da Sie den ersten Versuch unterdrückt haben. Um die Anzeige zu erzwingen, legen Sie die Popup.IsOpen-Eigenschaft innerhalb des Handlers auf true fest. Gehen Sie dabei sorgfältig vor, da das Öffnen des Kontextmenüs im Handler erneut das ContextMenuOpening-Ereignis auslöst. Wenn Sie ihren Handler erneut eingeben, wird dieser unendlich rekursiv. Deshalb müssen Sie immer nach null suchen oder einen Flag verwenden, wenn Sie ein Kontextmenü aus einem ContextMenuOpening-Ereignishandler heraus öffnen.

Unterdrücken aller vorhandenen Kontextmenüs und Anzeigen keines Kontextmenüs

Das letzte Szenario, bei dem ein Handler geschrieben wird, der ein Menü vollständig unterdrückt, ist ungewöhnlich. Wenn ein bestimmtes Steuerelement ein Kontextmenü nicht anzeigen soll, gibt es wahrscheinlich mehr geeignete Möglichkeiten, dies zu gewährleisten, als das Menü genau dann zu unterdrücken, wenn ein Benutzer es anfordert. Wenn Sie jedoch den Handler verwenden möchten, um ein Kontextmenü zu unterdrücken und nichts anzuzeigen, sollte Ihr Handler Handled einfach in den Ereignisdaten auf true festlegen. Der ContextMenuService, der für das Anzeigen eines Kontextmenüs verantwortlich ist, überprüft die Ereignisdaten des Ereignisses, das für das Steuerelement ausgelöst wurde. Wenn das Ereignis an einer beliebigen Stelle der Route als Handled markiert wurde, wird die Aktion zum Öffnen des Kontextmenüs, die das Ereignis initiiert hat, unterdrückt.

    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;
        }
    }
}

Weitere Informationen