Click here to Skip to main content
15,896,118 members
Articles / Desktop Programming / Windows Forms

Drag and Drop Persisting TreeView Control (VB.NET)

Rate me:
Please Sign up or sign in to vote.
4.55/5 (23 votes)
29 Nov 2012CPOL4 min read 150.5K   2.9K   67  
Extending the TreeView control to support drag and drop and persistance.
Public Class DragDropPersistingTreeView
    Inherits System.Windows.Forms.TreeView

    ''' <summary>
    ''' The TreeViewDataAccess class allows the nodes within a TreeView to be 
    ''' persisted to xml for later retrevial and supports drag and drop 
    ''' operation on nodes.
    ''' </summary>

#Region " Windows Form Designer generated code "

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    '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()
        components = New System.ComponentModel.Container
    End Sub

#End Region

#Region "Structures"

    ''' <summary>
    ''' TreeViewData structure represents the root node collection of a TreeView
    ''' and provides the PopulateTreeView function to add these nodes to a specified
    ''' TreeView instance.
    ''' </summary>
    <Serializable()> Public Structure TreeViewData

        ''' <summary>Array of TreeNodeData objects representing the root nodes in a TreeView.</summary>
        Public Nodes() As TreeNodeData

        ''' <summary>
        ''' Creates new instance of the TreeViewData structure based from the 
        ''' specified TreeView.
        ''' </summary>
        ''' <param name="treeview">TreeView to build the TreeViewData instance from.</param>
        Public Sub New(ByVal treeview As TreeView)

            'Check to see if there are any root nodes in the TreeView
            If treeview.Nodes.Count = 0 Then Exit Sub

            'Populate the Nodes array with child nodes
            ReDim Nodes(treeview.Nodes.Count - 1)
            For i As Integer = 0 To treeview.Nodes.Count - 1
                Nodes(i) = New TreeNodeData(treeview.Nodes(i))
            Next
        End Sub

        ''' <summary>
        ''' Populates the specified TreeView with the current TreeViewData instance.
        ''' </summary>
        ''' <param name="treeview">TreeView instance to populate.</param>
        Public Sub PopulateTree(ByVal treeview As TreeView)
            'Check to see if there are any root nodes in the TreeViewData
            If Me.Nodes Is Nothing OrElse Me.Nodes.Length = 0 Then Exit Sub

            'Clear the existing nodes from the TreeView
            treeview.Nodes.Clear()

            'Populate the TreeView with child nodes
            treeview.BeginUpdate()
            For i As Integer = 0 To Me.Nodes.Length - 1
                treeview.Nodes.Add(Me.Nodes(i).ToTreeNode)
            Next
            treeview.EndUpdate()
        End Sub

    End Structure

    ''' <summary>
    ''' TreeNodeData structure represents a TreeNode and provides the
    ''' ToTreeNode function to convert the instance to a TreeNode object.
    ''' </summary>
    <Serializable()> Public Structure TreeNodeData

        ''' <summary>String representing the Text property of the TreeNode.</summary>
        Public Text As String
        ''' <summary>Integer representing the ImageIndex property of the TreeNode.</summary>
        Public ImageIndex As Integer
        ''' <summary>Integer representing the SelectedImageIndex property of the TreeNode.</summary>
        Public SelectedImageIndex As Integer
        ''' <summary>Boolean representing the Checked state of the TreeNode.</summary>
        Public Checked As Boolean
        ''' <summary>Boolean representing the Expanded state of the TreeNode.</summary>
        Public Expanded As Boolean
        ''' <summary>Object representing the Tag property of the TreeNode.</summary>
        Public Tag As Object
        ''' <summary>Array of TreeNodeData objects representing the root nodes in a TreeView.</summary>
        Public Nodes() As TreeNodeData

        ''' <summary>
        ''' Creates new instance of the TreeNodeData structure based on the specified TreeNode.
        ''' </summary>
        ''' <param name="node">TreeNode to build the TreeNodeData instance from.</param>
        Public Sub New(ByVal node As TreeNode)
            'Set the basic TreeNode properties
            Me.Text = node.Text
            Me.ImageIndex = node.ImageIndex
            Me.SelectedImageIndex = node.SelectedImageIndex
            Me.Checked = node.Checked
            Me.Expanded = node.IsExpanded

            'See if there is an object in the tag property and if it is serializable
            If (Not node.Tag Is Nothing) AndAlso node.Tag.GetType.IsSerializable Then Me.Tag = node.Tag

            'Check to see if there are any child nodes
            If node.Nodes.Count = 0 Then Exit Sub

            'Recurse through child nodes and add to Nodes array
            ReDim Nodes(node.Nodes.Count - 1)
            For i As Integer = 0 To node.Nodes.Count - 1
                Nodes(i) = New TreeNodeData(node.Nodes(i))
            Next
        End Sub

        ''' <summary>
        ''' Returns as TreeNode built from the instance of the TreeNodeData object.
        ''' </summary>
        Public Function ToTreeNode() As TreeNode
            'Create TreeNode based on instance of TreeNodeData and set basic properties
            ToTreeNode = New TreeNode(Me.Text, Me.ImageIndex, Me.SelectedImageIndex)
            ToTreeNode.Checked = Me.Checked
            ToTreeNode.Tag = Me.Tag
            If Me.Expanded Then ToTreeNode.Expand()

            'Recurse through child nodes adding to Nodes collection
            If Me.Nodes Is Nothing OrElse Me.Nodes.Length = 0 Then Exit Function
            For i As Integer = 0 To Me.Nodes.Length - 1
                ToTreeNode.Nodes.Add(Me.Nodes(i).ToTreeNode)
            Next
        End Function

    End Structure

#End Region

#Region "Constructors"

    Public Sub New()
        MyBase.New()

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

        'Add any initialization after the InitializeComponent() call
        Me.AllowDrop = True

    End Sub

#End Region

#Region "Public"

    ''' <summary>
    ''' Populates the DragDropPersistingTreeView from the serialized TreeViewData structure file specified.
    ''' </summary>
    ''' <param name="path">Serialized Xml representation of TreeViewData</param>
    Public Sub LoadFromXml(ByVal path As String)
        'Create as serializer and get the file to deserialize
        Dim ser As New System.Xml.Serialization.XmlSerializer(GetType(TreeViewData))
        Dim file As New System.IO.FileStream(path, IO.FileMode.Open)
        Dim reader As New System.Xml.XmlTextReader(file)

        'Deserialize the file and populate the treeview
        Dim treeData As TreeViewData = CType(ser.Deserialize(reader), TreeViewData)
        treeData.PopulateTree(Me)

        'Tidy up
        reader.Close()
        file.Close()
        file = Nothing
    End Sub

    ''' <summary>
    ''' Saves the DragDropPersistingTreeView in serialized TreeViewData structure file specified.
    ''' </summary>
    ''' <param name="path">Path to store serialized file.</param>
    Public Sub SaveToXml(ByVal path As String)
        'Create as serializer and file to save TreeViewData
        Dim ser As New System.Xml.Serialization.XmlSerializer(GetType(TreeViewData))
        Dim file As New System.IO.FileStream(path, IO.FileMode.Create)
        Dim writer As New System.Xml.XmlTextWriter(file, Nothing)

        'Generate TreeViewData from DragDropPersistingTreeView and serialize the file.
        ser.Serialize(writer, New TreeViewData(Me))

        'Tidy up
        writer.Close()
        file.Close()
        file = Nothing
    End Sub

#End Region

#Region "Protected"

    Protected Overrides Sub OnItemDrag(ByVal e As System.Windows.Forms.ItemDragEventArgs)

        'Ensure the base method is called
        MyBase.OnItemDrag(e)

        'Set the drag node and initiate the DragDrop
        DoDragDrop(e.Item, DragDropEffects.Move)

    End Sub

    Protected Overrides Sub OnDragEnter(ByVal e As System.Windows.Forms.DragEventArgs)

        'Ensure the base method is called
        MyBase.OnDragEnter(e)

        'See if there is a TreeNode being dragged
        If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", True) Then
            'TreeNode found allow move effect
            e.Effect = DragDropEffects.Move
        Else
            'No TreeNode found, prevent move
            e.Effect = DragDropEffects.None
        End If

    End Sub

    Protected Overrides Sub OnDragOver(ByVal e As DragEventArgs)

        'Ensure the base method is called
        MyBase.OnDragOver(e)

        'Check that there is a TreeNode being dragged
        If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", True) = False Then Exit Sub

        'As the mouse moves over nodes, provide feedback to the user
        'by highlighting the node that is the current drop target
        Dim pt As Point = PointToClient(New Point(e.X, e.Y))
        Dim targetNode As TreeNode = GetNodeAt(pt)

        'See if the targetNode is currently selected, if so no need to validate again
        If Not (SelectedNode Is targetNode) Then
            'Select the node currently under the cursor
            SelectedNode = targetNode

            'Check that the selected node is not the dropNode and also that it
            'is not a child of the dropNode and therefore an invalid target
            Dim dropNode As TreeNode = CType(e.Data.GetData("System.Windows.Forms.TreeNode"), TreeNode)
            Do Until targetNode Is Nothing
                If targetNode Is dropNode Then
                    e.Effect = DragDropEffects.None
                    Exit Sub
                End If
                targetNode = targetNode.Parent
            Loop
        End If

        'Currently selected node is a suitable target, allow the move
        e.Effect = DragDropEffects.Move

    End Sub

    Protected Overrides Sub OnDragDrop(ByVal e As System.Windows.Forms.DragEventArgs)

        'Ensure the base method is called
        MyBase.OnDragDrop(e)

        'Check that there is a TreeNode being dragged
        If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", True) = False Then Exit Sub

        'Get the TreeNode being dragged
        Dim dropNode As TreeNode = CType(e.Data.GetData("System.Windows.Forms.TreeNode"), TreeNode)

        'The target node should be selected from the DragOver event
        Dim targetNode As TreeNode = SelectedNode

        'Remove the drop node from its current location
        dropNode.Remove()

        'If there is no targetNode add dropNode to the bottom of the TreeView root
        'nodes, otherwise add it to the end of the dropNode child nodes
        If targetNode Is Nothing Then
            Nodes.Add(dropNode)
        Else
            targetNode.Nodes.Add(dropNode)
        End If

        'Ensure the newley created node is visible to the user and select it
        dropNode.EnsureVisible()
        SelectedNode = dropNode

    End Sub

    'UserControl 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
        End If
        MyBase.Dispose(disposing)
    End Sub

#End Region

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
Software Developer (Senior) RedFrog.com Limited
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions