代码之家  ›  专栏  ›  技术社区  ›  Greg D

如何在ContextMenu中为MenuItem设置CommandTarget?

  •  11
  • Greg D  · 技术社区  · 16 年前

    another one ,但差异很大,因此我认为有必要将其放在这里。)

    这是一个(被严重剪断的) 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

    <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>
    

    通过各种技术,一个 可以动态添加到

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

    我遇到的问题与 用户控制 ContextMenu CanExecute 用户控制 试图执行 ViewTaskProperties 指挥部 CanExecute 处理程序从不运行,菜单项保持禁用状态。如果我然后单击某个UI元素(例如,按钮)只是为了给某个焦点,那么 CanExecute 处理程序使用 CanExecuteRoutedEventArgs

    任务 不是元素,所以我不认为它可以是源)。

    我想可能是我错过了比赛 CommandTarget 土地上的财产 MenuItem 用户控制

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

    由于绑定无效,此操作失败。我不知道为什么。然后我想,“嗯,我在找树,也许我需要的是一个相对资源”,我试了一下:

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

    上下文菜单

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

    这也失败了。

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

    1. 这提供了修改命令源的机制,是否正确?
    2. 我如何从一个文件夹绑定 在里面 UserControl.ContextMenu 给拥有者 ? 或者我做错事仅仅是因为我觉得有必要这么做?
    3. 我希望由单击以生成上下文菜单的元素设置命令的上下文,而不是在上下文菜单之前具有焦点的元素,这是不正确的吗?也许我需要编写自己的命令,而不是使用 RoutedUICommand

      private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task's details.", "ViewTaskProperties", typeof(TaskCommands));
      public static RoutedUICommand ViewTaskProperties
      {
          get { return viewTaskPropertiesCommand; }
      }
      
    4. 我的设计中是否存在一些更深层次的基本缺陷?这是我的第一个重要的WPF项目,我在自己的时间里做这件事,作为一种学习经验,所以我绝对不反对学习一个优秀的解决方案体系结构。

    2 回复  |  直到 8 年前
        1
  •  7
  •   Judah Gabriel Himango    15 年前

    CommandTarget 控制RoutedCommand从何处开始路由。

    ContextMenu 有一个 PlacementTarget

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

    三,;4:我认为你的愿望是合理的。由于executehandler位于窗口上,所以现在无关紧要,但是如果应用程序中有不同的区域,每个区域对同一命令都有自己的executehandler,那么焦点在哪里就无关紧要了。

        2
  •  2
  •   David    14 年前

    我发现的类似解决方案是使用父对象的Tag属性获取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>