【问题标题】:Adding a lot of items to WPF Datagrid consumes lots of CPU time向 WPF Datagrid 添加大量项目会消耗大量 CPU 时间
【发布时间】:2012-11-16 19:40:24
【问题描述】:

我正在创建具有一些简单嗅探器功能并显示用户转储数据包的应用程序。从转储文件中读取数据包,该文件会根据当前流量实时更新,然后将捕获的数据包添加到 Datagrid(每个数据包都是新行)。我使用数据绑定从实现 ICollectionChanged 接口的转储读取器获取数据包,因此 Datagrid 在每个新数据包上都会收到通知。 Datagrid 使用虚拟化和延迟滚动。几乎一切正常 - 唯一的例外是巨大的 CPU 消耗。这种 CPU 使用率是由于我读取所有新数据包(每秒数千个)、格式化它们以供显示和每个数据包上升 CollectionChanged 事件,这会更新 Datagrid。对软件的要求是用户不必实时查看所有新数据包——它们每秒出现数十万个,因此没有人会注意到它们。用户只能查看一些数据包,当他想要时,他可以向下/向上滚动条到适当的位置以查看他想要的数据包,并且只有在这种情况下必须从文件中读取数据包。

问题是我希望 Datagrid 每秒更新一次以显示新数据包到达,最好将滚动条缩放到实际数据包数量,但无需连续读取它们、格式化和调用CollectionChanged,因为它需要 CPU 时间,而且如果他不滚动条,用户将看不到新数据包。我被告知到达的数据包数量,因此我知道所有数据包的数量。

我尝试添加虚假数据包(从而避免读取和格式化每个新数据包)只是为了强制 Datagrid 扩展到实际数据包计数。它几乎起作用了,因为 CPU 使用率下降并且当我滚动条时数据包只读。但是过了一会儿,当新数据包被添加到 Datagrid 时,当前视图中的行开始随机重复,即 在视图中的第一时刻数据包显示第 1、第 2、第 3、第 4、第 5、第 6 等等,但过了一会儿它们显示第 1、第 2、第 3,然后又显示第 1、第 2、第 3。

我还尝试刷新绑定 - 它有效,但消耗的资源比第一种方法多,所以我放弃了它。我还尝试刷新 Datagrid 的 listcollectionview - 效果与刷新绑定相同。

接下来是当我想在一次调用 CollectionChanged 时添加数据包列表

NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list));

我收到 PresentationFramework.dll!System.Windows.Data.ListCollectionView.ValidateCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 引发的 Range 操作不受支持的错误

所以我不得不为数据包列表中的每个数据包调用 CollectionChanged,这也增加了 CPU 使用率。

总结,我想通知Datagrid有新的数据包/行,所以Datagrid滚动条会缩放到数据包的总数,但是只有在用户滚动条。

我会非常感谢每一个如何解决我的问题的建议。

我刚刚意识到,有一件重要的事情要提,很抱歉我一开始就忘了说。我的 ObservableCollection 支持某种数据虚拟化,在内存中我只保存一些必要的数据包。当新数据包到达时,我只是通知我的集合新数据包的数量,我不必调用集合的 Add 方法,因为我实际上没有向它添加任何数据包 - 所以实际上没有数据包被读取。集合有关于那一刻数据包总数的信息,只有少量实际保存在内存中,以便网格可以显示它们。仅在必要时动态读取数据包,当它们不显示时,它们被释放。 但是要通知网格有关新数据包的信息,我必须(我不知道如何做另一种更好的方法)调用 CollectionChanged 并且在此调用的参数中我需要提供每个新数据包。而这个对 CollectionChanged 的​​调用使得数据包被实际读取,这会消耗 CPU。 我想重新缩放网格,以便滚动条大小通知新数据包到达,但无需读取数据包并在每个新数据包调用 CollectionChanged (每秒可能发生数千次)。我希望仅在滚动条位置发生变化时读取数据包,因此处理每个数据包的昂贵操作将仅针对少数当前可见的数据包完成,而不是针对每个数据包。

【问题讨论】:

    标签: c# data-binding wpfdatagrid inotifycollectionchanged


    【解决方案1】:

    如果您的 DataGrid 绑定到 ObservableCollection。每个 T 包含有关给定数据包的信息,诀窍是使“T”类实例化非常快。

    现在听起来您正在创建每个 T 并在将它们添加到集合时计算显示所需的所有信息。

    它应该只在它的构造函数中接收足够的信息,以便稍后当用户滚动网格以查看其行时,它可以检索有关数据包的信息。

    【讨论】:

    • 感谢您的回答。我的基础集合仅在滚动条滚动时接收数据。但问题是我希望滚动条大小反映数据包的数量,所以我必须(也许不是必须,但我不知道如何用另一种方式)读取每个新数据包并将其添加到 Datagrid - 然后所有计算发生。我想避免它。我尝试将空包添加到网格(没有任何计算),唯一的目标是保留空间。我这样做了,因为实际的数据包读取和显示发生在用户滚动条之后。它几乎可以工作,但数据包开始在网格上随机移动
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-01
    • 2021-10-18
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 2013-05-29
    相关资源
    最近更新 更多