Производительность DataGridView в сочетании с источником данных BindingList

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

Я думаю, что большая часть этого связана с отрисовкой фактического dataGridView. Я немного подправил dataGridView в надежде, что он уже повысит производительность. (например, отключить автоматический размер, специальные стили и т. д.)

В недавнем дополнении я добавил раскраску строк, которая и требовалась. В настоящее время мое приложение работает следующим образом:

  1. Я получаю данные из внешней системы
  2. Я помещаю данные в очередь (ConcurrencyQueue) по потоку
  3. Другой поток получает данные из этой очереди, обрабатывает их и добавляет в BindingList, привязанный к таблице.

Фактическое добавление происходит в функции, которая имеет 2 параметра: 1. Список, содержащий элементы для столбцов (элементов) 2. Цвет для строки. (Цвет)

это выглядит так (полу-псевдо):

/* Store the color for the row in the color list so it is accessible from the event */  

rowColors.Add(rowColor);    //Class variable that stored the colors of the rows used in the DataGridCellFormatting event

/* Create the row that is to be added. */
ResultRow resultRow = new ResultRow();

foreach(item in items)
{
    resultRow.Set(item); /* It's actually a dictionary because some fields are optional, hence this instead of a     direct constructor call) */
}

bindingList.Add(resultRow);

/* Row coloring based on error is done in the OnCellFormatting() */


/* Auto scroll down */
if (dataGrid.Rows.Count > 0)
{
    dataGrid.FirstDisplayedScrollingRowIndex = dataGrid.Rows.Count - 1;
}

Как видно из приведенного выше кода, цвет, который я получаю, добавляется в список, который используется в событии datagridview следующим образом:

void DataGridCellFormattingEvent(object sender, DataGridViewCellFormattingEventArgs e)
{
    // done by column so it happens once per row
    if (e.ColumnIndex == dataGrid.Columns["Errors"].Index)
    {
        dataGrid.Rows[e.RowIndex].DefaultCellStyle.BackColor = rowColors[e.RowIndex];
}
} 

BindingList определяется следующим образом:

BindingList bindingList;

где ResultRow - это класс с такой структурой:

public class ResultRow
{
    private int first = 0;
    private string second = "";
    private UInt64 third = 0;
    private IPAddress fourth = null;
    //etc

    public ResultRow()
    {
    }

    public void Set (<the values>) //In actuallity a KeyValuePair
    {
        //field gets set here
    }

    public UInt64 Third
    {
        get { return third; }
        set { third = value; }
    }

    /* etc. */

Могу ли я сделать что-нибудь относительно простое, чтобы повысить производительность? Я думал о том, чтобы, возможно, отключить рисование сетки данных, пока обработка занята, и рисовать, когда это будет сделано. (хотя и не желательно) Еще одна вещь, возможно, обновляется реже, а не после каждого полученного элемента. (BindingList, похоже, автоматически обновляет DataGridView, когда к нему что-то добавляется)

Я надеюсь, что кто-то захочет / сможет помочь.

-редактировать-

Отзывчивость формы тоже очень плохая, когда она обрабатывает данные описанным выше способом, особенно по прошествии некоторого времени. (несмотря на то, что описанный выше процесс происходит в фоновых работниках и фоновых потоках)


person Arnold4107176    schedule 08.05.2012    source источник


Ответы (2)


Да, вы можете сделать несколько вещей, чтобы ускорить его.

Во-первых - виртуализируйте сетку данных. Это встроенный механизм для таблицы данных winforms, который будет только заполнять строки и раскрашивать клиентскую область для видимых элементов данных. Поэтому, если ваш элемент управления показывает только 20 строк (и полосу прокрутки для остальных), тогда только 20 элементов фактически обрабатываются в сетке данных как пользовательский интерфейс. Затем, когда вы прокручиваете сетку для просмотра других элементов, сетка данных заполняется и отображает запрошенные строки по запросу. Вам нужно будет немного поработать с этим (на события CellValueNeeded нужно будет подписаться), и вам может потребоваться отредактировать элементы данных источника привязки. См. http://msdn.microsoft.com/en-us/library/ms171622.aspx для получения дополнительной информации.

Второе, что вы можете сделать, - это приостановить обновление пользовательского интерфейса, пока вы заполняете «кусок» своих данных. Как вы уже указали, список привязок будет автоматически обновлять сетку, когда вы добавляете в нее элементы. Но, приостановив обновление пользовательского интерфейса, а затем снова включив его с определенным интервалом (скажем, каждую секунду), вы будете передавать данные с менее постоянной скоростью. Однако обратите внимание, что для данных все равно потребуется выполнить ту же обработку, поэтому маловероятно, что это полностью решит ваши данные и может быть более эффективным для уменьшения мерцания экрана. См. Control.SuspendLayout() и Control.ResumeLayout() для получения дополнительной информации.

На мой взгляд, виртуализация будет вашим наиболее эффективным инструментом, поскольку ее единственная цель - улучшить функциональность сетки для очень больших наборов данных.

person Patrick McCurley    schedule 08.05.2012
comment
Я применил механизм приостановки / возобновления в своей сетке данных. Сначала это не слишком помогло. Теперь я создал нового фонового работника, отвечающего за обновление сетки данных с довольно большим интервалом, и это, похоже, помогает. (Но больше потоков и больше вызовов. Не уверен, что это хорошо ...) Я действительно сталкивался с этой статьей о своевременной загрузке данных раньше, и она казалась довольно сложной, поэтому я надеялся, что мне не придется этого делать. Пример также основан на базе данных, поэтому я не могу напрямую его применить. Но, думаю, придется постараться в этом разобраться для дальнейшего повышения производительности. - person Arnold4107176; 08.05.2012
comment
Рад его помощи. Это правда, что руководства по msdn не всегда самые полезные. codeproject.com/Articles/23937/ - лучший ресурс, это не лучшая из когда-либо выложенных статей кода проекта, но он должен содержать правильную информацию. Виртуализация относительно проста: если вы приложите усилия для обеспечения сетки с вашими свойствами объекта данных через CellValueNeeded, вы сразу же приступите к работе. - person Patrick McCurley; 08.05.2012
comment
Это кажется вполне объяснимым. Однако я чувствую, что в статье отсутствует некоторая информация, чтобы заставить ее работать. Я реализовал это на данный момент, но моя сетка данных остается пустой. Подробности моей проблемы я опубликовал здесь: stackoverflow.com/questions/10512981/ - person Arnold4107176; 09.05.2012

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

Но сначала попробуйте отложить обновление сетки и добавлять новые записи партиями, т.е. уменьшить частоту обновления. Итак, перед каждым пакетным обновлением:

// stop raising update events
bindingList.RaiseListChangedEvents = false; 

А потом:

// restore update events, raise reset event
bindingList.RaiseListChangedEvents = true;
bindingList.ResetBindings() 

После последней строки перейдите к последней строке.

person gwiazdorrr    schedule 08.05.2012