【发布时间】:2011-07-15 16:12:57
【问题描述】:
总结
我有一个快速变化的大型数据集,我希望将其绑定到 UI(带分组的数据网格)。变化分为两个层面;
- 经常从集合中添加或删除项目(单程每秒 500 个)
- 每个项目都有 4 个属性,在其生命周期内最多会更改 5 次
数据的特点如下;
- 集合中有大约 5000 个项目
- 可以在一秒钟内添加一个项目,然后进行 5 次属性更改,然后将其删除。
- 项目也可能会在某个临时状态下保持一段时间,并应显示给用户。
我遇到问题的关键要求;
- 用户应该能够按对象上的任何属性对数据集进行排序
我想做什么;
- 仅每 N 秒更新一次 UI
- 仅引发相关的 NotifyPropertyChangedEvents
如果项目 1 有一个属性 State 从 A -> B -> C -> D 在 间隔我需要/只想要一个“状态”变化 要引发的事件,A->D。
我很欣赏用户不需要每秒更新数千次 UI。如果添加了一个项目,它的状态发生了变化,并且在 UI 更新之间 N 秒的窗口内全部删除,它不应该命中 DataGrid。
数据网格
DataGrid 是我用来显示数据的组件。我目前正在使用 XCeed DataGrid,因为它可以轻松地提供动态分组。我对它没有感情上的投入,如果我可以提供一些动态分组选项(包括经常更改的属性),股票 DataGrid 会很好。
我系统的瓶颈是 目前正在重新排序 当项目的属性发生变化时
这会占用 YourKit Profiler 中 98% 的 CPU。
问题的不同表述方式
给定两个 BindingList / ObservableCollection 实例 最初是相同的,但 从那以后,第一份名单有一系列 附加更新(您可以 听),生成最小集合 将一个列表变成 其他。
外读
我需要的是与 George Tryfonas 的 ArrayMonitor 等效,但一般化以支持添加和删除项目(它们永远不会被移动)。
请注意,如果有人能想到更好的摘要,我会非常感谢他们编辑问题的标题。
编辑 - 我的解决方案
XCeed 网格将单元格直接绑定到网格中的项目,而排序和分组功能由 BindingList 上引发的 ListChangedEvents 驱动。这有点违反直觉,并排除了下面的 MontioredBindingList,因为行会在组之前更新。
相反,我自己包装项目,捕捉属性更改事件并将它们存储在 HashSet 中,正如 Daniel 建议的那样。这对我来说效果很好,我会定期迭代这些项目并要求他们通知任何更改。
MonitoredBindingList.cs
这是我对可以轮询更新通知的绑定列表的尝试。它可能有一些错误,因为它最终对我没有用。
它创建一个添加/删除事件队列,并通过列表跟踪更改。 ChangeList 与基础列表具有相同的顺序,因此在我们通知添加/删除操作后,您可以针对正确的索引提出更改。
/// <summary>
/// A binding list which allows change events to be polled rather than pushed.
/// </summary>
[Serializable]
public class MonitoredBindingList<T> : BindingList<T>
{
private readonly object publishingLock = new object();
private readonly Queue<ListChangedEventArgs> addRemoveQueue;
private readonly LinkedList<HashSet<PropertyDescriptor>> changeList;
private readonly Dictionary<int, LinkedListNode<HashSet<PropertyDescriptor>>> changeListDict;
public MonitoredBindingList()
{
this.addRemoveQueue = new Queue<ListChangedEventArgs>();
this.changeList = new LinkedList<HashSet<PropertyDescriptor>>();
this.changeListDict = new Dictionary<int, LinkedListNode<HashSet<PropertyDescriptor>>>();
}
protected override void OnListChanged(ListChangedEventArgs e)
{
lock (publishingLock)
{
switch (e.ListChangedType)
{
case ListChangedType.ItemAdded:
if (e.NewIndex != Count - 1)
throw new ApplicationException("Items may only be added to the end of the list");
// Queue this event for notification
addRemoveQueue.Enqueue(e);
// Add an empty change node for the new entry
changeListDict[e.NewIndex] = changeList.AddLast(new HashSet<PropertyDescriptor>());
break;
case ListChangedType.ItemDeleted:
addRemoveQueue.Enqueue(e);
// Remove all changes for this item
changeList.Remove(changeListDict[e.NewIndex]);
for (int i = e.NewIndex; i < Count; i++)
{
changeListDict[i] = changeListDict[i + 1];
}
if (Count > 0)
changeListDict.Remove(Count);
break;
case ListChangedType.ItemChanged:
changeListDict[e.NewIndex].Value.Add(e.PropertyDescriptor);
break;
default:
base.OnListChanged(e);
break;
}
}
}
public void PublishChanges()
{
lock (publishingLock)
Publish();
}
internal void Publish()
{
while(addRemoveQueue.Count != 0)
{
base.OnListChanged(addRemoveQueue.Dequeue());
}
// The order of the entries in the changeList matches that of the items in 'this'
int i = 0;
foreach (var changesForItem in changeList)
{
foreach (var pd in changesForItem)
{
var lc = new ListChangedEventArgs(ListChangedType.ItemChanged, i, pd);
base.OnListChanged(lc);
}
i++;
}
}
}
【问题讨论】:
标签: c# wpf observablecollection large-data-volumes bindinglist