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
If AllowMultiRowDrag Then
Exit Sub
End If
Else
dragBoxFromMouseDown = Rectangle.Empty
lastLeftMouseDownArgs = Nothing
End If
End If
MyBase.OnMouseDown(e)
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
If AllowMultiRowDrag Then MyBase.OnMouseDown(lastLeftMouseDownArgs)
End If
MyBase.OnMouseUp(e)
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
Dim row As DataGridViewRow = Me.Rows(rowIndexFromMouseDown)
row.Selected = True
If Me.AllowMultiRowDrag Then
Dim dropEffect As DragDropEffects = Me.DoDragDrop(Me.SelectedRows, DragDropEffects.Move)
Else
Dim dropEffect As DragDropEffects = Me.DoDragDrop(row, DragDropEffects.Move)
End If
End If
End If
MyBase.OnMouseMove(e)
End Sub
End Class