How to set CommandTarget for MenuItem inside ContextMenu?

(This question is related to another , but quite different, which, in my opinion, requires placement here.)

Here's a (heavily cut off) 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> 

and here is the (heavily cut off) 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> 

Using various methods, UserControl can be dynamically added to the Window . Perhaps through a button in the window. Perhaps more problematic from a permanent backup storage when starting the application.

As you can see from xaml, I decided that it makes sense for me to try using Commands as a way to handle various operations that the user can perform using Task s. I do this with the ultimate goal of factoring all of the command logic into a more formally defined controller layer, but I'm trying to reorganize one step at a time.

The problem I'm encountering is related to the interaction between the command in UserControl ContextMenu and the CanExecute command defined in the window. When the application is first launched and saved Tasks are restored in the TaskStopwatches in the Window, the actual user interface elements are not selected. If I immediately r-clicked a UserControl in the Window to execute the ViewTaskProperties command, the CanExecute handler never starts and the menu item remains disabled. If I then click on some user interface element (for example, a button) just to focus something, CanExecute handlers CanExecute launched using the CanExecuteRoutedEventArgs Source property set to the user interface element that has focus.

In some respects, this behavior seems to be known. I found out that the menu will direct the event through the element that was last focused to avoid sending the event from the menu item. However, I think that the source of the event should be the control itself, or the Task that the control wraps around (but Task not an element, so I don’t think it can be the source).

I thought that maybe I lost the CommandTarget property in the MenuItem in UserControl , and I thought I wanted the command to appear from UserControl, so naturally I tried first:

 <MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" CommandTarget="{Binding ElementName=This}" /> 

This is not performed as an invalid binding. I do not know why. Then I thought: β€œHmm, I look at the tree, so maybe I need a RelativeSource,” and I tried this:

 <MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TaskStopwatchControl}}}" /> 

This also failed, but when I looked at my xaml again, I realized that ContextMenu is in the UserControl property, this is not a child. Therefore, I guessed (and at that moment it was an assumption):

 <MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" CommandTarget="{Binding RelativeSource={x:Static RelativeSource.Self}}" /> 

And this also failed.

One of the unsuccessful conjectures and checks like this is enough to make me step back and realize that I don’t understand some fundamental concept here. So what should I do?

  • As far as I understand, the role of CommandTarget correct in that it provides a mechanism for changing the source of a command?
  • How to associate with MenuItem in UserControl.ContextMenu with the owner of UserControl ? Or am I doing something wrong because I feel the need?
  • Is my desire to have the context of the command given by the element that was pressed to generate the context menu, unlike the element that had focus in front of the context menu, incorrect? Maybe I need to write my own command instead of using RoutedUICommand :

     private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task details.", "ViewTaskProperties", typeof(TaskCommands)); public static RoutedUICommand ViewTaskProperties { get { return viewTaskPropertiesCommand; } } 
  • Is there any deeper fundamental flaw in my design? This is my first significant WPF project, and I do it at one time as a learning experience, so I am definitely not against learning an excellent solution architecture.

+10
wpf contextmenu user-controls
Mar 05 '09 at 19:14
source share
2 answers

1: Yes, CommandTarget controls where the RoutedCommand starts routing from.

2: ContextMenu has a PlacementTarget that will allow you to access your UserControl:

 <MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" CommandTarget="{Binding PlacementTarget, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/> 

To avoid repeating this in every MenuItem, you can use a style.

3 and 4: I would say that your desire is reasonable. Since the Execute handler is in the window, it doesn't matter right now, but if you had different areas of the application, each of which had its own Execute handler for the same command, it would be important where the focus was.

+7
Mar 05 '09 at 20:22
source share

A similar solution I found is to use the parent's Tag property to capture the datacontext:

 <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> 
+1
Dec 22 2018-10-22
source share



All Articles