winforms C#如何从SQL数据表中删除datagridview SelectedRows

xtfmy6hx  于 2022-11-17  发布在  C#
关注(0)|答案(2)|浏览(179)

我有一个数据绑定的datagridview和一些按钮,用于编辑、添加和删除SQL数据表中的数据。我的主要目标是创建一个按钮,用于从用户选择的数据表中删除SelectedRows。
因为我只需要第一列的数据,所以Column.Index将始终为0
现在,我将用户选择的行中的Row.Index保存在一个数组中,这样我就可以确定并删除哪些行是后来选择的行。问题是,我不能在foreach循环之外使用SelectedRowIndexes数组,因为它是一个局部变量。例如,当我运行Console.WriteLine命令时,我会得到一个错误,即我的数组在当前上下文中不存在。我是不是想错了?有什么解决办法吗?

private void BtnRemoveClick(object sender, System.EventArgs e)
{
    foreach (DataGridViewRow row in datagridview1.SelectedRows)
    {
        int[] SelectedRowIndexes = { row.Index };
    }

    for (int i=0; i<SelectedRowIndexes.Length; i++)
    {
        Console.WriteLine(SelectedRowIndexes[i]); // error CS0103: The name 'SelectedRowIndexes' does not exist in the current context
    }
}
kdfy810k

kdfy810k1#

如果您的网格绑定到DataTable,则每行的DataBoundItem都是DataRowView。您可以使用循环或LINQ查询将网格的SelectedRows中的每个DataRowView获取到数组或集合中,对每个SelectedRows调用Delete,然后对数据适配器调用Update以保存所有更改。例如:

var rows = myDataGridView.SelectedRows
                         .Cast<DataGridViewRow>()
                         .Select(dgvr => dgvr.DataBoundItem)
                         .Cast<DataRowView>()
                         .ToArray();

foreach (var row in rows)
{
    row.Delete();
}

myDataAdapter.Update(myDataTable);

这假设myDataTable系结至myDataGridView,且myDataAdapter已设定适当的DeleteCommand
如果您已经通过BindingSource绑定了DataTable(您应该这样做),那么您也可以用另一种方式来完成:

var rowIndexes = myDataGridView.SelectedRows
                               .Cast<DataGridViewRow>()
                               .Select(dgvr => dgvr.Index)
                               .ToArray();

for (var i = rowIndexes.GetUpperBound(0); i >= 0; i--)
{
    var rowIndex = rowIndexes[i];

    myBindingSource.RemoveAt(rowIndex);
}

然后使用数据适配器以相同的方式保存更改。

azpvetkf

azpvetkf2#

您没有提到在DataGridView中显示的是什么类型的项。

class Product
{
    ...
}

将窗体与数据库访问分离

您需要访问数据库。表单不必知道数据的存储方式和存储位置。它所需要知道的只是,您可以通过某种方式将数据放入某个位置,然后再将其取回。
这样做的好处是可以将表单与数据库分离。如果您以后决定更改访问数据库的方式,例如决定使用实体框架而不是SQL,则不必更改表单。或者,如果需要以不同的表单访问数据库,则可以重用该数据库。
隐藏数据库访问方式的类通常称为Repository。您可以在其中存储项目。以后您可以查询存储的项目。
在存储库中,至少需要以下方法

Interface IProductRepository
{
     IEnumerable<Product> FetchProducts(...); // TODO: invent a suitable name
     void DeleteProducts(IEnumerable<Product> productsToDelete);

     ... // other useful methods
}

FetchProducts将获取您需要在DataGridView中显示的产品。由于您的存储库不知道产品显示在DataGridView中,因此我不能使用FetchProductsToDisplay或类似的命令。参数必须过滤产品。

class ProductRepository : IProductRepository
{
    // TODO: implement
}

实现不是这个问题的一部分。你可以使用实体框架,Dapper,或者普通的SQL。你的类的用户不会知道,也不必知道你是如何访问数据的。

访问DataGridView中的产品

您说您已经将数据绑定到DataGridView。因此,我假设您知道如何示例化DataGridView及其列等。详细信息不在此问题的范围内。
属性将允许您访问显示的产品:

// access all Products in the DataGridView
public BindingList<Product> DisplayedProducts
{
    get => (BindingList<Product>) this.DataGridView1.DataSource;
    set => this.DataGridView1.DataSource = value;
}

// returns the current Product or null if there is no current Product
Product CurrentProduct => this.DataGridView1.CurrentRow?.DataBoundItem as Product;

// returns the (possible empty) sequence of selected Products
IEnumerable<Product> SelectedProducts => this.DataGridView1.SelectedRows
    .Cast<DataGridViewRow>()
    .Select(row => row.DataBoundItem)
    .Cast<Product>();

换句话说:从SelectedRows的序列中,将每一行都转换为DataGridViewRow。从此DataGridViewRows序列中的每一行中获取DataBoundItem的值。将每个DataBoundItem都转换为Product。
若要使用初始产品清单填入DataGridView:

IProductRepository ProductRepository {get;} // initialized in constructor

void InitializeProductsDataGridView()
{
    IEnumerable<Product> productsToDisplay = this.ProductRepository.FetchProductsToDisplay(...);
    this.DisplayedProducts = new BindingList<Product>(productsToDisplay.ToList());
}

返回您的问题

我的主要目标是创建一个按钮,用于从用户选择的数据表中删除SelectedRows。
现在您已经在DataGridView中正确定义了对Products的访问权限和对数据库的访问权限,这是一个两行的方法:

void DeleteSelectedProducts()
{
    IEnumerable<Product> selectedProducts = this.SelectedProducts;
    this.ProductRepository.DeleteProducts(selectedProducts);
}

摘要

不要只做一个大表格就能做所有的事情。把你关心的问题分开(如果你不知道这个术语,可以在谷歌上搜索)。在这种情况下:将窗体与对数据库的访问分离,并将对DataGridView中产品的访问分离。
这样做的好处是,更容易理解类的功能。更容易更改类,而不必更改类的用户。更容易测试这些较小的类,也更容易重用项。
我的大多数具有DataGridView的窗体都具有我上面定义的访问属性。实际上,我创建了通用扩展方法,因此我可以将它们用于每个DataGridView。我只需要为它们编写一次单元测试。

相关问题