Пользовательская коллекция ObservableCollection или BindingList с поддержкой периодических уведомлений

Резюме

У меня есть большой быстро меняющийся набор данных, который я хочу привязать к пользовательскому интерфейсу (Datagrid с группировкой). Изменения происходят на двух уровнях;

  • Элементы часто добавляются или удаляются из коллекции (по 500 в секунду в каждую сторону)
  • Каждый элемент имеет 4 свойства, которые изменяются до 5 раз за время своего существования

Характеристики данных следующие:

  • В коллекции ~ 5000 элементов.
  • Элемент может быть добавлен в течение секунды, затем у него будет 5 изменений свойств, а затем он будет удален.
  • Элемент также может оставаться в некотором промежуточном состоянии какое-то время и должен отображаться для пользователя. перемещается из A -> B -> C -> D в интервал мне нужно / хочу только одно изменение состояния событие, которое должно быть вызвано, A-> D.

    Я понимаю, что пользователю не нужно обновлять пользовательский интерфейс тысячи раз в секунду. если элемент добавлен, его состояние изменено и удалено в пределах окна N секунд между обновлениями пользовательского интерфейса, он никогда не должен попадать в DataGrid.

    DataGrid

    DataGrid - это компонент, который я использую для отображения данных. В настоящее время я использую XCeed DataGrid, поскольку он обеспечивает простую динамическую группировку. Я эмоционально не вкладываюсь в это, стандартный DataGrid был бы прекрасен, если бы я мог предоставить некоторые параметры динамической группировки (которые включают часто меняющиеся свойства).

    Узким местом в моей системе является в настоящее время требуется время для повторной сортировки при изменении свойств элемента

    Это занимает 98% ЦП в YourKit Profiler.

    Другой способ сформулировать вопрос

    Учитывая два экземпляра BindingList / ObservableCollection которые изначально были идентичны, но первый список с тех пор содержит серию дополнительные обновления (которые вы можете слушать), сгенерировать минимальный набор изменений, чтобы превратить один список в другое.

    Внешнее чтение

    Мне нужен эквивалент этого ArrayMonitor Джорджа Трифонаса, но обобщенный для поддержки добавления и удаления элементов (они никогда не будут перемещены).

    NB I был бы очень признателен, если бы кто-то отредактировал заголовок вопроса, если бы он мог придумать лучшее резюме.

    РЕДАКТИРОВАТЬ - Мое решение

    Сетка XCeed привязывает ячейки непосредственно к элементам в сетке, тогда как функции сортировки и группировки управляются с помощью ListChangedEvents, поднятых в BindingList. Это немного противоречит интуиции и исключает приведенный ниже список MontioredBindingList, поскольку строки будут обновляться до групп.

    Вместо этого я оборачиваю сами элементы, улавливая события изменения свойства и сохраняю их в HashSet, как предложил Даниэль. Это хорошо работает для меня, Я периодически просматриваю элементы и прошу их уведомлять о любых изменениях.

    MonitoredBindingList.cs

    Вот моя попытка создать список привязки, который можно опросить на предмет уведомлений об обновлениях. Скорее всего, в нем есть какие-то ошибки, так как в итоге он мне не пригодился.

    Он создает очередь событий добавления / удаления и отслеживает изменения через список. Список изменений имеет тот же порядок, что и базовый список, поэтому после того, как мы уведомим об операциях добавления / удаления, вы можете поднять изменения по правильному индексу.

    /// 
    ///  A binding list which allows change events to be polled rather than pushed.
    /// 
    [Serializable]
    
    public class MonitoredBindingList : BindingList
    {
        private readonly object publishingLock = new object();
    
        private readonly Queue addRemoveQueue;
        private readonly LinkedList> changeList;
        private readonly Dictionary>> changeListDict;
    
        public MonitoredBindingList()
        {
            this.addRemoveQueue = new Queue();
            this.changeList = new LinkedList>();
            this.changeListDict = new Dictionary>>();
        }
    
        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());
                        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++;
            }
        }
    }
    

10
задан CityView 16 March 2011 в 15:18
поделиться