Recent

Author Topic: TTreeNode.FindNode Issue  (Read 15088 times)

famichael

  • New member
  • *
  • Posts: 8
TTreeNode.FindNode Issue
« on: November 11, 2012, 11:25:02 am »
Hi,

I'm having trouble with TTreeNode.FindNode.
I have a TTreeView with several parent, children nodes ... About 1000 nodes.
so treeview1 has many levels of treenodes. I need to be able to search the treeview and
have the node focused. My root node is selected. If I put a button on the form and onclick event
It doesn't find the node.  How should I use this functionality?

Do I need to loop through the Treeview nodes ( var i: integer; for i:= 0 to Treeview1.Items.Count -1 do something) ???  Does treenode.findnode have a native
ability to search the Treeview without me having to use a for or while loop?

What am I doing wrong. When I use a for loop, I can find the node, however, I have to still use
the mouse to scroll down, since the line indicator doesn't go to the found node :(

...
var Anode: TTreeNode;
begin

   Anode.FindNode('Astring');
   ShowMessage(Anode.Text);
end;

Blaazen

  • Hero Member
  • *****
  • Posts: 3241
  • POKE 54296,15
    • Eye-Candy Controls
Re: TTreeNode.FindNode Issue
« Reply #1 on: November 11, 2012, 01:51:23 pm »
Hi, this
Code: [Select]
Anode.FindNode('Astring');
is a function so you have to assign result:
Code: [Select]
Anode:=Anode.FindNode('Astring');

@ When I use a for loop, I can find the node, ...
Try this:
Code: [Select]
TreeView1.Selected:=aNode;
it should select node but it may depend on some TreeView options (like AutoExpand etc.).

EDIT: I'm not sure but it seems the FindNode(); searchs only in one sublevel and not full tree.
« Last Edit: November 11, 2012, 02:02:39 pm by Blaazen »
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1272
Re: TTreeNode.FindNode Issue
« Reply #2 on: November 11, 2012, 10:29:05 pm »
Sorry - I'm away from code at the moment, so the actual calls may be wrong.  You'll get the idea though.

Searching through a TreeNodes as a flat list isn't recommended.  And as Blaazen noted, FindNode only works at a particular sub level.  This is by design.

You'll need to recurse over the tree.
Start at TreeView.Items[0]
Node.FirstChild will get you down a level
Node.GetNextSibling will help you scan along a level

Both of these calls return Nil when you get the end, so you can use this to put required stops into your recursion routine.

When you do find your Node, then you need to make two calls.
Node.Selected := True;
and here by memory fails - it's something like Node.MakeVisible;

I'll dig out example code in a few hours and post...
Lazarus Trunk/FPC latest fixes on Windows 11
  I'm getting old and stale.  Slowly getting used to git, I'll get there...

famichael

  • New member
  • *
  • Posts: 8
Re: TTreeNode.FindNode Issue
« Reply #3 on: November 11, 2012, 11:34:01 pm »

Thanks for the reply Blaazen & Mike.Cornflake


Mike - 
   If you get a chance, a code sample would be GREATLY appreciated!!!

This is what my TreeView looks like

 -Root
    |___ Location 
                 |___ <location code (50 children location codes)
                        |______ Purchases
                                     |________ Item ( ~1000 children Item ID's)
                                                      |____Codes
                                                               |_____Service
                                                                          |____BuyHistory

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1272
Re: TTreeNode.FindNode Issue
« Reply #4 on: November 12, 2012, 01:52:20 am »
G'day,

What's your Search String?   ie what are you passing to FindNode?

Here's some code I wrote to initialise a TreeView by location (think full path in Windows Explorer).  (This is assuming your TreeView is already loaded).

Root
  - Location
    - <Location Code>
In the above example location would be 'Root\Location\<Location Code>'.  In order to initialise the tree above using the code below, you'd make the call
  SelectNode(MyTreeView, 'Root\Location\<Location Code>', '\');

I'm not saying the code below is elegant, but it gets the job done :-) 

You'll see I'm calling ExpandNode.  That's not normally required.  For one of my uses of the below code it was a TShellTreeView I was initialising - and the child nodes are not pre-loaded by default (to speed the initial loading of the tree).  the Child Nodes in TShellTreeView, which are folders in your filesystem are only loaded as required.

If this is your first time writing recursion, let me know if you have problems reading the code and I'll tighten and comment the code more.    Recursion is great for trees, but it doesn't lead to linear code :-(   the first call into the Recursive routine is on about the fifth last time of the entire block!

And forgive my hungarian notation - apparently this makes me old school nowadays :-)

And apparently FindNode replaces the need for FirstChild/GetNextSibling.  It's either newer than Delphi 5, or I didn't know of it's existence in Delphi 5 :-)

Code: [Select]
Function SelectNode(oTree: TCustomTreeView; sLocation, sDelimiter: String): Boolean;

  Function SelectNode2(oNode: TTreeNode; sRemainingLocation: String): Boolean;
  Var
    sNode: String;
    oTemp: TTreeNode;
    bLast: Boolean;

  Begin
    If Assigned(oNode) Then
    Begin
      oNode.Expand(False);

      bLast := (Pos(sDelimiter, sRemainingLocation) = 0);

      If Not bLast Then
        sNode := TextBetween(sRemainingLocation, '', sDelimiter)
      Else
        sNode := sRemainingLocation;

      oTemp := oNode.FindNode(sNode);

      If Assigned(oTemp) Then
      Begin
        oTemp.Selected := True;
        oTemp.MakeVisible;

        If bLast Then
          Result := True
        Else
          Result := SelectNode2(oTemp, TextBetween(sRemainingLocation, sDelimiter, ''));
      End
      Else
        Result := False;
    End
    Else
      Result := False;
  End;

Var
  oNode: TTreeNode;
  sNode: String;

Begin
  // Good ol' recursion - it's been a while...

  oNode := oTree.TopItem;

  If Assigned(oNode) Then
  Begin
    While Assigned(oNode.GetPrev) Do
      oNode := oNode.GetPrev;

    // We'll manually locate the first node ourselves.
    If Copy(sLocation, 1, Length(sDelimiter)) = sDelimiter Then
      sLocation := Copy(sLocation, 2, Length(sLocation));

    sNode := TextBetween(sLocation, '', sDelimiter);

    While Assigned(oNode) And (oNode.Text <> sNode) Do
      oNode := oNode.GetNext;

    If Assigned(oNode) Then
      Result := SelectNode2(oNode, TextBetween(sLocation, sDelimiter, ''))
    Else
      Result := False;
  End
  Else
    Result := False;
End;

- bah - several edits as I replace my own library routines with stuff you're likely to have :-(  Sorry about that
« Last Edit: November 12, 2012, 01:59:40 am by Mike.Cornflake »
Lazarus Trunk/FPC latest fixes on Windows 11
  I'm getting old and stale.  Slowly getting used to git, I'll get there...

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1272
Re: TTreeNode.FindNode Issue
« Reply #5 on: November 12, 2012, 01:56:59 am »
Whoops - just realised you'll also the following function:

Basically TextBetween does what you think it does.  Returns a substring located between two other substrings.

TextBetween('Hello World', 'He', 'ld') would produce 'ello Wor'.   You can pass empty strings into the start and end substrings, and it'll treat these as start of string and end of string.   I've found over the years this function handles a lot of basic text processing requirements...  In the code above it's used for consuming the text between start of string and first delimiter.


Code: [Select]
Function TextBetween(sSource, sStart, sEnd: String): String;
Var
  iStart, iEnd, iLen: Integer;
  iTemp: Integer;
Begin
  iLen := Length(sSource);

  If sStart = '' Then
    iStart := 1
  Else
    iStart := Pos(sStart, sSource) + Length(sStart);

  If sEnd = '' Then
    iEnd := iLen + 1
  Else
  Begin
    iTemp := iStart - 1;

    iEnd := iTemp + Pos(sEnd, Copy(sSource, iTemp, iLen));
  End;

  Result := Copy(sSource, iStart, (iEnd - iStart));
End;
Lazarus Trunk/FPC latest fixes on Windows 11
  I'm getting old and stale.  Slowly getting used to git, I'll get there...

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: TTreeNode.FindNode Issue
« Reply #6 on: November 12, 2012, 08:24:17 am »
Did you try TreeView.Items.FindNodeWithText ?

Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1272
Re: TTreeNode.FindNode Issue
« Reply #7 on: November 12, 2012, 06:35:06 pm »
Quote
Did you try TreeView.Items.FindNodeWithText ?

Personally, I can't depend on each TreeNode having unique Text, which this assumes.  For the original poster though, this may work just fine.

But I've also been bitten bad in the past with other code that tries to treat a Tree as a simple List - resulting in a slow search...  I haven't seen the code for .GetNext (which I assume FindNoteWithText uses), but there has to be search overheads when you get to the end of each branch.  Any more than a few thousand, and I've found its significantly faster to recurse through nodes than to call .GetNext.

Trees are set up for the kind of search best handled by recursion.  Sure my code isn't elegant, but there are whole swathes of the tree that don't get searched because they are not needed, which more than compensates for the string handling overheads I introduce.

Just trying to give Famichael as much information as I can so he can decide which fits his need better :-)   As I say, I haven't tried actually tried FindNodeWithText.  If Famichael's nodes contain unique text, FindNodeWithText might prove acceptably fast, and certainly the code will be more elegant :-)
Lazarus Trunk/FPC latest fixes on Windows 11
  I'm getting old and stale.  Slowly getting used to git, I'll get there...

famichael

  • New member
  • *
  • Posts: 8
Re: TTreeNode.FindNode Issue
« Reply #8 on: November 19, 2012, 05:37:50 am »
Thanks for the time and effort Mike.Cornflake, I really appreciate it.


 

TinyPortal © 2005-2018