Быстрая прокрутка в Delphi Virtual Treeview - PullRequest
3 голосов
/ 07 февраля 2010

[Это обновленная версия вопроса, опубликованного ранее, предыдущее название было Выбор узла по индексу в виртуальном древовидном представлении Delphi .]

После большей части дня я считаю, что у меня есть компонент Virtual Treeview (мощный, но сложный), работающий в простой форме, учитывающей данные двух таблиц.

Теперь я пытаюсь просто выбрать 1,512-й (например) из узлов верхнего уровня. Я не вижу никакого способа сделать это, кроме как получить первый узел верхнего уровня и затем вызвать GetNextSibling 1,511 в цикле.

Это кажется ненужным. Есть ли более простой способ?

UPDATE

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

Когда пользователь запускает форму в диалоговом режиме с уже выбранной записью базы данных, я должен переместить дерево к этому узлу, прежде чем пользователь увидит форму. Это проблема, потому что, если запись ближе к концу дерева, это может занять десять секунд, пока я иду по дереву от первого узла. Каждый раз, когда я могу получить GetNextSibling (), узел инициализируется, даже если подавляющее большинство этих узлов не отображается пользователю. Я бы предпочел отложить инициализацию этих узлов до того момента, когда они станут видны пользователю.

Я знаю, что должен быть лучший способ, потому что, если я открою дерево без выбранной записи и использую вертикальную полосу прокрутки, чтобы в одной операции переместиться в середину дерева, тогда отображаются правильные узлы без инициализации узлов, которые я пропустил .

Этого эффекта я хотел бы достичь, открывая дерево с выбранной записью. Я знаю индекс узла, к которому я хочу перейти, но если я не могу попасть туда по индексу, я могу выполнить бинарный поиск по дереву, предполагая, что я могу перепрыгнуть некоторое количество узлов назад и вперед (аналогично прокрутке непосредственно к середина дерева).

В качестве альтернативы, возможно, есть какая-то настройка состояний, которую я могу сделать для представления дерева, которая оставит промежуточные узлы неинициализированными, когда я пересекаю сетку. Я пробовал начать / закончить обновление, но это не помогло.

Ответы [ 3 ]

3 голосов
/ 12 февраля 2010

Чтобы получить брат или узел без инициализации, просто используйте указатель NextSibling (см. Объявление TVirtualNode).

3 голосов
/ 07 февраля 2010

Управление деревом построено так же, как классические деревья, о которых вы узнали бы на уроке информатики. Единственный способ добраться от корня дерева до 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, чтобы его положение относительно остальных узлов дерева больше не имело значения (то есть вы можете, например, отсортировать их без потери ссылки на нужный вам узел)?

0 голосов
/ 11 февраля 2010

Вы пишете в своем обновлении:

Я знаю, что должен быть лучший способ, потому что, если я открою дерево без выбранной записи и использую вертикальную полосу прокрутки, чтобы в одной операции переместиться в середину дерева, тогда отобразятся правильные узлы без инициализации узлов, которые я пропустил .

Здесь есть разница, потому что вертикальная прокрутка изменяет логическую координату 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;
...