Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

VB.NET: File System Treeview Control

0.00/5 (No votes)
6 Nov 2008 1  
All the code needed to add file system controls to a VB.NET 2005/2008 application
screen_shot.gif

Contents

Introduction

The code in the accompanying demo project includes all the routines needed to add file system treeview and listview controls to a VB.NET project. The demo project illustrates how to set up a treeview and listview control that provides access to the local file system similar to Windows Explorer.

Background

In the project I'm currently working on, I have to include an FTP window. To do so, I first created a class that encapsulates all FTP functionality. Then I proceeded to design a user interface to front the FTP class. This user interface has three sections. One is for navigating and manipulating the local file system. Another is for connecting to a remote FTP server. The third is for navigating and manipulating the remote file system. While putting together the sections for connecting to and navigating the remote machine proved fairly simple, exposing the file system on the local machine proved to be surprisingly difficult.

I was not aware that, unlike VB 6, there are no file system controls in VB.NET. I soon also discovered that, short of paying a premium for such controls, I could not find comprehensive sample code on the Internet. After successfully cobbling together the needed code, I thought there might be others who would appreciate finding a complete set of code for VB.NET file system controls.

Toolkit Classes And Methods

There are a few classes and methods in the demo project that are from my toolkit project. Including the entire toolkit would prove unwieldy and confusing. Hence, I have included in the demo project only those classes and methods needed to support the demo project code.

There are three classes in the demo project that came from my toolkit.

  • C_FileExists ... In addition to checking whether a file exists, this class includes methods for deleting and renaming a file.
  • C_FolderExists ... In addition to checking whether a folder exists, this class includes methods for creating new paths, deleting a folder including all its contents, and renaming a folder.
  • C_StringBuilder ... The .NET Stringbuilder encapsulated in my toolkit so that I can use it without having to import the System.Text namespace.

Then there are methods from my C_Toolkit class that have been made available in the demo project M_Common module. These transplanted methods include, but are not limited to the following.

  • AddChar ... If not present, appends a character to the passed string. The default character is a backslash.
  • ByteUnits ... Converts a total number of bytes into its highest units (Bytes, KB, MB, GB)
  • FilenameFromPath ... An overloaded method that extracts the filename from a full path.
  • NewName ... Asks the user for a new folder or file name. Validates that the name does not include invalid characters.
  • RemoveTrailingCharacter ... Removes one or more occurrences of one or more trailing characters from a string.
  • ValidFileName ... Tests a file (or folder) name for invalid characters.

Extended Textboxes

The demo project includes one of my extended user controls; the extended textbox. You can read about this useful control in this article:

VB.NET User Controls: Extended and Date Textboxes

Functional Highlights

As you can see in the demo window above, the code provided in the demo project includes full support for a label that displays the path for the currently selected folder, a treeview for displaying and navigating the file system structure, plus a listview that shows the files in the currently selected folder. Using the buttons, the user can add folders, rename folders and files, delete files as well as folders plus their entire contents, and refresh the controls. When the Refresh button is clicked, the treeview is repopulated and the originally selected node is reselected and made visible regardless of how deep the originally selected node appears in the file system tree.

In this article I will not attempt to describe the entire demo project. Instead, I will highlight that code which performs significant functions within the project. First, a note about imported namespaces. Aside from the three classes included in the demo project from my toolkit project, there is only one Imports statement which is in the main demo project form.

Imports System.IO

For reference purposes, the controls in the demo project have these names.

  • lblLocalPath ... a label that displays the full path for the currently selected treeview node
  • tvwLocalFolders ... a treeview that displays the file system structure
  • lvwLocalFiles ... a listview that shows the files in the currently selected folder
  • btnLocalAddDir ... the Add Dir button
  • btnLocalRename ... the Rename button
  • btnLocalDelete ... the Delete button
  • btnLocalRefresh ... the Refresh button

As to why the pervasive use of "Local" in the control names ... remember that this code is from an FTP window which has like named "Remote" controls.

Initializing The Treeview

At application startup, the ListRootNodes and ListLocalSubFolders methods handle the chore of initializing the treeview control. When the ListRootNodes method is invoked from within the Form Load event, a root node for each drive in the local file system is added to the treeview. In addition, the first layer of folders below each drive node is also added so that, at startup, the treeview includes "plus" buttons for each drive that has subfolders.

    Private Sub ListRootNodes()
        Dim nodeText As String = ""
        Dim sb As New C_StringBuilder
        tvwLocalFolders.Nodes.Clear()
        tvwLocalFolders.BeginUpdate()
        With My.Computer.FileSystem
            For i As Integer = 0 To .Drives.Count - 1
                '** Build the drive's node text
                sb.ClearText()
                sb.AppendText(.Drives(i).DriveType.ToString)
                sb.AppendText(gtSPACE)
                sb.AppendText(gtLEFT_PAREN)
                sb.AppendText(RemoveTrailingChar(.Drives(i).Name, gtBACKSLASH))
                sb.AppendText(gtRIGHT_PAREN)
                nodeText = sb.FullText
                '** Add the drive to the treeview
                Dim driveNode As TreeNode
                driveNode = tvwLocalFolders.Nodes.Add(nodeText)
                driveNode.ImageIndex = 0
                driveNode.SelectedImageIndex = 1
                driveNode.Tag = .Drives(i).Name
                '** Add the next level of subfolders
                Try
                    ListLocalSubFolders(driveNode, .Drives(i).Name)
                Catch ex As Exception
                End Try
                driveNode = Nothing
            Next
        End With
        tvwLocalFolders.EndUpdate()
    End Sub

After each drive root node is added in the ListRootNodes method, the ListLocalSubFolders method is invoked to insert any folders immediately below the drive root node just added.

    Private Sub ListLocalSubFolders(ByVal ParentNode As TreeNode, _
                                    ByVal ParentPath As String)
        Dim FolderNode As String = ""
        Try
            For Each FolderNode In Directory.GetDirectories(ParentPath)
                Dim childNode As TreeNode
                childNode = ParentNode.Nodes.Add(FilenameFromPath(FolderNode))
                With childNode
                    .ImageIndex = 0
                    .SelectedImageIndex = 1
                    .Tag = FolderNode
                End With
                childNode = Nothing
            Next
        Catch ex As Exception
        End Try
    End Sub

Note that in both the ListRootNodes and ListLocalSubFolders methods the Tag property for each treeview node is set to the full directory path for that node. Having the full path available for each node is key to making all of the other methods in the demo project work.

Populating The Files Listview

The listview shows all of the files in the folder that is selected in the treeview. The list of files is updated, along with the displayed path for the currently selected node, each time a node is selected and the tvwLocalFolders.AfterSelect event fires. This is the first example of where the full directory paths stored in each node's Tag property is put to use.

In this and other methods there is a test to determine whether the method is to be ignored. Each of these tests prevents unwanted methods from being executed when the processing launched by the Refresh button is triggered.

    Private Sub tvwLocalFolders_AfterSelect( _
                ByVal sender As Object, _
                ByVal e As _
                      System.Windows.Forms.TreeViewEventArgs) _
                Handles tvwLocalFolders.AfterSelect
        '** Bail if this routine is to be ignored
        If fbIgnoreClick Then
            Exit Sub
        End If
        '** Display the path for the selected node
        TurnOnHourglass()
        Dim folder As String = tvwLocalFolders.SelectedNode.Tag
        lblLocalPath.Text = folder
        '** Populate the listview with all files in the
        '** selected node
        ListLocalFiles(folder)
        TurnOnArrow()
    End Sub

Within the tvwLocalFolders_AfterSelect method, the files listview is populated by invoking the ListLocalFiles method.

    Private Sub ListLocalFiles(ByVal ParentPath As String)
        lvwLocalFiles.Items.Clear()
        lvwLocalFiles.BeginUpdate()
        Try
            For Each filePath As String In Directory.GetFiles(ParentPath)
                Select Case _
                    (File.GetAttributes(filePath) And FileAttributes.Hidden)
                    Case FileAttributes.Hidden
                        '** Ignore hidden files
                    Case Else
                        Dim lvi As New ListViewItem
                        lvi.SubItems(0).Text = FilenameFromPath(filePath)
                        lvi.SubItems.Add(File.GetLastAccessTime(filePath))
                        lvi.SubItems.Add(ByteUnits( _
                            CType(New FileInfo(filePath).Length, Integer)))
                        lvi.Tag = filePath
                        lvwLocalFiles.Items.Add(lvi)
                        lvi = Nothing
                End Select
            Next
        Catch ex As Exception
        End Try
        lvwLocalFiles.EndUpdate()
    End Sub

Since the Directory.GetFiles method does not discriminate between hidden and non-hidden files, the FileAttributes property of each file is examined to weed out the hidden files.

Add Dir Button

Folders can be added to the local file system with the Add Dir button. When the Add Dir button is clicked, a dialog box is displayed from the btnLocalAddDir.Click event that queries the user for the name of a folder to add below the currently selected treeview node.

Add Dir screen

The dialog box is displayed by the NewName method which ensures the new folder name does not contain any invalid characters. If the user enters a folder name that contains invalid characters, an error message is displayed by the ValidFilename method.

    Public Function NewName(ByVal InputBoxMsg As String, _
                            ByVal InputBoxTitle As String) As String
        Dim name As String = ""
        Do
            name = InputBox(InputBoxMsg, InputBoxTitle, name)
            If name.Length <= 0 Then
                Exit Do
            End If
        Loop While Not ValidFilename(name)
        Return name
    End Function

Once a valid folder name has been entered, the remainder of the btnLocalAddDir.Click event adds the new folder and updates the treeview.

        '** Build the full path for the new folder
        With sb
            .ClearText()
            .AppendText(AddChar(lblLocalPath.Text, gtBACKSLASH))
            .AppendText(newFolder)
            newFolder = .FullText
        End With
        sb = Nothing
        '** Check if the folder already exists
        Dim nf As New C_FolderExists(newFolder)
        If nf.FolderExists Then
            DisplayOops("That folder already exists", False)
            lblLocalPath.Focus()
            Exit Sub
        End If
        '** Add the new folder
        If Not nf.CreatePath Then
            DisplayOops(nf.CreatePathError, False, "Error")
            lblLocalPath.Focus()
            Exit Sub
        End If
        '** Refresh the selected node
        TurnOnHourglass()
        RefreshLocalFolders( _
            tvwLocalFolders.SelectedNode, _
            tvwLocalFolders.SelectedNode.Tag, _
            tvwLocalFolders.SelectedNode.IsExpanded)
        TurnOnArrow()
        lblLocalPath.Focus()

The RefreshLocalFolders method updates the treeview so that the new folder is displayed.

    Private Sub RefreshLocalFolders( _
                ByVal SelectedNode As TreeNode, _
                ByVal SelectedNodeTag As String, _
                ByVal SelectedNodeExpanded As Boolean)
        '** Remove all child nodes under the selected node
        tvwLocalFolders.BeginUpdate
        Dim deleteNode As TreeNode = SelectedNode.FirstNode
        Do Until deleteNode Is Nothing
            deleteNode.Remove()
            deleteNode = SelectedNode.FirstNode
        Loop
        '** Refresh the child nodes below the selected node
        ListLocalSubFolders(SelectedNode, _
                            SelectedNodeTag)
        '** If the selected node is expanded, refresh
        '** the child nodes of each of the selected
        '** node's child nodes
        If SelectedNodeExpanded Then
            Dim childNode As TreeNode = SelectedNode.FirstNode
            Do Until childNode Is Nothing
                ListLocalSubFolders(childNode, _
                                    childNode.Tag)
                childNode = childNode.NextNode
            Loop
        End If
        tvwLocalFolders.EndUpdate
    End Sub

Rename Button

Both folders and files can be renamed by clicking the Rename button. What happens when the Rename button is clicked depends on whether any files are selected in the listview, and whether the node selected in the treeview is a root node.

  • If one or more files are selected, new names for those files are requested from the user first. Then, assuming a root node is not selected, the user is queried for a new folder name for the selected node.
  • If no files are selected, and the selected node is not a root node, a new name for the selected folder is requested.
  • If no files are selected, and the selected node is a root node, a Beep is sounded.

Rename screen

The same Rename dialog box is used for querying the user for new folder and file names. Only the Folder or File section in the dialog box is active when the window is displayed. Which section is active is controlled from within the btnLocalRename.Click event with a file path placed in one of two global post office box variables located in the M_Common module (gtPOBox01 or gtPOBox02).

Before entering the file renaming the For...Next loop in the btnLocalRename.Click event, all of the paths for the selected files are captured in an array. This is necessary because, as the files are renamed and the listview updated, the sequence in which the files are listed will probably change.

    Private Sub btnLocalRename_Click(ByVal sender As System.Object, _
                                     ByVal e As System.EventArgs) _
                                     Handles btnLocalRename.Click
        '** A node in the folder treeview must be selected
        If lblLocalPath.Text.Length <= 0 Then
            Beep()
            lblLocalPath.Focus()
            Exit Sub
        End If
        '** If one or more files are being renamed, do
        '** them first
        If lvwLocalFiles.SelectedItems.Count > 0 Then
            '** Ensure the folder rename is turned off
            '** in the Rename dialog box
            gtPOBox01 = ""
            '** Capture all of the paths for the files
            '** being renamed
            Dim filePaths() As String
            ReDim filePaths(lvwLocalFiles.SelectedItems.Count - 1)
            For i As Integer = 0 To filePaths.GetUpperBound(0)
                filePaths(i) = lvwLocalFiles.SelectedItems(i).Tag
            Next
            '** Rename each file
            For j As Integer = 0 To filePaths.GetUpperBound(0)
                gtPOBox02 = filePaths(j)
                Dim re As New F_Rename
                Select Case re.ShowDialog
                    Case Windows.Forms.DialogResult.OK
                        '** Changed name ... do the rename
                        Try
                            RenameLocalFile(filePaths(j), gtPOBox02)
                        Catch ex As Exception
                            DisplayOops(ex.Message, _
                                        False, _
                                        "Error")
                            lblLocalPath.Focus()
                            Exit Sub
                        End Try
                    Case Windows.Forms.DialogResult.Ignore
                        '** Clicked Skip ... nothing to do
                    Case Windows.Forms.DialogResult.Cancel
                        '** Clicked cancel ... bail
                        lblLocalPath.Focus()
                        Exit Sub
                End Select
                re = Nothing
            Next
            ReDim filePaths(-1)
        End If
        '** If other than a root folder is selected,
        '** see if the user wants to rename it
        If lblLocalPath.Text.Length > 3 Then
            '** Ensure the file rename is turned off
            '** in the Rename dialog box
            gtPOBox02 = ""
            '** Rename the folder
            gtPOBox01 = tvwLocalFolders.SelectedNode.Tag
            Dim re As New F_Rename
            If re.ShowDialog = Windows.Forms.DialogResult.OK Then
                RenameLocalFolder(gtPOBox01)
            End If
            re = Nothing
        End If
        lblLocalPath.Focus()
    End Sub

The RenameLocalFile and RenameLocalFolder methods do the actual renaming chores.

    Private Sub RenameLocalFile(ByVal FilePath As String, _
                                ByVal NewFileName As String)
        '** Rename the file
        Dim sf As New C_FileExists(FilePath)
        Try
            sf.RenameFile(NewFileName)
        Catch ex As Exception
            Throw New ApplicationException(ex.Message)
        End Try
        sf = Nothing
        '** Refresh the file listview
        Try
            ListLocalFiles(tvwLocalFolders.SelectedNode.Tag)
        Catch ex As Exception
        End Try

    End Sub
    Private Sub RenameLocalFolder(ByVal NewFolderName As String)
        '** Rename the folder
        Dim sf As New C_FolderExists(lblLocalPath.Text)
        Try
            sf.RenameFolder(NewFolderName)
        Catch ex As Exception
            DisplayOops(ex.Message, False, "Error")
            Exit Sub
        End Try
        sf = Nothing
        '** Refresh all nodes below the parent of the node
        '** that was renamed
        RefreshLocalFolders( _
            tvwLocalFolders.SelectedNode.Parent, _
            tvwLocalFolders.SelectedNode.Parent.Tag, _
            tvwLocalFolders.SelectedNode.Parent.IsExpanded)
    End Sub

Delete Button

The Delete button enables the user to delete both folders and files from the local file system. Like the Rename button, what happens when the Delete button is clicked depends on whether any files are selected, and whether a root node is selected in the treeview.

  • If one or more files are selected, those files are deleted first. Then, assuming a root node is not selected, the selected folder is deleted.
  • If no files are selected, and the selected node is not a root node, the selected folder is deleted.
  • If no files are selected, and the selected node is a root node, a Beep is sounded.

Of course, nothing is deleted without first getting an OK from the user. Unlike with the Rename button, a different dialog box is displayed to confirm deletes for files and folders. This arrangement is used so that a Delete All option can be provided when deleting files. Such an option is not relevant when deleting just one folder.

Delete File screen

The Delete File dialog box is displayed in turn for each file being deleted unless the user clicks the Delete All button. At that point, the remainder of the selected files, starting with the one displayed in the Delete File dialog box, are deleted without first displaying the dialog box.

Delete Folder screen

Another reason to have separate dialog boxes for files and folders is to stress the point that when the selected folder is deleted, all of the folder's contents are also removed.

The entire file and folder delete process is controlled within the btnLocalDelete.Click event. As with the Rename process, the paths for all of the files being deleted are first captured so that the removal of deleted files from the listview will not interfer with the For...Next loop that controls the deletion of files.

    Private Sub btnLocalDelete_Click(ByVal sender As System.Object, _
                                     ByVal e As System.EventArgs) _
                                     Handles btnLocalDelete.Click
        '** A node in the folder treeview must be selected
        If lblLocalPath.Text.Length <= 0 Then
            Beep()
            lblLocalPath.Focus()
            Exit Sub
        End If
        '** If one or more files are being deleted,
        '** do them first
        If lvwLocalFiles.SelectedItems.Count > 0 Then
            '** Capture all of the paths for the
            '** files being deleted
            Dim filePaths() As String
            ReDim filePaths(lvwLocalFiles.SelectedItems.Count - 1)
            For i As Integer = 0 To filePaths.GetUpperBound(0)
                filePaths(i) = lvwLocalFiles.SelectedItems(i).Tag
            Next
            '** Ask the user if each file is to
            '** be deleted
            For j As Integer = 0 To filePaths.GetUpperBound(0)
                gtPOBox02 = filePaths(j)
                Dim de As New F_Delete
                Select Case de.ShowDialog
                    Case Windows.Forms.DialogResult.OK
                        '** Delete just the one file
                        TurnOnHourglass()
                        If Not DeleteLocalFile(filePaths(j), _
                                               tvwLocalFolders.SelectedNode.Tag) Then
                            lblLocalPath.Focus()
                            Exit Sub
                        End If
                        TurnOnArrow()
                    Case Windows.Forms.DialogResult.Ignore
                        '** Delete all files starting with the one just displayed
                        TurnOnHourglass()
                        For k As Integer = j To filePaths.GetUpperBound(0)
                            If Not DeleteLocalFile(filePaths(k), _
                                                   tvwLocalFolders.SelectedNode.Tag) Then
                                lblLocalPath.Focus()
                                Exit Sub
                            End If
                        Next
                        TurnOnArrow()
                        Exit For
                    Case Windows.Forms.DialogResult.Cancel
                        '** Clicked cancel ... bail
                        lblLocalPath.Focus()
                        Exit Sub
                End Select
                de = Nothing
            Next
            ReDim filePaths(-1)
        End If
        '** If other than a root folder is
        '** selected, delete it
        If lblLocalPath.Text.Length > 3 Then
            Dim sb As New C_StringBuilder
            With sb
                .AppendText("This will delete folder ")
                .AppendText(lblLocalPath.Text)
                .AppendText(" and all of its contents")
                .AppendText(ControlChars.CrLf)
                .AppendText(ControlChars.CrLf)
                .AppendText("Continue with delete?")
                gtMsg = .FullText
            End With
            sb = Nothing
            If MessageBox.Show(gtMsg, _
                               "Delete Folder", _
                               MessageBoxButtons.YesNo, _
                               MessageBoxIcon.Question, _
                               MessageBoxDefaultButton.Button2) = _
                                            Windows.Forms.DialogResult.Yes Then
                TurnOnHourglass()
                DeleteLocalFolder()
                TurnOnArrow()
            End If
        End If
        lblLocalPath.Focus()
    End Sub

The actual deleting of files and folders is handled by the DeleteLocalFile and DeleteLocalFolder methods.

    Private Function DeleteLocalFile(ByVal FilePath As String, _
                                     ByVal NodePath As String) As Boolean
        Try
            File.Delete(FilePath)
            ListLocalFiles(NodePath)
            Return True
        Catch ex As Exception
            DisplayOops(ex.Message, False, "Delete Error")
            Return False
        End Try
    End Function
    Private Sub DeleteLocalFolder()
        '** Capture info on the deleted
        '** folder's parent node
        Dim selectedNodeParent As TreeNode = tvwLocalFolders.SelectedNode.Parent
        Dim selectedNodeParentTag As String = tvwLocalFolders.SelectedNode.Parent.Tag
        Dim selectedNodeParentIsExpanded As Boolean = _
                                        tvwLocalFolders.SelectedNode.Parent.IsExpanded
        '** Delete the folder
        Dim sf As New C_FolderExists(lblLocalPath.Text)
        Try
            sf.DeleteFolder()
        Catch ex As Exception
            DisplayOops(ex.Message, False, "Error")
            Exit Sub
        End Try
        sf = Nothing
        '** Refresh all nodes below the parent
        '** of the node that was deleted
        RefreshLocalFolders(selectedNodeParent, _
                            selectedNodeParentTag, _
                            selectedNodeParentIsExpanded)
    End Sub

Refresh Button

In my opinion, the code that accomplishes the refreshing of the treeview control is the most interesting code in the demo project. Here's the psuedo code for doing what, at first blush, seemed like it should be a simple, straight-forward task.

  1. Build an array of node directory paths starting with the currently selected node and then working back up to the parent root node. (Another instance of using the paths stored in each node's Tag property.)
  2. Clear both the treeview and listview.
  3. Initialize the treeview with drive root nodes and all child nodes at the next level down under each root node. (Same process as performed at application startup.)
  4. Using the last element in the array of node directory paths from step 1, find the root node that starts the branch down to the originally selected node.
  5. Using the remainder of the array of node directory paths from step 1 (in reverse order), re-expand the branch of nodes from the original root node back down to the originally selected node.
  6. Ensure the originally selected node is visible in the treeview.
  7. Relist any files in the originally selected folder.
  8. Display the path for the selected node.

The process of re-expanding the treeview down to the originally selected node (steps 4 and 5) has to be robust enough to handle a broken chain. In other words, if a folder in the original branch of selected nodes is deleted or renamed in some other application before the Refresh button is clicked, the re-expansion process has to be able to stop at the point where the branch is broken without throwing an exception.

Here's the btnLocalRefresh.Click event where all the above steps get done. This was certainly the most challenging process in the demo project to write.

    Private Sub btnLocalRefresh_Click(ByVal sender As System.Object, _
                                      ByVal e As System.EventArgs) _
                                      Handles btnLocalRefresh.Click
        '** A node in the folder treeview
        '** must be selected
        If lblLocalPath.Text.Length <= 0 Then
            Beep()
            lblLocalPath.Focus()
            Exit Sub
        End If
        '** Capture an array of the nodes leading
        '** from the currently selected node back up
        '** to the branch root node
        TurnOnHourglass()
        Dim branchPaths() As String
        Dim i As Integer = -1
        Do
            i += 1
            ReDim Preserve branchPaths(i)
            branchPaths(i) = tvwLocalFolders.SelectedNode.Tag
            tvwLocalFolders.SelectedNode = tvwLocalFolders.SelectedNode.Parent
        Loop While tvwLocalFolders.SelectedNode IsNot Nothing
        '** Clear both the treeview and listview
        lvwLocalFiles.Items.Clear()
        lvwLocalFiles.Refresh()
        tvwLocalFolders.Nodes.Clear()
        tvwLocalFolders.Refresh()
        '** Reinitialize the treeview with the
        '** drives and each drive's child nodes
        ListRootNodes()
        '** Find the root node that is the
        '** start of the branch
        Dim foundNode As Boolean = False
        For j As Integer = 0 To tvwLocalFolders.Nodes.Count - 1
            If tvwLocalFolders.Nodes(j).Tag = branchPaths(i) Then
                tvwLocalFolders.SelectedNode = tvwLocalFolders.Nodes(j)
                foundNode = True
                Exit For
            End If
        Next
        '** If, for some reason, the root node
        '** could not be found, bail
        If Not foundNode Then
            tvwLocalFolders.Focus()
            TurnOnArrow()
            Exit Sub
        End If
        '** Reexpand the path from the original
        '** root node back down to the originally
        '** selected node
        tvwLocalFolders.BeginUpdate()
        fbIgnoreClick = True
        i -= 1
        Do While i > -1
            '** Instantiate the first child node
            '** below the currently selected node
            Dim child As TreeNode = tvwLocalFolders.SelectedNode.FirstNode
            '** Scan the child nodes looking for
            '** the next lower branch node
            foundNode = False
            Do
                If child.Tag = branchPaths(i) Then
                    '** Select the newly found
                    '** branch node
                    tvwLocalFolders.SelectedNode = child
                    '** Add the child nodes below
                    '** the just found branch node
                    ListLocalSubFolders(tvwLocalFolders.SelectedNode, _
                                        tvwLocalFolders.SelectedNode.Tag)
                    '** Expand the newly found branch
                    '** node
                    tvwLocalFolders.SelectedNode.Expand()
                    '** Successfully exit the search
                    '** at this node level
                    foundNode = True
                    Exit Do
                End If
                child = child.NextNode
            Loop While child IsNot Nothing
            If foundNode Then
                child = Nothing
            Else
                '** Not the normal exit. This means a
                '** branch node could not be found.
                Exit Do
            End If
            '** Jump down to the next lower branch node
            i -= 1
        Loop
        fbIgnoreClick = False
        tvwLocalFolders.EndUpdate()
        '** Refresh the listview and ensure the
        '** originally selected node is visible
        If foundNode Then
            ListLocalFiles(tvwLocalFolders.SelectedNode.Tag)
            tvwLocalFolders.SelectedNode.EnsureVisible()
        End If
        '** Display the path for the selected node
        Try
            lblLocalPath.Text = tvwLocalFolders.SelectedNode.Tag
        Catch ex As Exception
        End Try
        '** Place the focus on the treeview
        tvwLocalFolders.Focus()
        TurnOnArrow()

    End Sub

Points of Interest

When initially populating the file system treeview, I found that it's not wise to try to populate all node levels. In other words, when first populating the treeview, don't try to populate it with the entire file system. Doing so incurs way too much overhead. Instead, only the nodes at the next level below each expanded node are populated at the time each node is expanded. This limits the overhead for adding nodes to only those that are needed when they are needed.

This selective population of treeview nodes is accomplished by the ListLocalSubFolders method (see Initializing The Treeview section above) which is called when the tvwLocalFolders.BeforeExpand event is triggered.

    Private Sub tvwLocalFolders_BeforeExpand( _
                ByVal sender As Object, _
                ByVal e As _
                    System.Windows.Forms.TreeViewCancelEventArgs) _
                Handles tvwLocalFolders.BeforeExpand
        '** Bail if this routine is to be ignored
        If fbIgnoreClick Then
            Exit Sub
        End If
        '** Display the path for the selected node
        TurnOnHourglass()
        lblLocalPath.Text = e.Node.Tag
        '** Populate all child nodes below the
        '** selected node
        Dim parentPath As String = AddChar(e.Node.Tag)
        tvwLocalFolders.BeginUpdate()
        Dim childNode As TreeNode = e.Node.FirstNode
        Do While childNode IsNot Nothing
            ListLocalSubFolders(childNode, _
                                parentPath & childNode.Text)
            childNode = childNode.NextNode
        Loop
        tvwLocalFolders.EndUpdate()
        '** Select the node being expanded
        tvwLocalFolders.SelectedNode = e.Node
        '** Populate the listview with all files
        '** in the selected node
        ListLocalFiles(parentPath)
        TurnOnArrow()
    End Sub

Conclusion

Writing the code in this project certainly helped me to better understand how the .NET treeview control functions. Now that the project is done and published, I hope others will take this opportunity to get more comfortable with the treeview control. I also hope that readers find the code in this project useful.

History

11/2008 ... Original article

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here