首页 > 解决方案 > 从 DataTable 绑定的 DataGridView 中删除选定的行

问题描述

考虑一个DataGridViewDataSource一个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;
}

标签: c#.netwinformsdatatabledatagridview

解决方案


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

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

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

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.SelectedCellsSelectionMode获取选定的行,无论属性值如何,它总是填充。评论

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. 提交更改,更新数据库。

推荐阅读