3

So I'm working with Delphi 2010 and it's been a while since I began using the VirtualTreeView (precisely VirtualStringTree)..and it seems that I'm doing something in a wrong way..since things aren't working as I'm expecting.

I'm trying to populate my VST with nodes that point to files/subfolders descriptions stored in records of data and generated by scanning a path given by the user..(more details are shown in the following image)

enter image description here

As it shown nodes are shown in weird way..& no matter what I do nodes data aren't initialized properly..nodes captions for column "File" is the only thing that work well.

& here's the code I use:

1- Node data declaration:

type  nodeData=record
            Text, Size, Path:String;
            ImageIndex:Integer;
           end;

  PNodeData=^nodeData;


var hashmap:TDictionary<String, PVirtualNode>; // hashmap-> to store parent nodes (Folder)
    filesList:TDictionary<Integer,nodeData>; // fileList to store records of data

2-Methods

a) scan a path given by the user

procedure AddAllFilesInDir(const Dir:String);
begin
// it scans the path 'Dir' and extract the "name & size" of each file/folder found in this dir
end;

b) generate the filesList tdictionary... & images are stored in "treeImageLIst" which is linked to the treeview.images property

procedure addFileToList(Name, Size:String);
var d:nodeData;
    parent:String;
    SHFileInfo :TSHFileINfo;
    Icon:TIcon;
begin
parent:=ExtractFileDir(Name);

//Get The Icon That Represents The File/Folder
SHGetFileInfo(PChar(Name), 0, SHFileInfo,  SizeOf(SHFileInfo),
                SHGFI_ICON or SHGFI_SMALLICON );


Icon := TIcon.Create;
Icon.Handle := SHFileInfo.hIcon;

// set The Name, Size, Path
d.Name:=ExtractFileName(Name);
d.Size:=Size;
d.Path:=parent;

// set the ImageIndex
d.ImageIndex:=Form1.treeImageList.AddIcon(Icon);

// add the node to fileList
filesList.Add(filesList.Count, d);

// Destroy the Icon
DestroyIcon(SHFileInfo.hIcon);
Icon.Free;
end;

c) create the tree "theTree"

procedure createTree();
var theNode, Node:PVirtualNode;
    d:PNodeData;
    parent:String;
    nData:nodeData;
    i:integer;
begin

for i := 0 to filesList.Count - 1 do
begin

 nData:=filesList.Items[i];  parent:=nData.Path;

 if(hashmap.ContainsKey(parent))then theNode:=hashmap.Items[parent]
 else theNode:=nil;

 Node:=Form1.theTree.AddChild(theNode);

 // add a checkbox and make it checked
 Node.CheckType:=ctCheckBox;
 Node.CheckState:=csCheckedNormal;

 // get the newly created node data
 d:=Form1.theTree.GetNodeData(Node);

 // assign a data  to the newly created node
 d^:=nData;

 // add the node to hashmap if it's a new folder node
 if((ExtractFileExt(nData.Text)='')and(not hashmap.ContainsKey(nData.Path+'\'+nData.Text)))
 then hashmap.Add(nData.Path+'\'+nData.Text, Node);

end;

 Form1.theTree.Expanded[Form1.theTree.TopNode]:=True;
end;

d) the treeview events

procedure TForm1.theTreeFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var d:PNodeData;
begin
  d := Sender.GetNodeData(Node);
  Finalize(d^);
end;


procedure TForm1.theTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
var d:PNodeData;
begin
 d:=Sender.GetNodeData(Node);

 case Column of
  0:CellText:=d^.Text;
  1:CellText:=d^.Size;
 end;
end;


procedure TForm1.theTreeGetImageIndex(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  var Ghosted: Boolean; var ImageIndex: Integer);

var d:PNodeData;
begin
d:=Sender.GetNodeData(Node);

if(Kind in [ikNormal, ikSelected])then
begin
 if(Column=0)then ImageIndex:=d^.ImageIndex;
end;

end;

I'm really frustrated right now..and I don't know why nodes arent created properly..although I tested the records data & they are created well but when I test the onNodeClick event I found that the data record pointed by the Node returns only the first field ..while the other fields are either empty or they generate an Access violation exception.

DZkid
  • 149
  • 1
  • 3
  • 9
  • 4
    You don't really "populate" a virtual control. A virtual control doesn't hold data for its nodes, it requests it whenever it needs to show a node's information. All you really need to get a virtual tree going is to set its root node count. An example of using a TVirtualStringTree that is getting its data from a separate data strcutre can be found in [my answer on how to have multiple nodes display the same data](http://stackoverflow.com/questions/5602090/is-it-possible-to-display-one-object-multiple-times-in-a-virtualstringtree/5604337#5604337) – Marjan Venema Apr 02 '13 at 17:36
  • @Marjan: yes but my data is hold in the "fileslist" tdictionary..I did the following **theTree.NodeDataSize:=sizeof(PNodeData)*5**; ..and things works great right now...any explanation about it? I'll try also to view the link you provided. – DZkid Apr 02 '13 at 17:53
  • 5
    If multiplying the size by an (seemingly) arbitrary number of 5 solved your issue, then I'd double and triple check your knowledge of what a pointer to a datatype is and how SizeOf works. SizeOf(SomePointerType) returns the size of a Pointer, not the thing it points to. So your multiplication by 5 making everything work should tell you that you are now telling VST to reserve enough memory to hold your records. Which again tells you should use the size of your Node's record, not the size of pointer to your record type. iow: `theTree.NodeDataSize:=SizeOf(nodeData)` (notice the absence of "P") – Marjan Venema Apr 02 '13 at 18:00
  • I was going to post about this issue..should the sizeOf() be SizeOF(Pointer) or SizeOf(RecodData)? Cause in a tutorial I've read I found that it's SizeOf(Pointer) (but the data was only a record of integer)..& that's sound a bit wrong to me. – DZkid Apr 02 '13 at 18:06
  • 1
    If the record only holds an integer, it has the same size as a pointer (at least in Win32). – Uwe Raabe Apr 02 '13 at 18:07
  • 2
    The tutorial you found was almost certainly wrong. Sometimes, people are confused about exactly where the node's data resides. If they think they have to allocate it themselves, then they usually do so, and then store a pointer to that data in the `GetNodeData` area. If you have a record with your node data, then you should *always* say `NodeDataSize := SizeOf(TNodeData)`. – Rob Kennedy Apr 02 '13 at 18:16
  • @Uwe: I guess so..following a misleading tutorial can lead to undesirable and awful results. Thanks everyone for your help. – DZkid Apr 02 '13 at 18:18
  • 1
    You should not be using `AddChild`. It's for compatibility when porting away from `TTreeView`. You should be using `OnInitNode` or `OnInitChildren` instead, and use it to connect the tree to your virtual data in the `TDictionary`, and then retrieve the data when the `OnGetText` event is triggered. You're trying to use it like a `TTreeView` that's not in virtual mode instead. There are demos available in the [repository at Google Code](http://code.google.com/p/virtual-treeview/). – Ken White Apr 02 '13 at 20:03
  • 1
    I even went to the trouble of looking at the source in the Demos. The `Minimal` project has a very simple example of using it properly [here in Main.pas](http://code.google.com/p/virtual-treeview/source/browse/trunk/Demos/Minimal/Main.pas) – Ken White Apr 02 '13 at 20:14
  • @Ken: I'm using the VST at this moment just accomplish this [Task](http://stackoverflow.com/questions/15589571) in the very near possible time..and the **AddChild** just worked fine for me. But I'll sure have to implement my code again when time is available to take advantage of the VST virtuality..and as for your "**trouble for looking at the source..**"..don't worry it's very appriciated and thank you everyone again. – DZkid Apr 02 '13 at 21:45
  • @DZKid: Your code doesn't work properly (in fact, it's nowhere near working code), and I've posted a link to a simple example of doing it correctly. Why waste your time doing it wrong in the first place when you already have to do major work on the code just to make it functional anyway? – Ken White Apr 02 '13 at 22:10
  • It's working well right now..and as I said **time is critical fact** to me at this moment. – DZkid Apr 02 '13 at 22:26
  • Doing it right would have saved you time now – David Heffernan Apr 03 '13 at 08:39

1 Answers1

1

You have posted entirely too much code to make a useful question. There are hundreds of questions posted every day which have no code samples, or not enough code samples. You have gone the other way, really far the other way.

The best way to "populate" a virtual tree view is to NOT populate it. Instead you only set the rootNodeCount and then iteratively, when you have to, set the child node counts for the child nodes. For a collapsed node, it is enough that the tree view knows that there are children, or that there are not. Once you expand a childnode, you can populate the sub-elements.

Note that when you do it the way Virtual Controls are meant to be done, you are not writing a tonne of code. Instead you are simply "answering questions about the underlying state of your model objects". How many root nodes are there? You tell virtual tree that number. Then from there, you simply answer questions when it asks you. What is the text for column 0, for node #3 under the root node? (It invokes an event which you handle, and you return that information). Note that initializing the DATA is something that most people who are new to VirtualTreeViews often misunderstand. Ideally that DATA should contain a pointer to a real model object that can answer the questions that VirtualTreeView asks it. The most efficient classes that answer those questions might not even need to instantiate a real model object for each visible node in the tree, although that's certainly acceptable and common. It's important that you understand that it's not strictly ESSENTIAL though.

Secondly, if your goal was to use the TVirtualTreeView to emulate a shell explorer, that code is already done for you, look at the sub-section of the Advanced VT demo is available from the VirtualTreeview website. See the tab I have indicated here:

enter image description here

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • As I said [*here*](http://stackoverflow.com/questions/15769790/delphi-what-is-the-ideal-method-to-populate-a-virtualstringtree/15775233?noredirect=1#comment22425035_15769790) I know that I'm not using the VST in the perfect way..but time is an essential fact for me at the current moment..and for the demo you pointed at ..I've seen it before and it seemed too much complicated for someone who never used the VST. – DZkid Apr 02 '13 at 22:22
  • 2
    You will not be able to work fast in a virtual control until you comprehend the model and ideas it uses. – Warren P Apr 02 '13 at 22:25
  • But I'm not looking for a virtual control at this moment..I just need a TreeView that's better than the TTreeView that comes with delphi. – DZkid Apr 02 '13 at 22:29
  • 2
    Then get another tree view. You'll go nuts trying to turn a virtual control into a non-virtual one. I'm telling you, I've spent 100+ hours on learning VirtualTreeView. If you want an easy-to-learn non-virtual tree view that is better (I assume you mean prettier?) than TTreeView, you need to buy one because there isn't any free non-virtual easy to use tree view for delphi. TTreeView, and TJvTreeView are all wrappers around the windows common controls tree view. – Warren P Apr 02 '13 at 22:32
  • With better I mean **features** not something that make it look **pretty**..I need VST just to accomplish [this task](http://stackoverflow.com/questions/15589571) in the next couple of days. – DZkid Apr 02 '13 at 22:42
  • The key thing I have never found for free is a non-virtual multi-column control that combines both Grid (columns) and Tree (nesting) capabilities in a single control. While VirtualTreeView is a wonderful component, it's not simple, and you're not the only one who finds learning the ins and outs of it tricky. – Warren P Apr 03 '13 at 15:40