WPF 列表视图上下文菜单

WPF ListView ContextMenu

Gitea Repo 我有一个带有 ListView 的 WPF C# 应用程序,里面有一个 GridView.

<ListView Name="Entries">
    <ListView.View>
        <GridView AllowsColumnReorder="False">
            <GridView.ColumnHeaderContainerStyle>
                <Style TargetType="{x:Type GridViewColumnHeader}">
                    <Setter Property="IsHitTestVisible" Value="False"/>
                </Style>
            </GridView.ColumnHeaderContainerStyle>
            <GridViewColumn Header="Type" Width="Auto" DisplayMemberBinding="{Binding Type}" />
            <GridViewColumn Header="Title" Width="Auto" DisplayMemberBinding="{Binding Title}" />
            <GridViewColumn Header="Date" Width="Auto" DisplayMemberBinding="{Binding Date}" />
            <GridViewColumn Header="Tags" Width="Auto" DisplayMemberBinding="{Binding Tags}" />
            <GridViewColumn Header="Categories" Width="Auto" DisplayMemberBinding="{Binding Categories}" />
        </GridView>
    </ListView.View>
</ListView>

当我右键单击 ListViewItem 时,应该出现 ContextMenu,当我单击它时,它应该执行一个函数。 像这样:

private void EntryClick(<identifier of click>) {
    MessageBox.Show("Clicked " + <maybe title to identify what i clicked>);
}

我到处都能找到这个,但我不知道如何处理它:

<MenuItem ... CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor,ListBox,1}} />

然后我发现了这个: 但是我什么都没有 item.Name

编辑:
我加了

<ListView.ContextMenu>
    <ContextMenu>
        <MenuItem Header="Remove"
            Command="{Binding RemoveItem}"
            CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" />
    </ContextMenu>
</ListView.ContextMenu>

using GalaSoft.MvvmLight.Command;

private ICommand _removeItem;

public ICommand RemoveItem
{
    get { return _removeItem ?? (_removeItem = new RelayCommand(p => RemoveItemCommand((string)p))); }
}

private void RemoveItemCommand(string item)
{
    if (!string.IsNullOrEmpty(item))
        MessageBox.Show(item);

}

但现在我得到这个错误:

Delegate System.Action does not take 1 arguments

我安装了 NuGet 包 GalaSoft.MvvmLight,因为我没有 RelayCommand。这是正确的还是我必须创建一个 class?

您原来的 CommandParameter 绑定不起作用,因为 ContextMenu 与其关联的控件不属于同一可视化树,并且祖先类型错误(ListBox而不是 ListView)。下面的原始绑定转换为 找到类型 ListBox 的第一个父级,获取它的 SelectedItem 并将其分配为 CommandParameter.

<MenuItem CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, ListBox, 1}} />

您可以改为利用 ContextMenuPlacementTarget 属性 包含对关联控件的引用(此处为 ListView)这一事实。所以下面的绑定转化为找到父ContextMenu,获取其关联控件SelectedItem并将其分配为CommandParameter.

<MenuItem Header="Remove"
          Command="{Binding RemoveItem}"
          CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" />

Delegate System.Action does not take 1 arguments

您收到此错误是因为您使用了错误的命令类型。一个RelayCommand constructor takes an execute delegate of type Action。该委托不接受任何参数。

public RelayCommand(Action execute, bool keepTargetAlive = false)

您要查找的是通用 RelayCommand<T> that takes an Action<T>

public RelayCommand(Action<T> execute, bool keepTargetAlive = false)

调整您的 属性 以使用 RelayCommand<string>,无需在此处转换为 string

public ICommand RemoveItem
{
    get { return _removeItem ?? (_removeItem = new RelayCommand<string>(RemoveItemCommand)); }
}

更新您的评论。您的 RemoveItem 命令在 window 的代码隐藏中定义。由于无论如何您都在进行代码隐藏并直接访问控件以设置 ItemsSource 等等,因此您可以在构造函数中将 window 的 DataContext 设置为它自己。

public MainWindow()
{
   InitializeComponent();
   
   // ...your other code.

   DataContext = this;
}

数据上下文是在控件中继承的,因此要访问数据上下文上的RemoveItem命令,您可以使用与上面相同的方法与PlacementTarget

<MenuItem Header="Remove"
          Command="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.RemoveItem}" 
          CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" />

请注意,有无数种方法可以解决此问题,使用 DataContext 只是其中一种,但由于不清楚您采用的是哪种设计方法,因此很难给出明确的答案。考虑研究 MVVM 模式,这将使这类问题变得容易得多。


更新您的评论,您得到 InvalidCastException

hexo_gui.Entries" cant be converted to "System.String".

这是因为项目集合包含 Entries 而非 string 类型的项目,但在您的命令中您期望 string ,因此例外。调整您的命令以期待一个 Entries 实例。

public ICommand RemoveItem => _removeItem ?? (_removeItem = new RelayCommand<Entries>(RemoveItemCommand));
private void RemoveItemCommand(Entries item)
{
   // No item was selected.
   if (item == null)
      return;

   // Do something with the item.
}