Passing Ursprung ContextMenu in WPF-Befehl
Frage
Interessantes Problem in Bezug auf Befehle aus dem Kontextmenüpunkt Brennen ...
Ich möchte einen Befehl feuern eine Zeile in meiner Kontrolle einzufügen, InsertRowCmd. Dieser Befehl muss wissen, wo die Zeile einzufügen.
Ich konnte verwenden Mouse.GetPosition (), aber das würde mir die Position der Maus erhält zur Zeit, die sie über den Menüpunkt wäre. Ich mag stattdessen den Ursprung des Kontextmenüs erhalten.
Hat jemand irgendwelche Vorschläge, wie die Herkunft des Kontextmenüs als Parameter an den Befehl übergeben?
Beispielcode:
<UserControl x:Name="MyControl">
<!--...-->
<ContextMenu x:Name="menu">
<MenuItem Header="Insert Row" Command="{x:Static customCommands:MyCommands.InsertRowCmd}" CommandParameter="?"/>
</ContextMenu>
</UserControl>
Meine aktuellen Ideen sind wie folgt:
-Use klicken Handler statt, so dass ich den Ursprung in Code zu finden. Das Problem ist, dass ich dann zu behandeln habe die Aktivierung / Deaktivierung.
-Handle Click-Ereignis und den Ursprung des Kontextmenüs speichern. Geben Sie diese gespeicherten Informationen in den Befehl. Ich habe, dass die Click-Ereignisse Feuer überprüft, bevor der Befehl ausgeführt wird.
Irgendwelche Ideen?
EDIT:
Ich bin mit Josh Smith CommandSinkBinding Route den Befehl in meine Viewmodel Klasse Handhabung. So ist der Code, der die Befehlsausführung Griffe weiß nichts über die Aussicht.
Lösung
Sie werden verwenden müssen TranslatePoint
die obere linke (0, 0) des ContextMenu
einer Koordinate in dem Gitter enthalten, zu übersetzen. Sie könnten dies tun, indem Sie die CommandParameter
zum ContextMenu
Bindung und einen Konverter verwenden:
CommandParameter="{Binding IsOpen, ElementName=_menu, Converter={StaticResource PointConverter}}"
Ein weiterer Ansatz wäre ein angebaute Verhalten sein, das eine angeschlossene Nur-Lese-Eigenschaft vom Typ Point
automatisch aktualisiert, sobald die ContextMenu
geöffnet werden. Verbrauch würde wie folgt aussehen:
<ContextMenu x:Name="_menu" local:TrackBehavior.TrackOpenLocation="True">
<MenuItem Command="..." CommandParameter="{Binding Path=(local:TrackBehavior.OpenLocation), ElementName=_menu}"/>
</ContextMenu>
So ist die TrackOpenLocation
angebracht Eigenschaft macht die Arbeit mit dem ContextMenu
für die Befestigung und eine zweite angefügten Eigenschaft (OpenLocation
) aktualisiert, wenn der ContextMenu
geöffnet wird. Dann kann die MenuItem
nur binden an OpenLocation
die Lage zu bekommen, an dem die ContextMenu
zuletzt geöffnet wurde.
Andere Tipps
Im Anschluss an Kent Antwort, habe ich seinen angefügten Eigenschaft Vorschlag und endete mit diesem (mit Josh Smith Beispiel für angebracht Verhalten ):
public static class TrackBehavior
{
public static readonly DependencyProperty TrackOpenLocationProperty = DependencyProperty.RegisterAttached("TrackOpenLocation", typeof(bool), typeof(TrackBehavior), new UIPropertyMetadata(false, OnTrackOpenLocationChanged));
public static bool GetTrackOpenLocation(ContextMenu item)
{
return (bool)item.GetValue(TrackOpenLocationProperty);
}
public static void SetTrackOpenLocation(ContextMenu item, bool value)
{
item.SetValue(TrackOpenLocationProperty, value);
}
public static readonly DependencyProperty OpenLocationProperty = DependencyProperty.RegisterAttached("OpenLocation", typeof(Point), typeof(TrackBehavior), new UIPropertyMetadata(new Point()));
public static Point GetOpenLocation(ContextMenu item)
{
return (Point)item.GetValue(OpenLocationProperty);
}
public static void SetOpenLocation(ContextMenu item, Point value)
{
item.SetValue(OpenLocationProperty, value);
}
static void OnTrackOpenLocationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var menu = dependencyObject as ContextMenu;
if (menu == null)
{
return;
}
if (!(e.NewValue is bool))
{
return;
}
if ((bool)e.NewValue)
{
menu.Opened += menu_Opened;
}
else
{
menu.Opened -= menu_Opened;
}
}
static void menu_Opened(object sender, RoutedEventArgs e)
{
if (!ReferenceEquals(sender, e.OriginalSource))
{
return;
}
var menu = e.OriginalSource as ContextMenu;
if (menu != null)
{
SetOpenLocation(menu, Mouse.GetPosition(menu.PlacementTarget));
}
}
}
und dann in der XAML zu verwenden, müssen Sie nur:
<ContextMenu x:Name="menu" Common:TrackBehavior.TrackOpenLocation="True">
<MenuItem Command="{Binding SomeCommand}" CommandParameter="{Binding Path=(Common:TrackBehavior.OpenLocation), ElementName=menu}" Header="Menu Text"/>
</ContextMenu>
Allerdings habe ich auch benötigt, um hinzuzufügen:
NameScope.SetNameScope(menu, NameScope.GetNameScope(this));
an den Konstruktor meiner Meinung nach, sonst wird die Bindung für das CommandParameter
nicht ElementName=menu
Nachschlag könnte.
Neben Kent Antwort, denkt über einen „normalen Weg“. F. E. wenn ein ListBox ein ContextMenu hat, brauchen Sie nicht Menü Position, weil das ausgewählte Element gesetzt wird, bevor das Menü aufgetaucht. Also, wenn Ihr Steuer wäre etwas, das „ausgewählte“ auf der rechten Seite klicken wird ...