ドラッグアンドドロップでListBoxのアイテムを移動する

注意:
この文書は以前「.NETでいきまっしょい!」で公開していたものですが、公開以降メンテナンスされていません。 今や古い情報となった内容が記載されている場合があるのでご注意ください。

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

ソースコード
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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