Суббота, 18.05.2024
Королевство Delphi
Главное меню
Статьи
Наш опрос
Нашли свой исходник?
Всего ответов: 94
Статистика
Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Главная » Статьи » Система » Компоненты

Виртуальный ListView

Содержание

Введение

Часто на форумах слышен вопрос: «Как в TListView (или TListBox)  добавить 1000 строк? Уже на 100-200 начинает подтормаживать!». Ответы обычно такие: «А кто будет просматривать тысячи строк? Ошибка дизайна». Приводятся абстрактные примеры, показывающие, за сколько времени человек сможет просмотреть этот список.

Но есть пример, перед которым все поднимают руки. Это Lingvo. Для тех, кто не в курсе – это электронный словарь и на основном окне у него список слов и TEdit для ввода слова. В этой статье вы узнаете, как сделать такое, и почему стандартный подход тормозит.

Почему тормозит?

Давайте для начала рассмотрим простой пример, который прольёт свет на это. Проанализируем следующий код:

function DupChar(c:char;count:integer):string;
 var i:integer;
begin
 Result:='';
 for i:=1 to count do
 Result:=result+c;
end;

Функция предельно проста – она получает на вход символ и количество повторений, а на выходе даёт строку с count символов c. Казалось, всё просто, но попробуйте протестировать эту функцию, когда количество символов достигает 1000-2000. Я сделал это на своём компьютере, смотрим график:

На этом графике видна линейная зависимость. Но при дальнейшем увеличении количества символов она превратится в экспоненциальную зависимость. Почему? Рассмотрим, что происходит при выполнении такой простой строки как s:=s+'*';.

  • определяется размер исходной строки s, пусть будет n. Это быстро – пара ассемблерных команд
  • определяется размер результирующей строки – это n+1
  • выделяется место под новую строку. Очень долгая операция.
  • старая строка копируется в новое место. Также не короткая операция.
  • дописывается один символ
  • удаляется старая строка – быстро, но не так, как хотелось

То есть, когда мы создаём строку на 1000 элементов, мы 1000 раз выделяем и 999 раз удаляем память.

Оптимизируем

А теперь посмотрите на такой вариант кода.

function DupChar(c:char;count:integer):string;
 var
i:integer;
begin
 SetLength(Result,count);
 for i:=1 to count do
 Result[i]:=c;
end;

Здесь память выделяется один раз, а потом заполняется вся строка. Казалось бы, малость, а посмотрите на графики теперь:

По моим выкладкам, скорость возросла в 25-30 раз. Неплохо, да? Кто там говорил, что Delphi делает медленный код?

Я думаю, теперь уже становится яснее, почему TListView, TMemo, TListBox при добавлении большого количества строк начинают подтормаживать? Их работа аналогична.

Но с TListView и другими компонентами есть ещё одна проблема. Мы дублируем данные. То есть у нас где то в массиве есть данные и также они есть в TListView. Это ничего, пока мы не начинаем редактировать или сортировать. И тут начинают изобретаться велосипеды. Лично видел код, где в TListView отображались данные, а также на форме были ещё с десяток невидимых TMemo, в которых хранилась вспомогательная информация. При удалении одной строки из списка передёргивались все компоненты. Сортировка была просто ужасной.

Виртуальный список

Выходом из сложившейся ситуации является виртуальный режим. В таком режиме TListView способен удерживать до 232 элементов. Согласитесь, это уже нечто. Заинтересовались? Тогда вперёд. Давайте сделаем простой пример. В списке будут числа и их квадраты. Числа будут до 10000.

Итак, открываем Delphi и ставим на форму TListView. Дальше настраиваем такие свойства:

object ListView1: TListView
 Left = 152
 Top = 168
 Width = 250
 Height = 150
 Columns= <
 item
 Caption = #1063#1080#1089#1083#1086
 Width = 100
 end
 item
 Caption = #1050#1074#1072#1076#1088#1072#1090
 Width = 100
 end>
 OwnerData = True
 TabOrder = 2
 ViewStyle = vsReport
End

Для тех, кому лень настраивать TListView, могут просто скопировать этот текст и, выбрав свою форму, нажать Ctrl+V. Главным свойством здесь является OwnerData. Именно оно переводит лист в виртуальный режим. Теперь список требует от нас, что бы мы указали, сколько будет элементов. Для этого я поставил кнопку и вписал туда строку

ListView1.Items.Count:=1000;

Теперь лист знает сколько элементов. Когда он захочет нарисовать их, он у нас попросит нужные элементы. Замечу, что «просить» их он может в произвольном порядке. Более того, просить он будет только те, которые нужно отрисовать. Если у вас на экране умещается 20 элементов, а в списке 1000, то попросит он только 20! Остальные будут запрашиваться по мере того, как вы будете прокручивать список. И ещё, он умеет их кэшировать. То есть, некоторые элементы повторно не будут запрашиваться.

Все запросы идут через событие OnData. В нём нас интересует параметр item – это переменная, куда мы должны заполнить данные. А номер элемента мы можем узнать через item.index. Итак, для наших целей нужен такой обработчик.

procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
begin
 item.Caption:=inttostr(item.Index);
 item.SubItems.Add(inttostr(sqr(item.Index)));
end;

Для тех, кто раньше работал с TListView, тут нет ничего необычного. Всё! Запускаем и наслаждаемся. На своём компьютере я выставлял 1000000 (миллион!) и всё работало моментально. Попробуйте в качестве эксперимента повторить классическим способом. Я думаю при 1000-2000 вам уже надоест ждать заполнения.

Замечания

Об особенностях виртуально режима.

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

Допустим, вы храните данные в массиве и обновили их. А TListView не знает об этом. И на экране будут отображаться устаревшие данные. Для этого подскажем TListView, что данные изменены – вызываем метод UpdateItems(n,m). Где n – начальный элемент для обновления, m – конечный. То есть, если нужно обновить только пятый элемент, то пишем UpdateItems(5,5). Но если элемент не отображается на экране в данный момент, то эта строка проигнорируется TListView’ом.

Так как параметр Item имеет свойство ImageIndex, то можно, подцепив ImageList, и картинки добавлять.

Также в виртуальном режиме очень хорошо делать список, элементы которого должны быстро обновляться. То есть, например, для менеджеров закачек, для таблички мониторинга состояния группы объектов. И главное, память экономится.

Приложение

В приложенном архиве вы найдёте рабочий пример, где есть динамический массив структур.

Получить ссылку на материал

Категория: Компоненты | Добавил: Барон (20.12.2011)
Просмотров: 2333 | Теги: ListView | Рейтинг: 5.0/1
[ Пожертвования для сайта ] [ Пожаловаться на материал ]

Если вам помог материал сайта кликните по оплаченной рекламе размещенной в центре

Поиск
Категории раздела
ActiveX [10]
CORBA и COM [16]
Kol и MCK [23]
WinAPI [28]
Компоненты [27]
Работа с Bluetooth [4]
Железо [8]
Текст [18]
Разное [98]
Королевство Delphi © 2010-2024
Яндекс цитирования