代码之家  ›  专栏  ›  技术社区  ›  patjbs

确定在执行ContextMenu菜单项时在ListView中单击了哪个ListViewItem

  •  3
  • patjbs  · 技术社区  · 15 年前

    我正试图使用ListView中的上下文菜单来运行一些代码,这些代码需要数据,这些数据源于哪个项目。

    最初我只是这样做:

    XAML:

        <ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
          <ListView.Resources>
            <ContextMenu x:Key="resourceContextMenu">
                <MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click" />
            </ContextMenu>
          </ListView.Resources>
          <ListView.ItemContainerStyle>
              <Style TargetType="{x:Type ListViewItem}">
                  <Setter Property="ContextMenu" Value="{StaticResource resourceContextMenu}" />
              </Style>
          </ListView.ItemContainerStyle>
     ...
    

    C:

        private void cmMetadata_Click(object sender, RoutedEventArgs e)
        {
          // code that needs item data here
        }
    

    但我发现原始的listview项不能通过这种方式访问。

    我读过一些关于如何绕过这个问题的策略,比如截取mousedown事件并为单击的listviewitem设置一个私有字段,但是我觉得这样传递数据有点不太好。WPF应该很简单,对吧?:)我读过这个 SO question 而这 MSDN forum question 但我仍然不确定如何真正做到这一点,因为这两篇文章在我的案例中似乎都不起作用。是否有更好的方法将单击的项传递到上下文菜单?

    谢谢!

    3 回复  |  直到 15 年前
        1
  •  3
  •   Charlie    15 年前

    在cmmetadata的click处理程序中,您只需查询lvresources.selectedItem属性,因为可以从click处理程序所在的代码隐藏文件中访问lvresources。它不优雅,但会起作用的。

    如果你想更优雅一点,你可以改变设置ContextMenu的位置。例如,您可以尝试如下操作:

    <ListView x:Name="lvResources" ScrollViewer.VerticalScrollBarVisibility="Visible">
     <ListView.Style>
      <Style TargetType="ListView">
       <Setter Property="ItemContainerStyle">
        <Setter.Value>
         <Style TargetType="{x:Type ListViewItem}">
          <Setter Property="Template">
           <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListViewItem}">
             <TextBlock Text="{TemplateBinding Content}">
              <TextBlock.ContextMenu>
               <ContextMenu>
                <MenuItem Header="Get Metadata" Name="cmMetadata" Click="cmMetadata_Click" 
                 DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
               </ContextMenu>
              </TextBlock.ContextMenu>
             </TextBlock>
            </ControlTemplate>
           </Setter.Value>
          </Setter>
         </Style>
        </Setter.Value>
       </Setter>
      </Style>
     </ListView.Style>
     <ListViewItem>One Item</ListViewItem>
     <ListViewItem>Another item</ListViewItem>
    </ListView>
    

    它的作用是为ListViewItem插入一个模板,然后可以使用便捷的TemplatedParent快捷方式将ListViewItem分配给菜单项的DataContext。

    现在,您的代码隐藏如下:

    private void cmMetadata_Click(object sender, RoutedEventArgs e)
    {
        MenuItem menu = sender as MenuItem;
        ListViewItem item = menu.DataContext as ListViewItem;
    }
    

    显然,缺点是您现在需要完成ListViewItem的模板,但我相信您可以很快找到一个适合您需要的模板。

        2
  •  3
  •   Bryce Kahle    15 年前

    类似于Charlie的答案,但不需要更改XAML。

    private void cmMetadata_Click(object sender, RoutedEventArgs e)
    {
        MenuItem menu = sender as MenuItem;
        ListViewItem lvi = lvResources.ItemContainerGenerator.ContainerFromItem(menu.DataContext) as ListViewItem;
    }
    
        3
  •  1
  •   patjbs    15 年前

    所以我决定尝试实现一个命令解决方案。我对它现在的工作情况很满意。

    首先,创建了我的命令:

    public static class CustomCommands
    {
        public static RoutedCommand DisplayMetadata = new RoutedCommand();
    }
    

    接下来,在自定义ListView控件中,我向构造函数添加了一个新的命令绑定:

    public SortableListView()
    {
        CommandBindings.Add(new CommandBinding(CustomCommands.DisplayMetadata, DisplayMetadataExecuted, DisplayMetadataCanExecute));
    }
    

    此外,还添加了事件处理程序:

    public void DisplayMetadataExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        var nbSelectedItem = (MyItem)e.Parameter;
    
        // do stuff with selected item
    }
    
    public void DisplayMetadataCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
        e.Handled = true;
    }
    

    我已经使用样式选择器动态地将样式分配给ListView项,因此,我必须在代码隐藏中设置绑定,而不是在XAML中执行此操作。不过,您也可以在XAML中这样做:

    public override Style SelectStyle(object item, DependencyObject container)
    {
        ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
        MyItem selectedItem = (MyItem)item;
        Style s = new Style();
    
        var listMenuItems = new List<MenuItem>();
        var mi = new MenuItem();
        mi.Header= "Get Metadata";
        mi.Name= "cmMetadata";
        mi.Command = CustomCommands.DisplayMetadata;
        mi.CommandParameter = selectedItem;
        listMenuItems.Add(mi);
    
        ContextMenu cm = new ContextMenu();
        cm.ItemsSource = listMenuItems;
    
        // Global styles
        s.Setters.Add(new Setter(Control.ContextMenuProperty, cm));
    
        // other style selection code
    
        return s;
    }
    

    我更喜欢这个解决方案的感觉,而不是尝试在鼠标单击时设置一个字段,然后尝试以这种方式访问所单击的内容。