Gewusst wie: Behandeln des ContextMenuOpening -Ereignisses
Das ContextMenuOpening-Ereignis kann in einer Anwendung so behandelt werden, dass es entweder ein vorhandenes Kontextmenü vor dem Anzeigen anpasst oder das andernfalls angezeigte Menü unterdrückt, indem es die Handled-Eigenschaft in den Ereignisdaten auf true festlegt. In der Regel wird für Handled der Wert true in den Ereignisdaten festgelegt, wenn das Menü vollständig durch ein neues ContextMenu-Objekt ersetzt werden soll (dafür ist manchmal das Abbrechen des Vorgangs und das Starten eines neuen Öffnungsvorgangs erforderlich). Wenn Sie Handler für das ContextMenuOpening-Ereignis schreiben, sollten Sie sich der Probleme bei der zeitlichen Steuerung zwischen einem ContextMenu-Steuerelement und dem Dienst bewusst sein, der zuständig für das Öffnen und Positionieren von Kontextmenüs für Steuerungen im Allgemeinen ist. In diesem Thema werden einige Codetechniken für verschiedene Szenarien des Öffnens von Kontextmenüs veranschaulicht. Außerdem wird ein Fall dargestellt, in dem die Probleme der zeitlichen Steuerung eine Rolle spielen.
Es gibt mehrere Szenarien, wie mit dem ContextMenuOpening-Ereignis umgegangen werden kann:
Anpassen der Menüelemente vor dem Anzeigen
Ersetzen des gesamten Menüs vor dem Anzeigen
Vollständiges Löschen eines vorhandenen Kontextmenüs, sodass kein Kontextmenü angezeigt wird
Beispiel
Anpassen der Menüelemente vor dem Anzeigen
Das Anpassen der vorhandenen Menüelemente ist recht einfach und wahrscheinlich das häufigste Szenario. Sie können so vorgehen, um Kontextmenüoptionen hinzuzufügen oder zu entfernen, als Reaktion auf aktuelle Statusinformationen in der Anwendung oder bestimmte Statusinformationen, die als Eigenschaft in dem Objekt verfügbar sind, in dem das Kontextmenü angefordert wird.
Die allgemeine Vorgehensweise besteht darin, die Quelle des Ereignisses abzurufen, d. h. das spezifische Steuerelement, auf das mit der rechten Maustaste geklickt wurde, und die ContextMenu-Eigenschaft daraus abzurufen. In der Regel wird die Items-Auflistung überprüft, um festzustellen, welche Kontextmenüelemente in dem Menü bereits vorhanden sind, und anschließend werden geeignete neue MenuItem-Elemente der Auflistung hinzugefügt bzw. daraus entfernt.
Private Sub AddItemToCM(ByVal sender As Object, ByVal e As ContextMenuEventArgs)
'check if Item4 is already there, this will probably run more than once
Dim fe As FrameworkElement = TryCast(e.Source, FrameworkElement)
Dim cm As ContextMenu = fe.ContextMenu
For Each mi As MenuItem In cm.Items
If CType(mi.Header, String) = "Item4" Then
Return
End If
Next mi
Dim mi4 As New MenuItem()
mi4.Header = "Item4"
fe.ContextMenu.Items.Add(mi4)
End Sub
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 gesamten Menüs vor dem Anzeigen
Alternativ dazu ist es auch möglich, das gesamte Kontextmenü zu ersetzen. Dazu könnten Sie natürlich auch eine Abwandlung des vorherigen Codes verwenden, alle Elemente eines vorhandenen Kontextmenüs entfernen und komplett neue Elemente hinzufügen. Die intuitivere Vorgehensweise zum Ersetzen aller Elemente im Kontextmenü besteht jedoch darin, ein neues ContextMenu zu erstellen, dieses mit Elementen zu füllen und anschließend die FrameworkElement.ContextMenu-Eigenschaft eines Steuerelements auf das neue ContextMenu festzulegen.
Im Folgenden wird ein einfacher Handlercode für das Ersetzen eines ContextMenu aufgeführt. Der Code verweist auf eine benutzerdefinierte BuildMenu-Methode, die abgetrennt wird, weil sie von den Beispielhandlern mehrfach aufgerufen wird.
Private Sub HandlerForCMO(ByVal sender As Object, ByVal e As ContextMenuEventArgs)
Dim fe As FrameworkElement = TryCast(e.Source, FrameworkElement)
fe.ContextMenu = BuildMenu()
End Sub
void HandlerForCMO(object sender, ContextMenuEventArgs e)
{
FrameworkElement fe = e.Source as FrameworkElement;
fe.ContextMenu = BuildMenu();
}
Private Function BuildMenu() As ContextMenu
Dim theMenu As New ContextMenu()
Dim mia As New MenuItem()
mia.Header = "Item1"
Dim mib As New MenuItem()
mib.Header = "Item2"
Dim mic As New MenuItem()
mic.Header = "Item3"
theMenu.Items.Add(mia)
theMenu.Items.Add(mib)
theMenu.Items.Add(mic)
Return theMenu
End Function
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 jedoch diesen Stil von Handler für ContextMenuOpening verwenden, kann möglicherweise ein Problem mit der zeitlichen Steuerung auftreten, wenn das Objekt, in dem Sie das ContextMenu festlegen, nicht über ein bereits bestehendes Kontextmenü verfügt. Wenn ein Benutzer mit der rechten Maustaste auf ein Steuerelement klickt, wird ContextMenuOpening ausgelöst, auch wenn das vorhandene ContextMenu leer oder NULL ist. Aber in diesem Fall erscheint das ContextMenu, das Sie im Quellelement festlegen, zu spät, um angezeigt zu werden. Auch wird, falls der Benutzer ein zweites Mal mit der rechten Maustaste klickt, das neue ContextMenu angezeigt, der Wert ist ungleich NULL, und der Handler ersetzt das Menü und zeigt es ordnungsgemäß an, wenn der Handler ein zweites Mal ausgeführt wird. Es gibt zwei Lösungen für diese Situation:
Stellen Sie sicher, dass die ContextMenuOpening-Handler immer mit Steuerelementen ausgeführt werden, für die mindestens ein ContextMenu-Platzhalter verfügbar ist, der durch den Handlercode ersetzt werden soll. In diesem Fall können Sie zwar weiterhin den Handler aus dem vorherigen Beispiel verwenden, aber in der Regel weisen Sie ein Platzhalter-ContextMenu im anfänglichen Markup zu:
<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>
Angenommen, der anfängliche ContextMenu-Wert wäre auf Grundlage einer vorläufigen Logik NULL. Sie können entweder das ContextMenu auf NULL prüfen oder ein Flag in Ihrem Code verwenden, um zu überprüfen, ob Ihr Handler mindestens einmal ausgeführt wurde. Da Sie annehmen, dass das ContextMenu kurz vor dem Anzeigen steht, legt Ihr Handler dann für Handled den Wert true in den Ereignisdaten fest. Für den ContextMenuService, der für die Kontextmenüanzeige zuständig ist, bedeutet ein true-Wert für Handled in den Ereignisdaten eine Anforderung, das Anzeigen der Kontextmenü-Steuerelement-Kombination abzubrechen, die das Ereignis ausgelöst hat.
Nachdem Sie nun das potenziell verdächtige Kontextmenü unterdrückt haben, ist der nächste Schritt das Bereitstellen eines neuen Menüs und dann das Anzeigen dieses Menüs. Zum Einrichten des neuen Menüs wird wie beim vorherigen Handler vorgegangen: Sie erstellen ein neues ContextMenu und legen die FrameworkElement.ContextMenu-Eigenschaft der Steuerelementquelle dafür fest. Zusätzlich müssen Sie nun die Anzeige des Kontextmenüs erzwingen, 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. Seien Sie vorsichtig, wenn Sie so vorgehen, da durch das Öffnen des Kontextmenüs im Handler das ContextMenuOpening-Ereignis erneut ausgelöst wird. Wenn Sie den Handler erneut ausführen, wird er endlos rekursiv. Daher müssen Sie immer auf null prüfen oder ein Flag verwenden, wenn Sie ein Kontextmenü innerhalb eines ContextMenuOpening-Ereignishandlers öffnen.
Löschen eines vorhandenen Kontextmenüs, sodass kein Kontextmenü angezeigt wird
Das letzte Szenario, beim dem ein Handler geschrieben wird, der ein Menü völlig unterdrückt, kommt nur selten vor. Wenn ein bestimmtes Steuerelement in einem Kontextmenü nicht angezeigt werden soll, gibt es wahrscheinlich geeignetere Möglichkeiten, dies sicherzustellen, als das Menü dann zu unterdrücken, wenn es durch einen Benutzer angefordert wird. Wenn Sie jedoch den Handler verwenden möchten, um ein Kontextmenü zu unterdrücken und nicht anzuzeigen, sollte Ihr Handler einfach für Handled den true-Wert in den Ereignisdaten festlegen. Der ContextMenuService, der für die Kontextmenüanzeige zuständig ist, prüft die Ereignisdaten des Ereignisses, das er für das Steuerelement ausgelöst hat. Wenn das Ereignis an irgendeiner Position während der Weiterleitung als Handled gekennzeichnet war, wird die Aktion des Öffnens des Kontextmenüs, die das Ereignis ausgelöst hat, unterdrückt.
Private Sub HandlerForCMO2(ByVal sender As Object, ByVal e As ContextMenuEventArgs)
If Not FlagForCustomContextMenu Then
e.Handled = True 'need to suppress empty menu
Dim fe As FrameworkElement = TryCast(e.Source, FrameworkElement)
fe.ContextMenu = BuildMenu()
FlagForCustomContextMenu = True
fe.ContextMenu.IsOpen = True
End If
End Sub
End Class
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;
}
}
}