首页 > 解决方案 > 使用过滤器在绑定的 DataGridView 中保存更改

问题描述

我有一个由 BindingSource 填充的 DataGridView。每次用户修改一行并且他/她移动到另一行(每次修改的行失去焦点)它都会触发事件RowValidated
在这种情况下,DataGridView.CurrentRow使用存储过程在 SQLServer 中修改元素。

当 BindingSource 有一个过滤器处于活动状态时,这种方法不会。因为如果修改了被过滤列的单元格,则行分散器(由于过滤器)和我的方法将其捕获DataGridView.CurrentRow为下一个。

我不能使用该方法TableAdapter.Update(),因为 DataSet 是多个表的连接并且它不会自动生成。

在过滤器效果之前是否有任何事件被触发?我会接受任何不同的方法作为答案。

编辑:当一个单元格被修改时,保存在一个列表中的那一行的 ID 值。里面的代码RowValidated封装在
if(ListOfIds.Contains(DataGridView.CurrentRow.Cells["ID"])){StoredProcedure();}

标签: c#winformsdatatabledatagridviewbindingsource

解决方案


自己读取和写入单元格的值很少是一个好主意。使用 DataBinding 通常是一种更简单的方法。

似乎您已经使用了 BindingSource,但由于某种原因,您从显示的数据中读取了更改的行,而不是从原始 BindingSource 中读取。

我不确定一旦操作员移动到新行就更新数据库是否明智。如果他不知道要填写的值,想向下滚动查看另一行怎么办?考虑更改界面,以便操作员可以指示他已完成所有数据的编辑。例如一个按钮。但这有点超出你的问题。

如果您将数据放在BindingList中,并将此数据分配给 DataGridView 的 DataSource,那么 DataGridView 中数据的每次更改都会在 BindingList 中自动更新。使用事件BindingList.ListChanged获得通知,或在处理更改的数据之前使用上述按钮。

BindingList<Customer> DisplayedCustomers {get; } = new BindingList<Customer>();

public MyForm()
{
    this.InitializeComponents();

    this.DataGridView1.DataSource = this.DisplayedCustomers();
    this.BindingListView.ListChanged += BindingListChanged;
}

显示一个空的 DataGridView。填写客户:

private InitializeDataGridView()
{
    IEnumerable<Customer> customers = this.QueryCustomers();
    foreach (var customer in Customers)
        this.Customers.Add(customer);
}

这将自动显示您的 DataGridView 中的所有客户。唉,每个客户您将获得一次该活动。如果您不希望这样,请考虑创建一个新的 BindingList 并将其分配给 DataGridView 的 DataSource。不要忘记取消订阅旧的 BindingList 并订阅新的 BindingList 的事件

private void BindingListChanged(object sender, ListChangedEventArgs e)
{
     BindingList<Customer> bindingList = (BindingList<Customer>)sender;
     // use e.ListChangedType to detect what has changed:
     // item added / removed / moved to a new location / value changed?

     switch (e.ListChangedType)
     {
         case ListChangedType.ItemChanged:
             var changedCustomer = bindingList[e.NewIndex];
             ProcessChangedCustomer(changeCustomer);
             break;
         case ...
    }
}

使用 BindingList 而不是摆弄 DataGridView 的单元格的值的优点是,您可以将数据与该数据与操作员的通信方式分离。这使得重用变得更容易(在 ComboBox 而不是 DataGridView 中显示它?继续吧,变化很小),更容易进行单元测试:如果没有表单,BindingList 也可以工作;因此更易于维护和更改。

最后一个建议:为了便于过滤属性(仅显示居住在纽约的客户),并简化排序,请考虑使用 nuget 包Equin.ApplicationFramework.BindingListView

List<Customer> customers = GetCustomers();
BindingListView<Customer> view = new BindingListView<Customer>(customers);
dataGridView1.DataSource = view;

和宾果游戏:通过鼠标单击列标题自由排序。它甚至会记住是升序还是降序排序,并显示排序字形(指示排序方向的箭头)。

过滤:

void FilerCustomersByCity(string city)
{
    view.ApplyFilter(delegate(Customer) customer => customer.City == city);
}

推荐阅读