Board index » delphi » Treeview is slowly ...

Treeview is slowly ...

{*word*104}Cat <Mauri...@bellsouth.net> a crit dans l'article
<3586fe75.18973...@news.mia.bellsouth.net>...

Quote
> On 16 Jun 1998 19:11:49 GMT, "Seb_Nath" <seb2...@biogate.com> wrote:

> >Can anyone tell me why TREEVIEW is so slow when adding items( after
adding
> >40 items...) and when using BEGIN UPDATE ans END UPDATE ?

> >It seems to me that is the good component to make a directory viewer
like
> >Windows 95 's explorer ...

> I think you'll have to post some of your code. I've written a
> descendant of TTreeView that scans and displays the folders on a hard
> drive, and it displays around a thousand nested folders in less than a
> second. You're obviously doing something wrong, and only your code
> could tell us what that might be.

> By the way, there is no real need to scan ALL the folders on a drive
> at once. You could initially scan and display the topmost folders (in
> the root directory) and only scan and display subfolders whenever the
> user opens a folder. That would be even faster, and would also use
> less memory.

Hi {*word*104}cat,

Effectively, i'm just scanning the subfolders of the folder clicked by user
and not at all but when some of folders are unfolding and the number of
items displayed is greater than 40 items, the Treeview component begin to
slowdown .... Nevertheless i tested my routine with another component like
LISTBOX and it's speed...

This is the code of subroutine :

procedure TForm1.scan( path : string ; node : TTreenode);
var f : TSearchRec;
    node1 : TtreeNode;
begin
     Node.deletechildren;
     If findfirst(path+'\*.*',fadirectory,f)=0 then begin
        while findnext(f)=0 do begin
              if f.attr=fadirectory then begin
                 if (f.name<>'..') and (f.name<>'.') then begin
                    if node.getlastchild=nil then
node1:=treeview0.items.addchild(node,f.name) else
                    node1:=treeview0.items.add(node.getlastchild,f.name);
                    node1.haschildren:=true;
                   end;
                 end;
              end;
        end;
     findclose(f);
end;

AND this is the same routine with a LISTBOX component and it's faster,
really faster

procedure TForm1.scan1( path : string ; node : TTreenode);
var f : TSearchRec;
    node1 : TtreeNode;
begin
     Node.deletechildren;
     If findfirst(path+'\*.*',fadirectory,f)=0 then begin
        while findnext(f)=0 do begin
              if f.attr=fadirectory then begin
                 if (f.name<>'..') and (f.name<>'.') then
listbox1.items.add(f.name);
                   end;
                 end;
              end;
        end;
     findclose(f);
end;

In these routines, there isn't BEGIN UPDATE and END UPDATE methods used.

I'm using DELPHI 2 on W95.

Thanks

Sbastien

 

Re:Treeview is slowly ...


On 17 Jun 1998 15:25:08 GMT, "Seb_Nath" <seb2...@biogate.com> wrote:

Quote
>Effectively, i'm just scanning the subfolders of the folder clicked by user
>and not at all but when some of folders are unfolding and the number of
>items displayed is greater than 40 items, the Treeview component begin to
>slowdown .... Nevertheless i tested my routine with another component like
>LISTBOX and it's speed...
>procedure TForm1.scan( path : string ; node : TTreenode);
>var f : TSearchRec;
>    node1 : TtreeNode;
>begin
>     Node.deletechildren;
>     If findfirst(path+'\*.*',fadirectory,f)=0 then begin
>        while findnext(f)=0 do begin
>              if f.attr=fadirectory then begin
>                 if (f.name<>'..') and (f.name<>'.') then begin
>                    if node.getlastchild=nil then
>node1:=treeview0.items.addchild(node,f.name) else
>                    node1:=treeview0.items.add(node.getlastchild,f.name);
>                    node1.haschildren:=true;
>                   end;
>                 end;
>              end;
>        end;
>     findclose(f);
>end;
>procedure TForm1.scan1( path : string ; node : TTreenode);
>var f : TSearchRec;
>    node1 : TtreeNode;
>begin
>     Node.deletechildren;
>     If findfirst(path+'\*.*',fadirectory,f)=0 then begin
>        while findnext(f)=0 do begin
>              if f.attr=fadirectory then begin
>                 if (f.name<>'..') and (f.name<>'.') then
>listbox1.items.add(f.name);
>                   end;
>                 end;
>              end;
>        end;
>     findclose(f);
>end;

Seb, you should note that the use of FindNext immediately following
the FindFirst means that you're missing the first file/directory
found. Since this is normally the '.' directory, you don't need the
"and (fname<>'.') test.

Your Scan routine is slow because you use GetLastChild unnecessarily -
and Add, when AddChild would be easier and faster.

Try the following code:

procedure TForm1.Scan(const Path: string; Node: TTreeNode);
begin
  Node.DeleteChildren;
  if FindFirst(Path+'\*.*',faDirectory,F)=0 then
    while FindNext(F)=0 do
      if (F.Attr=faDirectory) and (F.Name<>'..') then
        begin
            NewNode:=TreeView.Items.AddChild(Node,F.Name);
        NewNode.HasChildren:=True
        end        
end;

Instead of simply assigning HasChildren := True, I'd use a function
that would check for the presence of children, but that's another
matter.

Let me know how it goes.

Re:Treeview is slowly ...


Just a passing thought.

You haven't set the sorted property on the TTreeview itself have you?
I have found that adding a node will slow it down and after about 40 node
then it is VERY noticeable!

Regards

Colin Dawson
cdaw...@athene.co.uk
http://www.athene.co.uk/homepages/cdawson

Quote
Seb_Nath wrote in message <01bd9a04$244091c0$221e67d1@m>...

>{*word*104}Cat <Mauri...@bellsouth.net> a crit dans l'article
><3586fe75.18973...@news.mia.bellsouth.net>...
>> On 16 Jun 1998 19:11:49 GMT, "Seb_Nath" <seb2...@biogate.com> wrote:

>> >Can anyone tell me why TREEVIEW is so slow when adding items( after
>adding
>> >40 items...) and when using BEGIN UPDATE ans END UPDATE ?

>> >It seems to me that is the good component to make a directory viewer
>like
>> >Windows 95 's explorer ...

>> I think you'll have to post some of your code. I've written a
>> descendant of TTreeView that scans and displays the folders on a hard
>> drive, and it displays around a thousand nested folders in less than a
>> second. You're obviously doing something wrong, and only your code
>> could tell us what that might be.

>> By the way, there is no real need to scan ALL the folders on a drive
>> at once. You could initially scan and display the topmost folders (in
>> the root directory) and only scan and display subfolders whenever the
>> user opens a folder. That would be even faster, and would also use
>> less memory.

>Hi {*word*104}cat,

>Effectively, i'm just scanning the subfolders of the folder clicked by user
>and not at all but when some of folders are unfolding and the number of
>items displayed is greater than 40 items, the Treeview component begin to
>slowdown .... Nevertheless i tested my routine with another component like
>LISTBOX and it's speed...

>This is the code of subroutine :

>procedure TForm1.scan( path : string ; node : TTreenode);
>var f : TSearchRec;
>    node1 : TtreeNode;
>begin
>     Node.deletechildren;
>     If findfirst(path+'\*.*',fadirectory,f)=0 then begin
>        while findnext(f)=0 do begin
>              if f.attr=fadirectory then begin
>                 if (f.name<>'..') and (f.name<>'.') then begin
>                    if node.getlastchild=nil then
>node1:=treeview0.items.addchild(node,f.name) else
>                    node1:=treeview0.items.add(node.getlastchild,f.name);
>                    node1.haschildren:=true;
>                   end;
>                 end;
>              end;
>        end;
>     findclose(f);
>end;

>AND this is the same routine with a LISTBOX component and it's faster,
>really faster

>procedure TForm1.scan1( path : string ; node : TTreenode);
>var f : TSearchRec;
>    node1 : TtreeNode;
>begin
>     Node.deletechildren;
>     If findfirst(path+'\*.*',fadirectory,f)=0 then begin
>        while findnext(f)=0 do begin
>              if f.attr=fadirectory then begin
>                 if (f.name<>'..') and (f.name<>'.') then
>listbox1.items.add(f.name);
>                   end;
>                 end;
>              end;
>        end;
>     findclose(f);
>end;

>In these routines, there isn't BEGIN UPDATE and END UPDATE methods used.

>I'm using DELPHI 2 on W95.

>Thanks

>Sbastien

begin 666 Colin Dawson.vcf
M0D5'24XZ5D-!4D0-"E9%4E-)3TXZ,BXQ#0I..D1A=W-O;CM#;VQI;@T*1DXZ
M0V]L:6X@1&%W<V]N#0I54DPZ:'1T<#HO+W=W=RYA=&AE;F4N8V\N=6LO:&]M
M97!A9V5S+V-D87=S;VX-"D5-04E,.U!2148[24Y415).150Z8V1A=W-O;D!A
M=&AE;F4N8V\N=6L-"E)%5CHQ.3DX,#8Q-U0Q.#4V-#):#0I%3D0Z5D-!4D0-
!"@``
`
end
end

Re:Treeview is slowly ...


In article <01bd9a04$244091c0$221e67d1@m>, "Seb_Nath" <seb2...@biogate.com>
writes:

Quote
>Hi {*word*104}cat,

>Effectively, i'm just scanning the subfolders of the folder clicked by user
>and not at all but when some of folders are unfolding and the number of
>items displayed is greater than 40 items, the Treeview component begin to
>slowdown .... Nevertheless i tested my routine with another component like
>LISTBOX and it's speed...

On my 100Mhz 32Mb PC I get :-

34 nodes 2 mS / node
75 nodes 4 mS / node
3000 node 4 ms / node
2500 nodes (recursing) 4 ms / node

 . . . for the following code . . .

procedure TForm1.Scan(Path : string ; Node : TTreenode);
var
  f : TSearchRec;
  Node1 : TTreeNode;
const
  Success : integer = 0;
begin
  Node.DeleteChildren;
  if FindFirst(Path + '\*.*', faDirectory, f) = Success then begin
    repeat {until FindNext(f) <> Success}
      if (f.Attr and faDirectory) > 0 then
        if (f.Name <> '..') and (f.Name <> '.') then begin
          Node1 := TreeView1.Items.AddChild(node, f.Name);
          Node1.HasChildren := DirHasContents(Path + '\' + f.Name);
        end; {fname not .. or .}
      {end if f.attr=fadirectory}
    until FindNext(f) <> Success;
    FindClose(f);
  end; {if findfirst etc}
end;

function TForm1.DirHasContents(Path : string) : boolean;
var
  f : TSearchRec;
  FNResult : integer;
const
  Success : integer = 0;
begin
  if FindFirst(Path + '\*.*', faDirectory, f) <> Success then
    Result := false
  else
    if (F.Name <> '.') and (F.Name <> '..') then
      Result := true
    else begin
      repeat
        FNResult := FindNext(f)
      until  (FNResult <> Success) or ((F.Name <> '..') and (F.Name <> '.'));
      Result := (FNResult = Success);
    end;
  {end; if FindFirst( etc}
  FindClose(f)
end;

This code includes a function to set HasChildren only if the node's directory
has files in it.

Alan Lloyd
alangll...@aol.com

Re:Treeview is slowly ...


In article <3586fe75.18973...@news.mia.bellsouth.net>, Mauri...@bellsouth.net

Quote
({*word*104}Cat) writes:
>By the way, there is no real need to scan ALL the folders on a drive
>at once. You could initially scan and display the topmost folders (in
>the root directory) and only scan and display subfolders whenever the
>user opens a folder. That would be even faster, and would also use
>less memory.

Maurice

How do you get the full directory to put into the FindFirst to scan - I've
tried the Node.Parent.Text and get a GPF, also tried allocating the Node in
OnExpanded to another Node and going up the line while Assigned( Node.Parent)
but it does not seem to work.

I would appreciate any suggestions.

Alan Lloyd
alangll...@aol.com

Re:Treeview is slowly ...


On 18 Jun 1998 19:33:19 GMT, alangll...@aol.com (AlanGLLoyd) wrote:

Quote
>How do you get the full directory to put into the FindFirst to scan - I've
>tried the Node.Parent.Text and get a GPF, also tried allocating the Node in
>OnExpanded to another Node and going up the line while Assigned( Node.Parent)
>but it does not seem to work.

I wonder why? Perhaps when your code reaches the top level, you poll
Node.Parent.Text BEFORE you perform the Assigned() test?

My code is simply this:

function TmvTreeView.NodePath(TreeNode: TTreeNode): string;
begin
  Result:='';
  while TreeNode<>nil do
    begin
      Result:=Concat(TreeNode.Text,'\',Result);
      TreeNode:=TreeNode.Parent
    end;
  Result:=Concat(FDriveSpec,'\',Result)
end;

where the only difference between my code and yours is that I use
TreeNode := TreeNode.Parent before anything else (as opposed to using
Node.Parent.whatever).

If what I think is causing the problem isn't it, please e-mail me some
of your component code and we'll get it sorted out right away.

Re:Treeview is slowly ...


On 18 Jun 1998 13:25:41 GMT, alangll...@aol.com (AlanGLLoyd) wrote:

Quote
>function TForm1.DirHasContents(Path : string) : boolean;
>var
>  f : TSearchRec;
>  FNResult : integer;
>const
>  Success : integer = 0;
>begin
>  if FindFirst(Path + '\*.*', faDirectory, f) <> Success then
>    Result := false
>  else
>    if (F.Name <> '.') and (F.Name <> '..') then
>      Result := true
>    else begin
>      repeat
>        FNResult := FindNext(f)
>      until  (FNResult <> Success) or ((F.Name <> '..') and (F.Name <> '.'));
>      Result := (FNResult = Success);
>    end;
>  {end; if FindFirst( etc}
>  FindClose(f)
>end;

FindFirst(Path + '\*.*', faDirectory, f) will find not only
directories, but also files. This means that DirHasContents may return
True even if the directory searched only contains files - in which
case your TreeView will indicate that a folder contains sub-folders
even when it doesn't.

Re:Treeview is slowly ...


In article <35897ffe.81066...@news.mia.bellsouth.net>, Mauri...@bellsouth.net

Quote
({*word*104}Cat) writes:
>FindFirst(Path + '\*.*', faDirectory, f) will find not only
>directories, but also files. This means that DirHasContents may return
>True even if the directory searched only contains files - in which
>case your TreeView will indicate that a folder contains sub-folders
>even when it doesn't.

Yes, and thanks <g> - I realised that when I started using the code in earnest.
It should be :-

var
  f : TSearchRec;
  FNResult : integer;
begin
  if FindFirst(Path + '\*.*', faDirectory, f) <> Success then
    Result := false
  else
    if (F.Name <> '.') and (F.Name <> '..') and (f.Attr = faDirectory) then
      Result := true
    else begin
      repeat
        FNResult := FindNext(f)
      until  (FNResult <> Success) or ((F.Name <> '..') and (F.Name <> '.') and
(f.Attr = faDirectory));
      Result := (FNResult = Success);
    end;
  {end; if FindFirst( etc}
  FindClose(f)

Thanks also for your other help - it works now.

Alan Lloyd
alangll...@aol.com

Re:Treeview is slowly ...


Quote
> Try the following code:

> procedure TForm1.Scan(const Path: string; Node: TTreeNode);
> begin
>   Node.DeleteChildren;
>   if FindFirst(Path+'\*.*',faDirectory,F)=0 then
>     while FindNext(F)=0 do
>       if (F.Attr=faDirectory) and (F.Name<>'..') then
>         begin
>             NewNode:=TreeView.Items.AddChild(Node,F.Name);
>    NewNode.HasChildren:=True
>         end        
> end;

> Instead of simply assigning HasChildren := True, I'd use a function
> that would check for the presence of children, but that's another
> matter.

> Let me know how it goes.

Allan and {*word*104}cat

It's a little bit faster but with an initial Treeview of 100
items and when i open a directory wich contains about 11 items, it tooks 8
seconds  for both routines.
I'm calling the sub routine from the  ON EXPANDING event
So the code is :

procedure TForm1.TreeView0Expanding(Sender: TObject; Node: TTreeNode; var
AllowExpansion: Boolean);
var temp : string;
begin
 AllowExpansion:=True;
 temp:=findpath(node,'');
 temp:=copy(temp,1, length(temp)-1);
 scan(temp,node);
 treeView0.sorttype:=sttext;
end;

Perhaps it's not the good event to call it...

Sbastien

Re:Treeview is slowly ...


In article <01bd9b89$caab7120$LocalHost@m>, "Seb_Nath" <seb2...@biogate.com>
writes:

Quote
>Allan and {*word*104}cat

>It's a little bit faster but with an initial Treeview of 100
>items and when i open a directory wich contains about 11 items, it tooks 8
>seconds  for both routines.
>I'm calling the sub routine from the  ON EXPANDING event
>So the code is :

>procedure TForm1.TreeView0Expanding(Sender: TObject; Node: TTreeNode; var
>AllowExpansion: Boolean);
>var temp : string;
>begin
> AllowExpansion:=True;
> temp:=findpath(node,'');
> temp:=copy(temp,1, length(temp)-1);
> scan(temp,node);
> treeView0.sorttype:=sttext;
>end;

There is something seriously weird here - I'm doing a lot more (when I place a
closed directory node I look through ALL the files of that directory to see if
there is a sub-directory in it and also to see if there is an .HTM /.HTML file
in it) and I'm consistentl getting 30mSec / node with my 100Mhz 32Mb Pentium
(not that fast).

My scan is also in the OnExpanding routine.

What sort of Graphics card (and PC) have you got ?

I can send you my source and .EXE if you like.

Alan Lloyd
alangll...@aol.com

Re:Treeview is slowly ...


On 19 Jun 1998 13:55:04 GMT, "Seb_Nath" <seb2...@biogate.com> wrote:

Quote
>It's a little bit faster but with an initial Treeview of 100
>items and when i open a directory wich contains about 11 items, it tooks 8
>seconds  for both routines.
>I'm calling the sub routine from the  ON EXPANDING event
>So the code is :

>procedure TForm1.TreeView0Expanding(Sender: TObject; Node: TTreeNode; var
>AllowExpansion: Boolean);
>var temp : string;
>begin
> AllowExpansion:=True;
> temp:=findpath(node,'');
> temp:=copy(temp,1, length(temp)-1);
> scan(temp,node);
> treeView0.sorttype:=sttext;
>end;

>Perhaps it's not the good event to call it...

There is nothing wrong with your choice of event, I use OnExpanding
myself.

I think your component is still slow because, as far as I can see,
SortType is always stText (you don't turn it off anywhere in the code
you have shown us). - But don't bother turning it on or off. Don't let
the TTreeView do the sorting at all, use a TStringList for that.
(Simply keep SortType=stNone.)

The following code is quite fast:

procedure TMainForm.TreeViewExpanding(Sender: TObject;
  Node: TTreeNode; var AllowExpansion: Boolean);
begin
  AllowExpansion:=InitFolders(Node)
end;

function TMainForm.InitFolders(Node: TTreeNode): boolean;
var TheMask: string; N,I: integer; SearchRec: TSearchRec;
  Child: TTreeNode;
begin
  TheMask:=NodePath(Node)+'*.*';
  Strings.Clear;
  Strings.Sorted:=False;
  N:=FindFirst(TheMask,faAnyFile,SearchRec);
  while N=0 do
    begin
      with SearchRec do
        if (Name[1]<>'.') and (Attr and faDirectory >0) then
          Strings.Add(Name);
      N:=FindNext(SearchRec)
    end;
  Strings.Sort;
  for I:=0 to Strings.Count-1 do
    begin
      Child:=TreeView.Items.AddChild(Node,Strings[I]);
      Child.HasChildren:=NodeHasChildren(Child)
    end;
  Result:=(Strings.Count>0)
end;

Strings is a TStringList that I've created to hold and sort the
directory names. Every time InitFolders is called, I clear the string
and tell it not to sort whatever strings I add to it (to speed things
up). After having added the directory names, I sort the strings, then
add them one by one to the TreeView.

In case you're interested, here are some of the routines that are used
by InitFolders:

function TMainForm.NodeHasChildren(Node: TTreeNode): boolean;
var TheMask: string; Found: boolean; N: integer; SearchRec:
TSearchRec;
begin
  TheMask:=NodePath(Node)+'*.*';
  Found:=False;
  N:=FindFirst(TheMask,faAnyFile,SearchRec);
  while (not Found) and (N=0) do
    begin
      if (SearchRec.Name[1]<>'.') and (SearchRec.Attr and faDirectory

Quote
>0) then

        Found:=True
      else
        N:=FindNext(SearchRec)
    end;
  Result:=Found
end;

function TMainForm.NodePath(Node: TTreeNode): string;
begin
  Result:='';
  while Node<>nil do
    begin
      Result:=Concat(Node.Text,'\',Result);
      Node:=Node.Parent
    end
end;

Re:Treeview is slowly ...


Hi
One thing is of big importance. Always disable treeupdate
before you do modifications on treenodes.

TreeView1.Items.BeginUpdate;
... do your job ...
TreeView1.Items.EndUpdate;

The command "BeginUpdate" prevents the tree from
updating itself during your work. That make the whole
process very faster.

Johnpeter

Quote
Seb_Nath wrote:
> > Try the following code:

> > procedure TForm1.Scan(const Path: string; Node: TTreeNode);
> > begin
> >   Node.DeleteChildren;
> >   if FindFirst(Path+'\*.*',faDirectory,F)=0 then
> >     while FindNext(F)=0 do
> >       if (F.Attr=faDirectory) and (F.Name<>'..') then
> >         begin
> >             NewNode:=TreeView.Items.AddChild(Node,F.Name);
> >       NewNode.HasChildren:=True
> >         end
> > end;

> > Instead of simply assigning HasChildren := True, I'd use a function
> > that would check for the presence of children, but that's another
> > matter.

> > Let me know how it goes.

> Allan and {*word*104}cat

> It's a little bit faster but with an initial Treeview of 100
> items and when i open a directory wich contains about 11 items, it tooks 8
> seconds  for both routines.
> I'm calling the sub routine from the  ON EXPANDING event
> So the code is :

> procedure TForm1.TreeView0Expanding(Sender: TObject; Node: TTreeNode; var
> AllowExpansion: Boolean);
> var temp : string;
> begin
>  AllowExpansion:=True;
>  temp:=findpath(node,'');
>  temp:=copy(temp,1, length(temp)-1);
>  scan(temp,node);
>  treeView0.sorttype:=sttext;
> end;

> Perhaps it's not the good event to call it...

> Sbastien

Re:Treeview is slowly ...


Quote
> There is nothing wrong with your choice of event, I use OnExpanding
> myself.

> I think your component is still slow because, as far as I can see,
> SortType is always stText (you don't turn it off anywhere in the code
> you have shown us). - But don't bother turning it on or off. Don't let
> the TTreeView do the sorting at all, use a TStringList for that.
> (Simply keep SortType=stNone.)

> The following code is quite fast .......

Great !!!!!

That's ok :)

Just the setting of SORTTYPE.......( i've tried speedly to set SORTYPE to
stNone before scanning and adding items and set to stText after.... and
that's faster too but not as much as Tstringlist method ).

Thanks.
Thanks everybody for your help.

Sbastien

Re:Treeview is slowly ...


In article <358acb10.165832...@news.mia.bellsouth.net>, Mauri...@bellsouth.net

Quote
({*word*104}Cat) writes:
> But don't bother turning it on or off. Don't let
>the TTreeView do the sorting at all, use a TStringList for that.
>(Simply keep SortType=stNone.)

If you want the items sorted call Node.AlphaSort after you've added the chilren
to Node. It sorts _only_ that node's children.

Alan Lloyd
alangll...@aol.com

Other Threads