有没有一种方法可以从BackgroundWorker“流式传输”一组结果(例如DataTable)到DataGridView。 我想要做的是查询数据,并在DataGridView中随着它们的到来填充结果(就像在SQL Server管理工具中查询网格结果)。 我的第一个想法是使用BackgroundWorker(以避免UI冻结效果),但是在BackgroundWorker加载结果时仍然会有可感知的“延迟”。
怎样做是最好的?
有没有一种方法可以从BackgroundWorker“流式传输”一组结果(例如DataTable)到DataGridView。 我想要做的是查询数据,并在DataGridView中随着它们的到来填充结果(就像在SQL Server管理工具中查询网格结果)。 我的第一个想法是使用BackgroundWorker(以避免UI冻结效果),但是在BackgroundWorker加载结果时仍然会有可感知的“延迟”。
怎样做是最好的?
你可以:
将DataGridView绑定到一个最初为空的DataTable。
然后,在您的工作线程中,请使用线程安全的集合(例如同步队列)和 Control.BeginInvoke 调用将记录信息传递到 UI 线程。
在 UI 线程中,您会从队列中取出项目并将相应的行添加到 DataTable 中。通过数据绑定的魔力,这些行将被添加到 gridview 中。
然而,通过使用多线程,您立即使您的程序更有可能出现错误!我没有尝试过这个特定的方案,并不知道当项目被添加到其中时,GridView是否会被有效地使用。我已经使用多线程填充了一棵树,这确实是一个很酷的效果。但是,由于它没有处理所有可能的用户交互的完全正确的实现,所以最终我禁用了该功能,导致出现了错误。
我已经尝试并完成了这个任务。应用程序和架构是在我加入公司之前完成的,我的工作是将其变为“分页”并实现异步化。
他们有一个已经具有分页的存储过程...所以我所做的是,在第一个请求时,发送总结果的“大小”(以及第一页的数据)。
在客户端,我向DataTable添加了空行(空白,除了"RowNumber"字段)。
在进一步的数据页(异步获取)中,我会得到行 [X],将其设置为新数组的 "ItemArray",并更新我的网格。例如:
myDataGridView.Rows[rowNumber].SetValues(valuesFromNewPage);
如果您只有一个线程可以修改底层集合,那么这个功能会更好。我有一个只读的DGV,这样添加/删除行的唯一方法是操作底层BindingSource。我有一个同步线程,会定期添加/删除BindingSource中的项目。
我确实需要做一件“棘手”的事情——如果要更新的项目是所选项目,则不能仅仅说
myBindingSource[n] = newItem;
但是你必须将新项目的值深度复制到现有项目中。否则,你会触发一个“更改”事件,这将重画任何绑定到你的数据源的其他内容。当我使用引用复制时,我看到了明显的闪烁,而切换到深度复制(仅适用于当前项目)解决了它。
当然,如果您允许用户直接从表单中操纵数据,而不是将其用作只读视图,那么您将打开一个全新(丑陋!)的问题。
如果过程不超过2秒,我会显示“忙碌”光标,并进行内联更新。有两个原因:
一个只需要几秒钟就能完成的操作,那个人完成时仍然处于 "专注" 的思维模式。潜意识中,他仍在等待自己的行动结果,还没有将意识大脑从特定的焦点中摆脱出来(只要应用程序显示 "繁忙" 信号)。
考虑到上述因素,我认为为了避免伴随着的所有危险,多线程所带来的认知负荷是不值得的。
如果这个过程需要五秒钟,那么我会首先集中精力将它缩减至两秒或更短时间。同样,通常更容易集中精力提高性能(例如,通过分页SQL)而不是引入多线程。即使使用了BackgroundWorker类型,多线程中涉及的非顺序交互仍然难以分析和保证安全。
如果您发现没有办法将延迟缩短到2秒或更短的时间,那么您可以采用像confuzatron建议的技术。但即使如此,您仍然可能希望为最终用户显示一个进度对话框,因为等待2秒以上后,他会进行上下文切换。进度对话框为他提供了易于吸收的信息,告诉他何时可以重新将注意力集中在您的上下文上。