ListBoxにドラッグアンドドロップ関連の処理を書き加えることで、ListBox内に表示されているアイテムをドラッグアンドドロップで移動できるように拡張したListBoxクラスを紹介する。

Public Class DragDropListBox

    Inherits ListBox

    ''' <summary>
    ''' ドラッグアンドドロップされるアイテムのデータ
    ''' </summary>
    Private Structure DragDropItemData

        Public Sub New(ByVal index As Integer)

            m_Index = index

        End Sub

        Private m_Index As Integer

        Public ReadOnly Property Index() As Integer
            Get
                Return m_Index
            End Get
        End Property

    End Structure

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

        MyBase.New()

        Me.AllowDrop = True

    End Sub

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

    ' ドラッグするアイテムのインデックス
    Private dragDropSourceItemIndex As Integer

    ''' <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)

            dragDropSourceItemIndex = Me.IndexFromPoint(e.X, e.Y)

        Else

            mouseDownPoint = Point.Empty

            dragDropSourceItemIndex = ListBox.NoMatches

        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 dragDropSourceItemIndex <> ListBox.NoMatches Then

                ' ドラッグアンドドロップ用のデータを作成
                Dim itemData As DragDropItemData = New DragDropItemData(dragDropSourceItemIndex)

                ' アイテムをドラッグアンドドロップする
                Dim effects As DragDropEffects = Me.DoDragDrop(itemData, 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(DragDropItemData)) 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(DragDropItemData)) Then

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

            If targetIndex = ListBox.NoMatches Then targetIndex = Me.Items.Count - 1

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

            ' アイテムを入れかえる
            Dim temp As Object = Me.Items(itemData.Index)

            Me.Items(itemData.Index) = Me.Items(targetIndex)

            Me.Items(targetIndex) = temp

            ' 入れ替えたアイテムを選択する
            Me.SelectedItem = temp

        End If

        MyBase.OnDragDrop(drgevent)

    End Sub

End Class