ResumenCustom ObservableCollection <T> o BindingList <T> con soporte para notificaciones periódicas
I tienen un gran un conjunto de datos que cambia rápidamente que deseo de unirse a una interfaz de usuario (Datagrid con la agrupación). Los cambios están en dos niveles;
- se añaden productos que con frecuencia o se elimina de la colección (500 un segundo cada trayecto)
- Cada artículo tiene un 4 propiedades que cambiarán hasta 5 veces en su vida útil
Las características de los datos son los siguientes;
- Hay ~ 5000 elementos de la colección
- puede menos de un segundo, se añadirá un artículo, entonces tienen 5 cambios de propiedades y luego se retira.
- Un artículo también puede permanecer en algún estado provisional por un tiempo y debe mostrarse al usuario.
El requisito clave con el que estoy teniendo problemas;
- El usuario debe ser capaz de ordenar el conjunto de datos por cualquier propiedad en el objeto
Lo que me gustaría hacer;
- actualizar la interfaz de usuario sólo cada N segundos
- presentar solamente los NotifyPropertyChangedEvents pertinentes
Si el artículo 1 tiene un estado de propiedad, que se mueve de A -> B -> C - > D en el intervalo Necesito/quiero solo un evento de 'Estado' que se va a generar, A-> D.
Aprecio que un usuario no necesite tener la UI actualizada miles de veces por segundo. si se agrega un elemento, se cambia su estado y se elimina todo dentro de la ventana de N segundos entre las actualizaciones de UI, nunca debe golpear DataGrid.
DataGrid
La cuadrícula de datos es el componente que estoy usando para mostrar los datos. Actualmente estoy usando XCeed DataGrid ya que proporciona una agrupación dinámica trivial. No estoy emocionalmente involucrado en él, el stock DataGrid estaría bien si pudiera proporcionar algunas opciones de agrupamiento dinámico (que incluye las propiedades que cambian con frecuencia).
El cuello de botella en mi sistema es actualmente en el tiempo necesario para reordenar cuando las propiedades de un elemento cambian
Esto toma el 98% de la CPU en el YourKit Profiler.
una forma diferente de expresar la pregunta
Habida cuenta de dos casos/ObservableCollection BindingList que eran inicialmente idénticos pero la primera lista desde entonces ha tenido una serie de actualizaciones adicionales (que se puede escuchar para), genere el conjunto mínimo de cambios para convertir una lista en otra.
lectura externa
Lo que necesito es un equivalente de este ArrayMonitor por George Tryfonas pero generalizado para apoyar la adición y eliminación de elementos (que nunca se moverán).
NB Realmente apreciaría que alguien edite el título de la pregunta si pueden pensar en un mejor resumen.
EDITAR - Mi solución
La rejilla XCEED se une a las células directamente a los elementos de la rejilla mientras que el & funcionalidad de agrupación de clasificación es impulsado por el ListChangedEvents planteadas en el BindingList. Esto es ligeramente contrario a la intuición y descarta MontioredBindingList a continuación, ya que las filas se actualizarían antes que los grupos.
En lugar de eso, envuelvo los artículos por sí mismos, atrapando los eventos cambiados en la propiedad y almacenándolos en un HashSet como sugirió Daniel. Esto funciona bien para mí, repito periódicamente los artículos y les pido que notifiquen cualquier cambio.
MonitoredBindingList.cs
Aquí está mi intento de una lista de enlaces que pueden ser sondeada en busca de notificaciones de actualización. Es probable que haya algunos errores con él, ya que no fue útil para mí al final.
Crea una cola de Agregar/Eliminar eventos y realiza un seguimiento de los cambios a través de una lista. ChangeList tiene el mismo orden que la lista subyacente, de modo que, después de que hayamos notificado las operaciones de agregar/eliminar, puede aumentar los cambios con respecto al índice correcto.
/// <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++;
}
}
}
Hola Daniel - Gracias por una respuesta tan rápida y en profundidad. He estado tratando de lograr exactamente lo que describes aunque hayas corregido un par de errores que estaba cometiendo. Cuando haya realizado la clase, la volveré a publicar aquí para que cualquiera pueda usarla o adaptarla. Tengo una pregunta adicional, en mi CustomObservableCollection, ¿qué evento debo plantear cuando un artículo cambia? ¿O cómo propago 'INotifyPropertyChanged' a la cuadrícula de datos? 'NotifyCollectionChangedAction.Replace' no parece correcto. – CityView
@CityView: no es tarea de la colección notificar a la cuadrícula de datos sobre los cambios en las propiedades de sus elementos. Cuando vincula una colección a una cuadrícula de datos, la cuadrícula de datos se une al evento CollectionChanged de su colección y al evento PropertyChanged de cada elemento individual que se muestra. –
@Daniel: Tiene sentido: este no es el caso de [XCeed Grid] (http://xceed.com/CS/blogs/dontpanic/archive/2009/04/01/i-notify-we-notify-we -all-wait-no-we-don-t.aspx) que es por lo que me estaba confundiendo. Para Exceed necesitarías extender BindingList, parecería. Gracias, publicaré mi intento hoy. – CityView