使用BindingList数据源的DataGridView性能表现

3
我正在开发一个应用程序,需要显示来自外部系统的数据。这些数据可能会非常快速地到达,而每行所占的字节数相对较小。这意味着每个时间单位需要添加很多行。目前我似乎接收数据的速度比我处理数据的速度还要快,这意味着我的内存使用量正在上升。
我认为其中很大一部分原因是绘制实际的dataGridView。我已经对dataGridView进行了一些微调,希望能提高性能(例如禁用自动大小调整、特殊样式等)。
最近我添加了对行的颜色标记,这是必需的。目前我的应用程序的工作方式如下:
1. 我从外部系统接收数据 2. 我通过一个线程将数据放入队列(ConcurrencyQueue) 3. 另一个线程从该队列获取数据,处理并将其添加到绑定到表格的BindingList中。
实际的添加过程发生在一个具有两个参数的函数中: 1. 包含列项的列表(items) 2. 行颜色。(color)
它看起来像这样(半伪代码):
/* Store the color for the row in the color list so it is accessible from the event */  

rowColors.Add(rowColor);    //Class variable that stored the colors of the rows used in the DataGridCellFormatting event

/* Create the row that is to be added. */
ResultRow resultRow = new ResultRow();

foreach(item in items)
{
    resultRow.Set(item); /* It's actually a dictionary because some fields are optional, hence this instead of a     direct constructor call) */
}

bindingList.Add(resultRow);

/* Row coloring based on error is done in the OnCellFormatting() */


/* Auto scroll down */
if (dataGrid.Rows.Count > 0)
{
    dataGrid.FirstDisplayedScrollingRowIndex = dataGrid.Rows.Count - 1;
}

如上面的代码所示,我收到的颜色被添加到一个List中,在datagridview的事件中使用,如下所示:

void DataGridCellFormattingEvent(object sender, DataGridViewCellFormattingEventArgs e)
{
    // done by column so it happens once per row
    if (e.ColumnIndex == dataGrid.Columns["Errors"].Index)
    {
        dataGrid.Rows[e.RowIndex].DefaultCellStyle.BackColor = rowColors[e.RowIndex];
}
} 

BindingList被定义为:

BindingList bindingList;

其中ResultRow是一个类,其结构如下:

public class ResultRow
{
    private int first = 0;
    private string second = "";
    private UInt64 third = 0;
    private IPAddress fourth = null;
    //etc

    public ResultRow()
    {
    }

    public void Set (<the values>) //In actuallity a KeyValuePair
    {
        //field gets set here
    }

    public UInt64 Third
    {
        get { return third; }
        set { third = value; }
    }

    /* etc. */

有哪些相对简单的方法可以提高性能?我考虑在处理忙碌时可能禁用datagrid的绘制,等到处理完成后再绘制(尽管不是首选)。另一件事可能是不那么频繁地更新,而不是每接收一个项目后都要更新(当添加新项目时,BindingList似乎会自动更新DataGridView)。希望有人愿意/能够帮助。
-编辑-
当以上述方式处理数据时,表单的响应性也变得非常糟糕,特别是经过一段时间后。(即使以上过程在backgroundworker(s)和background threads中进行)
2个回答

5

由于网格中的行数过多,性能可能会随着时间而下降。您应该尝试使用虚拟模式

但首先,请尝试推迟网格的更新,并批量添加新条目,即减少更新频率。因此,在每次批量更新之前:

// stop raising update events
bindingList.RaiseListChangedEvents = false; 

之后:
// restore update events, raise reset event
bindingList.RaiseListChangedEvents = true;
bindingList.ResetBindings() 

在最后一行之后继续向下滚动。


0

是的,有一些事情可以加快速度。

首先-虚拟化数据网格。这是winforms数据网格中的内置机制,它只会填充可见的数据项的行并绘制客户端区域。因此,如果您的控件仅显示20行(和其他行的滚动条),则实际上只有20个项目被处理为UI进入数据网格。 然后,当您滚动网格以查看其他项目时,数据网格将按需填充并显示请求的行。 您需要对此进行一些调整(需要订阅CellValueNeeded事件),并且可能需要编辑您的bindingsource数据项。 有关更多信息,请参见http://msdn.microsoft.com/en-us/library/ms171622.aspx

第二件事情是在填充数据的时候暂停UI更新。如你所述,BindingList会在添加项目时自动更新网格。但是通过暂停UI更新,然后在一定时间间隔(比如每秒钟)重新启用它,你将以较不连续的速率传输数据。
请注意,相同的处理仍然需要针对数据进行,因此这不太可能完全解决你的数据问题,但可以更有效地减少屏幕闪烁。请参阅Control.SuspendLayout()和Control.ResumeLayout()以获取更多信息。
在我看来,虚拟化将成为您最有效的工具,因为它的唯一目的是提高大型数据集的网格功能。

我已经在我的数据网格上应用了挂起/恢复机制。起初这并没有太大帮助。我现在创建了一个新的后台工作程序,负责在相当大的时间间隔内更新数据网格,这似乎有所帮助。(但是需要更多线程和更多调用。不确定这是否是一件好事...)我之前看到过即时数据加载的文章,但它看起来相当复杂,所以我希望不必这样做。示例也是基于数据库的,因此我不能直接应用它。但我想我将不得不尝试弄清楚如何进一步提高性能。 - Arnold4107176
很高兴它能帮到你。的确,MSDN教程并不总是最有帮助的。http://www.codeproject.com/Articles/23937/Paging-Data-with-DataGridView-in-VirtualMode 是一个更好的资源,虽然不是CodeProject上排版最好的文章,但应该包含正确的信息。如果你专注于通过CellValueNeeded提供数据对象属性给网格,虚拟化相对简单,你很快就能上手了。 - Patrick McCurley
那似乎很容易理解。然而,我觉得这篇文章缺少一些信息才能使它起作用。我目前已经实现了它,但我的数据网格仍然是空的。我在这里发布了我的问题详细信息: https://dev59.com/-2PVa4cB1Zd3GeqP7aDJ - Arnold4107176

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接