在Delphi的虚拟树视图中快速滚动

[这是之前发布的问题的更新版本,以前的标题是在Delphi的虚拟树视图中通过索引选择节点。]

经过一天中最好的时间之后,我相信我已经有了一个虚拟Treeview组件(功能强大但复杂)以简单的双表数据感知方式工作。

现在,我试图简单地选择第1512个(例如)顶级节点。 除了获取第一个顶层节点,然后在一个循环中调用GetNextSibling 1,511之外,我看不出任何其他方法。

这似乎不必要地涉及。 有一种更简单的方法吗?

UPDATE

因为初始化我的树中的节点需要访问数据库,所以在启动时初始化所有节点是不可行的。 当用户从没有选择记录的表单开始时,没关系。 当用户在树上滚动时,会填充足够的节点以将当前窗口显示到树中,并且性能很好。

当用户在已经选择了数据库记录的对话模式下启动表单时,我必须在用户看到表单之前将树推进到该节点。 这是一个问题,因为如果记录是朝向树的末尾,那么当我从第一个节点走树时可能需要十秒。 每次我可以GetNextSibling()时,都会初始化一个节点,即使绝大多数这些节点没有显示给用户。 我宁愿将这些节点的初始化推迟到它们对用户可见的点。

我知道必须有更好的方法,因为如果我在没有选定记录的情况下打开树并使用垂直滚动条在单个操作中移动到树的中间,那么显示正确的节点而不必初始化我跳过的节点。

这是我想在打开选中记录的树时实现的效果。 我知道我想要去的节点的索引,但是如果我不能通过索引达到目标,我可以在树上进行二分搜索,假设我可以跳转一些节点向后和向前跳转(类似于直接滚动到树的中间)。

或者,也许我可以对树视图进行一些状态设置,这样在我遍历网格时会留下未初始化的中间节点。 我已经尝试了开始/结束更新,但似乎没有办法。


要获得节点的兄弟节点而不进行初始化,只需使用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指针,因此它相对于树中其余节点的位置不再重要(例如,可以对它们进行排序,而不会丢失对所需节点的引用)?


你在更新中写道:

我知道必须有更好的方法,因为如果我在没有选定记录的情况下打开树并使用垂直滚动条在单个操作中移动到树的中间,那么显示正确的节点而不必初始化我跳过的节点。

这里有所不同,因为垂直滚动更改了客户端位置0处显示的逻辑Y坐标。控件计算滚动条位置和滚动范围的偏移量,然后计算控件顶部可见的节点。 需要绘制滚动到视图中的区域时,节点才会再次初始化。

如果你有一个节点的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;
链接地址: http://www.djcxy.com/p/5961.html

上一篇: Fast scrolling in Delphi's Virtual Treeview

下一篇: Multiple Expanding Nodes in Virtual TreeView possible?