VirtualTreeview Nodes, pass them to another form?
Asked Answered
T

1

1

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.

Tapir answered 4/2, 2011 at 20:13 Comment(0)
O
13

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.

Overtone answered 4/2, 2011 at 20:47 Comment(20)
My tree only has 1 level of nodes. I am not really sure of how to do what you just said. Would you mind giving me an pseudo example? I am not really that deep into Delphi, I usually just work with visual controls at the moment.Tapir
@Tapir do you mean there is no hierarchy? Everything is at the same level? If that's so, then just store your nodes in a list!Dosser
There is no hierachy, no. But the note's Data Record contains objects like TStringlists and many variables, so if you mean store them in a TStringlist, I dont think thats possible :PTapir
@David What other list could I store them in that is "passable"?Tapir
@Tapir how about a TObjectList?Dosser
@Rob Thanks! @David I never messed with that before.. Currently waiting to see Robs example :)Tapir
@Tapir If you are currently using records, since that is what virtual tree view wants, then the most natural container for them is Generics.Collections.TList<TData> where TData is your record. This is just an array exactly as in Rob's excellent example, but it presents an easier interface to you. To add a new item you call Add, to delete one you call Delete, and so on.Dosser
@Rob Thanks for the example! How will I add new objects to the Array though?Tapir
@Tapir SetLength(Data, Count); for i := 0 to Count-1 do Data[i] := ReadItem;Dosser
@Tapir or with Generics.Collections.TList<TData> it would go for i := 0 to Count-1 do Data.Add(ReadItem);Dosser
@David I havent used arrays as much as I should have, that I can see. :P Whats the ReadItem, if I may ask? (I have a feeling I am sounding pretty dumb right now :P )Tapir
@jeff ReadItem is your code! It's whatever populates your data. You have to try and see the patterns and the techniques rather than the detailed specifics of your particular problem.Dosser
@David I am trying to do that, however at the moment, I really just need a solution I can implement. I will try do get these methods to work however. Thanks!Tapir
@David - Ah, I guess the ReadItem would be a function that returns my data, correct?Tapir
@jeff by george I think you've got it!!Dosser
Virtual Treeview by being a data-driven control is less likely to be abused this way than, say, a regular treeview control, and yet, people still use it as their model data-holder.Margarethe
@Warren I use VT solely for it's speed and vast ammount of options. :)Tapir
@Tapir Warren understands that. He's not saying that you shouldn't use it. He's saying that you shouldn't mis-use it. Anyway, I think Warren really means "event-driven" rather than "data-driven". At least that's how I'd describe it.Dosser
@David what is ment by "mis-use" it? If its the easiest thing to do, why not do it? :PTapir
@Tapir If you want to do the same thing in two different places of your code, it's easiest just to copy the code in one place to the other place. But is the best thing to do? Does it not occur to you that the reason you asked this question is because you've been misusing VTV? It's an easy trap to fall into, everyone does so. But you now have the opportunity to learn something.Dosser

© 2022 - 2024 — McMap. All rights reserved.