(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> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <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.