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