5

Lets say i'm using a VirtualStringTree in virtual mode.

I want to indicate that the tree has some nodes:

VirtualStringTree1.RootNodeCount := 999983;

And since this tree is in virtual mode; all the data management happens in the application and on demand.

Now sometime later i want to mark a node as selected. Something has happened in my underlying TList (at index 797977) that would cause one of the nodes (if it's visible) to need to change its selected status.

Since everything in a virtual tree is based on Node.Index, ideally there would be a way to indicate that an index is selected:

VirtualStringTree1.RootNodeCount := 999983;
VirtualStringTree1.Selected[797977] := True; //indicate that node at index 797977 is now selected

But the tree doesn't have an overload to set selection by index.

The only method to alter a node's selected state is to:

  • a) have a PVirtualNode
  • b) pass that to VirtualStringTree1.Selected[node] := True;

How can i mark a node as selected, when i don't have the node?

Windows ListView solution

A Windows ListView control in virtual mode solves it pretty simply.

  • everything is based equivalently on Node.Index
  • and the control uses callbacks to ask the application on demand for display information

So you are given a LVN_GETDISPINFO callback. And that is when you populate the callback structure, and give the tree the information it is asking for on demand:

  • Text
  • State (e.g. Selected)
  • ImageIndex
  • Indent

And so the way i would mark an item in the tree as Selected is i would call:

ListView1.Invalidate;

If the item #797,977 is currently on screen: it will redraw during the next paint cycle as selected.

What's the Virtual Treeivew equivalent of marking a node as Selected when all you have is a virtual-mode index?

Hack

function GetNodeByIndex(Tree: TVirtualStringTree; Index: Integer): PVirtualNode;
var
   node: PVirtualNode;
begin
    Result := nil;

    node := Tree.GetFirstChildNoInit(nil);
    while Assigned(node) do
    begin
       if node.Index = Index then
       begin
          Result := node; 
          Exit;
       end;
       node := Tree.GetNextNoInit(node);
    end;
end;

But traversing a linked list of 797,977 items in order to get node n is very bad.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • 2
    You have the data in a TList. When you want a specific item in the list to be selected, just call vtreeview.Invalidate. GetText gives you node and node.index. If that matches your TList selection, just mark that node as selected. – LU RD Nov 25 '19 at 21:52
  • @LURD I thought that was wonderfully, amazingly, sneaky - until i realized that the selection would not be valid unless the user first made that node visible. Until then the `getFirstSelected` would be incorrect. I have to **set** the node selected. – Ian Boyd Nov 25 '19 at 22:11
  • 2
    I see the predicament now. When RootNodeCount is set, use the OnInitNode event to store the node pointer in your TList. Would that be possible? (Or store it in another crossref list). – LU RD Nov 25 '19 at 23:09
  • 1
    You can make (and maintain) dynamic array of nodes. There is no other way than the mentioned traverse. – TLama Nov 26 '19 at 05:23

0 Answers0