Реализует ли Windows Forms DataGridView настоящий виртуальный режим? - PullRequest
10 голосов
/ 07 января 2010

У меня есть таблица SQL, содержащая в настоящее время 1 миллион строк, которые со временем будут расти.

Существует специальное требование пользователя представить сортируемую сетку, которая отображает все строки без разбивки на страницы. Пользователь ожидает, что сможет очень быстро переходить от строки к строке и сверху вниз с помощью полосы прокрутки.

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

Windows Forms DataGridView предоставляет виртуальный режим, который выглядит так, как будто он должен быть ответом. Однако в отличие от других виртуальных режимов, с которыми я сталкивался, он по-прежнему выделяет память для каждой строки (подтверждено в ProcessExplorer). Очевидно, что это приводит к ненужному значительному увеличению общего использования памяти, и при выделении этих строк происходит заметная задержка. Производительность прокрутки также страдает от 1 миллиона + строк.

В реальном виртуальном режиме не нужно выделять память для строк, которые не отображаются. Вы просто даете ему общее количество строк (например, 1 000 000) и все, что делает сетка, это соответственно масштабирует полосу прокрутки. При первом отображении сетка просто запрашивает данные по первым n (скажем, 30) видимым строкам, мгновенное отображение.

Когда пользователь прокручивает сетку, предоставляется простое смещение строки и количество видимых строк, которые можно использовать для извлечения данных из хранилища данных.

Вот пример кода DataGridView, который я сейчас использую:

public void AddVirtualRows(int rowCount)
{
    dataGridList.ColumnCount = 4;


    dataGridList.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
    dataGridList.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;

    dataGridList.VirtualMode = true;

    dataGridList.RowCount = rowCount;

    dataGridList.CellValueNeeded += new DataGridViewCellValueEventHandler(dataGridList_CellValueNeeded);


}
void dataGridList_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
    e.Value = e.RowIndex;
}

Я что-то здесь упускаю или «виртуальный» режим DataGridView на самом деле совсем не виртуален?

[Update]

Похоже, старый добрый ListView реализует именно тот виртуальный режим, который я ищу. Но, к сожалению, ListView не имеет возможностей форматирования ячеек DataGridView, поэтому я не могу его использовать.

Для других, которые могут это сделать, я протестировал его с ListView с четырьмя столбцами (в режиме детализации), VirtualMode = True и VirtualListSize = 100 000 000 строк.

Список отображается сразу с видимыми первыми 30 строками. Затем я могу быстро перейти к нижней части списка без задержки. Использование памяти постоянно 10 МБ.

Ответы [ 4 ]

17 голосов
/ 07 декабря 2011

У нас просто было аналогичное требование, чтобы иметь возможность отображать произвольные неиндексированные таблицы строк 1M + в нашем приложении с «очень хорошей» производительностью, используя набор DataGridView. Сначала я подумал, что это невозможно, но с достаточным количеством царапин на голове мы нашли что-то, что работает очень хорошо после нескольких дней, проливая на Reflector и .NET Profiler. Это было трудно сделать, но результаты того стоили.

Мы решили эту проблему, создав класс, реализующий ITypedList и IBindingList (например, можно назвать LargeTableView) для управления асинхронным поиском и кэшированием информации из базы данных. Мы также создали один класс наследования PropertyDescriptor (например, LargeTableColumnDescriptor) для извлечения данных из каждого столбца.

Когда для свойства DataGridView.DataSource задан класс, реализующий IBindingList, он переходит в псевдовиртуальный режим , который отличается от обычного VirtualMode, где, например, когда закрашивается каждая строка (например, когда пользователь прокручивает), DataGridView обращается к индексатору [] для IBindingList и соответствующим GetValue методам для каждого столбца PropertyDescriptor для извлечения значений по мере необходимости. Событие CellValueNeeded не вызывается. В нашем случае мы обращаемся к базе данных при обращении к индексатору, а затем кешируем значение, чтобы последующие перерисовки не попадали в базу данных.

Я провел аналогичные тесты с использованием памяти. DataGridView выделяет массив, который является размером списка (то есть 1М строк), однако каждый элемент в массиве изначально ссылается на один DataGridViewRow, поэтому использование памяти приемлемо. Я не уверен, является ли поведение таким же, когда VirtualMode имеет значение true. Нам удалось устранить задержку прокрутки , немедленно вернув String.Empty в методе GetValue, если строка не кэширована, а затем выполнив запрос к базе данных асинхронно. Когда асинхронный запрос завершен, вы можете вызвать событие IBindingList.ListChanged, чтобы сообщить DataGridView, что ему следует перекрасить ячейки, за исключением этого времени чтения из кэша, который легко доступен. Таким образом, пользовательский интерфейс никогда не блокируется в ожидании вызовов базы данных.

Одна вещь, которую мы заметили, заключается в том, что производительность значительно выше, если вы установите DataSource или количество виртуальных строк до , добавив DataGridView в форму - это сократит время инициализации вдвое. Кроме того, убедитесь, что для автоматического изменения размера строки и столбца установлено значение None, иначе у вас возникнут дополнительные проблемы с производительностью.

Примечание: способ «загрузки» такой большой таблицы в наше приложение .NET заключался в создании временной таблицы на сервере SQL, которая перечисляла первичные ключи в нужном порядке сортировки вместе с IDENTITY ( номер строки), а затем сохранить соединение для последующих запросов строки. Это, естественно, требует времени для инициализации (примерно 3-5 с на достаточно быстром SQL-сервере), но без знания доступных индексов у нас нет лучшей альтернативы. Затем в нашей реализации ITypedList мы запрашиваем строки на страницах по 100 строк, где 50-я строка - это строка, которая рисуется, так что мы ограничиваем количество запросов, выполняемых при каждом обращении к индексатору, и получаем вид наличие всех данных, доступных в нашем приложении.

Дальнейшее чтение:

http://msdn.microsoft.com/en-us/library/ms404298.aspx

http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.aspx

2 голосов
/ 07 января 2010
1 голос
/ 06 декабря 2011

Ответ НЕТ

см. Первый комментарий здесь

если кто-нибудь знает лучший способ, пожалуйста, сообщите нам

0 голосов
/ 06 ноября 2013

Я бы сказал да ... пока вы придерживаетесь событий, вызванных поведением виртуального режима (например, CellValueNeeded), и что вы хорошо позаботились об очистке своего встроенного буфера. Я уже отображал большой объем данных, более 1 млн. Без суеты.

Мне немного любопытно узнать, как Кевин Маккормик использует DataSource на основе ITypedList или любых реализаций интерфейса, связанных с IList. Я предполагаю, что это просто еще один уровень абстракции, который использует внутренний и прозрачный буфер в дополнение к тому, чтобы позволить пользователю или разработчику использовать DataGridView с этим, но все же внутренне иметь дело с собственным VirtualMode для отображения информации, загруженной в буфер.

Помимо модного способа обхода виртуального режима, для меня единственной сложной проблемой, остающейся с DataGridView, является ограничение RowCount: это по-прежнему Int32.Max. Вероятно, это связано с унаследованной системой рисования Winforms ... точно так же, как изображения или даже все элементы размера, ширина, высота элементов управления Winform ... почему бы не придерживаться типа UInt32?

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

См. Мой ответ там ниже, он может помочь вам, если вы все еще застряли с этой проблемой, если, я думаю, она уже давно решена. https://stackoverflow.com/a/16373108/1906567

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