TreeViewにドラッグアンドドロップ関連の処理を書き加えることで、TreeView内に表示されているTreeNodeをドラッグアンドドロップで移動できるように拡張したTreeViewクラスを紹介する。 このコードでは、ドロップ先のノードとドラッグされているノードの親子関係をチェックし、ドロップできるか否かを判定している。

Public Class DragDropTreeView

    Inherits TreeView

    ''' <summary>
    ''' ドラッグアンドドロップされるノードのデータ
    ''' </summary>
    Private Structure DragDropNodeData

        Public Sub New(ByVal node As TreeNode)

            m_Node = node

        End Sub

        Private m_Node As TreeNode

        Public ReadOnly Property Node() As TreeNode
            Get
                Return m_Node
            End Get
        End Property

    End Structure

    ''' <summary>
    ''' 
    ''' </summary>
    Public Sub New()

        MyBase.New()

        Me.AllowDrop = True

    End Sub

    ' ドラッグアンドドロップの開始点
    Private mouseDownPoint As Point

    ' ドラッグするノード
    Private dragDropNode As TreeNode

    ''' <summary>
    ''' MouseDown
    ''' </summary>
    Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)

        If (e.Button And MouseButtons.Left) = MouseButtons.Left Then

            ' ドラッグアンドドロップの開始点
            mouseDownPoint = New Point(e.X, e.Y)

            dragDropNode = Me.GetNodeAt(mouseDownPoint)

        Else

            mouseDownPoint = Point.Empty

            dragDropNode = Nothing

        End If

        MyBase.OnMouseDown(e)

    End Sub

    ''' <summary>
    ''' MouseUp
    ''' </summary>
    Protected Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)

        mouseDownPoint = Point.Empty

        MyBase.OnMouseUp(e)

    End Sub

    ''' <summary>
    ''' MouseMove
    ''' </summary>
    Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)

        If (e.Button And MouseButtons.Left) = MouseButtons.Left Then

            ' ドラッグアンドドロップの範囲にあるか否かをチェックする
            Dim dragBound As Rectangle = New Rectangle(e.X - SystemInformation.DragSize.Width \ 2, e.Y - SystemInformation.DragSize.Height \ 2, SystemInformation.DragSize.Width, SystemInformation.DragSize.Height)

            If Not dragBound.Contains(mouseDownPoint) AndAlso Not dragDropNode Is Nothing Then

                ' ドラッグアンドドロップ用のデータを作成
                Dim nodeData As New DragDropNodeData(dragDropNode)

                ' ノードをドラッグアンドドロップする
                Me.DoDragDrop(nodeData, DragDropEffects.Move)

            End If

        End If

        MyBase.OnMouseMove(e)

    End Sub

    ''' <summary>
    ''' DragEnter
    ''' </summary>
    Protected Overrides Sub OnDragEnter(ByVal drgevent As DragEventArgs)

        If drgevent.Data.GetDataPresent(GetType(DragDropNodeData)) Then

            drgevent.Effect = DragDropEffects.Move

        Else

            drgevent.Effect = DragDropEffects.None

        End If

        MyBase.OnDragEnter(drgevent)

    End Sub

    ''' <summary>
    ''' DragDrop
    ''' </summary>
    Protected Overrides Sub OnDragDrop(ByVal drgevent As DragEventArgs)

        If drgevent.Data.GetDataPresent(GetType(DragDropNodeData)) Then

            ' 入れ替え先のインデックス
            Dim targetNode As TreeNode = Me.GetNodeAt(Me.PointToClient(New Point(drgevent.X, drgevent.Y)))

            If Not targetNode Is Nothing Then

                ' ドラッグアンドドロップするアイテムのデータを取得
                Dim nodeData As DragDropNodeData = DirectCast(drgevent.Data.GetData(GetType(DragDropNodeData)), DragDropNodeData)

                ' 移動対象を含むノード一覧を取得
                Dim parentNodes As TreeNodeCollection

                If targetNode.Parent Is Nothing Then

                    parentNodes = Me.Nodes

                Else

                    parentNodes = targetNode.Parent.Nodes

                End If

                ' ノードのインデックスを取得
                Dim targetNodeIndex As Integer = parentNodes.IndexOf(targetNode)

                ' 一度削除する
                Me.Nodes.Remove(nodeData.Node)

                ' 目的の場所に挿入
                parentNodes.Insert(targetNodeIndex, nodeData.Node)

                ' 選択されているノードを設定
                Me.SelectedNode = nodeData.Node

            End If

        End If

        MyBase.OnDragDrop(drgevent)

    End Sub

End Class