Быстрая прокрутка виртуального дерева Дельфи
-
19-09-2019 - |
Вопрос
Это обновленная версия вопроса, опубликованного ранее, предыдущий заголовок был Выбор узла по индексу в виртуальном дереве Delphi.]
Я полагаю, что после большей части дня у меня есть виртуальный компонент Treeview (мощный, но сложный), работающий в простой моде с двумя таблицами.
Теперь я пытаюсь просто выбрать 1512-й (например) узлов верхнего уровня. Я не вижу никакого способа сделать это, кроме как получить первый узел верхнего уровня, а затем позвонить в петлю.
Это кажется излишне вовлеченным. Есть более простой способ?
ОБНОВИТЬ
Поскольку инициализация узлов в моем дереве требует доступа к базе данных, инициализация всех узлов при запуске невозможна. Когда пользователь начинает с формы без уже выбранной записи, это нормально. Когда пользователь прокручивается вокруг дерева, достаточно узлов, чтобы отобразить текущее окно в дерево, и производительность в порядке.
Когда пользователь запускает форму в диалоговом режиме с уже выбранной записи базы данных, я должен перенести дерево к этому узлу, прежде чем пользователь увидит форму. Это проблема, потому что, если запись приближается к концу дерева, она может занять десять секунд, когда я выхожу на дерево из первого узла. Каждый раз, когда я могу получить инициализируется узел, даже если подавляющее большинство этих узлов не отображаются пользователю. Я бы предпочел отложить инициализацию этих узлов до такой степени, что они становятся видимыми пользователю.
Я знаю, что должен быть лучший способ, потому что, если я открою дерево без выбранной записи и использую вертикальную полосу прокрутки, чтобы перемещаться в одну операцию, к середине дерева, тогда отображаются правильные узлы без необходимости инициализировать узлы, которые я пропустил.
Это эффект, который я хотел бы достичь при открытии дерева с выбранной записи. Я знаю индекс узла, к которому я хочу перейти, но если я не смогу добраться туда по индексу, я смогу выполнить бинарный поиск на дереве, предполагая, что я смогу прыгнуть в некоторое количество узлов назад и вперед (аналогично прокрутке непосредственно к середина дерева).
В качестве альтернативы, возможно, есть какая -то настройка состояния, которое я могу сделать для представления дерева, который оставит промежуточные узлы ненициализированными, когда я пересекаю сетку. Я пытался начать обновление начала/конец, и это, похоже, не делает свое дело.
Решение
Чтобы получить брат узела, не инициализируя его, просто используйте NextSibling
Указатель (см. Объявление TVirtualNode
).
Другие советы
Контроль дерева структурирован так же, как классические деревья, о которых вы узнали бы в компьютерном классе. Единственный способ добраться от корня дерева до 1512 -го ребенка - это ходить по ссылкам один за другим. Если вы делаете это самостоятельно или используете метод управления деревьями, это все равно должно быть сделано таким образом. Я не вижу ничего, предоставленного в самом контроле, так что вы можете использовать эту функцию:
function GetNthNextSibling(Node: PVirtualNode; N: Cardinal;
Tree: TBaseVirtualTree = nil): PVirtualNode;
begin
if not Assigned(Tree) then
Tree := TreeFromNode(Node);
Result := Node;
while Assigned(Result) and (N > 0) do begin
Dec(N);
Result := Tree.GetNextSibling(Result);
end;
end;
Если вы часто делаете это часто, вы можете сделать себя индексом. Это может быть так же просто, как сделать множество PVirtualNode
указатели и хранение всех значений верхнего уровня в нем, так что вы можете просто прочитать 1512-е значение из него. Контроль дерева не нуждается в такой структуре данных, поэтому оно не поддерживает ее.
Вы также можете пересмотреть, ты Нужна такая структура данных. Ты В самом деле Нужно получить доступ к узлам по такому индексу? Или вместо этого мог сохранить PVirtualNode
Указатель, поэтому его положение относительно остальных узлов в дереве больше не имеет значения (это означает, что вы можете, например, отсортировать их, не теряя ссылки на узел, который вы хотели)?
Вы пишете в своем обновлении:
Я знаю, что должен быть лучший способ, потому что, если я открою дерево без выбранной записи и использую вертикальную полосу прокрутки, чтобы перемещаться в одну операцию, к середине дерева, тогда отображаются правильные узлы без необходимости инициализировать узлы, которые я пропустил.
Здесь существует разница, потому что вертикальная прокрутка меняет логическую координату Y, которая отображается в положении клиента 0. Управление вычисляет смещение из позиции прокрутки и диапазона прокрутки, а затем вычисляет, какой узел виден в верхней части элемента управления. Узлы снова инициализируются только тогда, когда необходимо окрасить область, которая была прокручена в поле зрения.
Если у вас есть координата y узла, вы можете получить указатель узла, вызывая
function TBaseVirtualTree.GetNodeAt(X, Y: Integer; Relative: Boolean;
var NodeTop: Integer): PVirtualNode;
Координата y узла - это сумма высот всех предыдущих видимых узлов. Предполагая, что у вас нет разрушенных узлов (так что это либо плоский список записей, либо все узлы с дочерними узлами расширены), и все они имеют высоту по умолчанию, это легко. Этот код должен быть хорошей отправной точкой:
procedure TForm1.SelectTreeNode(AIndex: integer; ACenterNodeInTree: boolean);
var
Y, Dummy: integer;
Node: PVirtualNode;
begin
Y := Round((AIndex + 0.5) * VirtualStringTree1.DefaultNodeHeight);
Node := VirtualStringTree1.GetNodeAt(0, Y, False, Dummy);
if Node <> nil then begin
Assert(Node.Index = AIndex);
VirtualStringTree1.ScrollIntoView(Node, ACenterNodeInTree);
VirtualStringTree1.Selected[Node] := True;
VirtualStringTree1.FocusedNode := Node;
end;
end;