Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/.htaccess/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何为TTreeNodes实现GetLastNode?_Delphi - Fatal编程技术网

Delphi 如何为TTreeNodes实现GetLastNode?

Delphi 如何为TTreeNodes实现GetLastNode?,delphi,Delphi,当我需要在TTreeView中查找第一个节点时,我调用TTreeNodes.GetFirstNode。但是,有时我需要定位树中的最后一个节点,并且没有相应的t reenodes.GetLastNode函数 我不想使用Items[Count-1],因为这样会导致整个树都被遍历Result:=Result.GetNext。当然,这只在树视图有很多节点时才重要。我完全理解虚拟容器控件的优点,但我现在还不打算切换到虚拟树视图 到目前为止,我已经得出以下结论: function TTreeNodes.Ge

当我需要在
TTreeView
中查找第一个节点时,我调用
TTreeNodes.GetFirstNode
。但是,有时我需要定位树中的最后一个节点,并且没有相应的
t reenodes.GetLastNode
函数

我不想使用
Items[Count-1]
,因为这样会导致整个树都被遍历
Result:=Result.GetNext
。当然,这只在树视图有很多节点时才重要。我完全理解虚拟容器控件的优点,但我现在还不打算切换到虚拟树视图

到目前为止,我已经得出以下结论:

function TTreeNodes.GetLastNode: TTreeNode;
var
  Node: TTreeNode;
begin
  Result := GetFirstNode;
  if not Assigned(Result) then begin
    exit;
  end;
  while True do begin
    Node := Result.GetNextSibling;
    if not Assigned(Node) then begin
      Node := Result.GetFirstChild;
      if not Assigned(Node) then begin
        exit;
      end;
    end;
    Result := Node;
  end;
end;
任何人都可以:

  • 在我的逻辑中发现一个缺陷
  • 提出改进建议
  • 编辑1


    我不愿意保留自己的节点缓存。我一直这样做,直到最近,但已经发现一些难以跟踪非常间歇性的AV,我相信这一定是由于我的缓存不同步。很明显,一个解决方案是让缓存同步代码正常工作,但我不喜欢缓存,因为当您出错时,很难跟踪错误。

    我以前使用的方法是使用列表维护TList。添加OnAddition事件和列表。在OnDeletion事件上删除(OnRemove?)。您可以立即访问List.Count-1(或任何您需要的内容)


    后期编辑-我不得不说,虽然这很好,但我长大了,转到了虚拟树视图:-)

    如果我要实现它,这可能是我的第一稿

    function TTreeNodes.GetLastNode: TTreeNode;
    var
      Node: TTreeNode;
      function GetLastSibling(aNode : TTreeNode) : TTreeNode;
      begin
        if not Assigned(aNode) then
          EXIT(nil);
        repeat
          Result := aNode;
          aNode := Result.GetNextSibling;
        until not Assigned(aNode) ;
      end;
    begin
      Node := GetFirstNode;
      if not Assigned(Node) then begin
        exit;
      end;
    
      repeat
        Result := GetLastSibling(Node);
        Node := Result.GetFirstChild;
      until not Assigned(Node);
    end;
    
    我觉得这更具可读性。不过可能会稍微慢一点


    我不确定这种方法是否会比items[Count-1]快,在某些情况下,它可能会慢一些,因为TTreeNodes实际上缓存了通过items属性访问的最后一个节点。

    虽然我不是一个非退出纯粹主义者,但我认为当它在保持可读性不变的情况下无需退出即可实现时,人们可能更喜欢这种选择

    这是完全相同的代码,因为我不认为您可以以任何其他方式(更快)到达结束节点,但不需要退出,也不需要稍微紧凑一些:

    function TTreeNodes.GetLastNode: TTreeNode;
    var
      Node: TTreeNode;
    begin
      Node := GetFirstNode;
      Result := Node;
      if Result <> nil then
        repeat
          Result := Node;
          if Node <> nil then
            Node := Result.GetNextSibling;
          if Node = nil then
            Node := Result.GetFirstChild;
        until Node = nil;
    end;
    
    函数TTreeNodes.GetLastNode:TTreeNode;
    变量
    节点:TTreeNode;
    开始
    节点:=GetFirstNode;
    结果:=节点;
    如果结果为零,则
    重复
    结果:=节点;
    如果节点为nil,则
    节点:=Result.GetNextSibling;
    如果Node=nil,则
    节点:=Result.GetFirstChild;
    直到Node=nil;
    结束;
    
    非常感谢您的建议。我已经考虑过这一点(对不起,应该提到这一点),不愿意维护自己的节点缓存。事实上,我一直在这样做,直到最近才发现难以追踪的问题,我认为这是由于我的缓存不同步造成的。它不是节点缓存,只是一个尾部条目。每当在根节点或当前尾部节点附加节点时,您都会用新的尾部替换旧的尾部。@RRUZ:如果最后一个可见的节点是子节点呢?@NGLN我正要发表同样的评论,我想我可能可以在一小时内用虚拟树视图替换您的树视图,这样会更快,看起来更好,我不必担心让treeNodes与我的模型数据保持同步,事实上,这是许多细微错误的根源,因此,实际上,您已经在缓存您的模型,在您的treeview中,或者您正在使用您的treeview作为模型的关键元素。两者都有问题。(比如说线程。)@warren线程呢?另外,虚拟树视图不是我不喜欢的本机控件。+1我赞成对GetLastSibling进行分解,这使外部循环更具可读性。关于
    t绿色节点中的缓存
    ,只有当访问的最后一个节点是最后一个节点或前一个节点时,才会有帮助。在我的使用场景中不太可能出现这种情况。如果你打算使用迭代方法,我认为这接近最优。我刚刚又检查了一次,当树为空时,在
    Result.getFirstChild
    处会出现访问冲突。