TVirtualStringTree и вертикальная прокрутка не работают должным образом

Я научился использовать TVirtualStringTree, и мне это понравилось. У меня есть один настраиваемый невизуальный список под названием PackedList, который заполняется другим потоком. И я хочу отображать все содержимое списка в TVirtualStringTree в реальном времени. Поэтому я установил один таймер на главную форму для обновления значения RootNodeCount HexLog (который является TVirtualStringTree) каждые 500 мс.

Все мои данные отображаются в VirtualStringTree, и у меня нет проблем со скоростью, очень хорошо. Но есть одна проблема с вертикальной полосой прокрутки. Когда я нажимаю Ctrl + End на элементе управления, чтобы перейти к концу списка, он попадает где-то посередине. Точно так же, когда я перетаскиваю полосу прокрутки до конца, она не доходит до конца. Но HexLog знает DataCount. Почему не до конца? Если я пару раз нажму Ctrl + END, он дойдет до конца.

Внутри подпрограммы таймера я хочу сказать HexLog, чтобы перейти к концу списка по коду. Как я могу это сделать и как правильно обрабатывать вертикальную полосу прокрутки?

procedure TMainForm.StatusUpdateTimerTimer(Sender: TObject);
begin
   if (FirpList.ComOperationCount > 0) and (PacketList.Items.Count <> FirpList.ComOperationCount) then
    begin
      HexLog.RootNodeCount := PacketList.Items.Count;
    end;
end;

procedure TMainForm.HexLogMeasureItem(Sender: TBaseVirtualTree;
  TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer);
begin
  if Sender.MultiLine[Node] then
  begin
    TargetCanvas.Font := Sender.Font;
    NodeHeight := HexLog.ComputeNodeHeight(TargetCanvas, Node, 1, FirpList.ComOperations[Node^.Parent^.Index].DataAsHexString(FAppSettings.HexLogColumnCharWidth) + #13#10);
  end;
end;

Внешний вид HexLog

Предлагаемый ответ TLama не работает должным образом, см. Изображение для объяснения: Решение TLama не работает

См. Ссылку для подробного объяснения изображения: http://i43.tinypic.com/1445thi.png


person Mehmet Fide    schedule 06.04.2012    source источник


Ответы (1)


Чтобы перейти к концу дерева, вызовите ScrollIntoView(GetLast).

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

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

Убедитесь, что вы обрабатываете событие OnMeasureItem и что параметр toVariableNodeHeight установлен в Options.MiscOptions. Если вы этого не сделаете, то элемент управления просто будет использовать текущую назначенную высоту для каждого узла и использовать высоту по умолчанию для любого неинициализированного узла.

Вы можете получить поведение, о котором сообщаете здесь, если вручную назначите NodeHeight вместо установки toVariableNodeHeight и обработки OnMeasureItem.

person Rob Kennedy    schedule 06.04.2012
comment
Привет, Роб, у меня есть обработчик на MeasureItem для вычисления высоты ребенка MultiLine. toVariableNodeHeight не был установлен в MiscOptions. Я сделал это правдой, и теперь ScrollIntoView () правильно переходит в конец. Но он немного прокручивается вниз и занимает около 2 секунд, чтобы добраться до конца, что недопустимо: / - person Mehmet Fide; 06.04.2012
comment
Главное, что потребовалось бы время для вычисления смещения последнего узла, помимо вычисления высот всех новых узлов, было бы, если бы кеш позиций узла был недействительным. Установите точку останова в GetDisplayRect и посмотрите, находитесь ли вы в состоянии tsUseCache при вызове ScrollIntoView. Когда кеш действителен, дерево может находить расположение узлов намного быстрее, но изменение количества узлов делает кеш недействительным. Рабочему потоку нужно время, чтобы повторно его проверить. - person Rob Kennedy; 06.04.2012
comment
(tsUseCache в FStates) в функции GetDisplayRect () возвращает false, когда я вызываю ScrollIntoView (). Что мне теперь делать? В этом проекте я использовал элемент управления RichEdit, из-за проблем со скоростью, с которыми я столкнулся, я изменил весь дизайн, чтобы использовать VirtualStringTree. Я снова нахожусь в той же точке: / На самом деле мне трудно понять, почему мне нужно рассчитывать высоту всего сообщения, чтобы перейти в конец списка. Я просто хочу показывать последние сообщения пользователю каждые 500 мс, пока транзакция данных активна. - person Mehmet Fide; 06.04.2012
comment
Вам нужна высота всех узлов, потому что элемент управления должен знать, какое смещение отображать. Если он не знает, с каким смещением он находится, он не может правильно отображать полосу прокрутки или прокручивать вверх должным образом. Используйте профилировщик, чтобы выяснить, какие части операции прокрутки занимают больше всего времени, а затем посмотрите, можно ли сократить время, затрачиваемое на эти части. - person Rob Kennedy; 06.04.2012
comment
Я сделал это. Без обработчика OnMeasureItem ScrollIntoView () занимает 720 мс. С обработчиком OnMeasureItem, показанным выше, это занимает 1799 мс. Есть ли способ заставить VirtualStringTree вычислять высоту узла без прокрутки, чтобы все узлы имели в своем состоянии tsUseCache? - person Mehmet Fide; 06.04.2012
comment
Хороший прогресс. Продолжайте расследование. Даже без переменной высоты вы все равно не уложитесь в свою временную цель. tsUseCache не является состоянием узла. Это состояние дерева. Это достигается, когда рабочий поток, запущенный ValidateCache, завершает вызов DoValidateCache. Может, вы сами могли бы назвать последнее. Возможно, вы также могли бы сократить время измерения. Вместо пересчета высоты просто используйте предыдущее значение NodeHeight. Вам нужно только пересчитать, если NodeHeight <> DefaultNodeHeight и HexLogColumnCharWith (sic; не width?) Не изменились. - person Rob Kennedy; 06.04.2012
comment
Мне нужен способ перейти в конец списка, не устанавливая значениеVariableNodeHeight в MiscOptions. Без toVariableNodeHeight прыжок занимает всего 1 мс. Но конечная позиция не точная, она прыгает где-то ближе к концу. Почему без toVariableNodeHeight требуется меньше времени? - person Mehmet Fide; 06.04.2012
comment
Я поставил очень большое число, например 5000, для DefaultNodeHeight, и теперь кажется, что он правильно перескакивает до конца без toVariableNodeHeight. В чем дело? - person Mehmet Fide; 06.04.2012
comment
Так что он как следует перескакивает до конца. Но правильно ли он прокручивается и в середине, и подходит ли ползунок прокрутки? Возможно нет. Когда переменная высота узла отключена, элемент управления может быстро перейти к любому узлу, потому что он просто умножает DefaultNodeHeight * <TotalNodeCount>, чтобы получить желаемое смещение. - person Rob Kennedy; 06.04.2012
comment
Это моя точка зрения, оценка сделана с использованием DefaultNodeHeight (что очень большое число) * TotalNodeCount полностью недействителен, но правильно переходит к концу. это означает, что VirtualTree на самом деле не нужно знать точное положение, чтобы перейти к концу? Но если пользователь хочет прокрутить куда-нибудь, кроме конца, он может выполнить все вычисления, указанные выше. Завтра попробую еще. Спасибо. - person Mehmet Fide; 06.04.2012
comment
Он переходит к концу, вычисляя неправильное смещение для нижнего узла, а затем закрашивая этот узел в нижней части окна просмотра. Это неправильное смещение используется для расчета размера ползунка, а диапазон полосы прокрутки используется для расчета относительных смещений при прокрутке в другом месте элемента управления. Вы должны увидеть много пустых пространств или других графических аномалий при прокрутке в другом месте элемента управления. - person Rob Kennedy; 06.04.2012
comment
Привет, Роб, я немного оптимизировал обработчик событий OnMeasureItem. Вместо функции ComputeNodeHeight (), которая использует холст для измерения, я реализовал свою собственную функцию. Это сэкономило 800 мс. У меня есть еще один вопрос. Вы знаете, что при первом вызове функции ScrollIntoView (HexLog.GetLast, False) требуется время для измерения всех узлов и т. Д. Но второй вызов выполняется очень быстро. Есть ли способ сохранить VST в кэше без прокрутки? Каждый раз, когда я обновляю RootNodeCount, я хочу, чтобы VST выполнял все вычисления, необходимые для прокрутки, чтобы пользователь не ждал первой прокрутки. Является ли это возможным? - person Mehmet Fide; 08.04.2012
comment
Я пробовал вызвать оба защищенных метода, но они не помогли: TVirtualStringTreeHack (HexLog) .ValidateCache; TVirtualStringTreeHack (HexLog) .DoValidateCache; - person Mehmet Fide; 09.04.2012