【问题标题】:Deleting selected row from DataTable bound DataGridView从 DataTable 绑定的 DataGridView 中删除选定的行
【发布时间】:2021-04-28 19:47:30
【问题描述】:

考虑一个DataGridView,其DataSource 是一个DataTable。 dataGridView 允许一次选择一行。有一个“删除”按钮,它应该从绑定的数据表中删除选定的行,按列之一。

从用户的角度来看,我已将DataGridView 配置为不可编辑。应该只能通过我创建的单独的“删除”Button 删除一行(从数据表和网格中)。其事件处理程序如下所示:

private void delete_Click(object sender, EventArgs args)
{
var usersTable = myGridView.DataSource as DataTable;
            foreach (DataGridViewRow selected in myGridView.SelectedRows)
            {
                string name = (string)selectedRow.Cells[0].Value;
                foreach (DataRow tableRow in usersTable.Rows)
                {
                    if (tableRow["Name"].ToString().Equals(name))
                        tableRow.Delete();
                }
                usersTable.AcceptChanges();
            }

            myGridView.DataSource = usersTable;
}

如何正确执行此操作,而不出现“集合已修改,枚举可能无法执行”错误?

网格填充如下:

private DataTable usersTable = new DataTable("Users");

void Populate()
{
    usersTable.Columns.Clear();
    usersTable.Rows.Clear();
    usersTable.Columns.Add("Name", typeof(string));
    // addd other columns...

    foreach (var user in usersCollection)
    {
        usersTable.Rows.Add(user.GetName()),
        // add other rows...
    }

    // bind the data
    myGridView.DataSource = usersTable;
}

【问题讨论】:

  • DataGridView 将允许用户简单地按下Delete 键来删除选定的行。无需代码。
  • @ŇɏssaPøngjǣrdenlarp 绑定的DataTable 会受此影响吗?这是重要的部分。
  • @Al2110 yes...当用户通过按下Delete键从GridView中删除行时,相应的行也将从绑定到GridView的DataTable中删除。
  • @ChetanRanpariya 我认为这里有些混乱。我已将DataGridView 配置为从用户的角度来看是不可编辑的。因此,从表中删除一行的唯一方法是以编程方式。我试图通过添加一个单独的“删除”Button 来实现这一点,其点击事件处理程序具有上面的代码。单击该按钮会引发“集合已修改;枚举可能无法执行”。我希望能够从DataTable 中删除选定的行,然后将其重新绑定到DataGridView
  • 我有一个可能的解决方案来解决您的问题...在此之前您能解释一下如何将 DataGridView 绑定到 DataTable 吗? DataTable 是在表单级别创建的吗?您还可以提一下您的情况下的 DataGridView 用户无法编辑的事实吗?

标签: c# .net winforms datatable datagridview


【解决方案1】:

... dataGridView 允许一次选择一行...这应该删除选定的行,通过其中一列...

DataGridView.SelectedRows 属性要求将DataGridView.SelectionMode 设置为FullRowSelectRowHeaderSelect 以返回选定的行。应该选择整行,而不仅仅是RowHeaderSelect 选项中的一些单元格。阅读属性的Remarks 部分。

在这种情况下,您可以像这样删除选定的行:

private void delete_Click(object sender, EventArgs e)
{
    if (myGridView.SelectedRows.Count > 0 && !myGridView.SelectedRows[0].IsNewRow))
    {
        myGridView.Rows.Remove(myGridView.SelectedRows[0]);

        // Don't call this if you have a DB to update.
        // (myGridView.DataSource as DataTable).AcceptChanges();
    }
}

或者,如果您启用了 DataGridView.MultiSelect 属性:

private void delete_Click(object sender, EventArgs e)
{
    if (myGridView.SelectedRows.Count > 0)
    {
        foreach (var r in myGridView.SelectedRows
            .Cast<DataGridViewRow>()
            .Where(r => !r.IsNewRow))
            myGridView.Rows.Remove(r);

        // Don't call this if you have a DB to update.
        // (myGridView.DataSource as DataTable).AcceptChanges();
    }
}

现在,如果您将SelectionMode 属性设置为其他值之一,那么您就不能指望SelectedRows 属性。您需要通过DataGridView.SelectedCells 获取选定的行,无论SelectionMode 属性值如何,它始终会被填充。 Remarks

private void delete_Click(object sender, EventArgs e)
{
   var selRows = myGridView.SelectedCells
        .Cast<DataGridViewCell>()
        .Where(c => !c.OwningRow.IsNewRow)
        .Select(c => c.OwningRow)
        .Distinct();

    foreach (var r in selRows)
        myGridView.Rows.Remove(r);

     // Don't call this if you have a DB to update.
     // (myGridView.DataSource as DataTable).AcceptChanges();
}

或者,通过DataGridViewRow.DataBoundItem 属性从选定行中获取DataRow 对象,并将它们从DataTable 中删除。

private void delete_Click(object sender, EventArgs e)
{
    var dt = myGridView.DataSource as DataTable;

    var selRows = myGridView.SelectedCells
        .Cast<DataGridViewCell>()
        .Where(c => !c.OwningRow.IsNewRow)
        .Distinct()
        .Select(c => ((DataRowView)c.OwningRow.DataBoundItem).Row);

    foreach (var r in selRows)
        dt.Rows.Remove(r);

    // Don't call this if you have a DB to update.
    // dt.AcceptChanges();
}

重要

正如@Jimi 评论的那样,如果您的项目中有数据库,那么DataTable.AcceptChanges 方法应该只在之后调用:

  1. 填充DataTable,从数据库中加载数据。
  2. 提交更改,更新数据库。

【讨论】:

  • 是的,这也很重要,因为您(用户)可能想要回滚所做的更改。如果您调用AcceptChanges() DataTable.RejectChanges() 方法将无助于取回您(标记为)删除的行,以防您犯了错误并希望它们恢复,因为所有行都将标记为Unchanged(@ 987654345@) 和 [DataTable].GetChanges(DataRowState.Deleted)?.Rows 将返回一个空集合,这样就完成了。
【解决方案2】:

在您的代码中,您试图在遍历导致修改集合的行集合时删除该行。这就是您收到此错误的原因。

您需要一种方法来直接从 DataTable 中删除行,而无需遍历 Rows 集合。

DataTable.Rows 集合具有Remove 方法,该方法将DataRow 的实例作为参数。

因此,如果您可以获取要删除的 DataRow,那么您需要做的就是调用 usersTable.Rows.Remove 方法。

你可以通过这样做来获取 GridView 的原始数据源

var usersTable = myGridView.DataSource as DataTable;

您可以获取绑定到 DataGridView 的各个行的各个 DataItem。在这种情况下,绑定到 DataGridView 行的 DataItem 将是 DataRowView

DataRowView rowView = selectedRow.DataBoundItem as DataRowView;

DataRowView 有一个属性Row,它代表绑定到GridVeiw 的表中的DataRow。所以你可以使用rowView.RowusersTable.Rows.Remove方法上调用Remove方法。

所以delete_Click的代码可以改成下面的代码来解决你的问题。

private void delete_Click(object sender, EventArgs args)
{
    foreach (DataGridViewRow selectedRow in myGridView.SelectedRows)
    {
        DataRowView rowView = selectedRow.DataBoundItem as DataRowView;

        if(rowView != null && rowView.Row != null)
        {
            // Removing row from the table created at the Form level.
            usersTable.Rows.Remove(rowView.Row);
        }
    }

    myGridView.Refresh();
}

希望这能帮助您解决问题。

附: :此方案仅适用于您将 DataTable 用作 GridView 的 DataSource 时。对于任何其他类型的数据源,可能需要不同的解决方案。

【讨论】:

  • 尝试此操作后,它会抛出“给定的 DataRow 不在当前 DataRowCollection 中”。
  • 代码中的哪一行抛出了错误?你使用的代码和我分享的一样吗?
【解决方案3】:

您的问题是在枚举期间修改集合。特别是两个foreach 循环。

您可以通过标记要删除的内容,然后在完成枚举之后执行实际删除来解决此问题。

var rowsToDelete = new List<DataRow>();
foreach (DataGridViewRow selected in myGridView.SelectedRows)
{
    string name = (string)selectedRow.Cells[0].Value;
    foreach (DataRow tableRow in usersTable.Rows)
    {
        if (tableRow["Name"].ToString().Equals(name))
            rowsToDelete.Add(tableRow);
    }
}
foreach (var row in rowsToDelete)
    row.Delete();

您可以改用for 循环(这不是枚举)。但是你必须跟踪索引的变化,所以我更喜欢这种方法。

这需要很少的代码更改。您可以直接从网格中删除行,而不是从数据源中删除。

无论采用何种方法,在枚举过程中仍需避免修改集合foreach

【讨论】:

    猜你喜欢
    • 2020-10-30
    • 2015-05-04
    • 2016-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-08
    • 1970-01-01
    相关资源
    最近更新 更多