我应该如何更新Virtual TreeView中的节点?

问题描述 投票:0回答:1

我正在使用Delphi XE3和Virtual TreeView。

我想使用Virtual TreeView来实现一棵树,当单击“开始”按钮时,该程序将递归搜索驱动器下的所有文件和文件夹,然后将它们一个一个地添加到树中,就像Windows资源管理器一样。此外,应该使用一个静态文本,像这样一个数字,指示文件夹下文件和子文件夹的数量:

VirtualTreeView - different color of text in the same node

在实施过程中,我有时发现号码未正确更新。

因此,我认为,只要文件/子文件夹的数量发生更改,就可以通过以下方式刷新节点:

  1. 调用tvItems.Change(PNode)来更新节点。

  2. 调用tvItems.InvalidateNode(PNode)。

  3. 调用tvItems.RepaintNode(PNode)。

  4. 调用tvItems.UpdateAction。

但是,1是无法调用的受保护方法。 2和3都可以,但是不知道哪个更新更好。 4没有记录,也不知道如何称呼它。

delphi virtualtreeview
1个回答
0
投票

基本思想是,如果幕后的事物发生变化,则需要对树进行重新粉刷。这意味着树下次绘制自身时,将使用新的基础值。

如果您的树坐在屏幕上:

enter image description here

您可以简单地致电:

tvItems.Invalidate;

这将告诉Windows entire树现在为“ invalid”,需要重新绘制。我可以代表这个“ invalid”区域,该区域将在树下次绘制自身时进行更新:

enter image description here

这很好,正确,并且可以正常运行。

性能改进

很多时候,强制entire树重新绘制自身的[[all完全合理。

但是也可以开始进行优化。如果您知道Windows控件的仅某个区域是

“ invalid”,则可以使该部分无效。

要执行此操作的Windows功能是InvalidateRect

InvalidateRect(tvItems.Handle, Rect(13, 18, 30, 38), True);

这将使在13,18处的30x38正方形无效:

enter image description here

事实上,所有TWinControl.Invalidate所做的只是转过头并调用Windows

InvalidateRect

函数://true means to also trigger an erase of the background InvalidateRect(Self.Handle, Self.BoundsRect, True);
使这样一个奇怪的矩形无效可能没有太多用处。但是您可能会想像其他要失效的矩形。

无效节点

Windows无法实现,但是您的控件代表一棵树,以及树和节点。有时候您可能想取消

“ node”:

的矩形enter image description here

而不是您必须弄清楚节点的坐标和大小,

TVirtualTree

已经为您提供了一个方便的方法来invalidate节点:function InvalidateNode(Node: PVirtualNode): TRect; virtual; // Initiates repaint of the given node and returns the just invalidated rectangle.
so:

tvItems.InvalidateNode(someNode);

它还提供了使节点

及其所有子节点]无效的方法:

procedure TBaseVirtualTree.InvalidateChildren(Node: PVirtualNode; Recursive: Boolean); // Invalidates Node and its immediate children. // If Recursive is True then all grandchildren are invalidated as well. // The node itself is initialized if necessary and its child nodes are created (and initialized too if // Recursive is True).
这在您的树

个孩子时很有用:
enter image description here

您可以使父节点和现在需要使用新编号更新的所有子节点失效:

tvItems.InvalidateChildren(someNode, True);

和其他辅助方法

虚拟树还有其他有用的方法,这些方法:

    获得某个有趣的矩形以使之无效
  • 调用Windows.InvalidateRect
  • 即:

    InvalidateToBottom(Node: PVirtualNode);从给定节点处开始重新绘制工作区。如果此节点不可见或尚未初始化,则什么都不会发生。
  • [TBaseVirtualTree.InvalidateColumn(Column: TColumnIndex);使列的客户区部分无效。
  • 无效与重绘

您的另一个问题是关于以下两者之间的区别的困惑:

    无效
  • 重新粉刷
  • 当您

    无效

矩形(例如,整个表单,整个控件或某个较小的矩形,例如节点,节点及其子元素或列)时,您正在告诉Windows它需要询问该控件画自我。那是:
屏幕上的像素现在为

无效

,必须重新粉刷
这将在下一次要求树进行自我绘制时发生。 Windows是基于消息的。在您的应用程序运行时,它会处理消息,包括

WM_PAINT消息。当VirtualTree收到WM_PAINT消息时,它会绘制要求重新绘制的部分。

这意味着要进行所有绘画,必须处理消息-即,必须让程序进入

“ idle”

如果您坐在一个繁忙的循环中,请不要让代码退出:

procedure TForm1.Button1Click(Sender: TObject); begin while (true) do begin tvItems.Invalidate; Sleep(100); end; end;

循环永远不会结束,树也永远不会有机会

实际上

绘制自身。
Delphi的可怕重绘技巧

Delphi有一个可怕的骇客,它

forces]要绘制的控件。

    假装是Windows要求控件自己绘制的Windows

  • 然后直接跳到控件的绘制例程
  • 这意味着该控件将自动绘制自身,即使它没有从Windows接收到WM_PAINT消息-它也只是进行绘制。
  • procedure TForm1.Button1Click(Sender: TObject); begin while (true) do begin tvItems.Repaint; //force the control to repaint itself Sleep(100); end; end;

    这是一个丑陋的hack,因为:

      在第一种情况下,我们的代码未处理Windows消息
    • 在修改后的情况下,我们仍然没有做正确的事,而是试图使用锤子来拧螺丝
    • 在这些情况下,正确的解决方案是有一个后台线程。并让后台线程向主应用程序发出信号,表明它需要使树无效。然后,主程序将收到WM_PAINT消息,并照常进行绘制。
    © www.soinside.com 2019 - 2024. All rights reserved.