Wie Command für MenuItem in einem ContextMenu setzen?
-
03-07-2019 - |
Frage
(Diese Frage bezieht sich auf die eine andere , aber unterschiedlich genug, dass ich denke, es ist garantiert Platzierung hier.)
Hier ist eine (stark snipped) Window
:
<Window x:Class="Gmd.TimeTracker2.TimeTrackerMainForm"
xmlns:local="clr-namespace:Gmd.TimeTracker2"
xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
x:Name="This"
DataContext="{Binding ElementName=This}">
<Window.CommandBindings>
<CommandBinding Command="localcommands:TaskCommands.ViewTaskProperties"
Executed="HandleViewTaskProperties"
CanExecute="CanViewTaskPropertiesExecute" />
</Window.CommandBindings>
<DockPanel>
<!-- snip stuff -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- snip more stuff -->
<Button Content="_Create a new task" Grid.Row="1" x:Name="btnAddTask" Click="HandleNewTaskClick" />
</Grid>
</DockPanel>
</Window>
und hier ist eine (stark snipped) UserControl
:
<UserControl x:Class="Gmd.TimeTracker2.TaskStopwatchControl"
xmlns:local="clr-namespace:Gmd.TimeTracker2"
xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
x:Name="This"
DataContext="{Binding ElementName=This}">
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="What goes here?" />
</ContextMenu>
</UserControl.ContextMenu>
<StackPanel>
<TextBlock MaxWidth="100" Text="{Binding Task.TaskName, Mode=TwoWay}" TextWrapping="WrapWithOverflow" TextAlignment="Center" />
<TextBlock Text="{Binding Path=ElapsedTime}" TextAlignment="Center" />
<Button Content="{Binding Path=IsRunning, Converter={StaticResource boolToString}, ConverterParameter='Stop Start'}" Click="HandleStartStopClicked" />
</StackPanel>
</UserControl>
Durch verschiedene Techniken kann ein UserControl
dynamisch den Window
hinzugefügt werden. Vielleicht über den Button im Fenster. Vielleicht problematische, von einem dauerhaften Sicherungsspeicher, wenn die Anwendung gestartet wird.
Wie aus der XAML zu sehen ist, habe ich beschlossen, dass es Sinn macht für mich Befehle zu versuchen, als eine Möglichkeit, verwenden, um verschiedene Operationen handhaben, dass der Benutzer mit Task
s durchführen kann. Ich tue dies mit dem Endziel alle Befehlslogik in eine formal definierte Controller-Schicht von Factoring, aber ich versuche, einen Schritt zu einer Zeit, Refactoring.
Das Problem, das mir begegnet ist zwischen dem Befehl im UserControl
des ContextMenu
auf die Interaktion im Zusammenhang und der CanExecute
Befehls im Fenster definiert. Wenn die Anwendung zum ersten Mal gestartet und die gespeicherten Aufgaben gestellt werden in TaskStopwatches auf dem Fenster, werden keine aktuellen UI-Elemente ausgewählt. Wenn ich dann sofort eine UserControl
im Window
in einem Versuch-r klicken Sie auf den ViewTaskProperties
Befehl, der CanExecute
Handler nie auszuführen läuft und der Menüpunkt bleibt deaktiviert. Wenn ich dann einig UI-Element klicken Sie einfach auf (zum Beispiel der Taste) etwas Schärfe zu geben, werden die CanExecute
Handler laufen mit dem Source-Eigenschaft des CanExecuteRoutedEventArgs
auf das UI-Element gesetzt, das den Fokus hat.
In gewisser Hinsicht scheint dieses Verhalten zu sein known-- ich gelernt habe, dass Menüs leiten die Veranstaltung durch das Element, das zuletzt konzentrieren mußte immer zu vermeiden, das Ereignis aus dem Menüpunkt sendet. Was ich denke, ich möchte aber ist für die Quelle des Ereignisses die Steuerung selbst, oder die Aufgabe zu sein, dass die Steuerung selbst ist Umwickeln (aber Task
ist kein Element, so dass ich glaube nicht, dass es sein kann, eine Quelle).
dachte ich, dass vielleicht war ich auf der CommandTarget
im MenuItem
die UserControl
Eigenschaft fehlt, und mein erster Gedanke war, dass ich den Befehl kommen aus dem Usercontrol, so natürlich ich zuerst versucht wollte:
<MenuItem x:Name="mnuProperties"
Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding ElementName=This}" />
Dies scheiterte als ungültige Bindung. Ich bin mir nicht sicher warum. Dann dachte ich: „Hmmm, ich suche den Baum, so vielleicht, was ich brauche, ist ein Relative“ und ich versuchte dies:
<MenuItem x:Name="mnuProperties"
Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TaskStopwatchControl}}}" />
Das scheitert auch, aber wenn ich an meinem XAML wieder sah, wurde mir klar, dass der ContextMenu
in einer Eigenschaft des Usercontrol ist, es ist nicht ein untergeordnetes Element. So vermuten ich (und an dieser Stelle war es eine Vermutung):
<MenuItem x:Name="mnuProperties"
Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding RelativeSource={x:Static RelativeSource.Self}}" />
Und das auch fehlgeschlagen.
Eine gescheiterte Vermutung-and-Check wie das ist genug, um mich wieder weg zu machen und erkennt, dass ich eine Art Grundkonzept fehle bin obwohl hier. Also, was soll ich tun?
- Ist mein Verständnis Re: die Rolle der
CommandTarget
korrigieren, dass dies einen Mechanismus bereit, um die Quelle eines Befehls zu ändern - Wie binde ich von einem
MenuItem
inUserControl.ContextMenu
an den besitzendenUserControl
? Oder mache ich etwas falsch, nur weil ich das Bedürfnis wahrnehmen? -
Ist mein Wunsch, den Kontext eines Befehls durch das Element zu haben, auf den geklickt wurde das Kontextmenü zu erzeugen, wie mit dem Element im Gegensatz, das den Fokus vor dem Kontextmenü hatte, nicht richtig? Vielleicht muss ich stattdessen meinen eigenen Befehl schreiben, der die
RoutedUICommand
mit:private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task's details.", "ViewTaskProperties", typeof(TaskCommands)); public static RoutedUICommand ViewTaskProperties { get { return viewTaskPropertiesCommand; } }
-
Gibt es eine tiefere fundamentalen Fehler in meinem Design? Dies ist mein erstes bedeutendes WPF-Projekt, und ich mache es auf meiner eigenen Zeit als Lernerfahrung, also ist ich auf jeden Fall nicht opposed zu einer überlegenen Lösungsarchitektur zu lernen.
Lösung
1: Ja, Command steuert, wo der RoutedCommand aus startet Routing.
2: ContextMenu hat einen Placement Eigenschaft, der Zugang zu Ihrem Usercontrol ermöglicht:
<MenuItem x:Name="mnuProperties" Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
Um zu vermeiden, dies in jeder Wiederholung MENUITEM könnten Sie einen Style verwenden.
3 & 4: Ich würde sagen, Ihr Wunsch, sinnvoll ist. Da das Handler Ausführen auf dem Fenster ist es jetzt egal ist, aber wenn man die verschiedenen Regionen der Anwendung hat, jede mit ihrer eigenen Execute-Handler für den gleichen Befehl, wäre es egal, wo der Fokus war.
Andere Tipps
ähnliche Lösung, die ich gefunden wurde, die Tag-Eigenschaft des Mutter mit der Datacontext greifen:
<Grid Tag="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem
Header="{Binding Path=ToolbarDelete, Mode=Default, Source={StaticResource Resx}}"
Command="{Binding RemoveCommand}"
CommandParameter="{Binding DataContext.Id, RelativeSource={RelativeSource TemplatedParent}}"/>
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Text="{Binding Name}" Padding="2" />
</Grid>