Click here to Skip to main content
15,892,674 members
Articles / Desktop Programming / Win32

ExpTreeLib Version 3 - Explorer-like Navigation and Operation for your Forms

Rate me:
Please Sign up or sign in to vote.
4.97/5 (54 votes)
9 Jan 2014CPOL22 min read 281K   14K   83  
A Class Library for building Forms with a folder navigation TreeView and form specific ListViews that can be tailored for your application and behave like Windows Explorer. Full documentation.
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading
Imports System.Windows.Forms
Imports ExpTreeLib.CShItem
Imports ExpTreeLib.ShellDll
Imports ExpTreeLib.ShellDll.ShellAPI
Imports ExpTreeLib.ShellDll.ShellHelper
Imports ExpTreeLib.SystemImageListManager

''' <summary>
''' ExpTree is a UserControl encapsulating a TreeView which will display all or part of the Windows Shell
''' Namespace.  The Shell Namespace is a superset of the Windows file system. It is the Tree commonly shown
''' by Windows Explorer, in Classic View. That is, it is a Tree rooted in the Desktop.
''' ExpTree supports Drag and Drop and standard Windows Context Menus.
''' </summary>
''' <remarks>ExpTree raises one major Event, ExpTreeNodeSelected. That event is raised whenever the 
''' Selected TreeNode changes because of User Action (i.e. -- clicking on the node)</remarks>
<DefaultProperty("StartUpDirectory"), DefaultEvent("StartUpDirectoryChanged")> _
Public Class ExpTree
    Inherits System.Windows.Forms.UserControl

    Private Root As TreeNode

    ''' <summary>
    ''' StartUpDirectoryChanged is raised when the root of the TreeView is changed via StartUpDirectory
    ''' Property. 
    ''' </summary>
    ''' <param name="newVal">One of the StartDir Enum values that represent the possible Start Up Directories.</param>
    ''' <remarks>Seldom listened for since, in typical use, the Method which set the StartUpDirectory value
    ''' is the only Method which is interested. It is also true that a by-product of setting the StartUpDirectory 
    ''' value is the Selection of the new root node.  That change in SelectedNode will cause an ExpTreeNodeSelected
    ''' Event to be raised.</remarks>
    Public Event StartUpDirectoryChanged(ByVal newVal As StartDir)

    ''' <summary>
    ''' ExpTreeNodeSelected is raised when a Node in the TreeView is Selected.
    ''' </summary>
    ''' <param name="SelPath">The Path of the CShItem represented by the TreeNode, and stored in the
    ''' TreeNode's Tag.</param>
    ''' <param name="Item">The CShItem represented by the TreeNode, and stored in the
    ''' TreeNode's Tag.</param>
    ''' <remarks></remarks>
    Public Event ExpTreeNodeSelected(ByVal SelPath As String, ByVal Item As CShItem)

    Private EnableEventPost As Boolean = True 'flag to supress ExpTreeNodeSelected raising during refresh and 

    Private WithEvents DropHandler As CtvDropWrapper

    Private DragHandler As CDragWrapper

    Private m_showHiddenFolders As Boolean = True

    Private m_windowsContextMenu As WindowsContextMenu = New WindowsContextMenu

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call


        'setting the imagelist here allows many good things to happen, but
        ' also one bad thing -- the "tooltip" like display of selectednode.text
        ' is made invisible.  This remains a problem to be solved.
        SystemImageListManager.SetTreeViewImageList(tv1, False)

        AddHandler StartUpDirectoryChanged, AddressOf OnStartUpDirectoryChanged

        AddHandler CShItemUpdate, AddressOf OnItemUpdate            '7/1/2012

        'RaiseEvent StartUpDirectoryChanged(StartDir.Desktop)        '11/08/2013 -- Removed 01/08/2014

    End Sub
    'ExpTree overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
            RemoveHandler CShItemUpdate, AddressOf OnItemUpdate     '7/1/2012
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer
    Friend WithEvents tv1 As System.Windows.Forms.TreeView

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.

    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.tv1 = New System.Windows.Forms.TreeView
        Me.SuspendLayout()
        '
        'tv1
        '
        Me.tv1.Dock = System.Windows.Forms.DockStyle.Fill
        Me.tv1.HideSelection = False
        Me.tv1.HotTracking = True
        Me.tv1.Location = New System.Drawing.Point(0, 0)
        Me.tv1.Name = "tv1"
        Me.tv1.ShowRootLines = False
        Me.tv1.Size = New System.Drawing.Size(200, 264)
        Me.tv1.TabIndex = 1
        '
        'ExpTree
        '
        Me.Controls.Add(Me.tv1)
        Me.Name = "ExpTree"
        Me.Size = New System.Drawing.Size(200, 264)
        Me.ResumeLayout(False)

    End Sub

#End Region

#Region "   Initialization/Destruction Events"

    <DllImport("uxtheme.dll", CharSet:=CharSet.Unicode)> _
    Private Shared Function SetWindowTheme(ByVal hWnd As IntPtr, ByVal pszSubAppName As String, ByVal pszSubIdList As String) As Integer
    End Function

    Private Sub tv1_HandleCreated(ByVal sender As Object, ByVal e As System.EventArgs) Handles tv1.HandleCreated
        DragHandler = New CDragWrapper(Me.tv1)
        If m_AllowDrop Then DropHandler = New CtvDropWrapper(Me.tv1) '7/11/2012
        SetWindowTheme(tv1.Handle, "explorer", Nothing)
        ' Update: Check against nothing as when the treeview is used in a modal 
        ' dialog and shown more than once a duplicate call causes a horizontal 
        ' scrollBar to appear in the control???
        'If tv1.TreeViewNodeSorter Is Nothing Then tv1.TreeViewNodeSorter = New TagComparer  '5/9/2012 Removed - not needed and can cause extreme delays
    End Sub

    Private Sub tv1_HandleDestroyed(ByVal sender As Object, ByVal e As EventArgs) Handles tv1.HandleDestroyed

    End Sub

#End Region

#Region "   Public Properties"

#Region "       AllowDrop - Property added 7/11/2012"

    Private m_AllowDrop As Boolean = False

    ''' <summary>
    ''' Turns this ExpTree Control's ability to accept Drops on or Off.<br />
    ''' True - Enables the ExpTree Control to accept Drops.<br />
    ''' False - Disables the ExpTree Control acceptance of  Drops.
    ''' </summary>
    ''' <returns>True or False</returns>
    ''' <remarks>Works by assigning or  removing an instance of CtvDropWrapper to the Local variable DropHandler.</remarks>
    Public Overrides Property AllowDrop() As Boolean
        Get
            Return DropHandler IsNot Nothing
        End Get
        Set(ByVal value As Boolean)
            m_AllowDrop = value
            If value Then
                If tv1.IsHandleCreated Then
                    If DropHandler Is Nothing Then      'otherwise, already running
                        DropHandler = New CtvDropWrapper(tv1)
                    End If
                End If
            Else
                If DropHandler IsNot Nothing Then
                    DropHandler.Dispose()
                    DropHandler = Nothing
                End If
            End If
        End Set
    End Property

#End Region

#Region "       RootItem"
    '''<summary>
    ''' RootItem is a Run-Time only Property. Setting this Item via an External call results in
    '''  re-setting the entire tree to be rooted in the input CShItem.
    ''' The new CShItem must be a valid CShItem of some kind of Folder (File Folder or System Folder).
    ''' Attempts to set it using a non-Folder CShItem are ignored.
    '''</summary>
    <Browsable(False)> _
    Public Property RootItem() As CShItem
        Get
            If Root Is Nothing OrElse Root.Tag Is Nothing Then  '11/05/2013
                Return CShItem.GetDeskTop                       '11/05/2013
            Else                                                '11/05/2013
                Return Root.Tag
            End If                                              '11/05/2013
        End Get
        Set(ByVal Value As CShItem)
            If Value.IsFolder Then
                tv1.BeginUpdate()
                ClearTree()
                Dim CSI() As CShItem = Value.Directories
                Root = New TreeNode(Value.DisplayName)
                BuildTree(CSI)
                Root.ImageIndex = SystemImageListManager.GetIconIndex(Value, False)
                Root.SelectedImageIndex = Root.ImageIndex
                Root.Tag = Value
                tv1.Nodes.Add(Root)
                Root.Expand()
                tv1.SelectedNode = Root
                tv1.EndUpdate()
            End If
        End Set
    End Property
#End Region

#Region "       SelectedItem"
    ''' <summary>
    ''' Run-time only Property which returns the CShItem underlying the SelectedNode of the TreeView.
    ''' </summary>
    ''' <returns>The underlying CShItem of the TreeView.SelectedNode. If none Selected, returns Nothing.</returns>
    <Browsable(False)> _
    Public ReadOnly Property SelectedItem() As CShItem
        Get
            If Not IsNothing(tv1.SelectedNode) Then
                Return tv1.SelectedNode.Tag
            Else
                Return Nothing
            End If
        End Get
    End Property
#End Region

#Region "       ShowHidden"
    ''' <summary>
    ''' ShowHiddenFolders sets or gets a Boolean indicating whether or not to Display Folders with the Hidden Attribute.
    ''' </summary>
    ''' <value></value>
    ''' <returns>True if ExpTree is Displaying Hidden Folders, False if not.</returns>
    ''' <remarks>Hidden Folders may be Displayed or not Displayed at run-time.</remarks>
    <Browsable(True), Category("Options"), _
    Description("Show Hidden Directories."), _
    DefaultValue(True)> _
   Public Property ShowHiddenFolders() As Boolean
        Get
            Return m_showHiddenFolders
        End Get
        Set(ByVal Value As Boolean)
            If m_showHiddenFolders Xor Value Then
                m_showHiddenFolders = Value
                If Root IsNot Nothing Then RefreshTree() 'Fix 2/5/2012
            End If
        End Set
    End Property
#End Region

#Region "       ShowRootLines"
    ''' <summary>
    ''' Exposes the normal TreeView ShowRootLines property.
    ''' </summary>
    ''' <value></value>
    ''' <returns>The state of the underlying TreeView property.</returns>
    ''' <remarks></remarks>
    <Category("Options"), _
  Description("Allow Collapse of Root Item."), _
  Browsable(True)> _
Public Property ShowRootLines() As Boolean
        Get
            Return tv1.ShowRootLines
        End Get
        Set(ByVal Value As Boolean)
            If Not (Value = tv1.ShowRootLines) Then
                tv1.ShowRootLines = Value
                tv1.Refresh()
            End If
        End Set
    End Property
#End Region

#Region "       StartupDir"
    ''' <summary>
    ''' The values representing the System's Special Folders.
    ''' </summary>
    ''' <remarks>Certain Special Folders are disallowed since they may not exist, or may cause program failure
    ''' on certain versions of Windows (primarily the older, unsupported versions).</remarks>
    Public Enum StartDir As Integer
        Desktop = &H0
        Programs = &H2
        Controls = &H3
        Printers = &H4
        Personal = &H5
        Favorites = &H6
        Startup = &H7
        Recent = &H8
        SendTo = &H9
        StartMenu = &HB
        MyDocuments = &HC
        'MyMusic = &HD
        'MyVideo = &HE
        DesktopDirectory = &H10
        MyComputer = &H11
        My_Network_Places = &H12
        'NETHOOD = &H13
        'FONTS = &H14
        ApplicatationData = &H1A
        'PRINTHOOD = &H1B
        Internet_Cache = &H20
        Cookies = &H21
        History = &H22
        Windows = &H24
        System = &H25
        Program_Files = &H26
        MyPictures = &H27
        Profile = &H28
        Systemx86 = &H29
        AdminTools = &H30
    End Enum

    Private m_StartUpDirectory As StartDir = StartDir.Desktop

    ' 11/04/2012 Removed DefaultValue Property from this declaration.
    ''' <summary>
    ''' Sets the initial Root directory of ExpTree.
    ''' </summary>
    ''' <value>Must be one of the StartDir Enum values.</value>
    ''' <returns>Current StartDir value.</returns>
    ''' <remarks></remarks>
    <Category("Options"), _
     Description("Sets the Initial Directory of the Tree"), _
     Browsable(True)> _
    Public Property StartUpDirectory() As StartDir
        Get
            Return m_StartUpDirectory
        End Get
        Set(ByVal Value As StartDir)
            If Array.IndexOf([Enum].GetValues(Value.GetType), Value) >= 0 Then
                m_StartUpDirectory = Value
                RaiseEvent StartUpDirectoryChanged(Value)
            Else
                Throw New ApplicationException("Invalid Initial StartUpDirectory")
            End If
        End Set
    End Property
#End Region

#End Region

#Region "   Public Methods"

#Region "       ExpandANode"
    ''' <summary>
    ''' Expands TreeNodes from the tree root through the input Path. All intermediate nodes between the
    ''' Tree Root and the input Path are Expanded. If the Optional Property SelectExpandedNode is True (the Default),
    ''' the Expanded Node will be Selected, Raising a ExpNodeSelected Event. If False, the current Selected Node is unchanged
    ''' and no Event is Raised.
    ''' </summary>
    ''' <param name="newPath">The FileSystem path of the Node node to be Expanded.</param>
    ''' <param name="SelectExpandedNode">If True(the Default) then Select the Expanded Node.<br />
    '''                                  If False, Do Not Select the Expanded Node.</param>
    ''' <returns>True if Successful, False otherwise.</returns>
    ''' <remarks>The preferred method is to use:
    ''' <pre lang="vbnet">Public Function ExpandANode(ByVal newItem As CShItem) As Boolean</pre> 
    ''' If the item defined by the input Path does not exist, False is returned.<br />
    ''' Calling with SelectExpandedNode = False is useful when it is not desired to Raise an
    ''' ExpTreeNodeSelected Event as a result of ExpandaNode.</remarks>
    Public Function ExpandANode(ByVal newPath As String, Optional ByVal SelectExpandedNode As Boolean = True) As Boolean  '7/13/2012
        ExpandANode = False     'assume failure
        Dim newItem As CShItem
        Try
            newItem = GetCShItem(newPath)
            If newItem Is Nothing Then Exit Function
            If Not newItem.IsFolder Then Exit Function
        Catch
            Exit Function
        End Try
        Return ExpandANode(newItem, SelectExpandedNode) '7/13/2012
    End Function
    ''' <summary>
    ''' Expands TreeNodes from the tree root through the input CShItem. All intermediate nodes between the
    ''' Tree Root and the input CShItem are Expanded. If the Optional Property SelectExpandedNode is True (the Default),
    ''' the Expanded Node will be Selected, Raising a ExpNodeSelected Event. If False, the current Selected Node is unchanged
    ''' and no Event is Raised.
    ''' </summary>
    ''' <param name="newItem">The CShItem representing the Shell Namespace object whose TreeNode is to
    ''' be expanded.</param>
    ''' <param name="SelectExpandedNode">If True(the Default) then Select the Expanded Node.<br />
    '''                                  If False, Do Not Select the Expanded Node.</param>
    ''' <returns>True if Successful, False otherwise.</returns>
    ''' <remarks>This is the preferred method of ExpandANode.<br />
    ''' Calling with SelectExpandedNode = False is useful when it is not desired to Raise an
    ''' ExpTreeNodeSelected Event as a result of ExpandaNode.</remarks>
    Public Function ExpandANode(ByVal newItem As CShItem, Optional ByVal SelectExpandedNode As Boolean = True) As Boolean   '7/13/2012
        ExpandANode = False     'assume failure
        Dim baseNode As TreeNode = Root
        tv1.BeginUpdate()
        baseNode.Expand() 'Ensure base is filled in
        'do the drill down -- Node to expand must be included in tree
        Dim testNode As TreeNode
        Dim lim As Integer = CShItem.PidlCount(newItem.PIDL) - CShItem.PidlCount(baseNode.Tag.pidl)
        'TODO: Test ExpandARow again on XP to ensure that the CP problem is fixed
        Do While lim > 0
            For Each testNode In baseNode.Nodes
                If CShItem.IsAncestorOf(testNode.Tag, newItem, False) Then
                    baseNode = testNode
                    'RefreshNode(baseNode)   'ensure up-to-date
                    baseNode.Expand()
                    lim -= 1
                    Continue Do
                End If
            Next
            GoTo XIT     'on falling thru For, we can't find it, so get out
NEXLEV: Loop
        'after falling thru here, we have found & expanded the node
        Me.tv1.HideSelection = False
        Me.Select()
        If SelectExpandedNode Then Me.tv1.SelectedNode = baseNode '7/13/2012
        ExpandANode = True
XIT:    tv1.EndUpdate()
        baseNode.EnsureVisible()       '12/18/13
    End Function
#End Region

#End Region

#Region "   Initial Dir Set Handler"

    Private Sub OnStartUpDirectoryChanged(ByVal newVal As StartDir)
        tv1.BeginUpdate()
        ClearTree()
        Dim special As CShItem
        special = GetCShItem(CType(Val(m_StartUpDirectory), ShellDll.ShellAPI.CSIDL))
        Root = New TreeNode(special.DisplayName)
        Root.ImageIndex = SystemImageListManager.GetIconIndex(special, False)
        Root.SelectedImageIndex = Root.ImageIndex
        Root.Tag = special
        BuildTree(special.Directories)
        tv1.Nodes.Add(Root)
        Root.Expand()
        tv1.EndUpdate()
    End Sub

    Private Sub BuildTree(ByVal L1 As CShItem())
        Array.Sort(L1)
        Dim CSI As CShItem
        For Each CSI In L1
            If Not (CSI.IsHidden And Not m_showHiddenFolders) Then
                Root.Nodes.Add(MakeNode(CSI))
            End If
        Next
    End Sub

    ''' <summary>
    ''' Creates a TreeNode whose .Text is the DisplayName of the CShItem.<br />
    ''' Sets the IconIndexes for that TreeNode from the CShItem.<br />
    ''' Sets the Tag of the TreeNode to the CShItem<br />
    ''' If the CShItem (a Folder) has or may have sub-Folders (see Remarks), adds a Dummy node to
    '''   the TreeNode's .Nodes collection. This is always done if the input CShItem represents a Removable device. Checking
    '''   further on such devices may cause unacceptable delays.
    ''' Returns the complete TreeNode.
    ''' </summary>
    ''' <param name="item">The CShItem to make a TreeNode to represent.</param>
    ''' <returns>A TreeNode set up to represent the CShItem.</returns>
    ''' <remarks>
    ''' This routine will not be called if the CShItem (a Folder) is Hidden and ExpTree's ShowHidden Property is False.<br />
    ''' If the Folder is Hidden and ShowHidden is True, then this routine will be called.<br />
    ''' If the Folder is Hidden and it only contains Hidden Folders (files are not considered here), then, 
    ''' the HasSubFolders attribute may be returned False even though Hidden Folders exist. In that case, we 
    ''' must make an extra check to ensure that the TreeNode is expandable.<br />
    ''' 
    ''' There are additional complication with HasSubFolders. 
    ''' <ul>
    ''' <li>
    ''' On XP and earlier systems, HasSubFolders was always
    ''' returned True if the Folder was on a Remote system. On Vista and above, the OS would check and return an 
    ''' accurate value. This extra check can take a long time on Remote systems - approximately the same amount of time as checking
    ''' item.GetDirectories.Count. Versions 2.12 and above of ExpTreeLib have a modified HasSubFolders Property which will always
    ''' return True if the Folder is on a Remote system, restoring XP behavior.</li>
    ''' <li>
    ''' On XP and earlier systems, compressed files (.zip, .cab, etc) were treated as files. On Vista and above, they are treated
    ''' as Folders. ExpTreeLib continues to treat such files as files. The HasSubFolder attribute will report a Folder which
    ''' contains only compressed files as True. In MakeNode, I simply accept the Vista and above interpretation, setting a dummy
    ''' node in such a Folder. An attempt to expand such a TreeNode will just turn off the expansion marker.
    ''' </li>
    ''' </ul>
    ''' </remarks>
    Private Function MakeNode(ByVal item As CShItem) As TreeNode
        Dim newNode As New TreeNode(item.DisplayName)
        newNode.Tag = item
        newNode.ImageIndex = SystemImageListManager.GetIconIndex(item, False)
        newNode.SelectedImageIndex = SystemImageListManager.GetIconIndex(item, True)
        If item.IsRemovable Then
            newNode.Nodes.Add(New TreeNode(" : "))
        ElseIf item.HasSubFolders Then
            newNode.Nodes.Add(New TreeNode(" : "))
        ElseIf item.IsHidden Then
            If item.GetDirectories.Count > 0 Then
                newNode.Nodes.Add(New TreeNode(" : "))
            End If
        End If
        Return newNode
    End Function

    Private Sub ClearTree()
        tv1.Nodes.Clear()
        Root = Nothing
    End Sub
#End Region

#Region "   RefreshTree"
    '''<summary>RefreshTree Method thanks to Calum McLellan</summary>
    <Description("Refresh the Tree and all nodes through the currently selected item")> _
    Private Sub RefreshTree(Optional ByVal rootCSI As CShItem = Nothing)
        'Modified to use ExpandANode(CShItem) rather than ExpandANode(path)
        'Set refresh variable for BeforeExpand method
        EnableEventPost = False
        'Begin Calum's change -- With some modification
        Dim Selnode As TreeNode
        If IsNothing(Me.tv1.SelectedNode) Then
            Selnode = Me.Root
        Else
            Selnode = Me.tv1.SelectedNode
        End If
        'End Calum's change
        Try
            Me.tv1.BeginUpdate()
            Dim SelCSI As CShItem = Selnode.Tag
            'Set root node
            If IsNothing(rootCSI) Then
                Me.RootItem = Me.RootItem
            Else
                Me.RootItem = rootCSI
            End If
            'Try to expand the node
            If Not Me.ExpandANode(SelCSI) Then
                Dim nodeList As New ArrayList()
                While Not IsNothing(Selnode.Parent)
                    nodeList.Add(Selnode.Parent)
                    Selnode = Selnode.Parent
                End While

                For Each Selnode In nodeList
                    If Me.ExpandANode(CType(Selnode.Tag, CShItem)) Then Exit For
                Next
            End If
            'Reset refresh variable for BeforeExpand method
        Finally
            Me.tv1.EndUpdate()
        End Try
        EnableEventPost = True
        'We suppressed EventPosting during refresh, so give it one now
        tv1_AfterSelect(Me, New TreeViewEventArgs(tv1.SelectedNode))
    End Sub
#End Region

#Region "   TreeView BeforeExpand Event"

    Private Sub tv1_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles tv1.BeforeExpand
        Dim oldCursor As Cursor = Cursor
        Cursor = Cursors.WaitCursor
        If e.Node.Nodes.Count = 1 AndAlso e.Node.Nodes(0).Text.Equals(" : ") Then
            PopulateNode(e.Node)            '8/26/2012
        End If
        e.Node.ImageIndex = DirectCast(e.Node.Tag, CShItem).IconIndexOpen
        Cursor = oldCursor
    End Sub

    ''' <summary>
    ''' Called to Populate the TreeNodes of a TreeNode that only contains a Dummy Node.
    ''' </summary>
    ''' <param name="NodeToFill">The unexpanded TreeNode to Fill</param>
    ''' <remarks>Should only be called to populate a TreeNode which only has a Dummy Node.<br />
    ''' Refactored code added 8/26/2012 so that this functionality could be used from more than one method.</remarks>
    Private Sub PopulateNode(ByVal NodeToFill As TreeNode)          '8/26/2012
        Dim CSI As CShItem = NodeToFill.Tag
        Dim D As ArrayList = New ArrayList(CSI.Directories)
        If D.Count > 0 Then
            D.Sort()    'uses the class comparer
            NodeToFill.Nodes.Clear()    '11/03/2012 DO NOT Clear out the dummy prior to calling .Directories which forces a UpdateRefresh!
            For Each Item As CShItem In D
                If Not (Item.IsHidden And Not m_showHiddenFolders) Then
                    NodeToFill.Nodes.Add(MakeNode(Item))
                End If
            Next
        Else        '11/03/2012 BUT DO get rid of any unnessesary Dummy
            NodeToFill.Nodes.Clear()
        End If
    End Sub
#End Region

#Region "   TreeView AfterSelect Event"
    Private Sub tv1_AfterSelect(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles tv1.AfterSelect
        Dim CSI As CShItem = e.Node.Tag
        If EnableEventPost Then 'turned off during RefreshTree
            If CSI.Path.StartsWith(":") Then
                RaiseEvent ExpTreeNodeSelected(CSI.DisplayName, CSI)
            Else
                RaiseEvent ExpTreeNodeSelected(CSI.Path, CSI)
            End If
        End If
    End Sub
#End Region

#Region "   TreeView VisibleChanged Event"
    '''<summary>When a form containing this control is Hidden and then re-Shown,
    ''' the association to the SystemImageList is lost.  Also lost is the
    ''' Expanded state of the various TreeNodes. 
    ''' The VisibleChanged Event occurs when the form is re-shown (and other times
    '''  as well).  
    ''' We re-establish the SystemImageList as the ImageList for the TreeView and
    ''' restore at least some of the Expansion.</summary> 
    Private Sub tv1_VisibleChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles tv1.VisibleChanged
        If tv1.Visible Then
            SystemImageListManager.SetTreeViewImageList(tv1, False)
            If Not Root Is Nothing Then
                Root.Expand()
                If Not IsNothing(tv1.SelectedNode) Then
                    tv1.SelectedNode.Expand()
                Else
                    tv1.SelectedNode = Me.Root
                End If
            End If
        End If
    End Sub
#End Region

#Region "   TreeView BeforeCollapse Event"
    '''<summary>Should never occur since if the condition tested for is True,
    ''' the user should never be able to Collapse the node. However, it is
    ''' theoretically possible for the code to request a collapse of this node
    ''' If it occurs, cancel it</summary>
    Private Sub tv1_BeforeCollapse(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles tv1.BeforeCollapse
        If Not tv1.ShowRootLines AndAlso e.Node Is Root Then
            e.Cancel = True
        Else
            e.Node.ImageIndex = DirectCast(e.Node.Tag, CShItem).IconIndexNormal
        End If
    End Sub
#End Region

#Region "   CtvDropWrapper Event Handling"

    ' dropNode is the TreeNode that most recently was DraggedOver or
    '    Dropped onto.  
    Private dropNode As TreeNode

    Private NodePoint As System.Drawing.Point

    'expandNodeTimer is used to expand a node that is hovered over, with a delay
    Private WithEvents expandNodeTimer As New System.Windows.Forms.Timer()

#Region "       expandNodeTimer_Tick"
    Private Sub expandNodeTimer_Tick(ByVal sender As Object, ByVal e As EventArgs) _
       Handles expandNodeTimer.Tick
        expandNodeTimer.Stop()
        If Not IsNothing(dropNode) Then
            RemoveHandler DropHandler.ShDragOver, AddressOf DragWrapper_ShDragOver
            Try
                tv1.BeginUpdate()
                dropNode.Expand()
                '7/12/2012 - The following block of code relocated from ShDragOver
                Dim delta As Integer = tv1.Height - NodePoint.Y
                If delta < tv1.Height / 2 And delta > 0 Then
                    If Not IsNothing(dropNode) AndAlso Not (dropNode.NextVisibleNode Is Nothing) Then
                        dropNode.NextVisibleNode.EnsureVisible()
                    End If
                End If
                If delta > tv1.Height / 2 And delta < tv1.Height Then
                    If Not IsNothing(dropNode) AndAlso Not (dropNode.PrevVisibleNode Is Nothing) Then
                        dropNode.PrevVisibleNode.EnsureVisible()
                    End If
                End If
                '7/12/2012 - end of relocated block
                dropNode.EnsureVisible()
            Finally
                tv1.EndUpdate()
                AddHandler DropHandler.ShDragOver, AddressOf DragWrapper_ShDragOver
            End Try
        End If
    End Sub
#End Region

    '''<summary>ShDragEnter does nothing. It is here for debug tracking</summary>
    Private Sub DragWrapper_ShDragEnter(ByVal pDataObj As IntPtr, _
                                        ByVal grfKeyState As Integer, _
                                        ByVal pdwEffect As Integer) _
                                Handles DropHandler.ShDragEnter
        'Debug.WriteLine("Enter ExpTree ShDragEnter. PdwEffect = " & pdwEffect)
    End Sub

    '''<summary>Drag has left the control. Cleanup what we have to</summary>
    Private Sub DragWrapper_ShDragLeave() Handles DropHandler.ShDragLeave
        expandNodeTimer.Stop()    'shut off the dragging over nodes timer
        'Debug.WriteLine("Enter ExpTree ShDragLeave")
        If Not IsNothing(dropNode) Then
            ResetTreeviewNodeColor(dropNode)
        End If
        dropNode = Nothing
    End Sub

    '''<summary>ShDragOver manages the appearance of the TreeView.  Management of
    ''' the underlying FolderItem is done in CDragWrapper
    ''' Credit to Cory Smith for TreeView colorizing technique and code,
    ''' at http://addressof.com/blog/archive/2004/10/01/955.aspx
    ''' Node expansion based on expandNodeTimer added by me.
    '''</summary>
    Private Sub DragWrapper_ShDragOver(ByVal Node As TreeNode, _
                                ByVal pt As System.Drawing.Point, _
                                ByVal grfKeyState As Integer, _
                                ByVal pdwEffect As Integer) _
                                Handles DropHandler.ShDragOver
        'Debug.WriteLine("Enter ExpTree ShDragOver. PdwEffect = " & pdwEffect)
        'Debug.WriteLine(vbTab & "Over node: " & CType(Node, TreeNode).Text)

        If IsNothing(Node) Then  'clean up node stuff & fix color. Leave Draginfo alone-cleaned up on DragLeave
            expandNodeTimer.Stop()
            If Not dropNode Is Nothing Then
                ResetTreeviewNodeColor(dropNode)
                dropNode = Nothing
            End If
        Else  'Drag is Over a node - fix color & DragDropEffects
            If Node Is dropNode Then
                Exit Sub    'we've already done it all
            End If

            expandNodeTimer.Stop() 'not over previous node anymore
            Try
                tv1.BeginUpdate()
                '7/12/2012 - the following block relocated to expandNodeTime.Tick
                'Dim delta As Integer = tv1.Height - pt.Y
                'If delta < tv1.Height / 2 And delta > 0 Then
                '    If Not IsNothing(Node) AndAlso Not (Node.NextVisibleNode Is Nothing) Then
                '        Node.NextVisibleNode.EnsureVisible()
                '    End If
                'End If
                'If delta > tv1.Height / 2 And delta < tv1.Height Then
                '    If Not IsNothing(Node) AndAlso Not (Node.PrevVisibleNode Is Nothing) Then
                '        Node.PrevVisibleNode.EnsureVisible()
                '    End If
                'End If
                '7/12/2012 - end of relocated block
                If Not Node.BackColor.Equals(SystemColors.Highlight) Then
                    ResetTreeviewNodeColor(tv1.Nodes(0))
                    Node.BackColor = SystemColors.Highlight
                    Node.ForeColor = SystemColors.HighlightText
                End If
            Finally
                tv1.EndUpdate()
            End Try
            dropNode = Node     'dropNode is the Saved Global version of Node
            NodePoint = pt      '7/12/2012 NodePoint is the Saved, Form Global Mouse Location (in client coordinates)
            If Not dropNode.IsExpanded Then
                expandNodeTimer.Interval = 500  '7/12/2012 - reduced from 1200
                expandNodeTimer.Start()
            End If
        End If
    End Sub

    Private Sub DragWrapper_ShDragDrop(ByVal Node As TreeNode, _
                                ByVal grfKeyState As Integer, _
                                ByVal pdwEffect As Integer) Handles DropHandler.ShDragDrop
        expandNodeTimer.Stop()
        'Debug.WriteLine("Enter ExpTree ShDragDrop. PdwEffect = " & pdwEffect)
        'Debug.WriteLine(vbTab & "Over node: " & CType(Node, TreeNode).Text)

        If Not IsNothing(dropNode) Then
            ResetTreeviewNodeColor(dropNode)
        Else
            ResetTreeviewNodeColor(tv1.Nodes(0))
        End If
        dropNode = Nothing
        'Debug.WriteLine("Leaving ExpTree ShDragDrop")
    End Sub

    Private Sub ResetTreeviewNodeColor(ByVal node As TreeNode)
        If Not node.BackColor.Equals(Color.Empty) Then
            node.BackColor = Color.Empty
            node.ForeColor = Color.Empty
        End If
        If Not node.FirstNode Is Nothing AndAlso node.IsExpanded Then
            Dim child As TreeNode
            For Each child In node.Nodes
                If Not child.BackColor.Equals(Color.Empty) Then
                    child.BackColor = Color.Empty
                    child.ForeColor = Color.Empty
                End If
                If Not child.FirstNode Is Nothing AndAlso child.IsExpanded Then
                    ResetTreeviewNodeColor(child)
                End If
            Next
        End If
    End Sub
#End Region

#Region "   LabelEdit "

    'V2.14: Added LabelEdit region -- Credit Calum

    Private m_allowFolderRename As Boolean
    ''' <summary>
    ''' Allow renaming of folders using LabelEdit
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Category("Behavior"), Description("Allow renaming of folders using LabelEdit")> _
    Public Property AllowFolderRename() As Boolean
        Get
            Return m_allowFolderRename
        End Get
        Set(ByVal value As Boolean)
            m_allowFolderRename = value
            tv1.LabelEdit = value
        End Set
    End Property

    'Newest code from Calum for Before and After LabelEdit. His remarks are:
    'I also made some changes to ExpTree, I added a check for a dummy node as after renaming a folder 
    'that hadn't been expanded I would receive an error as a BeforeLabelEdit event was fired for the 
    'dummyx node. This only happened with SharePoint folders 
    '(Note that SharePoint folder ALWAYS return true for HasSubFolders and this was happening on 
    'folders without subfolders...) I also replaced the IsFileSystem check (always false for SharePoint) 
    'with a check for a special folder path - this seemed to cover everthing that CanRename didn't cover.
    'I removed the character check in AfterLabelEdit as SetNameOf shows the user a message with illegal 
    'characters if there are any. 

    Private Sub tv1_BeforeLabelEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.NodeLabelEditEventArgs) Handles tv1.BeforeLabelEdit
        If e.Node.Text = " : " Then
            e.CancelEdit = True
            Exit Sub
        End If
        Dim item As CShItem = DirectCast(e.Node.Tag, CShItem)
        'If item.Path.StartsWith("::") Or item.IsDisk Or (Not m_allowFolderRename) Or _
        '    item.Path = CShItem.GetCShItem(CSIDL.MYDOCUMENTS).Path Or _
        '    Not (item.CanRename) Then
        ' Changed 11/28/2010
        If item.Path.StartsWith("::") OrElse item.IsDisk OrElse (Not m_allowFolderRename) OrElse _
            item.Path = CShItem.GetCShItem(CSIDL.MYDOCUMENTS).Path OrElse _
            Not (item.CanRename) Then
            System.Media.SystemSounds.Beep.Play()
            e.CancelEdit = True
        End If
    End Sub

    Private Sub tv1_AfterLabelEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.NodeLabelEditEventArgs) Handles tv1.AfterLabelEdit
        Dim item As CShItem = DirectCast(e.Node.Tag, CShItem)
        Dim NewName As String

        Try
            NewName = e.Label.Trim
        Catch ex As Exception
            e.CancelEdit = True
            System.Media.SystemSounds.Beep.Play()
            Exit Sub
        End Try

        Dim newPidl As IntPtr = IntPtr.Zero
        If item.Parent.Folder.SetNameOf(tv1.Handle, CShItem.ILFindLastID(item.PIDL), NewName, SHGDN.NORMAL, newPidl) = S_OK Then
            'the following line is not needed since use of SetNameOf will cause a renamed WM_Notify msg 
            'which will be handled thru normal change notification processes
            'item.Update(newPidl, CShItemUpdater.CShItemUpdateType.Renamed)
        Else
            System.Media.SystemSounds.Beep.Play()
            e.CancelEdit = True
        End If
    End Sub

#End Region

#Region "   Context Menu Methods"
    ' Credit Calum 

    Private m_useWindowsContextMenu As Boolean = True
    ''' <summary>
    ''' Sets whether or not the control should use Windows System context menu for TreeNode items.
    ''' </summary>
    ''' <returns>The current setting (True or False).</returns>
    ''' <remarks>Setting this Property to False prevents the display and processing of Windows Context Menus on a Right-Click on a TreeNode.</remarks>
    <Category("Behavior"), Description("Whether the control should use windows context menus."), _
    DefaultValue(True)> _
    Public Property UseWindowsContextMenu() As Boolean
        Get
            Return m_useWindowsContextMenu
        End Get
        Set(ByVal value As Boolean)
            m_useWindowsContextMenu = value
        End Set
    End Property


    Private Sub ExpTree_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles tv1.MouseUp
        If e.Button = Windows.Forms.MouseButtons.Right Then
            Dim tn As TreeNode
            Dim pt As System.Drawing.Point = PointToClient(MousePosition)
            tn = tv1.GetNodeAt(pt)
            If m_useWindowsContextMenu And Not IsNothing(tn) Then
                Dim itms(0) As CShItem
                itms(0) = DirectCast(tn.Tag, CShItem)
                Dim cmi As CMInvokeCommandInfoEx = Nothing
                If m_windowsContextMenu.ShowMenu(Me.Handle, itms, MousePosition, m_allowFolderRename, cmi) Then
                    'Check for rename
                    Dim cmdBytes(256) As Byte
                    m_windowsContextMenu.winMenu.GetCommandString(cmi.lpVerb.ToInt32, GCS.VERBA, 0, cmdBytes, 256)

                    Dim cmdName As String = szToString(cmdBytes).ToLower
                    If cmdName.Equals("rename") Then
                        tv1.LabelEdit = True
                        tn.BeginEdit()
                    Else
                        m_windowsContextMenu.InvokeCommand(m_windowsContextMenu.winMenu, cmi.lpVerb, itms(0).Parent.Path, pt)
                    End If
                    'Marshal.ReleaseComObject(m_windowsContextMenu.winMenu)
                    m_windowsContextMenu.ReleaseMenu()
                End If
            End If
        End If
    End Sub

    ''' <summary>
    ''' Windows Message Handler for receiving Messages associated with a System Menu. 
    ''' This is what causes Cascading menus to Display
    ''' </summary>
    ''' <param name="m">A Windows Message</param>
    ''' <remarks>Only Handles Messages relating to Windows Context Menus</remarks>
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        'For send to menu in the explorer context menu
        Dim hr As Integer = 0
        If m.Msg = WM.INITMENUPOPUP Or m.Msg = WM.MEASUREITEM Or m.Msg = WM.DRAWITEM Then
            If Not m_windowsContextMenu.winMenu2 Is Nothing Then
                hr = m_windowsContextMenu.winMenu2.HandleMenuMsg(m.Msg, m.WParam, m.LParam)
                If hr = 0 Then
                    Return
                End If
            End If
        ElseIf m.Msg = WM.MENUCHAR Then
            If Not m_windowsContextMenu.winMenu3 Is Nothing Then
                hr = m_windowsContextMenu.winMenu3.HandleMenuMsg2(m.Msg, m.WParam, m.LParam, IntPtr.Zero)
                If hr = 0 Then
                    Return
                End If
            End If
        End If
        MyBase.WndProc(m)
    End Sub
#End Region

#Region "   Dynamic Update Handler"

    'Private WithEvents DeskTopItem As CShItem = CShItem.GetDeskTop     '7/1/2012

    Private Sub OnItemUpdate(ByVal sender As Object, ByVal e As ShellItemUpdateEventArgs) 'Handles DeskTopItem.CShItemUpdate  '7/1/2012 removed Handles clause
        'Debug.WriteLine("Enter ExpTree OnItemUpdate -- " & e.Item.DisplayName & " - " & e.UpdateType.ToString)
        If e.Item IsNot Nothing AndAlso e.Item.IsFolder Then  'no interest in non-folder events (or UpdateDir)
            Dim Parent As CShItem = DirectCast(sender, CShItem)
            Dim pNode As TreeNode
            If GetTreeNode(Parent, pNode) Then
                'Debug.WriteLine("Located Parent Node " & pNode.Text & " of Item " & e.Item.Path)
                Try
                    tv1.BeginUpdate()
                    Select Case e.UpdateType
                        Case CShItemUpdateType.Created  'A new Dir has been created under Parent/pNode
                            Dim Node As TreeNode = MakeNode(e.Item)
                            'Debug.WriteLine("Adding Node " & NodePath(Node))
                            InsertNode(Node, pNode) '6/25/2012
                            'pNode.Nodes.Add(Node)  '6/25/2012
                            'tv1.Invalidate()   '6/18/2012 - Trust tv1 to do right thing on an Add
                            Exit Select
                        Case CShItemUpdateType.Deleted  'An old Dir has been deleted from Parent/pNode
                            For Each Node As TreeNode In pNode.Nodes
                                If Node.Tag IsNot Nothing AndAlso Node.Tag Is e.Item Then
                                    'Debug.WriteLine("Removing Node " & NodePath(Node))
                                    pNode.Nodes.Remove(Node)
                                    'tv1.Invalidate()   '6/18/2012 - Trust tv1 to do right thing on a Delete
                                    Exit Select
                                End If
                            Next
                            'In the Renamed case, pnode is the Parent CShitem Before the rename,
                            'get the current Parent CShItem from the renamed CShItem(e.Item)
                        Case CShItemUpdateType.Renamed  'A directory has been renamed under Parent/pNode
                            For Each Node As TreeNode In pNode.Nodes
                                If Node.Tag IsNot Nothing AndAlso Node.Tag Is e.Item Then
                                    Dim wasSelected As Boolean = tv1.SelectedNode Is Node
                                    Node.Text = e.Item.DisplayName
                                    pNode.Nodes.Remove(Node)
                                    Dim curPNode As TreeNode
                                    If GetTreeNode(e.Item.Parent, curPNode) Then
                                        InsertNode(Node, curPNode) '6/25/2012
                                        'curPNode.Nodes.Add(Node)  '6/25/2012
                                        If wasSelected Then     '6/25/2012
                                            tv1.SelectedNode = Node
                                            Node.EnsureVisible()
                                        End If
                                    End If
                                    'tv1.Invalidate()   '6/18/2012 - Trust tv1 to do right thing on an Add or Delete
                                    Exit Select
                                End If
                            Next
                        Case CShItemUpdateType.MediaChange  'Media has been added/removed
                            For indx As Integer = 0 To pNode.Nodes.Count - 1
                                If pNode.Tag IsNot Nothing AndAlso pNode.Nodes(indx).Tag Is e.Item Then
                                    Dim node As TreeNode = pNode.Nodes(indx)
                                    Dim item As CShItem = node.Tag
                                    Dim wasExpanded As Boolean = node.IsExpanded
                                    If wasExpanded Then
                                        node.ImageIndex = item.IconIndexOpen
                                    Else
                                        node.ImageIndex = item.IconIndexNormal
                                    End If
                                    node.Collapse(False)
                                    node.Nodes.Clear()
                                    If item.HasSubFolders Then
                                        node.Nodes.Add(New TreeNode(" : "))
                                    End If
                                    If wasExpanded Then node.Expand()
                                    tv1.Invalidate()
                                    If node Is tv1.SelectedNode Then
                                        If e.Item.Path.StartsWith(":") Then
                                            RaiseEvent ExpTreeNodeSelected(e.Item.DisplayName, e.Item)
                                        Else
                                            RaiseEvent ExpTreeNodeSelected(e.Item.Path, e.Item)
                                        End If
                                    End If
                                    Exit Select
                                End If
                            Next
                        Case CShItemUpdateType.Updated  '5/24/2012 - In this case, it is the Item that had some change. Check if Expandability has changed
                            Dim UNode As TreeNode
                            If GetTreeNode(e.Item, UNode) Then    'otherwise don't care
                                'If UNode.IsExpanded Then        'Earlier msgs will update the nodes
                                '    SortNodes(UNode)
                                'Else    '6/5/2012 - check Expandable - in case a Folder added or Deleted which may happen without another message (Async ops)
                                If UNode.Nodes.Count = 0 Then     'Was not Expandable, should it be? (Folder may have been added)
                                    If e.Item.IsRemovable Then
                                        UNode.Nodes.Add(New TreeNode(" : "))
                                    ElseIf e.Item.HasSubFolders Then
                                        UNode.Nodes.Add(New TreeNode(" : "))
                                    ElseIf e.Item.IsHidden Then
                                        If e.Item.GetDirectories.Count > 0 Then
                                            UNode.Nodes.Add(New TreeNode(" : "))
                                        End If
                                    End If
                                ElseIf UNode.Nodes.Count = 1 AndAlso UNode.Nodes(0).Text.Equals(" : ") Then 'Should it still have dummy? (Folder may have been Deleted)
                                    If Not e.Item.IsRemovable AndAlso _
                                       ((e.Item.IsHidden AndAlso e.Item.Directories.Length = 0) OrElse _
                                        Not e.Item.HasSubFolders) Then
                                        UNode.Nodes.Clear()
                                    End If
                                End If
                                'End If
                            End If
                        Case Else
                            'Don't care about any other type of change
                    End Select
                Catch ex As Exception
                    Debug.WriteLine("ExpTree Update Error -- " & ex.ToString)
                Finally
                    tv1.EndUpdate()
                End Try
            Else        'no find means that node not expanded and therefore of no interest
            End If
        End If
    End Sub

    Private Function GetTreeNode(ByVal shellItem As CShItem, ByRef treeNode As TreeNode) As Boolean
        Dim pathList As New List(Of CShItem)
        If shellItem Is Nothing Then shellItem = GetDeskTop() '6/18/2012

        Do While Not shellItem.Parent Is Nothing
            pathList.Add(shellItem)
            shellItem = shellItem.Parent
        Loop
        pathList.Add(shellItem)

        pathList.Reverse()

        If tv1.Nodes.Count < 1 Then Return False '11/05/2012
        treeNode = tv1.Nodes(0)
        Dim i As Integer = 0
        'since pathList starts from Desktop and the tree may start somewhere below that, first locate
        'the tree base in the path
        Dim found As Boolean = False
        Do While i < pathList.Count
            If pathList(i) Is treeNode.Tag Then
                found = True
                Exit Do
            End If
            i += 1
        Loop
        If Not found Then           'failed to find match between top of tree and top of pathlist -- so exit
            treeNode = Nothing
            Return False
        End If
        'have top of tree and pathList(i) as equal -- find actual node, down from top of tree
        i += 1
        Do While i < pathList.Count
            found = False         'reset for next loop
            For Each node As TreeNode In treeNode.Nodes
                If Not node.Tag Is Nothing AndAlso node.Tag Is pathList(i) Then
                    treeNode = node
                    found = True
                    Exit For
                End If
            Next node

            If (Not found) Then
                treeNode = Nothing
                Return False
            End If
            i += 1
        Loop
        Return True
    End Function

    ''' <summary>
    ''' Insert a TreeNode into its' Parent's Nodes list in its' proper location
    ''' in its' Parent Node's Nodes list.
    ''' </summary>
    ''' <param name="Node">The Node to be inserted</param>
    ''' <param name="ParentNode">The Parent Node of the Node to be inserted.</param>
    ''' <remarks>Only called from Dynamic update code when the Parent Node is known (ie displayed).</remarks>
    Private Sub InsertNode(ByVal Node As TreeNode, ByVal ParentNode As TreeNode)    '6/25/2012
        Dim Item As CShItem = Node.Tag
        'It is possible that the ParentNode has only a dummy node. Since we are adding a Node,
        'it is necessary to remove that dummy, beforehand. Note that this case cannot occur if all
        'prior references to the ParentNode occur only within ExpTree. In that case, ParentNode.Tag.Directories will not have
        'been Initialized so no Create or Rename messages will be passed to ExpTree - thus no InsertNode call.
        If ParentNode.Nodes.Count = 1 AndAlso ParentNode.Nodes(0).Text.Equals(" : ") Then
            PopulateNode(ParentNode)        '8/26/2012 - PopulateNode will insert the node correctly
        Else                                '8/26/2012 - Otherwise Insert Node in correct location
            For i As Integer = 0 To ParentNode.Nodes.Count - 1
                If DirectCast(ParentNode.Nodes(i).Tag, CShItem).CompareTo(Item) > 0 Then
                    ParentNode.Nodes.Insert(i, Node)
                    Exit Sub
                End If
            Next
            'on fall thru, did not find a spot to insert, so it goes at the end
            ParentNode.Nodes.Add(Node)
        End If                              '8/26/2012
    End Sub

    ''' <summary>
    ''' Sorts the Nodes of the input TreeNode
    ''' </summary>
    ''' <param name="N">The Node whose Nodes.Collection is to be sorted</param>
    ''' <remarks></remarks>
    Private Sub SortNodes(ByVal N As TreeNode)
        If N.Nodes.Count > 1 Then     'if not, why sort
            Dim tmp(N.Nodes.Count - 1) As TreeNode
            N.Nodes.CopyTo(tmp, 0)
            Array.Sort(tmp, New TagComparer)
            'tv1.BeginUpdate()      '6/18/2012 - not needed already in BeginUpdate when this rtn called
            N.Nodes.Clear()
            N.Nodes.AddRange(tmp)
            'tv1.EndUpdate()        '6/18/2012 - not needed already in BeginUpdate when this rtn called
        End If
    End Sub
#End Region

    ''' <summary>
    ''' NodePath returns the Text version of the full path of a TreeNode.
    ''' </summary>
    ''' <param name="node">The TreeNode to return the full path for.</param>
    ''' <returns>The full path to the input node within a tree</returns>
    ''' <remarks>Used only for some Debug.WriteLine statements.</remarks>
    Private Function NodePath(ByVal node As TreeNode) As String
        Dim pathlist As New List(Of TreeNode)
        pathlist.Add(node)
        Do While node.Parent IsNot Nothing
            pathlist.Add(node.Parent)
            node = node.Parent
        Loop
        pathlist.Reverse()
        Dim SB As New StringBuilder
        For Each N As TreeNode In pathlist
            SB.Append(N.Text) : SB.Append("\")
        Next
        Return SB.ToString
    End Function

    'Public Event TreeKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs)

    'Private Sub tv1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles tv1.KeyDown
    '    Debug.WriteLine("KeyDown in ExpTree Char = " & e.KeyData.ToString)
    '    If e.KeyData = Keys.Escape Then e.Handled = True
    '    RaiseEvent TreeKeyDown(sender, e)
    'End Sub
End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
After 30+ years working in the IT field, mostly managing SysAdmins, I have retired. One of my hobbies returns me to programming, basically just to keep my hand in.

Comments and Discussions