Fill a TreeView with recursivity
I decided to put in 1 single place an article to help build a treeview with recursivity.
There is a lot of forum or Question and Answser related on TreeView but i think is a good idea to write 1 article on the subject.
For many people, anything that works with node is a little bit difficult. The structure itself is not easy. For example, xml file a compose of node. If you don't like to work with node, you will probably choose to work with text file. Text File are simple. They work line by line. So is very sequential.
TreeView work the same way. They use node to structure his own databse. Each node have a parent. That means that the Node can't exist alone.
TreeView are composed of nodes after nodes. If your TreeView represent a kind of explorer, you might have several types of object such as folder and files.
In this post, I have to give you a warning. I am not interested to vomit the answer. Instead, I’ll try to explain how I could reach the reach the answer so eventually; you could understand how I was able to build the code without any copy paste from any forum or blog over the internet. This post might not for you. If not, you will immediately go straight to the answer at the far end of this article. You copy and paste the code and voilà. Here is the link: [Search File - Improve performance with Recursivity]
If you force yourself to read this article, please note I will separate the article into 4 parts
To be honest, I can’t remember making any treeview for a Windows Start Menu in my lifetime. I never Bing’d or Google’d for the answer. I simply applied a logical pathway to create the code I want less than 10 minutes. Yes, under 10 minutes with music on my headphone. Anyway.
Part 1 : Basic recursive search file
I pick a code from one of my previous post : search file search file - Improve performance . In that post a made a recursive function to search file file and directory over and over. All the result are injected in a ListView and not in a TreeView. Enventually, we will have to adapt the code because a ListView and a TreeView works differently.
While ListView group the items in a collection, Treeview are grouping the data with nodes.
Please take a look at the code and go to the next post.
Private Sub findfiles() Dim dirPath As String Dim path_valid As Boolean path_valid = True dirPath = "c:\temp\" ' temp = no access problem dirPath = "c:\System Recovery" ' System Recovery = access problem '---------------------------------------------- ' CHECK PATH IF VALID '---------------------------------------------- 'trim the sPath to remove space before and after the String dirPath = Trim(dirPath) 'check if sPath is not zero length If Len(dirPath) > 0 Then Else path_valid = False End If 'check for invalid char ' \ * | : ? < > / except the \ because is a directory seperator ' and * that means to ignore whatever inside the other chars. (optional) If InStr(dirPath, "|", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, ">", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, "<", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, "?", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, ":", CompareMethod.Binary) = 2 Then 'very basic way to check if there is a ":" in the Path. Dim str_temp As String = dirPath.Substring(2, Len(dirPath) - 2) If InStr(str_temp, ":", CompareMethod.Binary) > 0 Then path_valid = False End If End If If InStr(dirPath, "*", CompareMethod.Binary) > 0 Then path_valid = False End If 'be careful with non-english caracters '---------------------------------------------- ' SECURITY '---------------------------------------------- Dim oDirectorySecurity As System.Security.AccessControl.DirectorySecurity Dim oAuthorizationRuleCollection As System.Security.AccessControl.AuthorizationRuleCollection Dim oRule As System.Security.AccessControl.FileSystemAccessRule Dim Security_valid As Boolean Security_valid = False oDirectorySecurity = IO.Directory.GetAccessControl(dirPath) oAuthorizationRuleCollection = oDirectorySecurity.GetAccessRules(True, True, Type.GetType("System.Security.Principal.NTAccount")) For Each oRule In oAuthorizationRuleCollection If oRule.FileSystemRights = Security.AccessControl.FileSystemRights.FullControl Then If InStr(oRule.IdentityReference.Value, My.User.Name) > 0 Then Security_valid = True End If End If Next '---------------------------------------------- ' SECURITY '---------------------------------------------- Dim Is_Folder_shortcut As Boolean Is_Folder_shortcut = False If dirPath.StartsWith("c:\Users", True, System.Globalization.CultureInfo.CurrentCulture) Then 'very basic way to force you to be very carefull with that folder Is_Folder_shortcut = True End If '---------------------------------------------- ' IF ALL OK, THEN DISPLAY THE RESULT '---------------------------------------------- If oForm2 Is Nothing OrElse oForm2.IsDisposed Then oForm2 = New Form2 oForm2.Show() End If oForm2.ListBox1.Items.Clear() If path_valid = True And Security_valid = True And Is_Folder_shortcut = False Then For Each foundFile As String In My.Computer.FileSystem.GetFiles( _ dirPath, FileIO.SearchOption.SearchAllSubDirectories, "*.dll") oForm2.ListBox1.Items.Add(foundFile) Next Else MsgBox("path_valid =" & path_valid & vbCrLf & "Security_valid =" & Security_valid) End If End Sub |
Now, this is a new code with minor change. All modifications are highlighted in yellow.
Private Sub findfiles(Optional dirPath As String = "c:\temp\", Optional String_to_Search As String = "*.dll") 'Dim dirPath As String ' you need to put in argument a "top" directory (optional if you want) Dim path_valid As Boolean path_valid = True 'dirPath = "c:\temp\" ' temp = no access problem 'dirPath = "c:\System Recovery" ' System Recovery = access problem '---------------------------------------------- ' CHECK PATH IF VALID '---------------------------------------------- 'trim the sPath to remove space before and after the String dirPath = Trim(dirPath) 'check if sPath is not zero length If Len(dirPath) > 0 Then Else path_valid = False End If 'check for invalid char ' \ * | : ? < > / except the \ because is a directory seperator ' and * that means to ignore whatever inside the other chars. (optional) If InStr(dirPath, "|", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, ">", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, "<", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, "?", CompareMethod.Binary) > 0 Then path_valid = False End If If InStr(dirPath, ":", CompareMethod.Binary) = 2 Then 'very basic way to check if there is a ":" in the Path. Dim str_temp As String = dirPath.Substring(2, Len(dirPath) - 2) If InStr(str_temp, ":", CompareMethod.Binary) > 0 Then path_valid = False End If End If If InStr(dirPath, "*", CompareMethod.Binary) > 0 Then path_valid = False End If 'be careful with non-english caracters '---------------------------------------------- ' SECURITY '---------------------------------------------- Dim oDirectorySecurity As System.Security.AccessControl.DirectorySecurity Dim oAuthorizationRuleCollection As System.Security.AccessControl.AuthorizationRuleCollection Dim oRule As System.Security.AccessControl.FileSystemAccessRule Dim Security_valid As Boolean Security_valid = False oDirectorySecurity = IO.Directory.GetAccessControl(dirPath) oAuthorizationRuleCollection = oDirectorySecurity.GetAccessRules(True, True, Type.GetType("System.Security.Principal.NTAccount")) For Each oRule In oAuthorizationRuleCollection If oRule.FileSystemRights = Security.AccessControl.FileSystemRights.FullControl Then If InStr(oRule.IdentityReference.Value, My.User.Name) > 0 Then Security_valid = True End If End If Next '---------------------------------------------- ' SECURITY '---------------------------------------------- Dim Is_Folder_shortcut As Boolean Is_Folder_shortcut = False If dirPath.StartsWith("c:\Users", True, System.Globalization.CultureInfo.CurrentCulture) Then 'very basic way to force you to be very carefull with that folder Is_Folder_shortcut = True End If '---------------------------------------------- ' IF ALL OK, THEN DISPLAY THE RESULT '---------------------------------------------- If oForm2 Is Nothing OrElse oForm2.IsDisposed Then oForm2 = New Form2 oForm2.Show() End If 'oForm2.ListBox1.Items.Clear() ' recursive search file : don't clear the listBox If path_valid = True And Security_valid = True And Is_Folder_shortcut = False Then For Each foundFile As String In My.Computer.FileSystem.GetFiles( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly, String_to_Search) 'recursive search file : you need to limit your search to 1 single directory : and put extention in search argument oForm2.ListBox1.Items.Add(foundFile) Next For Each foundDirectory As String In My.Computer.FileSystem.GetDirectories(dirPath, FileIO.SearchOption.SearchTopLevelOnly) findfiles(foundDirectory) ''recursive search file : re-use "himself" Next Else MsgBox("path_valid =" & path_valid & vbCrLf & "Security_valid =" & Security_valid) End If End Sub |
Part 2 : Analyzing the needs
This is the best part. You need to analyze your code. Try to understand what the function needs to do what you want. This is not an easy step. With practice, you could build you brain to code directly in Visual Studio.
Lets only focus on this part of the code:
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly, String_to_Search) oForm2.ListBox1.Items.Add(foundFile) Next For Each foundDirectory As String In My.Computer.FileSystem.GetDirectories( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly) findfiles(foundDirectory) ''recursive search file : re-use "himself" Next |
We see that for every found file, it will inject the name inside the ListBox1. Simple right? Yes it is. Ok, What if we replace the ListBox1 for TreeView ?
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly, String_to_Search) 'oForm2.ListBox1.Items.Add(foundFile) TreeView1.Nodes.Add(foundFile) Next For Each foundDirectory As String In My.Computer.FileSystem.GetDirectories( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly) findfiles(foundDirectory) ''recursive search file : re-use "himself" Next |
Congratulations, your treeview will get all the file names on the root directory. Now, what do you have to do to put the files from the sub-directories? You know that the 2nd loop is capable to get the finds the files from all subdirectories but your recursive function will work.
What do you want?
I want the TreeView to display the files under his directory using a recursive function.
What do you have now?
I have the ListBox that gets all the files and directories injected using a recursive function.
What your TreeView need to give what you want?
Need a file path and an active node.
Is your current recursive function having all the parameter for the TreeView?
The TreeView need the active node to put the files Names. The Nodes is part of the structure of the TreeNodes.
Is not really easy to put in words what you have in mind. Some people will see another way to get to the results. Some solutions are more efficient while others are more appropriate.
Part 3 : Implementing the changes
Ok, time to change the recursive function with the question you ask yourself. You may have to do some tries or test before getting something to work perfectly. Normally, if you analyze your problem and ask yourself a lot of question, you should be able to make the changes easily.
Before :
Private Sub findfiles(Optional dirPath As String = "c:\temp\", Optional String_to_Search As String = "*.dll") |
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly, String_to_Search) oForm2.ListBox1.Items.Add(foundFile) Next For Each foundDirectory As String In My.Computer.FileSystem.GetDirectories( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly) findfiles(foundDirectory) ''recursive search file : re-use "himself" Next |
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly, String_to_Search) 'recursive search file : you need to limit your search to 1 single directory : and put extension in search argument 'oForm2.ListBox1.Items.Add(foundFile) 'TreeView1.Nodes.Add(foundFile) no1.Nodes.Add(foundFile) Next For Each foundDirectory As String In my.Computer.FileSystem.GetDirectories( dirPath, FileIO.SearchOption.SearchTopLevelOnly) 'findfiles(foundDirectory) ''recursive search file : re-use "himself" Dim no2 As TreeNode no2 = no1.Nodes.Add(foundDirectory) findfiles(no2, foundDirectory, String_to_Search) ''recursive search file : re-use "himself" Next |
After:
Private Sub findfiles(no1 As TreeNode, Optional dirPath As String = "c:\temp\", Optional String_to_Search As String = "*.dll") |
I added no1 for node one, this will represent the main node or current node. It works exactly the same way compared to dirPath. dirPath acts like the current path. All the stuff in the argument in a recursive function are constantly reused and is always base from the current data. The current data here in this example is the current node with a current directory.
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _ dirPath, FileIO.SearchOption.SearchTopLevelOnly, String_to_Search) 'recursive search file : you need to limit your search to 1 single directory : and put extension in search argument 'oForm2.ListBox1.Items.Add(foundFile) 'TreeView1.Nodes.Add(foundFile) no1.Nodes.Add(foundFile) Next For Each foundDirectory As String In my.Computer.FileSystem.GetDirectories( dirPath, FileIO.SearchOption.SearchTopLevelOnly) 'findfiles(foundDirectory) ''recursive search file : re-use "himself" Dim no2 As TreeNode no2 = no1.Nodes.Add(foundDirectory) findfiles(no2, foundDirectory, String_to_Search) ''recursive search file : re-use "himself" Next |
And here how I decided to start my findfiles function. I injected a first node called MAIN.
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click 'position() 'Fill_TreeView() Me.TreeView1.Nodes.Add("MAIN") Dim no As TreeNode no = Me.TreeView1.Nodes.Item(0) findfiles(no, "C:\ProgramData\Microsoft\Windows\Start Menu", "*.lnk") End Sub |
Part 4 : Running the Code
Let’s remember I made a sample of a Windows 8 Start menu using VB.NET with Visual Studio 2010. I was doing the program in the menu. Is not something hard, is just logic and simple if you carefully think of what are you doing.
Run the Visual Basic code when you are ready. Here you want to fill a TreeView. Here is the final result:
Of course, the TreeView is terribly ugly. The list is upside down. There are no icons. The text is small and the whole path is displayed.
I guest you might do the same exercises, check for your existing code and find what is missing.
About
I invite you to visit my blog for more articles and leave a comment. Check Technologies represents more than 10 years .... Computer and computer aided design.
0 Response to "TreeView with recursivity"
Posting Komentar