出于性能原因,我需要浏览树形视图的项目,而不使用递归。
TTreeview提供GlobalCount和ItemByGlobalIndex方法,但它只返回可见项
我搜索了根类代码,但没有找到所有节点的私有列表,FGlobalItems似乎只包含需要呈现的项
有没有办法按顺序浏览树视图的所有项目(包括不可见和折叠的节点)?
此问题适用于Delphi XE3/FM2
谢谢,
[编辑2月3日]
我接受了默认的答案(不可能开箱即用),尽管我正在寻找一种方法来修补火猴TreeView在这方面的问题。
经过进一步分析,我发现FGlobalItems列表只包含展开的项,并且在TCustomTreeView.UpdateGlobalIndex.方法中进行维护;
注解FMX.TreeView的第924行(如果AItem.IsExpanded Then...)导致构建节点的完整索引,并允许使用ItemByGlobalIndex()按顺序浏览所有节点,但可能会导致其他性能问题和错误...
如果没有更多的线索,我将保留我的递归代码。
7条答案
按热度按时间kknvjkwl1#
下面是我以非递归方式遍历TreeView的函数。如果您有一个节点,并且希望移动到下一个或上一个节点,而不必遍历整个树,则使用起来很简单。
GetNextItem的功能是查看它的第一个子项,或者如果没有子项,则查看它的父项以查找其后面的下一个子项(并在必要时进一步查看父项)。
GetPrevItem查看父项以查找前一项,并使用GetLastChild查找该项的最后一个子项(它使用递归BTW)。
注意,编写的代码只遍历展开的节点,但可以很容易地修改为遍历所有节点(只需删除对IsExpanded的引用)。
ulydmbyx2#
这个问题实质上是问如何在没有递归的情况下遍历树。遍历树的方法有很多种;树恰好用可视控件中的节点表示,这一点无关紧要。
对于某些算法来说,更容易想到递归形式的遍历。这样,通过将当前活动的节点作为堆栈上的参数保留,您可以让编程语言跟踪您在树中的位置。如果您不想使用递归,那么您只需自己跟踪进度。常见的工具包括堆栈和队列。
预序遍历意味着当您访问一个节点时,您先对该节点的数据执行操作,然后再对该节点的子节点执行操作。它对应于从上到下访问树视图控件的每个节点。您可以像这样使用堆栈实现它:
以相反的顺序将孩子们推到堆栈上,这样他们就会按所需的顺序弹出。
在该代码中,
Action
代表您需要对每个节点执行的任何任务。您可以将其用作代码中指定的外部函数,也可以编写包含特定于任务的代码的专用版本的PreorderVisit
。不过,TTreeView实际上并不表示树。它真的是一片“森林”(树木的集合)。这是因为没有代表根的单个节点。不过,您可以轻松地使用上面的函数来处理树中的所有节点:
另一种利用TTreeView的特定结构执行预排序遍历的方法是使用每个节点的内置
GetNext
方法:似乎没有办法获得火猴树视图的隐藏节点。通过迭代内部树数据结构而不是尝试从图形用户界面中提取信息,您可能会得到更好的结果。
ztigrdn83#
在XE8中,这适用于我:
kgsdhlau4#
Item.ParentItem
也可以为零!这就是我将行Parent := Item.ParentItem
替换为以下行的原因:修正后的完整函数
GetNextItem
:在Delphi 10.3.2上测试
zpf6vheq5#
我会添加一个功能,将部分文本搜索到树视图中,从树视图(TV)上放置的TEdit(搜索)中搜索。(特别感谢本答案所基于的前一篇帖子)
使用Enter键开始搜索,按F3键继续搜索,可以很好地工作。
6ju8rftf6#
我已经为我的项目做了这个功能,又快又简单,你可以试一下
v6ylcynt7#
我利用Delphi中的类帮助器和匿名过程来循环访问TreeView中的项。这可以很容易地扩展以构建索引列表。
我的类帮助器是这样的:
我是这样使用它的:
上面的示例来自我的实际项目,您将在匿名过程中使用您自己的逻辑,但真正整洁的部分是最后的
TreeView1.Selected := TN;
,因为即使TN是不可见的项,TreeView也会选择它并展开其所有父节点。现在,你说你想要避免递归,但实际上你想要避免递归递归。因为您必须首先构建索引,在构建索引时,可以在其中使用一次递归。遵循相同的方法,只需继续并向您的类帮助器添加一个新方法:
并像这样使用它:
干杯!