[英]C# How to delete datagridview SelectedRows from SQL datatable
我有一个数据绑定数据网格视图和一些按钮,用于从 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
}
}
如果您的网格绑定到DataTable
,则每行的DataBoundItem
都是DataRowView
。 您可以使用循环或 LINQ 查询将网格的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);
}
然后,您将使用数据适配器以相同的方式保存更改。
您没有提到您在 DataGridView 中显示的项目类型。 为了简化讨论,我假设您展示了一系列Products
class Product
{
...
}
您需要访问您的数据库。 您的表单不必知道数据的存储方式和存储位置。 它所需要知道的是,您可以通过某种方式将数据放入其中,然后再将其取回。
这样做的好处是您可以将表单与数据库分离。 如果您稍后决定更改访问数据库的方式,例如您决定使用实体框架而不是 SQL,那么您的表单不必更改。 或者,如果您需要以不同的形式访问数据库,您可以重用该数据库。
隐藏数据库访问方式的 class 通常称为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。 class 的用户不会知道,也不必知道您如何访问数据。
您说您已将数据数据绑定到 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 中产品的访问权限和对数据库的访问权限,这是一个两行方法:
void DeleteSelectedProducts()
{
IEnumerable<Product> selectedProducts = this.SelectedProducts;
this.ProductRepository.DeleteProducts(selectedProducts);
}
不要做一个可以做任何事情的大表格。 分开你的顾虑(如果你不知道这个词,谷歌它)。 在这种情况下:将您的表单与对数据库的访问分开,并将对 DataGridView 中产品的访问分开。
这样做的好处是更容易理解你的类是做什么的。 更改它们更容易,无需更改 class 的用户。 测试这些较小的类更容易,并且更容易重用项目。
我的大多数具有 DataGridView 的 forms 都具有我在上面定义的访问属性。 事实上,我创建了通用扩展方法,因此我可以将它们用于每个 DataGridView。 我只需要为他们编写一次单元测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.