代码之家  ›  专栏  ›  技术社区  ›  Larry Lustig

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

  •  3
  • Larry Lustig  · 技术社区  · 15 年前

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

    在一天的大部分时间之后,我相信我已经让虚拟树视图组件(功能强大但复杂)以一种简单的双表数据感知方式工作。

    现在,我尝试简单地选择顶级节点的1512th(例如)。除了获取第一个顶级节点,然后在一个循环中调用GetNextSibling1511之外,我看不到任何方法可以做到这一点。

    这似乎不必要。有更简单的方法吗?

    更新

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

    当用户以对话框模式启动表单,并且已选择数据库记录时,我必须在用户看到表单之前将树前进到该节点。这是一个问题,因为如果记录接近树的末尾,当我从第一个节点遍历树时,可能需要10秒钟。每次我可以getNextSibling()时,都会初始化一个节点,即使绝大多数节点没有显示给用户。我希望将这些节点的初始化推迟到用户可以看到它们的时间点。

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

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

    或者,我可以对树视图进行一些状态设置,这样在遍历网格时中间节点就不会被初始化。我尝试过开始/结束更新,但这似乎不起作用。

    3 回复  |  直到 15 年前
        1
  •  3
  •   Ondrej Kelle    15 年前

    要在不初始化节点的情况下获取该节点的同级,只需使用 NextSibling 指针(参见声明 TVirtualNode )

        2
  •  3
  •   Rob Kennedy    15 年前

    树控件的结构与在计算机科学课上学习的经典树一样。从树根到1512th子代的唯一方法是逐个遍历链接。无论您是自己做的,还是使用树控件的方法,都必须这样做。我看不到控件本身提供的任何内容,因此您可以使用此函数:

    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 指针并将所有顶级值存储在其中,这样您就可以从中读取1512th值。树控件本身不需要这样的数据结构,因此它不维护这样的数据结构。

    你也可以重新考虑 需要这样的数据结构。你…吗 真正地 需要按这样的索引访问节点吗?或者可以维持 虚拟节点 指针,因此它相对于树中其余节点的位置不再重要(这意味着您可以对它们进行排序,而不会丢失对所需节点的引用)?

        3
  •  0
  •   mghie    15 年前

    您在更新中写道:

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

    这里有一个区别,因为垂直滚动更改了显示在客户机位置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;