我的应用程序中有一个显示一些数据的表单。当我第一次显示Form时,我将一些数据加载到DataTable中,然后将DataTable绑定到DataGridView。我还启动了一个执行一些较慢的数据库查询的异步方法。当这些慢查询完成时,我需要在DataTable中更新几百行,填写从较慢查询返回的值,如下所示:
foreach (DataRow row in data.Rows)
{
SlowLoadingData slow_stuff = slow_query_results[(int)row["id"]];
row.BeginEdit();
row[column_one] = slow_stuff.One;
row[column_two] = slow_stuff.Two;
row[column_three] = slow_stuff.Three;
row.EndEdit();
}
这非常慢,将UI线程暂停一分钟或更长时间,大概是因为每一行都在触发重绘。
经过一番研究,我发现了一种快速的方法。首先,将DataGridView绑定到绑定到DataTable的BindingSource,而不是直接绑定到DataTable。然后,在对DataTable进行更改时执行以下操作:
binding_source.SuspendBinding();
binding_source.RaiseListChangedEvents = false;
// foreach (DataRow in Data.Rows) ... code above
binding_source.RaiseListChangedEvents = true;
binding_source.ResumeBinding();
grid.Refresh();
但是存在一个问题,并且它是一个问题:上面的代码阻止DataGridView检测添加到DataTable的新行。 添加到表中的任何新行都不会出现在网格中。如果使用箭头键将当前单元格选择移出网格的底端,网格也可能会抛出异常,因为底层数据源有更多行,但网格没有创建网格行来显示它们。
所以,我可以看到两种可能的解决方案:
在更改基础DataTable时是否有更好的方法来抑制绑定更新?
是否有一种简单的方法可以告诉DataGridView正常刷新其网格行集合以匹配基础DataTable行的数量? (注意:我尝试过调用BindingSource.ResetBindings,但是如果你从DataTable中删除了删除行,它似乎会触发更多异常。)
答案 0 :(得分:6)
您可以尝试使用DataTable上的Merge method。我将尝试创建一个简单的演示应用程序并在此处发布,但这个想法很简单。如果要更新网格,请将结果查询到新的DataTable中,然后将旧表与新表合并。只要两个表都有主键(如果它们不从DB返回,您可以将它们创建为内存),那么它应该跟踪更改并无缝更新DataGridView。它还具有不会丢失用户在网格上的位置的优点。
好的,这是一个样本。我创建了一个带有两个按钮和一个dataGridView的表单。在按钮1上单击,我用一些数据填充主表,并将网格绑定到它。然后,在第二次单击时,我创建具有相同模式的另一个表。向其添加数据(一些具有相同的主键,一些具有新的主键)。然后,他们将它们合并回原始表。它按预期更新网格。
public partial class Form1 : Form
{
private DataTable mainTable;
public Form1()
{
InitializeComponent();
this.mainTable = this.CreateTestTable();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
this.mainTable.Rows.Add(String.Format("Person{0}", i), i * i);
}
this.dataGridView1.DataSource = this.mainTable;
}
private void button2_Click(object sender, EventArgs e)
{
DataTable newTable = this.CreateTestTable();
for (int i = 1; i <= 15; i++)
{
newTable.Rows.Add(String.Format("Person{0}", i), i + i);
}
this.mainTable.Merge(newTable);
}
private DataTable CreateTestTable()
{
var result = new DataTable();
result.Columns.Add("Name");
result.Columns.Add("Age", typeof(int));
result.PrimaryKey = new DataColumn[] { result.Columns["Name"] };
return result;
}
}
答案 1 :(得分:4)
您是否考虑在填充表格时重新连接dataGrid或bindingSource并重新连接?它可能看起来有点难看,但它应该快得多。
答案 2 :(得分:3)
如果您使用BindingSource
进行复杂数据绑定,请务必了解SuspendBinding
和ResumeBinding
仅暂停并恢复当前项目的绑定。这允许您禁用当前项的绑定并更改其一堆属性,而不会将属性的任何单个更改推送到绑定控件。 (BindingSource
的文档中没有解释这一点,它会有用,哦不是:它出现在CurrencyManager
的文档中。)
您对列表中的其他项目所做的任何更改,即除当前项目之外的所有内容都会引发ListChanged
事件。如果禁用这些事件,BindingSource将停止告知绑定控件有关列表更改的信息,直到重新启用它们为止。这有您看到的结果:您将所有行添加到基础DataTable
,但由于您已关闭ListChanged
个事件,BindingSource
不会告诉{ {1}}关于他们,因此DataGridView
仍为空。
正确的解决方案是调用DataGridView
,它使用绑定列表中的当前值强制ResetBindings
刷新绑定到它的所有控件。
致电BindingSource
后,您会遇到哪些例外情况?因为它对我来说很好,无论我是添加,编辑,删除还是删除基础ResetBindings
中的行。
答案 3 :(得分:2)
我遇到了类似的问题。这是一个更简单的解决方案(如果不那么优雅)。
我发现了这个:
dataGridView.DataSource = null;
dataTable.BeginLoadData();
foreach(DataRow row in dataTable.Rows){
//modify row
}
dataTable.EndLoadData();
dataGridView.DataSource = dataTable;
......比这更快:
dataTable.BeginLoadData();
foreach(DataRow row in dataTable.Rows){
//modify row
}
dataTable.EndLoadData();
干杯 - DC
答案 4 :(得分:1)
只需将此作为解决方案发布:已在评论和帖子中添加一些注释。 BFree提到的Merge Table方法是一种非常好的方法,我认为正确的方法不是太简单和优雅。 这是我的笔记为什么和大的我不确定是否有任何人抓住了服务器上的查询命中。 操作员在对BFree的评论中表示,他需要复制表格来做他需要做的事情,当然哪个表格我不确定,因为他的代码:
foreach (DataRow row in data.Rows)
这些行来自他的名为data的表,其中需要的副本 - 他已经拥有它。
然后在这个循环的每次迭代中都会出现这样的事情:
SlowLoadingData slow_stuff = slow_query_results[(int)row["id"]];
OP是否真的在每次迭代这些行时查询数据库(如果它是一个大表,我们说的是100,000行+)。 想想服务器上的负载(他的应用程序也必须生成此查询请求!),以及它在网络上执行此操作的流量! 如果它是唯一的应用程序,也许它没问题,但即便如此,如果我想提高效率,也不是我想做的事情。
如果在一个查询中收集数据库中的数据似乎很多 - 那么可能更好的方法是将其数据分页并进行合并。
SlowLoadingData Page1_SlowLoadingData = slow_query_results[Page1] as DataTable;
data.Merge(Page1_SlowLoadingData);
SlowLoadingData Page2_SlowLoadingData = slow_query_results[Page2] as DataTable;
data.Merge(Page2_SlowLoadingData);
答案 5 :(得分:0)
我发现使用resetBindings似乎移动了滚动条,用户还在想我做了什么?我发现使用bindingList作为数据源,使用一个使用INotifyPropertyChanged的对象,然后当我编辑行时(绑定到一个对象)。在表单上的单击或选择更改之前,行没有更新。
但是调用dgv.Refresh()似乎解决了这个问题,没有滚动更改。
答案 6 :(得分:0)
我发现Ravi LVS在codeproject上的解决方案效果很好:
BindingSource bs = new BindingSource();
DataTable dt = new DataTable();
bs.DataSource = dt;
bs.SuspendBinding();
bs.RaiseListChangedEvents = false;
bs.Filter = "1=0";
dt.BeginLoadData();
//== some modification on data table
dt.EndLoadData();
bs.RaiseListChangedEvents = true;
bs.Filter = "";
链接到原始页面:http://www.codeproject.com/Tips/55730/Achieve-performance-while-updating-a-datatable-bou