Виртуальный элемент TListView->Подразделы->>Назначить() во время обновления триггеров onData и, следовательно, бесконечных обновлений

#c builder #vcl

Вопрос:

Ведение более старого проекта с использованием c Builder 2009

В TListView экземпляре ( ViewStyle = vsReport ) настройка для работы с виртуальным ( OwnerData = true ) Я хотел попытаться как можно больше увеличить скорость. Каждая крошечная частичка помогает. В OnData случае, если я заметил, что Item->SubItems->Capacity для начала = 0, и оно увеличивается на 4 по мере добавления подпунктов. Я читаю в документах, которые Capacity доступны только для чтения, но я хочу TStrings как можно больше избегать внутреннего перераспределения. Поскольку мне также нужно выполнить кэширование, я решил использовать TStringList кэш as, который уже вырос до необходимой емкости. Я предполагаю TStrings Assign() , что затем сразу же выделит массив, достаточно большой для хранения необходимого количества строк ?

 Item->SubItems->Assign(Cache.SubItems) ;
 

Пока это работает, я заметил, что это заставляет ListView снова и снова вызывать onData и … заставлять его никогда не останавливаться.

Легко исправить это снова, сделав это:

 for (int x = 0 ; x < Cache.SubItems->Count ; x  )
    Item->SubItems->Add(Cache.SubItems->Strings[x]) ;
 

Но, конечно, весь смысл состоял в том, чтобы с SubItems самого начала определить количество строк.

Я понимаю, что, возможно, сталкиваюсь со старой проблемой VCL ? Это уже давно решено ? Или в этом поведении есть какой-то смысл, которого я сейчас не понимаю ?

Есть ли способ «разрешить» Capacity принимать входные данные ? Чтобы он выделял достаточно места для строк, которые будут добавлены ?

Комментарии:

1. У меня сейчас нет доступа ни к одному конструктору C , но разве нет BeginUpdate(); и EndUpdate(); , которое можно было бы использовать, чтобы отключить запуск событий во время обновления? Что-то вроде obj->BeginUpdate(); ...; Item->SubItems->Assign(Cache.SubItems); ...; obj->EndUpdate(); . Если нет, возможно, сохранение обработчика OnData событий в переменной и установка его в NULL значение до Assign() , а затем установка его обратно после обновления все может сработать.

2. Отличное предложение, Тед, но это не помогает, возможно, мне нужно немного переписать свой вопрос, потому OnData что он не вызывается во время Assign() . OnData завершается, как обычно, вызывается для всех отображаемых объектов, но последовательность для всех объектов начинается снова после этого, и снова, и снова. Обычно это прекращается, когда нет никакой активности, теперь это просто никогда не прекращается

3.При выполнении дополнительных тестов выясняется , что, когда я устанавливаю Capacity значение, оно остается неизменным (когда я снова его зачитываю). В документах говорится, что это не так, но я не подумал о том, что TListViewItem SubItems реализация является классом, который наследуется от TStrings

4. Прошло некоторое время с тех пор, как я работал TListView в виртуальном режиме, но у меня никогда не было проблем с этим, как вы описали. У меня есть исходный код VCL от CB2009, поэтому я рассмотрю его завтра и опубликую ответ. Но в то же время в документации CB2009 говорится TStrings::Capacity , что это чтение/запись, может быть , вы путаете ее с TStrings::Count тем, что доступно только для чтения? TStrings::Assign() звонки (Begin|End)Update() внутри компании. И IIRC TStrings::Assign() просто запускает Add() цикл.

5. @Питер, я добавил ответ сейчас.

Ответ №1:

TListView::OnData Событие запускается всякий раз, когда ListView требуются данные для данного элемента списка, например (но не ограничиваясь) операциями рисования.

Обратите внимание, что когда OnData событие запускается, TListItem::SubItems оно уже было Clear() отредактировано заранее. TStringList::Clear() устанавливает значение Capacity 0, освобождая его текущий string массив. Вот почему Count и Capacity всегда равны 0 при вводе вашего OnData обработчика.

SubItems Свойство реализовано как TSubItems объект, который является производным от TStringList . Задатчик TStrings::Capacity свойств реализован в TStringList и выполняет то , что вы ожидаете, для предварительного выделения string массива. Но это все, что он делает — выделяет память VCL для массива, не более того. Все еще существует аспект обновления самих элементов ListView на уровне Win32 API, и это необходимо делать индивидуально по string мере добавления каждого из них в SubItems .

Когда ваш OnData обработчик вызывает SubItems->Assign() , вы вызываете TStrings::Assign() (как TStringList и TSubItems не переопределяйте его). Тем не менее, ПРЕДВАРИТЕЛЬНО TStrings::Assign() не распределяет string массив по размеру исходного TStrings объекта, как можно было бы ожидать (по крайней мере, не в CB2009, я не знаю, делают ли это современные версии или нет). Внутренне, Assign() просто вызывает Clear() , а затем TStrings::AddStrings() (что TStringList ни TSubItems в коем случае не переопределяет предварительное выделение массива). AddStrings() просто вызывает TStrings::AddObject() в цикле (который и TStringList то, и TSubItems другое переопределяет).

Вся эта логика очистки и добавления заключена в паре TStrings::(Begin|End)Update() вызовов. Это важно отметить, потому TSubItems что реагирует на счетчик обновлений. Когда счетчик падает до 0, TSubItems запускается TListView для выполнения некоторых внутренних обновлений, включая вызов Invalidate() самого себя, который запускает полную перерисовку и, таким образом, запускает новую серию OnData событий для элементов списка, которые нуждаются в повторной прорисовке.

С другой стороны, когда вы вызываете SubItems->Add() свой собственный ручной цикл и пропускаете (Begin|End)Update() вызовы, вы пропускаете перерисовку всего списка. TSubItems переопределяет TStrings::Add/Object() (среди прочего) обновление только определенного элемента ListView, с которым он связан. Он не перекрашивает весь вид списка.

Таким образом, вы должны быть в состоянии установить Capacity перед входом в ручной цикл, если вы действительно хотите:

 Item->SubItems->Capacity = Cache.SubItems->Count;
for (int x = 0; x < Cache.SubItems->Count;   x)
    Item->SubItems->Add(Cache.SubItems->Strings[x]);
 

В этом случае вы можете использовать AddStrings() вместо ручного цикла:

 Item->SubItems->Capacity = Cache.SubItems->Count;
Item->SubItems->AddStrings(Cache.SubItems);
 

Комментарии:

1. ага.. «TSubItems реагирует на счетчик обновлений. Когда счетчик падает до 0, TSubItems запускает TListView, чтобы сделать некоторые внутренние обновления, которые включают в себя вызов Invalidate() на себя», объясняет все это, спасибо