删除 WPF DataGrid 中的选定项(多选)

Delete selected items in WPF DataGrid (multiselect)

我有一个带有 DataGrid、菜单和按钮的 WPF 应用程序。选择 DataGrid 中的行时,将激活按钮和菜单项,以启用从数据库中删除数据的功能。

此主 window 的部分 XAML:

<Button ToolTip="Delete Record" Command="{Binding DeleteCommand}" Name="button_delete" IsEnabled="False"/>
<MenuItem>
    <MenuItem Header="Delete" IsEnabled="False" Name="menuItem_delete" Command="{Binding DeleteCommand}"/>
</MenuItem>

<DataGrid Name="BooksDataGrid" ItemsSource="{Binding BooksList}" SelectionChanged="dataGrid_selectionChanged">
    <DataGrid.Columns>
         <DataGridTextColumn Header="Title" Binding="{Binding title_long}"/>
         <DataGridTextColumn Header="ISBN" Binding="{Binding isbn}"/>
    </DataGrid.Columns>
</DataGrid>

DeleteCommand 将在 class 中定义,即上面主要 window 的 DataContext。 class部分代码如下:

sealed class BookViewModel
{
    public ObservableCollection<IBook> Books { get; private set; }

    // load data command code

    // delete record command code
    // ...
    public void deleteAction(IEnumerable<string> isbnList)
    {
        // delete data from database
        // this already works
    }
}

已经执行了从数据库加载数据的命令。这是以与以下问题的答案非常相似的方式实施的:How to bind WPF button to a command in ViewModelBase?

要实现的目标:

  1. 选择 DataGrid 中的项目后,如果选择了一个或多个项目,删除命令的 UI 元素将被激活。这已经通过以下事件处理程序实现,在主要 window:
  2. 的代码隐藏中
private void dataGrid_selectionChanged(object sender, SelectionChangedEventArgs args)
{
    // this works

    // if nothing is selected, disable delete button and menu item
    if (BooksDataGrid.SelectedItems.Count == 0)
    {
         button_deleteBook.IsEnabled = false;
         menuItem_deleteBook.IsEnabled = false;
    }
    else
    {
        // delete command can now be executed, as shown in the binding in XAML
        button_deleteBook.IsEnabled = true;
        menuItem_deleteBook.IsEnabled = true;
    }
}
  1. 要执行的删除命令。目前尚不清楚的是如何将参数传递给在 ViewModel 中实现的命令(DataContext 用于视图)。我是 WPF 的新手,正在尝试了解命令的工作原理。具体来说,此命令应采用 IEnumerable<string> 的参数,或者可能是 string 的集合。我已经完成并测试了 deleteAction 方法。 string 对象将是 DataGrid.
  2. 的选定行的 "ISBN" 列中的值

您遇到了 wpf/mvvm 的棘手问题之一,因为无法绑定您理想中想要使用的内容。或者至少不是直接开箱即用。

如果您只想要一个项目 select 并删除,那么您可以将 selecteditem 绑定到 window 视图模型中的 属性。该命令可以使用提供的 IBook 对象进行删除。

由于您需要多个 selection 和删除,这很复杂,因为您无法绑定 selecteditems 的整个列表。这不是可绑定的依赖项 属性.

有很多方法可以解决这个问题。

您可以子 class 数据网格并扩展。

你可以使用行为。 这些允许您做的是封装一大块面向事件的代码并添加附加的依赖项 属性 来存储数据。这本身就是可绑定的。 我建议您阅读一般行为,并 google 稍微看一下示例。 绑定 selecteditems 是一个相当普遍的要求,您应该会得到很多点击。 不过这里有一个。

Select multiple items from a DataGrid in an MVVM WPF project

如果 IBook 可以在您的视图模型中使用,您最终会得到一个 observablecollection 列表。

我推荐 observablecollection,您可以在视图模型中订阅 collectionchanged 事件,以便查看计数。将 0 到 return false 用于您的命令的 CanExecute 和 1+ true.

https://docs.microsoft.com/en-us/dotnet/api/system.windows.input.icommand.canexecute?view=netcore-3.1#System_Windows_Input_ICommand_CanExecute_System_Object_

您的 iBook 听起来不像是视图模型。 它应该是。 您要绑定的几乎所有未明确标记为 OneTime 的内容都应该是实现 inotify属性changed 的​​视图模型。这是因为存在一个长期存在的错误,否则会导致内存泄漏。不要担心你的视图模型是否会泄漏。只需始终使用视图模型并构建一个基本视图模型实现 inpc,这样您就可以轻松地从中继承所有内容。

最简单的解决方案是使用内置 DataGrid.DeleteCommand.

DataGrid默认已经支持row/cell删除。通常不需要自己创建复杂的东西。我会认为这是浪费时间。

DataGrid 公开了一个 static DataGrid.DeleteCommand,这是一个路由命令。 DataGrid 使用 CommandManager.RegisterClassCommandBinding 侦听此命令。
除了DataGrid.DeleteCommand,还支持DataGrid.BeginEditCommand(Key.F2)、DataGrid.CommitEditCommandDataGrid.CancelEditCommand(Key.Escape)、DataGrid.SelectAllCommandApplicationCommands.Copy.

你可以随时按CRTL + A到select所有行然后按DEL删除select编辑行。

如果要添加鼠标输入控制删除,需要在每一行添加一个删除按钮。这是最直观和最成熟的table设计。
这样做,要删除一行,用户不需要执行四个操作:首先导航到目标行,然后在该行上执行 select 行命令,然后第三次导航到删除按钮,最后按下删除按钮。用户现在可以直接按行的删除按钮。
这将删除两个操作以实现目标(删除一行)。在用户体验 (UX) 方面,必须使用尽可能少的用户操作和尽可能少的鼠标移动来实现目标。
Multiselect 删除仍然是可能的。用户必须 select 目标行,然后简单地按下随机删除按钮。 DataGrid 完成其余的工作,例如enable/disable 删除按钮。

以下示例使用 DataGridTemplateColumn 向每一行添加删除 Button。该示例假定 table 两列,它们是自动生成的。通过将第三个删除按钮列的 DataGridTemplateColumn.DisplayIndex 设置为 2,将此列放在最右边。
您可以使用 DataGrid.FrozenColumnCount 属性 来防止删除列滚动,即固定列,这需要将删除列放在最左边。

<DataGrid AutoGenerateColumns="True">
  <DataGrid.Columns>
    <DataGridTemplateColumn DisplayIndex="2">
      <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
          <Button Content="X" 
                  Command="{x:Static DataGrid.DeleteCommand}" />
        </DataTemplate>
      </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
  </DataGrid.Columns>
</DataGrid>