B4A Library [Class] Treeview Class (Tree and Node)

After finding how to communicate between classes I improved the previous version and since it is completely different, I opened a new thread for it.

There is a Tree class, in which you have to define the nature of the tree (what database it displays). This class manages the nodes which are defined by another class.

I implemented a horizontal "scroll panel" to compensate for the scrollview's lack of it.

The main is a demo with one tree in portrait and two trees in landscape node.

Edit: Version 3 has automatic setting of the treeview panel height and width, so linewidth is calculated, not supplied.
The datalist is sorted.
Arrows added to the slidepanel to hint about its use.
Edit: version 3 updated to have automatic width reduction when nodes close.
Version 4 can scroll horizontally thanks to Informatix additions.
Edit: version 5 use Informatix new 2D ScrollView.

Edit: Updated to ver. 6 :
- The tree is isolated from the specific use (like file manager or family tree), the definition of the data that populates the nodes is done at the calling activity using sub GetData which is called by the tree when required.( Edit: not all the way, in the node_open you still have to change the tag and the text of the new nodes to suit other kinds of trees...)
- LongClick on a node is passed for action to the calling activity to a sub NodeLongClicked.
- OpenRoot method enables display of the root node open (by code, the user can of course open it by pressing the + button)
- NullImages - Possibility to remove the images of file and folder (the label will move left accordingly).
- Wait cursor activated smartly (!!) only when opening nodes with many children, not to slow the program too much.

Edit: Updated to ver. 7 :
See details in this post. http://www.b4x.com/forum/additional...ss-treeview-class-tree-node-3.html#post149450

Edit: updated to ver. 8, the only change from 7 is that data type is in the tree class , not in the main.
Also the Reflection library is not used and can be ticked off.

Edit: update to ver 9. I saw that with new devices of high resolution the lineheight is too large so I adjusted it to give better results for all devices.
If you encounter line size problems please tell me (with the specific device parameter).

Edit: see post #85 for a method to open all nodes of the tree by code.

Looking forward to get feedback !

Note: For B4A ver. 7 replace in tree line 190 Doevent with sleep(0)
 

Attachments

  • screen3.png
    screen3.png
    58.8 KB · Views: 3,016
  • screen4.png
    screen4.png
    57.7 KB · Views: 2,634
  • Treeview9.1.zip
    24.6 KB · Views: 1,000
Last edited:

derez

Expert
Licensed User
Longtime User
I checked with your code in resume and it works. You need only tree2.OpenRoot there.
I don't know why it does not work for you.
Where do you initialize ? where do you add the view ? Does it happan in all devices ?
What happens if you run the demo program ? There is a ResetTree menu, add the tree2.OpenRoot command and see what happens.
The OpenRoot command does the same as clicking the root button (the +). What happens then in your code ?
 
Last edited:

derez

Expert
Licensed User
Longtime User
Version 9 update, to fix large lineheight in high resolution devices.
 

crawler

Member
Licensed User
Longtime User
Hi, I really like your TreeviewC9 and I'm looking at how to merge it with DBRequestManager to RDC an external database (MS SQL Server).
I have RDC set up and working well with a ListView or a ScrollView.
Can you recommend a simple way to do this with your Treeview?
 

derez

Expert
Licensed User
Longtime User
Treeview is just the way of presenting the data.
you should create a sub named GetData and it should provide the data from the DB to the tree for opening a node.
Here is an example from my Family tree application where there are two types of trees - roots and tree, selected by treeflag value.
B4X:
'Set imgtflag 0 for no image, 1 for folder , 2 for file (general), 3 for specific image by imgdir and imgfile, 4 for image by bitmap using da.bmp
Sub GetData(tr As Tree, st As String) 'ignore
Dim k, ColorSet, P_id(2) As Int 'ignore
Dim str, Tag,Text,BtnTag As String 'ignore
k = st
If treeflag Then                                      ' root
    cur = SQL.ExecQuery2("SELECT Fcod, Mcod FROM FTable WHERE No = ?",Array As String(k))
    cur.Position = 0
    P_id(0) = cur.GetInt("Fcod")
    P_id(1) = cur.GetInt("Mcod")
    For j = 0 To 1
        Dim da As Data
        If P_id(j) > 0 Then
            da.Tag = P_id(j)
            If relpnl.Visible AND check_rlist(P_id(j)) Then
                da.ColorSet = 1
            Else
                da.ColorSet = 0
            End If
          
            cur = SQL.ExecQuery2("SELECT Surname, Name, Fcod, Mcod FROM FTable WHERE No = ?",Array As String(P_id(j)))
            cur.Position = 0
            da.Text = cur.GetString("Surname")& " " & cur.GetString("Name")
          
            If cur.GetInt("Fcod") > 0 OR cur.GetInt("Fcod")>0 Then
                da.BtnTag = True
            Else
                da.BtnTag = False
            End If
          
            If File.Exists(File.DirRootExternal & "/Family/jpg",P_id(j) & ".jpg") Then
                da.imgflag = 3
                da.imgdir = File.DirRootExternal & "/Family/jpg"
                da.imgfile = P_id(j) & ".jpg"
            Else
                da.imgflag = 0
            End If
          
            tr.DataList.Add(da)
        End If
    Next
Else                                                    ' tree
    Dim cur1 As Cursor
    Dim kk, scod As Int
    cur = SQL.ExecQuery2("SELECT No,Surname,Name,Scod FROM FTable WHERE Fcod = ? OR Mcod = ?",Array As String(k,k))
    If cur.RowCount > 0 Then
        For i = 0 To cur.RowCount - 1
            Dim da As Data
            cur.Position = i
            kk = cur.GetInt("No")
            scod = cur.GetInt("Scod")
            da.Tag = kk
            If relpnl.Visible AND check_rlist(kk) Then da.ColorSet = 1 Else da.ColorSet = 0
            If scod > 0 Then
                da.Text = cur.GetString("Surname") & " " & cur.GetString("Name") & " + " & Get_Name(scod)
            Else
                da.Text = cur.GetString("Surname") & " " & cur.GetString("Name")
            End If
            cur1 = SQL.ExecQuery2("SELECT No FROM FTable WHERE Fcod = ? OR Mcod = ?",Array As String(kk,kk))
            If cur1.RowCount > 0 Then
                da.BtnTag = True
            Else
                da.BtnTag = False
            End If
          
            If File.Exists(File.DirRootExternal & "/Family/jpg",kk & ".jpg") Then
                da.imgflag = 3
                da.imgdir = File.DirRootExternal & "/Family/jpg"
                da.imgfile = kk & ".jpg"
            Else
                da.imgflag = 0
            End If
            tr.DataList.Add(da)
        Next
    End If
End If
End Sub
 

crawler

Member
Licensed User
Longtime User
Thanks, I'll see what I can do.
Please let me know if you get the Treeview to RDC with your MySQL database.
 

derez

Expert
Licensed User
Longtime User
This application does not work with RDC, but anyway the treeview is just a way to display the data, getting it from/to the RDC is not part of the treeview.
 

Troberg

Well-Known Member
Licensed User
Longtime User
Is it possible to make a "root-less" tree (or, if you prefer, a multi-root tree)?

Say, for example, that I wan't to make a media list, with the levels being artist, album and title, such as:

The Sisters of Mercy
+ Some girls wander by mistake
++ Temple of Love
++ Floorshow
+ Floodland
++ Lucretia
Rammstein
+ Herzeleid
++ Seemann
+ Mutter
++ Ich Will
++ Sonne

In this example, both The Sisters of Mercy and Rammstein would be at the root level.
 

Troberg

Well-Known Member
Licensed User
Longtime User
Oh, by the way, another thing that would be incredibly handy is a way to add a node with a path, and it takes care of creating intermediary nodes as needed automatically.

In my example above, I would do something like (now, I'm ignoring some bits just to keep it clear):

item.Path="The Sisters of Mercy/Some girls wander by mistake/Temple of Love"
tree.AddByPath(item)

This would then add item as a node, and automatically create the nodes "The Sisters of Mercy" and "Some girls wander by mistake" at the correct position if they don't already exist.

It's not something that's always useful (for example, when listing a file system, it's not that useful), but in other cases, it's immensely practical.

I've made my own subclassed treeview for VB with (among other things) this method, and it really helps simplify and clean up usage.
 

derez

Expert
Licensed User
Longtime User
Is it possible to make a "root-less" tree (or, if you prefer, a multi-root tree)?
A tree by definition must have a root. In your example each album name is the tree root and you need trees as the number of albums.
a way to add a node with a path, and it takes care of creating intermediary nodes as needed automatically.
The tree as is works like this, with the understanding that someone (you ! ) has to give it the path. In directory/file tree the logic is by listing files in directory which is embedd in the language. For your case you need to create the database so the creation of a node can look for the data ("path" ) somewhere.
The sub that should take care of this data is "GetData" which you have to put in the module that calls the tree.
 

Troberg

Well-Known Member
Licensed User
Longtime User
Yep, I've looked a bit more now, andI realize I've misunderstood a couple of things.

As it is, it does not look like it's the right component for me, as I don't want to fill it gradually as nodes are expanded, I just want to stuff it all in there. I get all my data in one operation, and it would require me to build up some searchable backend for caching my data. Too much work, I'll build another solution.

Thanks anyway, it's a neat solution, if one has the right needs.
 

Bernhard Svavarsson

Member
Licensed User
Longtime User
Treeview is just the way of presenting the data.
you should create a sub named GetData and it should provide the data from the DB to the tree for opening a node.
Here is an example from my Family tree application where there are two types of trees - roots and tree, selected by treeflag value.
B4X:
'Set imgtflag 0 for no image, 1 for folder , 2 for file (general), 3 for specific image by imgdir and imgfile, 4 for image by bitmap using da.bmp
Sub GetData(tr As Tree, st As String) 'ignore
Dim k, ColorSet, P_id(2) As Int 'ignore
Dim str, Tag,Text,BtnTag As String 'ignore
k = st
If treeflag Then                                      ' root
    cur = SQL.ExecQuery2("SELECT Fcod, Mcod FROM FTable WHERE No = ?",Array As String(k))
    cur.Position = 0
    P_id(0) = cur.GetInt("Fcod")
    P_id(1) = cur.GetInt("Mcod")
    For j = 0 To 1
        Dim da As Data
        If P_id(j) > 0 Then
            da.Tag = P_id(j)
            If relpnl.Visible AND check_rlist(P_id(j)) Then
                da.ColorSet = 1
            Else
                da.ColorSet = 0
            End If
         
            cur = SQL.ExecQuery2("SELECT Surname, Name, Fcod, Mcod FROM FTable WHERE No = ?",Array As String(P_id(j)))
            cur.Position = 0
            da.Text = cur.GetString("Surname")& " " & cur.GetString("Name")
         
            If cur.GetInt("Fcod") > 0 OR cur.GetInt("Fcod")>0 Then
                da.BtnTag = True
            Else
                da.BtnTag = False
            End If
         
            If File.Exists(File.DirRootExternal & "/Family/jpg",P_id(j) & ".jpg") Then
                da.imgflag = 3
                da.imgdir = File.DirRootExternal & "/Family/jpg"
                da.imgfile = P_id(j) & ".jpg"
            Else
                da.imgflag = 0
            End If
         
            tr.DataList.Add(da)
        End If
    Next
Else                                                    ' tree
    Dim cur1 As Cursor
    Dim kk, scod As Int
    cur = SQL.ExecQuery2("SELECT No,Surname,Name,Scod FROM FTable WHERE Fcod = ? OR Mcod = ?",Array As String(k,k))
    If cur.RowCount > 0 Then
        For i = 0 To cur.RowCount - 1
            Dim da As Data
            cur.Position = i
            kk = cur.GetInt("No")
            scod = cur.GetInt("Scod")
            da.Tag = kk
            If relpnl.Visible AND check_rlist(kk) Then da.ColorSet = 1 Else da.ColorSet = 0
            If scod > 0 Then
                da.Text = cur.GetString("Surname") & " " & cur.GetString("Name") & " + " & Get_Name(scod)
            Else
                da.Text = cur.GetString("Surname") & " " & cur.GetString("Name")
            End If
            cur1 = SQL.ExecQuery2("SELECT No FROM FTable WHERE Fcod = ? OR Mcod = ?",Array As String(kk,kk))
            If cur1.RowCount > 0 Then
                da.BtnTag = True
            Else
                da.BtnTag = False
            End If
         
            If File.Exists(File.DirRootExternal & "/Family/jpg",kk & ".jpg") Then
                da.imgflag = 3
                da.imgdir = File.DirRootExternal & "/Family/jpg"
                da.imgfile = kk & ".jpg"
            Else
                da.imgflag = 0
            End If
            tr.DataList.Add(da)
        Next
    End If
End If
End Sub



Interesting - Not sure what is or where is 'Treeflag' defined ?

Regards

Bernhard
 

derez

Expert
Licensed User
Longtime User
Not sure what is or where is 'Treeflag' defined ?
Treeflag is a global boolean, made false or true by the user who wants to see, for the presented person, either the roots tree or the offsprings tree.
To clarify, it is not part of the class but of the specific application.
 

Bernhard Svavarsson

Member
Licensed User
Longtime User
Treeflag is a global boolean, made false or true by the user who wants to see, for the presented person, either the roots tree or the offsprings tree.
To clarify, it is not part of the class but of the specific application.

Thanks - yes of course it's not part of the class

Regards

Bernhard
 

derez

Expert
Licensed User
Longtime User
In post #44 above I give an example of two trees which are selected by the boolean treeflag. In your data base these are the two tables.
You should take a decision in sub GetData - if the node clicked is the root(inventory) you have to add the two table roots as nodes to inventory, otherwise open the node clicked from the required table (a or b). How to tell which table to use ? depends on your data.
As you see, GetData is not actually part of the tree, it is "just" providing the data to the opened nodes.
 

ToolboxZX

Member
Licensed User
Longtime User
derez,

Is it possible to add code to change the image of the node next to the + / - symbol when a user expands or collapses a tree? I see in the code how to change what images are added as the nodes are created, and also how to change out the actual + and - images for whatever I want in the code. But it is not clear how to change the other image (next to the plus or minus). Thanks!
 

derez

Expert
Licensed User
Longtime User
Node.initialize includes an img parameter, so you have to pass it from the main to tree class in the da type.
 

ToolboxZX

Member
Licensed User
Longtime User
Derez thanks for the reply :) Can you give me an example, or show the section of code this would be in? I having trouble visualizing it :( What I am after basically is to be able to change the icon of the folder from closed to open when you expand or collapse that section of the tree (think Windows File manager). :)
 

derez

Expert
Licensed User
Longtime User
What I am after basically is to be able to change the icon of the folder from closed to open when you expand or collapse that section of the tree (think Windows File manager).
I don't understand. The feature you describe is exactly what the first icon (+/-) does !
Please look at the example: in Node module sub btn_click calls node_open or node_close in Tree module, and switch the icons +-.
After that, node_open calls the main module to get the data about the node's content and node_close removes the node's content.
 
Last edited:

ToolboxZX

Member
Licensed User
Longtime User
Sorry, I used a poor example (Windows File Manager). Yes, the first icon (+/-) does change. In addition to that, I am trying to change the other icon, on the same line as the +/- icon, associated with the node. Here are some images to clarify
 

Attachments

  • closed folder example2.png
    closed folder example2.png
    5 KB · Views: 231
  • open folder example2.png
    open folder example2.png
    6.7 KB · Views: 215
  • open folder example3.png
    open folder example3.png
    9.8 KB · Views: 218

derez

Expert
Licensed User
Longtime User
change fileimg and folderimg to be the images in your example, instead of the attached images to my example:
B4X:
fileimg.Initialize(File.DirAssets,"file.png")
folderimg.Initialize(File.DirAssets,"folder2.png")
 
Top