如何在 ContextMenu 中为 MenuItem 设置 CommandTarget?中为、如何在、CommandTarget、ContextMenu

由网友(陌殇、怀素)分享简介:(这个问题与 另一个,但足够不同,我认为它值得放在这里.)(This question is related to another one, but different enough that I think it warrants placement here.)这是一个(严重剪断)Window:Here's...

(这个问题与 另一个,但足够不同,我认为它值得放在这里.)

(This question is related to another one, but different enough that I think it warrants placement here.)

这是一个(严重剪断)Window:

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

这是一个(严重剪断)UserControl:

and here's a (heavily 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>

通过各种技术,可以将UserControl动态添加到Window.也许通过窗口中的按钮.也许,更有问题的是,当应用程序启动时,来自持久性后备存储.

Through various techniques, a UserControl can be dynamically added to the Window. Perhaps via the Button in the window. Perhaps, more problematically, from a persistent backing store when the application is started.

从 xaml 可以看出,我认为尝试使用命令来处理用户可以使用 Task 执行的各种操作对我来说是有意义的.我这样做的最终目标是将所有命令逻辑分解为更正式定义的控制器层,但我试图一次重构一个步骤.

As can be seen from the xaml, I've decided that it makes sense for me to try to use Commands as a way to handle various operations that the user can perform with Tasks. I'm doing this with the eventual goal of factoring all command logic into a more formally-defined Controller layer, but I'm trying to refactor one step at a time.

我遇到的问题与UserControlContextMenu中的命令和命令的CanExecute的交互有关,在窗口中定义.当应用程序第一次启动并将保存的任务恢复到窗口上的 TaskStopwatches 时,没有选择实际的 UI 元素.如果我随后立即 r-单击 Window 中的 UserControl 以尝试执行 ViewTaskProperties 命令,则 CanExecute 处理程序永远不会运行,并且菜单项保持禁用状态.如果我随后单击某个 UI 元素(例如,按钮)只是为了获得焦点,则 CanExecute 处理程序将在 CanExecuteRoutedEventArgs 的 Source 属性设置为 UI 元素的情况下运行有重点.

The problem that I'm encountering is related to the interaction between the command in the UserControl's ContextMenu and the command's CanExecute, defined in the Window. When the application first starts and the saved Tasks are restored into TaskStopwatches on the Window, no actual UI elements are selected. If I then immediately r-click a UserControl in the Window in an attempt to execute the ViewTaskProperties command, the CanExecute handler never runs and the menu item remains disabled. If I then click some UI element (e.g., the button) just to give something focus, the CanExecute handlers are run with the CanExecuteRoutedEventArgs's Source property set to the UI element that has the focus.

在某些方面,这种行为似乎是已知的——我了解到菜单将通过最后一个具有焦点的元素路由事件,以避免总是从菜单项发送事件.不过,我想我想要的是事件的源是控件本身,或者是控件自身包裹的任务(但 Task 不是元素,所以我不要认为它可以是来源).

In some respect, this behavior seems to be known-- I've learned that menus will route the event through the element that last had focus to avoid always sending the event from the menu item. What I think I would like, though, is for the source of the event to be the control itself, or the Task that the control is wrapping itself around (but Task isn't an Element, so I don't think it can be a source).

我想也许我在 UserControl 中的 MenuItem 上缺少 CommandTarget 属性,我的第一个想法是我想要命令来自 UserControl,所以自然我首先尝试了:

I thought that maybe I was missing the CommandTarget property on the MenuItem in the UserControl, and my first thought was that I wanted the command to come from the UserControl, so naturally I first tried:

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

由于绑定无效而失败.我不确定为什么.然后我想,嗯,我正在查找树,所以也许我需要的是一个 RelativeSource",然后我尝试了这个:

This failed as an invalid binding. I'm not sure why. Then I thought, "Hmmm, I'm looking up the tree, so maybe what I need is 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}}}" />

这也失败了,但是当我再次查看我的 xaml 时,我意识到 ContextMenu 在 UserControl 的属性中,它不是子元素.所以我猜到了(在这一点上是一个猜测):

That also failed, but when I looked at my xaml again, I realized that the ContextMenu is in a property of the UserControl, it's not a child element. So I guessed (and at this point it was a guess):

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

那也失败了.

像这样一次失败的猜测和检查就足以让我退缩并意识到我在这里遗漏了某种基本概念.那我该怎么办?

One failed guess-and-check like this is enough to make me back off and realize that I'm missing some sort of fundamental concept here, though. So what do I do?

我对 CommandTarget 的作用的理解是否正确,因为它提供了一种修改命令源的机制?如何从 UserControl.ContextMenu 中的 MenuItem 绑定到拥有的 UserControl?还是我做错了什么只是因为我觉得有必要?

我希望通过单击以生成上下文菜单的元素设置命令的上下文,而不是在上下文菜单之前具有焦点的元素,不正确吗?也许我需要编写自己的命令而不是使用 RoutedUICommand: Is my understanding re: the role of CommandTarget correct in that this provides a mechanism to modify the source of a command? How do I bind from a MenuItem in UserControl.ContextMenu to the owning UserControl? Or am I doing something wrong simply because I perceive a need to?

Is my desire to have the context of a command set by the element that was clicked to generate the context menu, as opposed to the element that had focus before the context menu, incorrect? Perhaps I need to write my own command instead of using the RoutedUICommand:

private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task's details.", "ViewTaskProperties", typeof(TaskCommands));
public static RoutedUICommand ViewTaskProperties
{
    get { return viewTaskPropertiesCommand; }
}

鼠标右键管理工具 Context Menu Manager下载 2015.6.0.24绿色中文版

我的设计中是否存在一些更深层次的基本缺陷?这是我的第一个重要的 WPF 项目,我在自己的时间做这件事作为学习经验,所以我绝对不反对学习卓越的解决方案架构.

Is there some deeper fundamental flaw in my design? This is my first significant WPF project, and I'm doing it on my own time as a learning experience, so I'm definitely not opposed to learning a superior solution architecture.

推荐答案

1:是的,CommandTarget 控制 RoutedCommand 从哪里开始路由.

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

2: ContextMenu 有一个 允许访问您的 UserControl 的 PlacementTarget 属性:

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

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

为避免在每个 MenuItem 中重复此操作,您可以使用样式.

To avoid repeating this in every MenuItem you could use a Style.

3 &4:我会说你的愿望是合理的.由于 Execute 处理程序位于 Window 上,所以现在无关紧要,但是如果您有应用程序的不同区域,每个区域都有自己的 Execute 处理程序用于相同的命令,那么焦点在哪里就很重要了.

3 & 4: I would say your desire is reasonable. Since the Execute handler is on the Window it doesn't matter right now, but if you had different regions of the application, each with their own Execute handler for the same command, it would matter where the focus was.

阅读全文

相关推荐

最新文章