Drag drop multiple selected rows of datagridview with left mouse button
Drag drop multiple selected rows of datagridview with left mouse button
If you have a multiselectable datagridview on your form, you most likely are looking for a method to process intuitive drag & drop of the selected rows to another control. But, as soon as you click onto a row (selected or not) with the mere left button any selection is gone immediately. One could think about a workaround by using the right mouse button, but again, that is not intuitive, since 99% of all dd operations are normally done by click and hold of the left button.
What to do? Of course: subclassing. I thought about it for awhile and detected the crucial point in the OnMouseDown event. Inside this event, the base class of the datagridview is doing the selection job. So we override this event and wait for the OnMouseUp event to determine, if a multiselect drag drop has been performed. In this case, we catch up the preceeding MouseDown event and everything works fine.
Since we still want the 'only one row' dd as well, I also implemented the property
AllowMultiRowDrag. Here is the trick:
Public Class DD_DataGridView
Inherits DataGridView
Public Property AllowMultiRowDrag As Boolean = False
Private dragBoxFromMouseDown As Rectangle
Private rowIndexFromMouseDown As Int32
Private lastLeftMouseDownArgs As MouseEventArgs
Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs)
If (e.Button And Windows.Forms.MouseButtons.Left) = Windows.Forms.MouseButtons.Left Then
rowIndexFromMouseDown = Me.HitTest(e.X, e.Y).RowIndex
If rowIndexFromMouseDown <> -1 Then
Dim dragSize As Size = SystemInformation.DragSize
dragBoxFromMouseDown = New Rectangle(New Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize)
lastLeftMouseDownArgs = e 'remember the MouseEventArgs
If AllowMultiRowDrag Then
Exit Sub 'Don't call the base function here to keep the selection
End If
Else
dragBoxFromMouseDown = Rectangle.Empty
lastLeftMouseDownArgs = Nothing
End If
End If
MyBase.OnMouseDown(e) 'in all other cases call the base function
End Sub
Protected Overrides Sub OnMouseUp(e As System.Windows.Forms.MouseEventArgs)
If lastLeftMouseDownArgs IsNot Nothing AndAlso (e.Button And Windows.Forms.MouseButtons.Left) = Windows.Forms.MouseButtons.Left Then
'there has been a multiselect drag operation so catch up the MouseDown event
If AllowMultiRowDrag Then MyBase.OnMouseDown(lastLeftMouseDownArgs)
End If
MyBase.OnMouseUp(e)'now call the base Up-function
End Sub
Protected Overrides Sub OnMouseMove(e As System.Windows.Forms.MouseEventArgs)
If lastLeftMouseDownArgs IsNot Nothing AndAlso (e.Button And Windows.Forms.MouseButtons.Left) = Windows.Forms.MouseButtons.Left Then
If (dragBoxFromMouseDown <> Rectangle.Empty) AndAlso (Not dragBoxFromMouseDown.Contains(e.X, e.Y)) Then
'we want to drag one/multiple rows
Dim row As DataGridViewRow = Me.Rows(rowIndexFromMouseDown)
row.Selected = True 'always select the row that was under the mouse in the OnMouseDown event.
If Me.AllowMultiRowDrag Then
'multiselect drag, so make all selected rows the drag data
Dim dropEffect As DragDropEffects = Me.DoDragDrop(Me.SelectedRows, DragDropEffects.Move)
Else
'only one row to drag
Dim dropEffect As DragDropEffects = Me.DoDragDrop(row, DragDropEffects.Move)
End If
End If
End If
MyBase.OnMouseMove(e) 'let's do the base class the rest
End Sub
End Class