摘要
我有一个快速变化的大型数据集,我希望将其绑定到UI(带有分组的Datagrid)。这些变化分为两个层次;
数据的特征如下;
我遇到问题的关键要求;
我想做什么;
如果第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的等价物,但是可以用来支持添加和删除项目(它们永远不会被移动)。
NB我真的很感谢有人编辑问题的标题,如果他们能想到更好的总结。
编辑 - 我的解决方案
XCeed网格将单元格直接绑定到网格中的项目,而排序&分组功能由BindingList上引发的ListChangedEvents驱动。这有点违反直觉,并排除了下面的MontioredBindingList,因为行会在组之前更新。
相反,我自己包装项目,捕获Property更改的事件并将其存储在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++;
}
}
}
答案 0 :(得分:5)
我们在谈论两件事:
界面INotifyCollectionChanged
需要由您的自定义集合实现。界面INotifyPropertyChanged
需要由您的商品实施。此外,PropertyChanged
事件仅告诉您项目上的哪个属性已更改,而不是之前的值
这意味着,您的项目需要具有如下所示的实现:
HashSet<string>
,其中包含已更改的所有属性的名称。因为它是一个集合,所以每个属性只能包含一次或零次。PropertyChanged
事件,然后将其清除。您的收藏会有类似的实施方式。然而,这有点困难,因为您需要考虑在计时器事件之间添加和删除的项目。这意味着,添加项目时,您可以将其添加到哈希集“addedItems”中。如果删除了某个项,则将其添加到“removedItems”哈希集(如果它尚未位于“addedItems”中)。如果它已经在“addedItems”中,请从那里删除它。我想你得到的照片。
为了遵循关注点分离和单一责任的原则,让您的项目以默认方式实现INotifyPropertyChanged
并创建一个包含事件合并的包装器会更好。这样做的好处是,您的项目不会包含不属于那里的代码,并且此包装器可以是通用的,并用于实现INotifyPropertyChanged
的每个类。
集合也是如此:您可以为实现INotifyCollectionChanged
的所有集合创建通用包装器,并让包装器对事件进行合并。