Question

My application will be looping thru the Virtual Nodes and check their data. I am using another form to do this than the form containing the VirtualStringTree. (I got my reasons ;) )

My question is: How can I pass those nodes + their data to a function in my other form, which will then be able to loop thru the nodes (I know how to loop, I just need the nodes to be available in my other form).

Also, please note that the form that contains the VirtualStringTree is destroyed once the Processing form is being shown!

How could I do that? I am thinking about creating a dynamic VirtualStringTree, and somehow pass the nodes from 1 tree to the other, but I would ask here first for any better solutions. :)

Thanks, Jeff.

Was it helpful?

Solution

I've mentioned before that you're doing things wrong, and now you'll see why.

You're using the tree control to store your data. It's meant for displaying the data. You should have a separate data structure whose only job is to store your data. It will probably be a tree, but not a tree control. It's this tree data structure that you'd give to the processing form since it has no need for displaying nodes.

When you want to display your data, you find out how many nodes there are in the first level of your tree, and then you set your tree control's RootNodeCount property to that number. The control will allocate that many nodes — don't call AddNewNode for bulk operations like populating the control. When the tree is going to display a node on the screen that it hasn't displayed before, it will fire the OnInitNode event handler. That's where you initialize the node and associate it with a value in your data structure. The tree control will tell you which node it's initializing — both via a PVirtualNode pointer and via an index that tells which node it is, relative to its parent. When you're initializing the node, you tell the tree whether the node has any children. You don't need to tell it how many children yet; if the control wants to know, it will ask you with another event.

Now that you've separated your data from the mere presentation of your data, you no longer have to worry about the lifetime of the presenter differing from the lifetime of your data. The processing form can process the data without regard for whether the tree-view control still exists because the tree-view control never owned the data in the first place.

See also:


You've said you only have one level of nodes. That's OK. A tree with only one level is more commonly known as a list. There are several things you can use to keep track of a list. The simplest is an array. You can also use a TList, or you can build your own linked list. This example will use an array because I want to focus on the tree control.

Let's suppose the data for each node is represented by a record, TData, so you have an array of those:

var
  Data: array of TData;

After you've loaded the array with information, from whatever source you have, you're ready to populate the tree control. That's as easy as two lines of code (one, if the control started out empty):

Tree.ResetNode(nil); // remove all existing nodes from tree
Tree.RootNodeCount := Length(Data); // allocate new nodes for all data

As the tree determines that it needs more information about any of those nodes, it will start by triggering the OnInitNode event. There's no much you need to do for that event since the node's Index field will be sufficient for us to find the TData record that corresponds to any given tree node.

procedure TJeffForm.TreeInitNode(Sender: TBaseVirtualTree;
    ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
begin
  Assert(Node.Index < Length(Data), 'More nodes than data elements!?');
  InitialStates := []; // If the node had children, or if it should be
                       // initially disabled, you'd set that here.
end;

When the tree wants to paint itself, it will ask you what text to display for each visible node by triggering the OnGetText event. The Index field of the node tells you which item it is, relative to its parent. (Since you just have a list, that index corresponds to the index in your list.)

procedure TJeffForm.TreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
    Column: TColumnIndex; TextType: TVSTTextType; var CellText: UnicodeString);
begin
  if TextType = ttStatic then
    exit;
  case Column of
    NoColumn,
    0: CellText := Data[Node.Index].Name;
    1: CellText := 'Second column';
    else
      Assert(False, 'Requested text for unexpected column');
  end;
end;

Above I've assumed that TData has a string field named Name and that that's what we should display in the main column. If the tree asks for text for anything past the second column, we'll get an assertion failure, signalling that we're not ready to release the product yet.

Notice how we use the node index to look into the entirely separate array data structure. We could destroy the tree control entirely, and the data would still exist. When your processing form needs to process data, give it the Data array, not the tree control.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top