【问题标题】:WPF Get the referenced item from a ContextMenu on a ListViewItemWPF 从 ListViewItem 上的 ContextMenu 获取引用的项目
【发布时间】:2010-10-25 15:03:22
【问题描述】:

我在每个具有Click 事件的ListViewItem 上都有一个ListViewContextMenu, 如何在事件处理程序中检测在此 ContextMenu 中单击了哪个项目? 我需要物品ID

 <Style TargetType="{x:Type ListViewItem}">

。 . .

 <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate TargetType="tv:TreeListViewItem">
                        <Grid>
                            <Grid.ContextMenu>
                                <ContextMenu>
                                    <MenuItem Header="Open in current tab" Click="MenuItemCurrentTab_Click"/>
                                    <MenuItem Header="Open in new tab" Click="MenuItemNewTab_Click"/>
                                </ContextMenu>
                            </Grid.ContextMenu>

【问题讨论】:

    标签: wpf contextmenu


    【解决方案1】:

    this线程..

    按照与链接中的答案相同的方式

    <Grid.ContextMenu> 
        <ContextMenu> 
            <MenuItem Header="Open in current tab"
                      Click="MenuItemCurrentTab_Click"
                      CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>
    

    ...

    private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
    {
        MenuItem menuItem = sender as MenuItem;
        if (menuItem != null)
        {
            ContextMenu parentContextMenu = menuItem.CommandParameter as ContextMenu;
            if (parentContextMenu != null)
            {
                ListViewItem listViewItem = parentContextMenu.PlacementTarget as ListViewItem;
            }
        } 
    }
    

    更新

    添加此项以从 Grid 中获取父 ListViewItem

    public T GetVisualParent<T>(object childObject) where T : Visual
    {
        DependencyObject child = childObject as DependencyObject;
        while ((child != null) && !(child is T))
        {
            child = VisualTreeHelper.GetParent(child);
        }
        return child as T;
    }
    
    private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
    {
        MenuItem menuItem = sender as MenuItem;
        if (menuItem != null)
        {
            ContextMenu parentContextMenu = menuItem.CommandParameter as ContextMenu;
            if (parentContextMenu != null)
            {
                Grid grid = parentContextMenu.PlacementTarget as Grid;
                ListViewItem listViewItem = GetVisualParent<ListViewItem>(grid);
            }
        } 
    }
    

    【讨论】:

    • 我试过了,因为我有一个控件模板,而不是列表视图项 parentContextMenu.PlacementTarget 为我检索控件模板内的网格:(
    【解决方案2】:
        private void MenuItemCurrentTab_Click(object sender, RoutedEventArgs e)
        {
            MenuItem menuItem = (MenuItem)e.Source;
            ContextMenu menu = (ContextMenu)menuItem.Parent;
            ListViewItem item = (ListViewItem)menu.PlacementTarget;
            // do something with item
        }
    

    但最好创建单个 ContextMenu,为其命名,并将其用于所有列表视图项。

    【讨论】:

    • 我做了你写的,但 menu.PlacementTarget 检索的是网格而不是列表视图项,我不知道该怎么做。
    【解决方案3】:

    一个反复出现的问题,有很多尝试解决,但都有其缺点。例如,这里接受的答案假设每个ListViewItem 都有自己的ContextMenu。这可行,但是,特别是对于大量列表项,在 XAML 复杂性方面有相当大的成本并且可能很慢。而且真的完全没有必要。如果我们只在ListView 本身上使用单个ContextMenu,建议使用其他一些解决方案

     <MenuItem CommandParameter="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
    

    乍一看似乎解决了问题(PlacementTarget指向ListView,其SelectedItem指向列表项,因此菜单项处理程序可以使用CommandParameter来获取原始列表项),但不幸的是,如果ListView 启用了多项选择(SelectedItem 将指向已选择的项目之一,但不一定是当前单击的项目),或者如果我们使用 ListView.PreviewMouseRightButtonDown 禁用右侧的选择 -点击(可以说,这是与多项选择有关的唯一合乎逻辑的事情)。

    但是,有一种方法可以带来所有好处:

    • ListView 本身上单个ContextMenu
    • 适用于所有选择方案,单个、多个、禁用;
    • 即使是多选,它也会将当前悬停的项目传递给处理程序。

    考虑一下ListView

    <ListView ContextMenuOpening="ListView_ContextMenuOpening">
      <ListView.ContextMenu>
        <ContextMenu>
          <MenuItem Header="Menu1" Click="Menu1_Click" CommandParameter="{Binding Parent, RelativeSource={RelativeSource Self}}" />
        </ContextMenu>
      </ListView.ContextMenu>
    </ListView>
    

    CommandParameter 用于传递MenuItem 的父级,即。 ContextMenu 本身。但主要技巧在于菜单打开处理程序:

    private void ListView_ContextMenuOpening(object sender, ContextMenuEventArgs e) {
      var menu = (e.Source as FrameworkElement).ContextMenu;
      menu.Tag = (FrameworkElement)e.OriginalSource;
    }
    

    在这个处理程序中,我们仍然知道事件的原始来源,即列表项DataTemplate 的根FrameworkElement。让我们将它存储在菜单的Tag 中以供以后检索。

    private void Menu1_Click(object sender, RoutedEventArgs e) {
      if (sender is MenuItem menu)
        if (menu.CommandParameter is ContextMenu context)
          if (context.Tag is FrameworkElement item)
            if (item.DataContext is DataType data) {
              //process data
            }
    }
    

    在菜单点击处理程序中,我们可以查找我们存储在命令参数中的原始ContextMenu,从中我们可以查找我们之前存储的列表项的根FrameworkElement,最后得到存储在列表项中的对象(DataType 类型)。

    【讨论】:

      【解决方案4】:
      ListViewItem item = myListView.SelectedItem as ListViewItem;
      

      当您右键单击该项目时,它似乎可以正常工作。

      【讨论】:

        猜你喜欢
        • 2020-04-05
        • 2011-08-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多