3

[这是之前发布的问题的更新版本,之前的标题是Selecting node by index in Delphi's Virtual Treeview。]

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

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

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

更新

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

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

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

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

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

4

3 回答 3

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指针,因此它相对于树中其余节点的位置不再重要(例如,这意味着您可以对它们进行排序而不会丢失对所需节点的引用)?

于 2010-02-07T00:56:42.560 回答
3

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

于 2010-02-12T13:46:07.487 回答
0

您在更新中写道:

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

这里有区别,因为垂直滚动会改变在客户端位置 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;
于 2010-02-11T16:32:08.210 回答