4

我正在尝试使用列表视图中的上下文菜单来运行一些代码,这些代码需要它来自哪个项目的数据。

我最初只是这样做:

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
    }

但我发现原始列表视图项无法以这种方式访问​​。

我已经阅读了一些有关如何解决此问题的策略,例如拦截 MouseDown 事件并为单击的 listviewitem 设置一个私有字段,但这并不适合我,因为传递数据似乎有点棘手方式。WPF 应该很简单,对吧?:) 我已经阅读了这个SO question和这个MSDN forum question,但我仍然不确定如何真正做到这一点,因为这些文章似乎都不适用于我的情况。有没有更好的方法将单击的项目传递到上下文菜单?

谢谢!

4

3 回答 3

4

类似于查理的答案,但不应该需要 XAML 更改。

private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
    MenuItem menu = sender as MenuItem;
    ListViewItem lvi = lvResources.ItemContainerGenerator.ContainerFromItem(menu.DataContext) as ListViewItem;
}
于 2009-07-15T04:03:55.037 回答
3

那么在 cmMetadata_Click 处理程序中,您可以只查询 lvResources.SelectedItem 属性,因为可以从单击处理程序所在的代码隐藏文件中访问 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 的模板,但我相信您可以很快找到适合您需要的模板。

于 2009-07-14T20:26:14.083 回答
1

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

首先,创建了我的命令:

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

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

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

我已经在使用样式选择器为列表视图项动态分配样式,因此我必须在代码隐藏中设置绑定,而不是在 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;
}

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

于 2009-07-14T20:46:24.027 回答