简体   繁体   English

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

[英]C# How to delete datagridview SelectedRows from SQL datatable

I have a databound datagridview and some buttons that edit, add, remove data from an SQL datatable.我有一个数据绑定数据网格视图和一些按钮,用于从 SQL 数据表中编辑、添加、删除数据。 My main goal is to make a button that deletes the SelectedRows from the datatable that the user has selected.我的主要目标是制作一个按钮,用于从用户选择的数据表中删除 SelectedRows。

Since I only need data from the first column my Column.Index will always be 0由于我只需要来自第一列的数据,因此我的 Column.Index 将始终为 0

Now from the rows that the user has selected I save the Row.Index in an array so I can pinpoint and delete which rows where the ones that were selected afterwards.现在,从用户选择的行中,我将 Row.Index 保存在一个数组中,这样我就可以查明并删除之后选择的行所在的行。 The thing is I can't use my SelectedRowIndexes array outside the foreach loop because it's a local variable.问题是我不能在 foreach 循环之外使用我的 SelectedRowIndexes 数组,因为它是一个局部变量。 For example when I run the Console.WriteLine command I get the error that my array does not exist in the current context.例如,当我运行 Console.WriteLine 命令时,我收到我的数组在当前上下文中不存在的错误。 Am I thinking about it the wrong way?我想错了吗? Is there some workaround?有一些解决方法吗?

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
    }
}

If your grid is bound to a DataTable then the DataBoundItem of each row is a DataRowView .如果您的网格绑定到DataTable ,则每行的DataBoundItem都是DataRowView You can use a loop or a LINQ query to get each of those from the SelectedRows of the grid into an array or collection, call Delete on each one and then call Update on a data adapter to save all the changes.您可以使用循环或 LINQ 查询将网格的SelectedRows中的每一个获取到数组或集合中,在每个上调用Delete ,然后在数据适配器上调用Update以保存所有更改。 Eg例如

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

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

myDataAdapter.Update(myDataTable);

This assumes that myDataTable is bound to myDataGridView and that myDataAdapter has been configured with an appropriate DeleteCommand .这假定myDataTable绑定到myDataGridView并且myDataAdapter已配置适当的DeleteCommand

If you have bound the DataTable via a BindingSource , which you should, then you can do it another way too:如果您已经通过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);
}

You would then use the data adapter to save the changes in the same way.然后,您将使用数据适配器以相同的方式保存更改。

You didn't mention what kind of items you are showing in your DataGridView.您没有提到您在 DataGridView 中显示的项目类型。 To ease the discussion I'll assume that you show a sequence of Products为了简化讨论,我假设您展示了一系列Products

class Product
{
    ...
}

Separate your Form from the database access将您的表单与数据库访问分开

You need to access your database.您需要访问您的数据库。 Your form does not have to know how and where the data is stored.您的表单不必知道数据的存储方式和存储位置。 All it has to know is that somehow there is something where you can put data in, and later get it back.它所需要知道的是,您可以通过某种方式将数据放入其中,然后再将其取回。

This has the advantage that you decouple your form from your database.这样做的好处是您可以将表单与数据库分离。 If you later decide to change how you access the database, for instance you decide to use Entity Framework instead of SQL, then your form does not have to change.如果您稍后决定更改访问数据库的方式,例如您决定使用实体框架而不是 SQL,那么您的表单不必更改。 Or if you need to access the database in a different form, you can reuse the database.或者,如果您需要以不同的形式访问数据库,您可以重用该数据库。

The class that hides how the database is accessed is quite often called a Repository .隐藏数据库访问方式的 class 通常称为Repository You can store items in it.您可以在其中存储物品。 Later you can query for stored items.稍后您可以查询存储的项目。

In your Repository you need at least the following methods在您的存储库中,您至少需要以下方法

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

     ... // other useful methods
}

FetchProducts will fetch the Products that you need to display in your DataGridView. FetchProducts将获取您需要在 DataGridView 中显示的产品。 As your repository doesn't know that the Products are shown in a DataGridView I can't use FetchProductsToDisplay or something like that.由于您的存储库不知道产品显示在 DataGridView 中,因此我不能使用FetchProductsToDisplay或类似的东西。 The parameters will have to filter the Products.参数必须过滤产品。

class ProductRepository : IProductRepository
{
    // TODO: implement
}

The implementation is not part of this question.实施不是这个问题的一部分。 You can use entity framework, Dapper, or plain old SQL.您可以使用实体框架、Dapper 或普通的旧 SQL。 Users of your class won't know, and won't have to know how you access the data. class 的用户不会知道,也不必知道您如何访问数据。

Access the Products in the DataGridView访问 DataGridView 中的产品

You say that you have databound the data to the DataGridView.您说您已将数据数据绑定到 DataGridView。 Therefore I assume you know how to instantiate the DataGridView, its columns, etc. The details are outside of this question.因此,我假设您知道如何实例化 DataGridView、其列等。详细信息不在此问题范围内。

Properties will allow you to access the displayed Products:属性将允许您访问显示的产品:

// 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>();

In words: from the sequence of SelectedRows, cast every row to a DataGridViewRow.换句话说:从 SelectedRows 的序列中,将每一行转换为 DataGridViewRow。 From every row in this sequence of DataGridViewRows take the value of the DataBoundItem.从此 DataGridViewRows 序列中的每一行获取 DataBoundItem 的值。 Cast every DataBoundItem to a Product.将每个 DataBoundItem 转换为 Product。

To fill your DataGridView with the initial list of Products:使用初始产品列表填充 DataGridView:

IProductRepository ProductRepository {get;} // initialized in constructor

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

Back to your question回到你的问题

My main goal is to make a button that deletes the SelectedRows from the datatable that the user has selected.我的主要目标是制作一个按钮,用于从用户选择的数据表中删除 SelectedRows。

Now that you have properly defined access to the Products in the DataGridView and the Access to the database, this is a two line method:现在您已经正确定义了对 DataGridView 中产品的访问权限和对数据库的访问权限,这是一个两行方法:

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

Summary概括

Don't make one big form that can do everything.不要做一个可以做任何事情的大表格。 Separate your concerns (if you don't know this term, google for it).分开你的顾虑(如果你不知道这个词,谷歌它)。 In this case: separate your form from the access to the database, and separate the access to the Products in your DataGridView.在这种情况下:将您的表单与对数据库的访问分开,并将对 DataGridView 中产品的访问分开。

This has the advantage that it is easier to understand what your classes do.这样做的好处是更容易理解你的类是做什么的。 It is easier to change them, without having to change the users of your class.更改它们更容易,无需更改 class 的用户。 It is easier to test these smaller classes, and it is easier to reuse items.测试这些较小的类更容易,并且更容易重用项目。

Most of my forms that have a DataGridView have the access properties I defined above.我的大多数具有 DataGridView 的 forms 都具有我在上面定义的访问属性。 In fact, I created generic extension methods, so I can use them for every DataGridView.事实上,我创建了通用扩展方法,因此我可以将它们用于每个 DataGridView。 I only needed to write unit tests for them once.我只需要为他们编写一次单元测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM