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

发布于 2024-07-26 11:49:11 字数 1392 浏览 2 评论 0原文

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

我最初只是这样做:

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问题并且这个MSDN 论坛问题,但我仍然不确定如何真正做到这一点,因为这两篇文章似乎都不适合我的情况。 有没有更好的方法将单击的项目传递到上下文菜单?

谢谢!

I'm trying to use the context menu in a listview to run some code that requires data from which item it originated from.

I initially just did this:

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
    }

But I found that the originating listview item was not accessible that way.

I've read some tactics about how to get around this, like intercepting the MouseDown event and setting a private field to the listviewitem that was clicked, but that doesn't sit well with me as it seems a bit hacky to pass data around that way. And WPF is supposed to be easy, right? :) I've read this SO question and this MSDN forum question, but I'm still not sure how to really do this, as neither of those articles seem to work in my case. Is there a better way to pass the item that was clicked on through to the context menu?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

纸短情长 2024-08-02 11:49:11

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

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

Similar to Charlie's answer, but shouldn't require XAML changes.

private void cmMetadata_Click(object sender, RoutedEventArgs e)
{
    MenuItem menu = sender as MenuItem;
    ListViewItem lvi = lvResources.ItemContainerGenerator.ContainerFromItem(menu.DataContext) as ListViewItem;
}
带上头具痛哭 2024-08-02 11:49:11

在 cmMetadata_Click 处理程序中,您只需查询 lvResources.SelectedItem 属性,因为可以从单击处理程序所在的代码隐藏文件访问 lvResources。它并不优雅,但它可以工作。

如果您想更优雅一点,您可以更改设置上下文菜单的位置。 例如,您可以尝试这样的操作:

<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 的模板,但我确信您可以很快找到一个适合您需求的模板。

Well in the cmMetadata_Click handler, you can just query the lvResources.SelectedItem property, since lvResources will be accessible from the code-behind file that the click handler is located in. It's not elegant, but it will work.

If you want to be a little more elegant, you could change where you set up your ContextMenu. For example, you could try something like this:

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

What this does is plug in a template for your ListViewItem, and then you can use the handy TemplatedParent shortcut to assign the ListViewItem to the DataContext of your menu item.

Now your code-behind looks like this:

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

Obviously the downside is you will now need to complete the template for a ListViewItem, but I'm sure you can find one that will suit your needs pretty quickly.

喵星人汪星人 2024-08-02 11:49:11

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

首先,创建我的命令:

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

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

So I decided to try and implement a command solution. I'm pretty pleased with how it's working now.

First, created my command:

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

Next in my custom listview control, I added a new command binding to the constructor:

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

And also there, added the event handlers:

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

I was already using a style selector to dynamically assign styles to the listview items, so instead of doing this in the xaml, I have to set the binding in the codebehind. You could do it in the xaml as well though:

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

I like the feel of this solution much better than attempting to set a field on mouse click and try to access what was clicked that way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文