Recent

Author Topic: [SOLVED] Populate Treeview from Table  (Read 1045 times)

Hansvb

  • Hero Member
  • *****
  • Posts: 618
[SOLVED] Populate Treeview from Table
« on: December 09, 2022, 04:29:33 pm »
Hi,

In a table you have the following data: GUID, NAME, PARENT.
The table is populated when a TTreeView is saved. The Rootnode then has a GUID but no Parent and the other nodes all have a
GUID and a PARENT. The PARENT guid of a child treenode is de GUID of its parent node. So its al realated.

Now I want to rebuild the Treeview with the in the table.

I think the first thing to do is read all de records of the tabel and put in een array so that I can easily loop through it
:

Code: Pascal  [Select][+][-]
  1.   ApiObjectData = record
  2.     Guid : String;
  3.     ParentFolder : String;
  4.     Name : String;
  5.   end;
  6.   AllApiObjectData = array of ApiObjectData;


Fill the array:

Code: Pascal  [Select][+][-]
  1. procedure TFolder.ReadFolderList(var fd: AllApiObjectData);
  2. var
  3.   SqlText : String;
  4.   i : Integer;
  5. begin
  6.   SqlText := 'Select GUID, NAME, PARENT ' +
  7.              'from ' + TableName.FOLDER_LIST;
  8.  
  9.   With DataModule1 do begin
  10.     try
  11.       ...
  12.       SQLQuery.SQL.Text := SqlText;
  13.       SQLQuery.Open;
  14.       SQLQuery.First;
  15.  
  16.       i := 0;
  17.       SetLength(fd, SQLQuery.RecordCount);
  18.  
  19.       while not SQLQuery.Eof do  begin
  20.         fd[i].Guid := SQLQuery.FieldByName('GUID').AsAnsiString;
  21.         fd[i].ParentFolder := SQLQuery.FieldByName('PARENT').AsAnsiString;
  22.         fd[i].Name := SQLQuery.FieldByName('NAME').AsAnsiString;
  23.         i += 1;
  24.         SQLQuery. next;
  25.       end;
  26.  
  27.       SQLQuery.Close;
  28.       SQLite3Connection.Close();
  29.  
  30.     except
  31.       on E : Exception do begin
  32.       ...
  33.       end;
  34.     end;
  35.   end;
  36. end;

Now the array is filled with all de records, I need to loop throug it and continue looping until all nodes have been read in and the nodes must be assigned to the correct parent. This is where I get stuck

Code: Pascal  [Select][+][-]
  1. procedure TFolder.PopulateTreeView(Trv: TTreeView);
  2. var
  3.   aGuid : String;
  4.   fd: AllApiObjectData;
  5.   i, j : Integer;
  6.   s : String;
  7.   RootNode : TTreeNode;
  8.   ChildNodes : Array of TTreeNode;
  9. begin
  10.   Trv.Items.Clear;
  11.   ReadFolderList(fd);
  12.  
  13.   for i:= 0 to Length(fd)-1 do begin
  14.     if (fd[i].Guid <> '') and  (fd[i].ParentFolder = '') then begin  // rootnode
  15.        RootNode := Trv.Items.Add(nil, fd[i].Name);
  16.        aGuid := fd[i].Guid;
  17.        break;
  18.     end;
  19.  
  20.     Now search in fd[i].ParentFolder = aGuid. There are a few hits.
  21.     How to continue  <----------------
  22.  
  23.   end;
  24. end;  



« Last Edit: December 13, 2022, 04:37:56 pm by Hansvb »

ASerge

  • Hero Member
  • *****
  • Posts: 2241
Re: Populate Treeview from Table
« Reply #1 on: December 09, 2022, 05:04:27 pm »
If there is so little data that they fit entirely into memory, then maybe not using the database, but just the SaveToFile and LoadFromFile methods built into TTreeView?

Hansvb

  • Hero Member
  • *****
  • Posts: 618
Re: Populate Treeview from Table
« Reply #2 on: December 09, 2022, 05:49:44 pm »
The data will grow and  the actual record is bigger than in my example.

cdbc

  • Hero Member
  • *****
  • Posts: 1078
    • http://www.cdbc.dk
Re: Populate Treeview from Table
« Reply #3 on: December 09, 2022, 07:42:56 pm »
Hi
I use something like this:
Code: Pascal  [Select][+][-]
  1. function bcGetNodeWithParentByTextAtLevel(aTree: TTreeView;
  2.                                           aParent: TTreeNode;
  3.                                           const aValue: string;
  4.                                           aVisible: boolean;
  5.                                           aLevel: integer): TTreeNode;
  6. var
  7.   Node: TTreeNode;
  8. begin
  9.   Result:= nil;
  10.   if aTree.Items.Count = 0 then exit; { nothing to do }
  11.   Node:= aTree.Items[0]; { ie.: root-node }
  12.   while Node <> nil do begin
  13.     if ((UpperCase(Node.Text) = UpperCase(aValue)) and
  14.        (Node.Level = aLevel) and (Node.Parent = aParent)) then begin
  15.       Result:= Node;
  16.       if aVisible then Result.MakeVisible;
  17.       break;
  18.     end;
  19.     Node:= Node.GetNext;
  20.   end;
  21. end; { bcGetNodeWithParentByTextAtLevel }
  22.  
and use it like this:
Code: Pascal  [Select][+][-]
  1. WeekNode:= bcGetNodeWithParentByTextAtLevel(trvNav,YearNode,anItem.Date.WeekNumberAsString,false,2);
  2.     if WeekNode <> nil then begin
  3.       // do stuff
  4.     end;
  5.  
HTH
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

ASerge

  • Hero Member
  • *****
  • Posts: 2241
Re: Populate Treeview from Table
« Reply #4 on: December 10, 2022, 01:47:52 pm »
The data will grow and  the actual record is bigger than in my example.
But you said: "The table is filled when saving TTreeView". That is, the data is completely in memory.

If the entire data is only in the database, and the part that the user wants to see is loaded into memory, then it is better to load data from the database only according to the visible node. And subload when opening a tree subnode.

Hansvb

  • Hero Member
  • *****
  • Posts: 618
Re: Populate Treeview from Table
« Reply #5 on: December 10, 2022, 04:08:49 pm »
@Aserge, I want to build the treeview with the data of the database table when de program starts. For that i put the data in an array and then i want to build the treeview with the data in the array. Until now i only seem to be able to build the rootnode and the first level child nodes with this:

Code: Pascal  [Select][+][-]
  1. procedure TFolder.PopulateTreeView(Pguid, Cguid : String);
  2. var
  3.   i: Integer;
  4.   ParentTn,  ChildTn  : TTreeNode;
  5. begin
  6.   if (Pguid = '') and (cGuid = '') then begin  // RootNode
  7.     Trv.Items.Clear;
  8.     for i:= 0 to Length(fd)-1 do begin
  9.       if (fd[i].Guid <> '') and  (fd[i].ParentFolder = '') then begin  // rootnode
  10.          ParentTn := Trv.Items.Add(nil, fd[i].Name);
  11.          pGuid := fd[i].Guid;
  12.          break;
  13.       end;
  14.     end;
  15.   end;
  16.  
  17.   if (ParentTn <> nil) and (Pguid <> '') and (cGuid = '') then begin  // first level
  18.     for i:= 0 to Length(fd)-1 do begin
  19.       if (fd[i].Guid <> '') and  (fd[i].ParentFolder = pGuid) then begin  // rootnode
  20.         ChildTn :=  Trv.Items.AddChild(ParentTn, fd[i].Name);
  21.       end;
  22.     end;
  23.   end;
  24. end;

egsuh

  • Hero Member
  • *****
  • Posts: 1289
Re: Populate Treeview from Table
« Reply #6 on: December 10, 2022, 04:55:01 pm »
I think you will need recursion and TDataSet's filter function. Haven't tested following codes, but this kind of logic is necessary.

Code: Pascal  [Select][+][-]
  1. procedure AddChildNodes(node: TTreeNode) ;
  2. var
  3.      gid : string;
  4.      tnode: TTreeNode;
  5.      aod: ApiObjectData;
  6. begin
  7.      if node = nil
  8.         then gid := ''
  9.         else gid := ApiObjectData(node.data).Guid;
  10.    
  11.      with SQLQuery do begin
  12.             Filter := 'PARENT=''' + gid + '''';   // SQLQuery.Filtered should have been true
  13.             First;
  14.             while not eof do begin
  15.                   New(aod);
  16.                   aod^.Name := FieldByName('name').AsString;
  17.                   aod^.Guid := FieldByName('Guid').AsString;
  18.                   aod^.ParentFolder := FieldByName('Parent').AsString;
  19.  
  20.                   TreeView.AddChildObject(node, aod^.Name, aod);
  21.                   Next;
  22.             end;
  23.      end;
  24.  
  25.      tnode := node.GetFirstChild;
  26.      while tnode <> nil do begin
  27.              AddchildNodes(tnode);
  28.              tnode := tnode.GetNextSibling;
  29.       end;
  30. end;
  31.  
  32.  
  33. //---------------- and calling the procedure somewhere...
  34.  
  35.  
  36. SQLQuery.Filtered := True;
  37. AddChildNodes(nil);
  38.  
  39.  
   
     



Hansvb

  • Hero Member
  • *****
  • Posts: 618
Re: Populate Treeview from Table
« Reply #7 on: December 11, 2022, 03:13:26 pm »
Hi, it finally works. The treeview is built and if you click on a Node, the data is displayed.


See below. This works, but if there is a better way, please let me know.


Code: Pascal  [Select][+][-]
  1. type
  2.   PtrApiObject = ^ApiObjectData;
  3.   ApiObjectData = record
  4.     Id                      : Integer;
  5.     Guid                    : String;
  6.     ParentFolder            : String;
  7.     Name                    : String;
  8.   end
  9.   AllApiObjectData = array of  ApiObjectData;
  10.  
  11.  
  12. var    
  13.   ParentNodes : Array of TTreeNode;
  14.   ChildNodes : Array of ApiObjectData;
  15.   fd: AllApiObjectData;
  16.   Trv : TTreeView;
  17.    
  18.  
  19. procedure TFolder.PopulateTreeView();
  20. var
  21.   i, p, Counter : Integer;
  22.   ParentTn : TTreeNode;
  23.   aod : PtrApiObject;
  24. begin
  25.   if (ParentNodes <> nil) and ( ChildNodes <> nil) then begin   // search for  ChildNodes in fd[i].ParentFolder
  26.     for p := 0 to Length(ParentNodes) do begin
  27.       for i:= 0 to Length(fd)-1 do begin
  28.         if ChildNodes[p].Guid = fd[i].ParentFolder then begin
  29.           Counter := Length(ParentNodes);
  30.  
  31.           New(aod);
  32.           aod^.Guid := fd[i].Guid;
  33.           aod^.ParentFolder := fd[i].ParentFolder;
  34.           aod^.ObjectType := 'Folder';
  35.           aod^.Name := fd[i].Name;
  36.  
  37.           ParentTn := Trv.Items.AddChildObject(ParentNodes[p], fd[i].Name, aod);
  38.  
  39.           SetLength(ParentNodes, Counter+1);
  40.           ParentNodes[Counter] := ParentTn;
  41.  
  42.           SetLength(ChildNodes, Counter+1);
  43.           ChildNodes[Counter].Guid := fd[i].Guid;
  44.         end;
  45.       end;
  46.  
  47.       delete(ChildNodes, p, 1);   //Syntax is Delete(fromArray, Index, NumberOfItems); Index is zero based.
  48.       delete(ParentNodes, p, 1);
  49.  
  50.       if (ParentNodes <> nil) and (ChildNodes <> nil) then begin
  51.         PopulateTreeView();
  52.       end;
  53.  
  54.       if (ParentNodes = nil) and (ChildNodes = nil) then begin
  55.         Exit;
  56.       end;
  57.  
  58.     end;
  59.   end
  60.   else begin
  61.     Counter := Length(ParentNodes);
  62.     for i:= 0 to Length(fd)-1 do begin
  63.       if (fd[i].Guid <> '') and  (fd[i].ParentFolder = '') then begin  // rootnode
  64.  
  65.         New(aod);
  66.         aod^.Guid := fd[i].Guid;
  67.         aod^.ParentFolder := fd[i].ParentFolder;
  68.         aod^.ObjectType := 'Folder';
  69.         aod^.Name := fd[i].Name;
  70.  
  71.         ParentTn := Trv.Items.AddObject(nil, fd[i].Name, aod);
  72.  
  73.         SetLength(ParentNodes, Counter + 1);
  74.         ParentNodes[Counter] := ParentTn;
  75.  
  76.         SetLength(ChildNodes, Counter + 1);
  77.         ChildNodes[Counter].Guid := fd[Counter].Guid;
  78.         break;
  79.       end;
  80.     end;
  81.     if (ParentTn <> nil)  then begin
  82.       PopulateTreeView();
  83.     end;
  84.   end;
  85. end;
« Last Edit: December 11, 2022, 04:18:04 pm by Hansvb »

egsuh

  • Hero Member
  • *****
  • Posts: 1289
Re: Populate Treeview from Table
« Reply #8 on: December 12, 2022, 04:35:18 am »
I have confirmed following codes work fine.

If you have to use array definitely, try TFPGMap, etc. so that you can select only child records of a parent node. But this is done by just filtering of TDataset. It makes algorithm very simple.

Code: Pascal  [Select][+][-]
  1. type
  2.    ApiObjectData = record
  3.       name,
  4.       guid,
  5.       parentfolder : string;
  6.    end;
  7.    PAOD = ^ApiObjectData;
  8.  
  9. implementation
  10.  
  11. procedure Tform1.PopulateTV(node: TTreeNode) ;
  12. var
  13.      gid : string;
  14.      aod: PAOD;
  15.      tnode: TTreeNode;
  16. begin
  17.      if node = nil
  18.         then gid := ''
  19.         else begin
  20.            aod := node.data;
  21.            gid := aod^.Guid;
  22.         end;
  23.  
  24.      with BufDataSet1 do begin
  25.             Filter := Format('PARENT=''%s''', [gid]);
  26.             First;
  27.  
  28.             while not eof do begin
  29.                   New(aod);
  30.                   aod^.Name := FieldByName('name').AsString;
  31.                   aod^.Guid := FieldByName('Guid').AsString;
  32.                   aod^.ParentFolder := FieldByName('Parent').AsString;
  33.  
  34.                   TreeView1.Items.AddChildObject(node, aod^.Name, aod);
  35.                   Next;
  36.             end;
  37.      end;
  38.      if node = nil
  39.         then tnode := Treeview1.Items.GetFirstNode
  40.         else tnode := node.GetFirstChild;
  41.      while tnode <> nil do begin
  42.              PopulateTV(tnode);
  43.              tnode := tnode.GetNextSibling;
  44.       end;
  45. end;
  46.  
  47.  
  48. procedure TForm1.Button2Click(Sender: TObject);
  49. begin
  50.    PopulateTV(nil);
  51.    BufDataSet1.Filter := '';
  52. end;

Hansvb

  • Hero Member
  • *****
  • Posts: 618
Re: Populate Treeview from Table
« Reply #9 on: December 13, 2022, 04:37:38 pm »
I will try that.

 

TinyPortal © 2005-2018