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

时间:2010-02-06 23:04:56

标签: delphi virtualtreeview

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

经过一天的大部分时间,我相信我已经拥有了一个简单的两表数据感知方式的Virtual Treeview组件(功能强大但复杂)。

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

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

更新

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

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

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

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

或者,也许我可以对树视图进行一些状态设置,当我遍历网格时,这将保留中间节点未初始化。我已经尝试过开始/结束更新,但这似乎没有做到这一点。

3 个答案:

答案 0 :(得分:3)

树形控件的结构就像您在计算机科学课程中学到的经典树木一样。从树的根到第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指针,因此它相对于树中其余节点的位置不再重要(例如,您可以对它们进行排序而不会丢失对所需节点的引用)?

答案 1 :(得分:3)

要获取节点的兄弟而不初始化它,只需使用NextSibling指针(参见TVirtualNode的声明)。

答案 2 :(得分:0)

您在更新中写道:

  

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

这里有区别,因为垂直滚动会更改在客户端位置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;