C# 绑定到 DataGridView.Datasource 时加载数据表很慢

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1145736/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 09:18:38  来源:igfitidea点击:

Loading DataTable Slow When Bound to DataGridView.Datasource

c#winformsdatagridviewc#-3.0

提问by Jonathan Kehayias

I've searched all over and I can't figure this one out. I am working on a Winforms UI that is pulling large volumes of rows that I need to display in a DataGridView. I have already read all about limiting row counts and paging and there is absolutely no good way for me to do this. Basically I am working on the TargetDataViewer control of the Extended Events Manager for SQL Server 2008 that I wrote on Codeplex.

我找遍了,我无法弄清楚这一点。我正在开发一个 Winforms UI,该 UI 正在拉出我需要在 DataGridView 中显示的大量行。我已经阅读了所有关于限制行数和分页的内容,我绝对没有好的方法可以做到这一点。基本上,我正在处理我在 Codeplex 上编写的 SQL Server 2008 扩展事件管理器的 TargetDataViewer 控件。

http://extendedeventmanager.codeplex.com/

http://extendedeventmanager.codeplex.com/

I am limited to what I can do based on the specific target and how it presents data. What I am trying to do is stream the data that has been read from a target into the DataGridView similar to how Profiler, or SQL Server Management Studio display data as it streams in. I rewrote a lot of code, and have a BackgroundWorker pulling data and processing it into a DataTable. If I don't set the DataGridView.DataSource = DataTable, I can load 300K+ rows of data into the DataTable in a few minutes, it really runs fast. As soon as I add the DataTable to the DataSource it slow to almost a halt (instead of a few minutes the same 300K rows can take a 1/2 hr).

我只能根据特定的目标以及它如何呈现数据来做我能做的事情。我想要做的是将已从目标读取的数据流式传输到 DataGridView 中,类似于 Profiler 或 SQL Server Management Studio 在数据流入时显示数据的方式。我重写了很多代码,并让 BackgroundWorker 拉取数据并将其处理为DataTable。如果不设置DataGridView.DataSource = DataTable,几分钟就可以加载300K+行数据到DataTable中,真的跑的很快。一旦我将 DataTable 添加到 DataSource,它就会变慢到几乎停止(而不是几分钟,同样的 300K 行可能需要 1/2 小时)。

I know that the problem isn't my processing code, it is specific to being bound to the DataGridView.DataSource, and I have timing code to prove this. I can't figure out how to get around this. For Performance I can late bind the control to the DataTable after the data is loaded, but that is a really crappy user experience. I see lots of people complaining about the DataGridView performance impact when loading data so this may just be a limitation I am stuck with? Any ideas?

我知道问题不是我的处理代码,而是特定于绑定到 DataGridView.DataSource,我有时间代码来证明这一点。我不知道如何解决这个问题。对于性能,我可以在加载数据后后期将控件绑定到 DataTable,但这是一种非常糟糕的用户体验。我看到很多人在加载数据时抱怨 DataGridView 性能影响,所以这可能只是我遇到的一个限制?有任何想法吗?

采纳答案by Robert Rossney

Think about what happens when you populate an unbound DataTablewith one row from a DataReader: A DataRowgets created, populated from the DataReader, and added to the Rowscollection. Then, when you create the binding, the DataGridViewpulls data from the table and builds the view on the screen.

想想当你DataTable用 a 中的一行填充 unbound 时会发生什么DataReader: ADataRow被创建,从 填充DataReader,并添加到Rows集合中。然后,当您创建绑定时,它会DataGridView从表中提取数据并在屏幕上构建视图。

What happens when you populate a DataTablewhose binding to the DataGridViewis enabled? A whole mess of event handling. Every time you change a bound property, the property-changed event gets raised and the bound control handles it. That's not happening 300,000 times, it's happening 300,000 times for each column.

当您填充DataTable其与 的绑定时会发生什么DataGridView?一团糟的事件处理。每次更改绑定属性时,都会引发 property-changed 事件并且绑定控件处理它。这不是发生 300,000 次,而是每发生 300,000 次。

What if you turn this off, and only update the bound control occasionally? Look at this method:

如果你把它关掉,只是偶尔更新绑定的控件怎么办?看看这个方法:

private void PopulateDataTable()
{
    int rowCount = 10000;

    bindingSource1.RaiseListChangedEvents = false;
    for (int i = 0; i < rowCount; i++)
    {
        DataRow r = DT.NewRow();
        for (int j = 0; j < ColumnCount; j++)
        {
            r[j] = "Column" + (j + 1);
        }
        DT.Rows.Add(r);

        if (i % 500 == 0)
        {
            bindingSource1.RaiseListChangedEvents = true;
            bindingSource1.ResetBindings(false);
            Application.DoEvents();
            bindingSource1.RaiseListChangedEvents = false;
        }
    }
    bindingSource1.RaiseListChangedEvents = true
}

You have to call ResetBindings to force the update of the bound control. This takes time, because you can't get around the cost of building the DataGridViewRowobjects, but taking out the events is a significant improvement. On my machine, if I populate a 10-column, 10000 row DataTablethat's bound to a DataGridView, it takes 2900 milliseconds. If I leave data-binding turned off the entire time, it takes 155 milliseconds. If I reset the bindings every 500 rows, it takes 840 milliseconds.

您必须调用 ResetBindings 来强制更新绑定控件。这需要时间,因为您无法避免构建DataGridViewRow对象的成本,但删除事件是一项重大改进。在我的机器上,如果我填充DataTable绑定到 a的 10 列、10000 行DataGridView,则需要 2900 毫秒。如果我一直关闭数据绑定,则需要 155 毫秒。如果我每 500 行重置一次绑定,则需要 840 毫秒。

Of course, if I were populating 300,000 row table, I wouldn't reset the bindings every 500 rows; I'd probably do it once at the 500-row mark and then turn it off until the operation completes. But even if you do this, you need to call Application.DoEventsevery so often, so that the UI can respond to events.

当然,如果我要填充 300,000 行表,我不会每 500 行重置一次绑定;我可能会在 500 行标记处执行一次,然后将其关闭,直到操作完成。但即使您这样做,您也需要Application.DoEvents经常调用,以便 UI 可以响应事件。

Edit

编辑

Never mind that bit about Application.DoEvents; you don't need to do that if you're populating the table in a background task.

别介意那一点Application.DoEvents;如果您在后台任务中填充表格,则不需要这样做。

But you do need to make sure that you're resetting the bindings in the BackgroundWorker's ProgressChangedevent handler, and not in the DoWorkmethod. And you're going to experience a world of hurt if you actually let the user editdata in the bound DataGridViewwhile you're populating its data source on another thread.

但是您确实需要确保在BackgroundWorkerProgressChanged事件处理程序中而不是在DoWork方法中重置绑定。如果您在另一个线程上填充数据源时实际上让用户在绑定中编辑数据,那么您将经历一个痛苦的世界DataGridView