【问题标题】:Delete selected items in WPF DataGrid (multiselect)删除 WPF DataGrid 中的选定项目(多选)
【发布时间】:2020-10-03 19:42:27
【问题描述】:

我有一个带有DataGrid、菜单和按钮的 WPF 应用程序。当DataGrid 中的行被选中时,按钮和菜单项被激活,以允许从数据库中删除数据。

此主窗口的部分 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 将在上述主窗口的DataContext 类中定义。该类部分代码如下:

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 元素将被激活。这已经通过以下事件处理程序在主窗口的代码隐藏中实现:
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&lt;string&gt; 的参数,或者可能是string 的集合。我已经完成并测试了deleteAction 方法。 string 对象将是 DataGrid 的选定行的“ISBN”列中的值。

【问题讨论】:

  • 可以将CommandParameter绑定到DataGrid.SelectedItem属性,该属性返回当前选中的行数据模型。

标签: c# .net wpf datagrid command


【解决方案1】:

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

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

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

您可以随时按 CRTL + A 选择所有行,然后按 DEL 删除所选行。

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

以下示例使用DataGridTemplateColumn 将删除Button 添加到每一行。该示例假定一个包含两列的表,它们是自动生成的。通过将第三个删除按钮列的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>

【讨论】:

    【解决方案2】:

    您已经陷入了 wpf / mvvm 的棘手问题之一,因为您理想地想要使用的东西无法被绑定。或者至少不是直接开箱即用。

    如果您只想选择和删除单个项目,则可以将 selecteditem 绑定到窗口视图模型中的属性。该命令可以使用给出的 IBook 对象进行删除。

    由于您需要多项选择和删除,这是一个复杂的操作,因为您无法绑定整个选定项目列表。这不是可绑定的依赖属性。

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

    您可以对数据网格进行子类化并扩展。

    或者

    你可以使用一个行为。 这些允许您做的是封装一大块面向事件的代码并添加附加的依赖属性来存储数据。这本身就是可绑定的。 我建议您阅读有关行为的一般性内容并在 Google 上搜索一下示例。 绑定 selecteditems 是一个相当普遍的要求,您应该会获得一些命中。 这是一个。

    Select multiple items from a DataGrid in an MVVM WPF project

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

    我推荐 observablecollection,您可以在 viewmodel 中订阅 collectionchanged 事件,以便检查计数。使用 0 为您的命令的 CanExecute 返回 false 和 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 的东西都应该是实现 inotifypropertychanged 的​​视图模型。这是因为存在一个长期存在的错误,否则会导致内存泄漏。不要担心您的视图模型是否会泄漏。只需始终使用视图模型并构建一个基本视图模型实现 inpc,这样您就可以轻松地从中继承所有内容。

    【讨论】:

    • 我想我最好一次只发送一件。您能否展示一种用于将 string 参数传递给 ViewModel 中实现的命令的模式?
    猜你喜欢
    • 2023-03-16
    • 2014-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 2019-10-26
    • 2014-05-17
    • 1970-01-01
    相关资源
    最近更新 更多