#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()
внутри компании. И IIRCTStrings::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() на себя», объясняет все это, спасибо