Быстрая прокрутка виртуального дерева Дельфи

StackOverflow https://stackoverflow.com/questions/2215090

  •  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;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top