Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations biv343 on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Treeview Beginner Help 2

Status
Not open for further replies.

jettson

Programmer
Dec 2, 2005
7
AU
I fairly new to programming. I did a search for treeview on the forum but could not find or understand what I am trying to do.

I want to be able to add a list of strings to a treeview.
str:=games\freeware\puzzle
str:=games\freeware\cards
str:=internet\chat
str:=internet\downloadmanagers
etc

games and internet would be parent nodes and of course the rest would be child nodes.

Could anyone point me in the right direction. Thanks
 
I forget to mention I am using Delphi 7

I can add a root and a child node this way but I get 3 root
nodes of games they don't combine so I have 1 root node of games.

procedure TForm1.Button3Click(Sender: TObject);
var
RootNode : TTreeNode;
begin
RootNode := TreeView1.Items.Add(nil, 'games');
TreeView1.Items.AddChild(RootNode, 'racing');
RootNode := TreeView1.Items.Add(nil, 'games');
TreeView1.Items.AddChild(RootNode, 'puzzle');
RootNode := TreeView1.Items.Add(nil, 'games');
TreeView1.Items.AddChild(RootNode, 'cards');
end;
 
You get three root nodes because you use the following line three times!

Code:
RootNode := TreeView1.Items.Add(nil, 'games');

The method 'Add' is appropriately named! You didn't say where your list of strings is coming from. I've built the following example to do what you want, where the strings are initially held in a list box

Code:
procedure TForm1.Button1Click(Sender: TObject);
var
  i, j: integer;
  str: string;
  CurrNode, PrntNode: TTreeNode;
  NodeNames: TStringList;
begin
  TreeView1.Items.Clear;
  NodeNames := TStringList.Create;
  try
    for i := 0 to ListBox1.Items.Count-1 do
    begin
      str := ListBox1.Items[i];
      str := StringReplace(str, '\', ',', [rfReplaceAll]);
      NodeNames.CommaText := str;

      PrntNode := nil;
      for j := 0 to NodeNames.Count-1 do
      begin
        CurrNode := GetNode(PrntNode, NodeNames[j]);
        //if node doesn't already exist then create it
        if CurrNode = nil then
          CurrNode := TreeView1.Items.AddChild(PrntNode, NodeNames[j]);
        PrntNode := CurrNode;
      end;
    end;
  finally
    NodeNames.Free;
  end;
end;

function TForm1.GetNode(ParentNode: TTreeNode; NodeName: string): TTreeNode;
var
  TmpNode: TTreeNode;
begin
  if ParentNode = nil then
    TmpNode := TreeView1.Items.GetFirstNode
  else
    TmpNode := ParentNode.GetFirstChild;

  while (TmpNode <> nil) and (TmpNode.Text <> NodeName) do
    TmpNode := TmpNode.GetNextSibling;

  Result := TmpNode;
end;

Hope that helps

Steve
 
Thanks very much donvittorio for your reply!


I think I did not explain myself very well in my first post. Your code is not 100% what I want, it's close and I am getting ideas from it.


I have been doing some research for the past day and a half and I am posting my latest approach. It still does not work but I feel I am getting closer. I am wanting to give files on my computer a category . It needs to get the category from inside each file from different directories and add them to a treeview. Any help it fixing up my code would be greatly appreciated.

I am using a listbox cause it was easy to type some strings\categories into it.

List.Box1 line 1 = program\games\racing\bikes
List.Box1 line 2 = program\games\racing\cars
List.Box1 line 3 = program\games\racing\trucks
List.Box1 line 4 = program\files\freeware\video
List.Box1 line 5 = program\files\shareware\video
List.Box1 line 6 = games\turn based\war
List.Box1 line 7 = games\cards\black jack
List.Box1 line 8 = games\cards\poker
List.Box1 line 9 = apps\files\freeware\dvd
List.Box1 line 10 = apps\files\shareware\dvd


procedure TForm1.Button1Click(Sender: TObject);
var
node,parent:TTreeNode;
i,y,x,go,getparent:Integer;
path:String;
begin
list := TStringList.Create;
i:=0;
y:=0;
while i<ListBox1.Count do
begin
list.Clear;
path:=ListBox1.Items;

// break up each line of listbox into it parts
//----------------------------------------------------------------------------
while(LastDelimiter('\',path)<>0) do
begin
if(IsDelimiter('\',path, y)) then
begin
list.Add(MidStr(path,1, y-1));
Delete(path,1, y);
y:= 0;
end
else
begin
y:=y+1;
end;
end;
list.Add(path); // need to add the last one to the list
//--------------------------------------------------------------------------------
go:=0;
x:=0;
node:= TreeView1.items.GetFirstNode;
if node.Index = -1 then TreeView1.Items.Add(TreeView1.Selected, list[0]);
While x < list.Count-1 do
begin
//*********************
while assigned(node) do
begin
parent:=node;
if node.Text = list[x] then
begin
go:=3;
node:= node.GetFirstChild;
end
else
begin
go:=2;
node:= node.GetNextSibling;
end;
end;

//*********************

ShowMessage('Stepping');//just a break to watch how tree forms
if go = 2 then parent:=TreeView1.Items.Add(TreeView1.Selected, list[X]);
if go = 3 then parent:=TreeView1.Items.AddChild(parent, list[x+1]);
x:=x+1;
end;

node:= TreeView1.Items.GetFirstNode;
i:=i+1;
end;
end;
 
jettson,

I'm not sure why the code I posted yesterday doesn't do what you want, what exactly are you trying to do? I have tried out your code and it obviously isn't quite right, but I can't really help any further because I don't understand what you want the treeview to look like. In the mean time, here are a couple of minor points on the code that you posted:

1) you are using while loops to iterate through ListBox and list. You would be better using for loops, (i.e. for i := 0 to ListBox1.Items.Count-1 and for x := 0 to list.Count-2), then you can remove the variable initialisation lines (i := 0 and x := 0) and the increment lines (i := i+1 and x:= x+1). This would simplify your code a little bit and make it more readable. Using this approach in future would also mean you are less likely to make mistakes, as it's very easy to forget code for initialising or incrementing variables!

2) the while loop that you have written to break up each line of the listbox into its parts is quite lengthy, it would probably be better to use the Delimiter and DelimitedText properties of the TStringList to do what you want. See for an example of using these properties. It would probably be as simple as:

Code:
list.Delimiter := '\';
list.DelimitedText := ListBox1.Items[i];

Unfortunately I can't use these properties because I'm currently living in the dark ages of Delphi 4, that's why in my previous post I replaced '\' with ',' and then used CommaText.

Steve
 
donvittorio, I have to make a confession and offer you my apologies.

I could not get your code to compile. I got missing semi colon
and Undeclared Identifer for getNode, and Parent node. So I tried my best to understand your code being a beginner and all. I honestly did not pick the comma.text part of your code so ended up making a stupid comment.

Thanks for posting again rather then just deciding to wipe me for my stupid comment. Could I ask you kindly to post all of your code from beginning to end.(including any delarations for variables).

Thanks.
 
No problem, here is the code for the entire unit. As you can see you should have a form witha treeview, a button and a listbox.

Code:
unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    function GetNode(ParentNode: TTreeNode; NodeName: string): TTreeNode;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  i, j: integer;
  str: string;
  CurrNode, PrntNode: TTreeNode;
  NodeNames: TStringList;
begin
  TreeView1.Items.Clear;
  NodeNames := TStringList.Create;
  try
    for i := 0 to ListBox1.Items.Count-1 do
    begin
      str := ListBox1.Items[i];
      str := StringReplace(str, '\', ',', [rfReplaceAll]);
      NodeNames.CommaText := str;

      PrntNode := nil;
      for j := 0 to NodeNames.Count-1 do
      begin
        CurrNode := GetNode(PrntNode, NodeNames[j]);
        //if node doesn't already exist then create it
        if CurrNode = nil then
          CurrNode := TreeView1.Items.AddChild(PrntNode, NodeNames[j]);
        PrntNode := CurrNode;
      end;
    end;
  finally
    NodeNames.Free;
  end;
end;

function TForm1.GetNode(ParentNode: TTreeNode; NodeName: string): TTreeNode;
var
  TmpNode: TTreeNode;
begin
  if ParentNode = nil then
    TmpNode := TreeView1.Items.GetFirstNode
  else
    TmpNode := ParentNode.GetFirstChild;

  while (TmpNode <> nil) and (TmpNode.Text <> NodeName) do
    TmpNode := TmpNode.GetNextSibling;

  Result := TmpNode;
end;

end.

Steve
 
You are simply a legend!

Got it working, thank you very much!!!
 
jettson,

thanks for your high praise! A word of warning on the code I posted though, using CommaText (as I did) or DelimitedText (as I recommended) will result in strings being split by spaces as well as backslashes. This is something that has annoyed me in the past, I think it makes the DelimitedText function almost useless. This means that you will have to split the strings up yourself, as you were doing in the code you posted.
Splitting strings like this is the first thing that I ever did in Delphi, and in my first attempt I did the same thing as you, i.e. read every character and see if it's the one I'm looking for. This is slow though, and I found it's quicker to use the Pos function. The example code I gave therefore becomes:

Code:
unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    function GetNode(ParentNode: TTreeNode; NodeName: string): TTreeNode;
    procedure DelimitStrings(aStrings: TStrings; aPath: string);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  i, j: integer;
  CurrNode, PrntNode: TTreeNode;
  NodeNames: TStringList;
begin
  TreeView1.Items.Clear;
  NodeNames := TStringList.Create;
  try
    for i := 0 to ListBox1.Items.Count-1 do
    begin
      DelimitStrings(NodeNames, ListBox1.Items[i]);

      PrntNode := nil;
      for j := 0 to NodeNames.Count-1 do
      begin
        CurrNode := GetNode(PrntNode, NodeNames[j]);
        //if node doesn't already exist then create it
        if CurrNode = nil then
          CurrNode := TreeView1.Items.AddChild(PrntNode, NodeNames[j]);
        PrntNode := CurrNode;
      end;
    end;
  finally
    NodeNames.Free;
  end;
end;

function TForm1.GetNode(ParentNode: TTreeNode; NodeName: string): TTreeNode;
var
  TmpNode: TTreeNode;
begin
  if ParentNode = nil then
    TmpNode := TreeView1.Items.GetFirstNode
  else
    TmpNode := ParentNode.GetFirstChild;

  while (TmpNode <> nil) and (TmpNode.Text <> NodeName) do
    TmpNode := TmpNode.GetNextSibling;

  Result := TmpNode;
end;

procedure TForm1.DelimitStrings(aStrings: TStrings; aPath: string);
var
  DelimiterPos: integer;
begin
  aStrings.Clear;
  DelimiterPos := Pos('\', aPath);
  while DelimiterPos > 1 do
  begin
    aStrings.Add(Copy(aPath, 1, DelimiterPos-1));
    Delete(aPath, 1, DelimiterPos);
    DelimiterPos := Pos('\', aPath);
  end;
  aStrings.Add(aPath); // need to add the last one to the list
end;


end.

I did a quick bit of testing and found this way of splitting the string to be almost twice as quick. Enjoy!

Steve
 
jettson,

Steve has done a lot of work for you. The least (and most) you can do is award him a star as a way of saying thank you. It's easy to do. Simply click on the red star at the bottom left of one of his postings.

Andrew
Hampshire, UK
 
Towerbase, You are absolutely right!

Star awarded.

Steve, You deserves more than a star for walking me through this. I am very grateful. Sorry, I did not answer your last post. I sent a quick post to say thanks when I got your code working then went off-line after jumping around the lounge room to work on my program. I am really glad about the speed increase in your next post cause I will be throwing a lot of strings at it.


Thanks again Steve. Have a great Christmas and a great New Year.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top