I am using Delphi XE3 and Virtual TreeView.
I want to use Virtual TreeView to implement a tree, when clicking "Start" button, the program will search all files and folders under a drive recursively, then add them one by one to the tree, just like Windows Explorer. Moreover, there should be a number indicating number of files and subfolders under a folder, using static text like this:
VirtualTreeView - different color of text in the same node
In my impelemention, I find sometimes the number is not updated correctly.
Therefore, I think the following way to refresh the node, whenever the number of files/subfolders are changed:
Call tvItems.Change(PNode) to update the node.
Call tvItems.InvalidateNode(PNode).
Call tvItems.RepaintNode(PNode).
Call tvItems.UpdateAction.
However, 1 is protected method that cannot be invoked. 2 and 3 are both OK but don't know which is better for updating. 4 is not documented and don't know how to call it.
The fundamental idea is that if something behind the scenes changes, the tree need to repainted. This means that the next time the tree draws itself, it will use the new underlying values.
If your tree is sitting there on screen:
You can simply call:
This tells Windows that the entire tree is now "invalid" and needs to be redrawn. I can represent this "invalid" region that will be updated the next time the tree paints itself:
And that's fine, correct, and will work perfectly adequately.
Performance improvements
A lot of times it's perfectly reasonable to force the entire tree to repaint all of itself.
But it's also possible to begin to optimize things. If you know that only a certain region of a Windows control is "invalid", then you could just invalidate that portion.
The Windows function to do that is InvalidateRect:
That will invalidate a 30x38 square at 13,18:
In fact all
TWinControl.Invalidateis doing is turning around and calling the Windows InvalidateRect function:There may not be much use for such a strange rectangle to be invalidated. But you can probably imagine other rectangles you'd like to be invalidated.
Invalidating nodes
Windows doesn't realize it, but your control represents a tree, and the tree and nodes. And there might be times when you want to invalidate the rectangle of a "node":
Rather than you having to figure out the coordinates and size of a node, the TVirtualTree already provides you a handy method to invalidate a node:
so:
It also provides a method to invalidate a node and all its children:
This is useful when your tree has children:
You can invalidate the parent node and all the children that now need to be updated with the new numbers:
And other helper methods
The Virtual Tree has other helpful methods that:
That is:
InvalidateToBottom(Node: PVirtualNode);Initiates repaint of client area starting at given node. If this node is not visible or not yet initialized then nothing happens.TBaseVirtualTree.InvalidateColumn(Column: TColumnIndex);Invalidates the client area part of a column.Invalidating vs Repaint
Your other question is in regards to the confusion over the difference between:
When you invalidate a rectangle (e.g. an entire form, an entire control, or some smaller rectangle such as a node, node and its children, or a column) you are telling Windows that it needs to ask the control to paint self. That is:
This will happen the next time the tree is asked to paint itself. Windows is message-based. And as your application runs, it processes messages, including a
WM_PAINTmessage. When theVirtualTreegets aWM_PAINTmessage, it paints the portions it was asked to repaint.This means that for any and all painting to happen, you have to be processing messages - i.e. you have to let your program go "idle".
If you sit there is a busy loop, never letting your code exit:
The loop never ends, and the tree is never given a chance to actually paint itself.
Delphi's horrible Repaint hack
Delphi has a horrible hack that forces a control to be painted.
This means that the control will paint itself, even though it's not received a
WM_PAINTmessage from Windows - it just does the scribbling.It's an ugly hack because:
The correct solution in these cases is to have a background thread. And have the background thread signal the main application that it needs to invalidate the tree. The main program will then receive a
WM_PAINTmessage and draw itself as normal.