代码之家  ›  专栏  ›  技术社区  ›  Josh G

将contextmenu的原点传递到wpf命令中

  •  6
  • Josh G  · 技术社区  · 15 年前

    有关从上下文菜单项触发命令的有趣问题…

    我想启动一个命令,在我的控件insertrowCmd中插入一行。此命令需要知道插入行的位置。

    我可以使用mouse.get position(),但这将使我得到当前鼠标的位置,该位置将位于菜单项上方。我想改为获取上下文菜单的原点。

    对于如何将上下文菜单的原点作为参数传递给命令,是否有人有任何建议?

    样例代码:

    <UserControl x:Name="MyControl">
    <!--...-->
            <ContextMenu x:Name="menu">
                <MenuItem Header="Insert Row" Command="{x:Static customCommands:MyCommands.InsertRowCmd}" CommandParameter="?"/>
            </ContextMenu>
    </UserControl>
    

    我目前的想法如下:

    -使用click处理程序,这样我就可以在代码中找到源代码。问题是我必须处理启用/禁用。

    -处理单击事件并保存上下文菜单的原点。将保存的信息传递到命令中。在执行命令之前,我已经验证了click events fire。

    有什么想法吗?

    编辑:

    我用的是乔希·史密斯的 CommandSinkBinding 将命令处理路由到我的ViewModel类中。所以处理命令执行的代码对视图一无所知。

    3 回复  |  直到 13 年前
        1
  •  5
  •   Tim Cooper    13 年前

    你需要使用 TranslatePoint 转换的左上角(0,0) ContextMenu 到包含网格中的坐标。您可以通过绑定 CommandParameter 上下文菜单 使用转换器:

    CommandParameter="{Binding IsOpen, ElementName=_menu, Converter={StaticResource PointConverter}}"
    

    另一种方法是附加行为,自动更新类型为的附加只读属性 Point 每当 上下文菜单 打开。用法如下:

    <ContextMenu x:Name="_menu" local:TrackBehavior.TrackOpenLocation="True">
        <MenuItem Command="..." CommandParameter="{Binding Path=(local:TrackBehavior.OpenLocation), ElementName=_menu}"/>
    </ContextMenu>
    

    所以 TrackOpenLocation 附加属性是指附加到 上下文菜单 更新第二个附加属性( OpenLocation 无论何时 上下文菜单 打开。然后 MenuItem 可以绑定到 开放位置 以获取 上下文菜单 最后一次打开。

        2
  •  5
  •   Wilka    15 年前

    根据肯特的回答,我使用了他附带的财产建议,最终得出了这个结论(使用乔希·史密斯的 example for attached behaviors ):

    public static class TrackBehavior
    {
     public static readonly DependencyProperty TrackOpenLocationProperty = DependencyProperty.RegisterAttached("TrackOpenLocation", typeof(bool), typeof(TrackBehavior), new UIPropertyMetadata(false, OnTrackOpenLocationChanged));
    
     public static bool GetTrackOpenLocation(ContextMenu item)
     {
      return (bool)item.GetValue(TrackOpenLocationProperty);
     }
    
     public static void SetTrackOpenLocation(ContextMenu item, bool value)
     {
      item.SetValue(TrackOpenLocationProperty, value);
     }
    
     public static readonly DependencyProperty OpenLocationProperty = DependencyProperty.RegisterAttached("OpenLocation", typeof(Point), typeof(TrackBehavior), new UIPropertyMetadata(new Point()));
    
     public static Point GetOpenLocation(ContextMenu item)
     {
      return (Point)item.GetValue(OpenLocationProperty);
     }
    
     public static void SetOpenLocation(ContextMenu item, Point value)
     {
      item.SetValue(OpenLocationProperty, value);
     }
    
     static void OnTrackOpenLocationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
     {
      var menu = dependencyObject as ContextMenu;
      if (menu == null)
      {
       return;
      }
    
      if (!(e.NewValue is bool))
      {
       return;
      }
    
      if ((bool)e.NewValue)
      {
       menu.Opened += menu_Opened;
    
      }
      else
      {
       menu.Opened -= menu_Opened;
      }
     }
    
     static void menu_Opened(object sender, RoutedEventArgs e)
     {
      if (!ReferenceEquals(sender, e.OriginalSource))
      {
       return;
      }
    
      var menu = e.OriginalSource as ContextMenu;
      if (menu != null)
      {
       SetOpenLocation(menu, Mouse.GetPosition(menu.PlacementTarget));
      }
     }
    }
    

    然后要在XAML中使用,只需要:

    <ContextMenu x:Name="menu" Common:TrackBehavior.TrackOpenLocation="True">
     <MenuItem Command="{Binding SomeCommand}" CommandParameter="{Binding Path=(Common:TrackBehavior.OpenLocation), ElementName=menu}" Header="Menu Text"/>
    </ContextMenu>
    

    但是,我还需要补充:

    NameScope.SetNameScope(menu, NameScope.GetNameScope(this));
    

    到我的视图的构造函数,否则为 CommandParameter 找不到 ElementName=menu .

        3
  •  1
  •   Sergey Aldoukhov    15 年前

    除了肯特的答案外,还要考虑一个“标准方法”。例如,当列表框具有ContextMenu时,您不需要菜单的位置,因为所选项目是在弹出菜单之前设置的。所以,如果控件右键单击会有“选中”的内容…