TTreeView Refresh is so slow.

I solved one problem and now have another one I didn't know existed.

I am going through a TreeView with thousands of items.

The process is taking a long time.

Is there a way I can speed the process up?  For example, I tried TreeView.Items.BeginUpdate / EndUpdate so the tree is not displayed during the redraw, but it still takes a while.

LVL 5
Tom KnowltonWeb developerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

edeyCommented:
Hmmmm...
please do not delete questions
http://www.experts-exchange.com/jsp/qShow.jsp?ta=delphi&qid=10238093 
that you have 'solved', simply because no 'answer' has been submitted.

As to your question, a tree view with 'thousands' of items is prob. not going to be quick.  You may be better of using multiple controls.


GL
Mike
0
williams2Commented:
edey:
What are you trying to say ?
I do agree with edey. You must admit, that the question is poorly commented, and the fact that searching the registry is a very VERY slow process. There is obviously a more efficient solution to his question. I would prefer those kind of questions get deleted rather than serving a poor purpose.

knowlton:
Your problems is, that every time you delete a node, the VCL needs to repaint the canvas. To stop the repainting while updating, you can use the..

TreeView1.Items.BeginUpdate;
TreeView1.Items.EndUpdate;

...methods

Regards,
Williams
0
williams2Commented:
.I ment agree with Knowlton..

 X-)
0
CompTIA Security+

Learn the essential functions of CompTIA Security+, which establishes the core knowledge required of any cybersecurity role and leads professionals into intermediate-level cybersecurity jobs.

Tom KnowltonWeb developerAuthor Commented:
edey:

I see no shortage of questions in Experts-Exchange right now.  Until every question is answered, there's still plenty of points for you to earn, so don't complain!

williams2:

As I stated in my question, even using BeginUpdate / EndUpdate does not make it fast enough.

Since this is all taking place in memory, I don't see why it is faster.  I am not doing anything very complex.  

Here is the procedure that is causing the slow down:


procedure UpdateAttachmentCount(TempTree : TTreeView);
var
  Node: TTreeNode;
  lang_counter,
  CurIndex : integer;
  Language : string;
  LangStatus : boolean;
begin
  TempTree.Items.BeginUpdate;
  Node := TempTree.TopItem;
  while Node <> nil do
  begin
    if(Node.Level = 1) then
    begin
      Node.ImageIndex := BatchMasterMain.ModuleArray[FindModuleIndex(Node.Text, Node.Parent.Text)].GetAttachmentCount;
      Node.SelectedIndex := Node.ImageIndex;
    end;
    if (Node.Level = 3) or (Node.Level = 0) then
    begin
        Node.ImageIndex := -1;
        Node.SelectedIndex := -1;
    end else if Node.Level = 2 then
    begin
      CurIndex := FindModuleIndex(Node.Parent.Text, Node.Parent.Parent.Text);
      for lang_counter := 0 to  BatchMasterMain.ModuleArray[CurIndex].LanguageCount - 1 do
      begin
        if Node.Text = BatchMasterMain.ModuleArray[CurIndex].GetFullLanguage(lang_counter) then
          Language := BatchMasterMain.ModuleArray[CurIndex].GetLanguage(lang_counter);
      end;
      //if LangStatus is true then display check box icon
      LangStatus := BatchMasterMain.ModuleArray[CurIndex].GetLangStatus(Language);
      if LangStatus then
      begin
        Node.ImageIndex := 21;
        Node.SelectedIndex := 21;
      end else
      begin
        Node.ImageIndex := -1;
        Node.SelectedIndex := -1;
        if Node.HasChildren then
          Node.DeleteChildren;
      end;
    end;
    Node := Node.GetNext;
  end;
  TempTree.Items.EndUpdate;
  //Necessary for bitmaps to show up
  TempTree.Repaint;
end;
0
Tom KnowltonWeb developerAuthor Commented:
Actually,

Node := TempTree.TopItem;

should read like this:

Node := TempTree.Items.GetFirstNode;

0
williams2Commented:
I can see that you are doing a lot of manipulating... :-)

I have a suspicion that, the slow update is caused by one major problem:

The ImageIndexes! ..You are right by trying to set the image indexes, should eliminate the image updating probelm, but i doesn't :-( The imagelist you are using are manipulated inside the nodes as long as there is an imagelist attached to the tree, and since those bitmaps are all DDB's, you WILL get a problem - even when trying to set the index to -1.

I suggest you to try the following code (as I cannot see either the size of the tree, the surrounding implementations or imagelist, I haven't tested this)

...Between the Begin/End update methods, try this:

...
Var
  TmpStateImages,
  TmpImages: TImageList
begin
  With TempTree do
  Begin
    Items.BeginUpdate;
    TmpStateImiage:= StateImages;
    TmpImages:= Images;
    StateImages:= nil;
    Images:= nil;
  End;
...

(Your code)

...
  TempTree.Images:= TmpImages;
  TempTree.StateImages:= TmpStateImages;
  TempTree.Items.EndUpdate;
End;

Another thing that I have discovered: Reading the nodes .Text property is slow ! If you can avoid this, then do so. It's much faster manipulating the Node's .Data property ..that is at least something that doesn't slow the TTreeView component. Not event if you attach the whole 'ModuleArray' thing you are using, it will still be a pointer!

I don't know if you are familiar with typecasting ? Anyway, using the .Data property is done by the following:

writing..

Var
  SomeObject: TSomeObject;
begin
  ...  
  Node.Data:= SomeObject;
  ...
End;


Reading...

Var
  Obj: TSomeObject;
Begin
//This is a perfect valid way of handling objects.
  Obj:= TSomeObject(Node.Data);
//or...
  Obj:= Node.Data as TSomeObject;
//means the exact same thing..
End;

Or if you got more than one type of objects

Var
  Obj1: TSomeObject;
  Obj2: TSomeOtherObject;
Begin
  If Node.Data is TSomeObject then
    Obj1:= TSomeObject(Node.Data) else  
  If Node.Data is TSomeOtherObject then
    Obj2:= TSomeOtherObject(Node.Data) else
    ShowMessage('I don't know this object!');
End;

if you then want to free them, you can use the OnNodeDeletetion event, like this:

procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode);
begin
  TSomeObject(Node.Data).Free;
end;

I hope this might help you out..

Good luck with your Programming

Regards,
Williams
0
Tom KnowltonWeb developerAuthor Commented:
williams2:

As far as the size of my tree, assume I have 1000 nodes.

I'm sure you've given me some good ideas, here, but I'm not sure how to apply them to solve my problem.

If I understand you correctly, you say the problem is that I am updating the ImageIndex and that is causing the slow down.

What are you doing on this line:

TmpImages:= Images;


---------------

Can you explain a little more about Node.Data vs Node.Text?  How is one better than the other?



0
Tom KnowltonWeb developerAuthor Commented:
A note on the ModuleArray object I refer to in my posted code:

BatchMasterMain.ModuleArray[FindModuleIndex(Node.Text, Node.Parent.Text)].GetAttachmentCount;

ModuleArray[item index of your choice] would contain info like this:

Product (string)
Group  (string)
Module (string)
Languages / Attachments (in a StringList)

GetAttachmentCount would tell me how many languages have attachments.

This doesn't have much to do with the problem I'm sure, but i thought the info might be nice to have.
     
0
Tom KnowltonWeb developerAuthor Commented:
I'm getting a comiler error message on this line:

  TmpImages := TempTree.Images;

The error message says:  "Incompatible types:  TImageList and TCustomImageList"

Recommendations?

0
williams2Commented:
To correct this error you can do one of two things that WILL work:

1. The TmpImages should be declared as:
Var
  TmpImages: TCustomImageList;

2. Or you just write:
....
TmpImages:= TCustomImagesList(TempTree.Images)

...You see:
The TmpImages variables is in fact only a pointer, which means a similar type of the neat "Pointer" type. The difference is though, that Delphi keeps track of the type of pointers you and the system are using, so when you want a pointer to be anything that what it really is, then you just may have to explicitely type it to Delphi, so it will understand that you really intend to do what you are doing, and not doing it by some faulty mistake!

...So what you also could do:

Procedure MakeUpSomeMess;
Var
  Button: TButton;
Begin
  Button:= TButton(ListBox1);
  ListBox1:= TListBox(ToolButton1);
  ...
  ListBox1:= TListBox(Button);
End;

There's absolutely no reason to do it, but it sure helps understanding, how delphi works in the first place, eh?


....So about the .Data thing:

You see, the .data thing is ALSO a pointer just like all the stuff i mentioned to you here.. this means, than you can say:

Node.Data:= Button1;
//here you'll get a copy of the pointer
//of type TButton.
Button1:= TButton(Node.Data);
//Here you tell the compiler, that the
//returned pointer is a TButton object.


To learn more and understand more, I really think you should go read some books about OOP (Object Oriented Programming).

Regards,
Williams
0
Tom KnowltonWeb developerAuthor Commented:
TCustomImageList needs images.dcu to work correctly.  I cannot find images.dcu anywhere on my machine.

Do you see my problem?  I cannot refer to TCustomImageList in any fashion, because Delphi will not know what it is.

It seems your basic idea is to disassociate the ImageList from the TreeView during the ImageIndex changes, then reassociate it to the TreeView later.  Is this correct?
0
williams2Commented:
yes, besides trying to learn you some objectthreory :-) that is correct. By setting the imagelist property to nil, the treeview will not bother updating images. Delphi got it's own checking algorithms to see whether an imagelist is available or not.

BTW:
Of'course you can find the unit, where TCustomImageList is declared :-)
...and if you have an imagelist on your form, you should allready have the unit included, as the "Custom" type is always in the same unit as the object you are using.


if you cannot find a unit, then try this:
You can go to your delphi/sources directory and search for *.pas files (Include sub directories), and find files where the sentence "TCustomImageList" appears.

It's not your fault, but Borland made a mistake writing, that TImageList belongs to Images.dcu, but that is not correct:

In Delphi 4, the TImageList and TCustomImageList belongs to ImgList.dcu/.pas
In Delphi 3, the same objects belongs to controls.dcu/.pas

HINT:
If you are using Delphi 4, then try pressing the ctrl key, and move your mouse above the word in your code saying TImageList. Then press the left mousebutton, and voila! ..the code implementation of TImiageList will appear before your eyes.. That's neat, don't you think?

I guess you must have found your way through it this time...

Regards,
Williams
0
Tom KnowltonWeb developerAuthor Commented:
I added ImgList to my USES statement, and now it allows TCustomImageList.

Yes, Borland screwed up!

Now that TCustomImageList works, let me try your code and see what happens.

Thanks for all of your help.
0
Tom KnowltonWeb developerAuthor Commented:
williams2:

Well, your code works fine, but I've seen no increase in speed.

This is how the procedure looks now (after your recommended changes):

procedure UpdateAttachmentCount(TempTree : TTreeView);
var
  Node: TTreeNode;
  lang_counter,
  CurIndex : integer;
  Language : string;
  LangStatus : boolean;
  TempImages : TCustomImageList;
begin
  TempTree.Items.BeginUpdate;
  TempImages:= TempTree.Images;
  TempTree.Images:= nil;
  Node := TempTree.Items.GetFirstNode;
//  Node := TempTree.TopItem;
  while Node <> nil do
  begin
    if(Node.Level = MODULE_LEVEL_NODE) then
    begin
      Node.ImageIndex := BatchMasterMain.ModuleArray[FindModuleIndex(Node.Text, Node.Parent.Text)].GetAttachmentCount;
      Node.SelectedIndex := Node.ImageIndex;
    end;
    if (Node.Level = ATTACHMENT_LEVEL_NODE) or (Node.Level = GROUP_LEVEL_NODE) then
    begin
        if Node.ImageIndex <> NO_ICON then
        begin
          Node.ImageIndex := NO_ICON;
          Node.SelectedIndex := Node.ImageIndex;
        end;
    end else if Node.Level = LANGUAGE_LEVEL_NODE then
    begin
      CurIndex := FindModuleIndex(Node.Parent.Text, Node.Parent.Parent.Text);
      for lang_counter := 0 to  BatchMasterMain.ModuleArray[CurIndex].LanguageCount - 1 do
      begin
        if Node.Text = BatchMasterMain.ModuleArray[CurIndex].GetFullLanguage(lang_counter) then
          Language := BatchMasterMain.ModuleArray[CurIndex].GetLanguage(lang_counter);
      end;
      //if LangStatus is true then display check box icon
      LangStatus := BatchMasterMain.ModuleArray[CurIndex].GetLangStatus(Language);
      if LangStatus then
      begin
        if Node.ImageIndex <> CHECKBOX_ICON then
        begin
          Node.ImageIndex := CHECKBOX_ICON;
          Node.SelectedIndex := CHECKBOX_ICON;
        end;
      end else
      begin
        if Node.ImageIndex <> NO_ICON then
        begin
          Node.ImageIndex := NO_ICON;
          Node.SelectedIndex := NO_ICON;
        end;
        if Node.HasChildren then
          Node.DeleteChildren;
      end;
    end;
    Node := Node.GetNext;
  end;
  TempTree.Images := TempImages;
  //Necessary for bitmaps to show up
  TempTree.Repaint;
  TempTree.Items.EndUpdate;
end;


0
williams2Commented:
I got another suggestion to you then:

...Before you update the treeview, go

TempTree.Visible:= False

and at the end make it visble again.

That should by any chance help a lot.

....but You haven't removed those ugly text comparissons, as I told you they ARE slow! ..just try to make a simple procedure running trhough the whole tree comparing a simple text like 'Hello world' - and you'll find that I'm very right about this!

Regards,
Williams
0
williams2Commented:
Continuing...

...Now then go try a comparrison where you try comparing the .data property like...

If node.data = Form1 then...
0
Tom KnowltonWeb developerAuthor Commented:
TreeView.Visible := false [true] did nothing to improve the speed.  Sorry.

Perhaps you are correct about these text comparisons taking so long.

I am sorry, but I still don't understand how using the .Data for the nodes is going to help speed up the process over .Text???

Can you give me 2 examples that show the difference?  Just tiny procedures that show comparisons, one with the Node.Text and one with the Node.Data, so I can see how the change would be implemented?

From what I have read, Node.Data ties a data structure to the TreeView component.  I am not so sure I want to do this.  All of the data in my tree comes from ModuleArray, but it is not quite that simple.  I am organizing the data by Group, then Module, then Languages for that Module.  I just don't see how it will be simple to provide a one to one correspondence for each node in the TreeView.
0
williams2Commented:
The .data is a pointer property. The pointer property is really just a number pointing at some address in memory. Comparing numbers and text id two different thing. When this is done, the computer wil try to match the sentences in each text property.

One thing you can always be sure about, if you are referencing two different objects, you are also referencing two different pointers.

The object system works like this:

You set

Button1 := nil;

...would mean, that you have actually "forgot" an object somwhere, and there is (often) no way to find it again.

You Set:

Button2:= Button1

means that you are now pointing at the xact same object as Button1, which means, that changing Button2.name wil also change the name of button1 (They are the same identical objects)

....Listen knowlton, I could probably tell you a very lot more about objects (as I have allready done so far) ..and you will first be able to undertand it for real, when you go try experiencing these matters on your own. There are plenty of books and FAQ's aound the Internet beeing a much better help than I can ever be in this forum as this is something, that you just have to get used to.
To get used to this, you will need to read a lot more about this topic and do some execises in between.

regards,
Williams
0
Tom KnowltonWeb developerAuthor Commented:
williams2:

I doubt that I will find much on the internet or in FAQ's to explain to me how Node.Data works.  That's the reason I use Experts-Exchange.

I would rather have you explain this concept to me HERE in Experts-Exchange.  If you have the patience to do this, I am willing to learn it.

For the most part, all we have to do is optimize the UpdateAttachmentCount procedure.  It is the only slow down in my program right now.

If you do not want to help me further, than please go ahead and ANSWER this question, and I will give you the points.  You have put a lot of work into this and I want to give you something.

Thanks,

Tom
0
williams2Commented:
Hi Tom:

Do not misunderstand me when saying I don't want you to understand how the Node.Data works.
But to understand how to use it means to understand how to use pointers and objects. If you do not want to learn that, you won't be able to solve this problem. In the end, this is what Delphi is all good at.
To be doing this, you will need to rewrite quite big parts of your program. But without rewriting it, you are bound to a slow treeview.
So go put some courage into this and try to understand how this works as it is something quite essential to develope efficient programs.

And DO understand this:
I do help people a lot, but I will use my time as efficient as I possible can. If you don't want to learn by studying, then please don't blame me for not solving your problem.

Let the question be for a moment to see if you will understand object-pascal.

Regards,
Wiliams
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.