How to drag and drop row within the same datagridv

2019-03-14 02:35发布

问题:

In a Windows App (Visual Studio)(VB) how do you drag and drop a single row to another postition to allow for the user to re-order the row? I haven't found any worthy examples for this yet.

回答1:

Here is a vb version from this C# answer: How could I Drag and Drop DataGridView Rows under each other?

The form class variables:

Private fromIndex As Integer
Private dragIndex As Integer
Private dragRect As Rectangle

The drag events:

Private Sub DataGridView1_DragDrop(ByVal sender As Object, _
                                   ByVal e As DragEventArgs) _
                                   Handles DataGridView1.DragDrop
  Dim p As Point = DataGridView1.PointToClient(New Point(e.X, e.Y))
  dragIndex = DataGridView1.HitTest(p.X, p.Y).RowIndex
  If (e.Effect = DragDropEffects.Move) Then
    Dim dragRow As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))
    DataGridView1.Rows.RemoveAt(fromIndex)
    DataGridView1.Rows.Insert(dragIndex, dragRow)
  End If
End Sub

Private Sub DataGridView1_DragOver(ByVal sender As Object, _
                                   ByVal e As DragEventArgs) _
                                   Handles DataGridView1.DragOver
  e.Effect = DragDropEffects.Move
End Sub

The mouse events:

Private Sub DataGridView1_MouseDown(ByVal sender As Object, _
                                    ByVal e As MouseEventArgs) _
                                    Handles DataGridView1.MouseDown
  fromIndex = DataGridView1.HitTest(e.X, e.Y).RowIndex
  If fromIndex > -1 Then
    Dim dragSize As Size = SystemInformation.DragSize
    dragRect = New Rectangle(New Point(e.X - (dragSize.Width / 2), _
                                       e.Y - (dragSize.Height / 2)), _
                             dragSize)
  Else
    dragRect = Rectangle.Empty
  End If
End Sub

Private Sub DataGridView1_MouseMove(ByVal sender As Object, _
                                    ByVal e As MouseEventArgs) _
                                    Handles DataGridView1.MouseMove
  If (e.Button And MouseButtons.Left) = MouseButtons.Left Then
    If (dragRect <> Rectangle.Empty _
    AndAlso Not dragRect.Contains(e.X, e.Y)) Then
      DataGridView1.DoDragDrop(DataGridView1.Rows(fromIndex), _
                               DragDropEffects.Move)
    End If
  End If
End Sub

Make sure you have the grids AllowDrop property set to true.



回答2:

UPDATE:

Instead of

 If dragIndex < 0 Then dragIndex = DataGridView1.RowCount - 1

change to

 If dragIndex > -1 Then 
      'action if not selected in the row header and blank space
 else
      'return error if selected in the column header and blank space
 end if

then a error occurs when you drag a row to the "blank zone", if you don't believe me, you must to try it.

the final code (Only for the part "The drag events") is this:

  Private Sub DataGridView1_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragDrop
        Dim p As Point = DataGridView1.PointToClient(New Point(e.X, e.Y))
        dragIndex = DataGridView1.HitTest(p.X, p.Y).RowIndex
    'Determine if dragindex is valid row index       
    If dragIndex > -1 Then
        If (e.Effect = DragDropEffects.Move) Then
            Dim dragRow As DataGridViewRow = CType(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)
            DataGridView1.Rows.RemoveAt(fromIndex)
            DataGridView1.Rows.Insert(dragIndex, dragRow)
            'Add this line of code if you want to put selected rows to the rows that change
            DataGridView1.Rows(dragIndex).Selected = True
        End If 
       Else 'Do any message here if selected in column header and blank space. 
       End If
    End Sub


回答3:

Here's a Control without the mentioned bug.

Set AllowUserToOrderRows and AllowDrop to True in the Windows Forms Designer and drag the row headers, not the content.

Imports System.ComponentModel

Public Class BetterDataGridView
    Inherits DataGridView

    <Category("Behavior"), DefaultValue(False)>
    Public Property AllowUserToOrderRows As Boolean = False

    Protected Overrides Sub OnMouseDown(e As MouseEventArgs)
        MyBase.OnMouseDown(e)

        Dim hitInfo As HitTestInfo = HitTest(e.X, e.Y)
        If AllowUserToOrderRows AndAlso
                e.Button = MouseButtons.Left AndAlso
                hitInfo.ColumnIndex = -1 AndAlso
                ValidRow(hitInfo.RowIndex) Then
            DoDragDrop(Rows(hitInfo.RowIndex), DragDropEffects.Move)
        End If
    End Sub

    Protected Overrides Sub OnDragOver(e As DragEventArgs)
        MyBase.OnDragOver(e)

        Dim dragRow As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))
        Dim targetIndex As Integer = GetRowIndex(e)
        e.Effect = If(ValidRowDragDrop(dragRow, targetIndex),
                      DragDropEffects.Move,
                      DragDropEffects.None)
    End Sub

    Protected Overrides Sub OnDragDrop(e As DragEventArgs)
        MyBase.OnDragDrop(e)

        Dim dragRow As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))
        Dim targetIndex As Integer = GetRowIndex(e)

        If e.Effect = DragDropEffects.Move AndAlso ValidRowDragDrop(dragRow, targetIndex) Then
            EndEdit()

            Rows.Remove(dragRow)
            Rows.Insert(targetIndex, dragRow)

            ClearSelection()
            dragRow.Selected = True
        End If
    End Sub

    Protected Function ValidRow(rowIndex As Integer) As Boolean
        Return rowIndex >= 0 AndAlso
            rowIndex < Rows.Count - If(AllowUserToAddRows, 1, 0)
    End Function

    Protected Function GetRowIndex(e As DragEventArgs) As Integer
        Dim clientPos As Point = PointToClient(New Point(e.X, e.Y))
        Return HitTest(clientPos.X, clientPos.Y).RowIndex
    End Function

    Protected Function ValidRowDragDrop(dragRow As DataGridViewRow, targetIndex As Integer) As Boolean
        Return dragRow IsNot Nothing AndAlso
            ValidRow(targetIndex) AndAlso
            targetIndex <> dragRow.Index AndAlso
            Rows.Contains(dragRow)
    End Function
End Class


回答4:

Thank you for everything, code working. I was getting only one error. I solved it.

if the datagridview "Enable Editing" is set, you receive an error when you throw line spacing. You can try. I solved it as follows:

    Private Sub DataGridView1(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragDrop

    Dim p As Point = DataGridView1.PointToClient(New Point(e.X, e.Y))
                    dragIndex = DataGridView1.HitTest(p.X, p.Y).RowIndex
                    If (e.Effect = DragDropEffects.Move) Then
                        Dim dragRow As DataGridViewRow = CType(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)

                        If dragIndex = DataGridView1.RowCount - 1 Then '**ADD THIS AREA**
                            DataGridView1.Rows.RemoveAt(fromIndex)
                            DataGridView1.Rows.Insert(DataGridView1.RowCount - 1, dragRow)
                        Else
                            If dragIndex < 0 Then dragIndex = DataGridView1.RowCount - 2 '**this is important**
                            DataGridView1.Rows.RemoveAt(fromIndex)
                            DataGridView1.Rows.Insert(dragIndex, dragRow)
                        End If
                    End If
End Sub

Thanks for all other information



回答5:

1.5 improvements for the event GridView.DragDrop:

  1. The first 50% improvement, To avoid the descriped error you can also use

    Private Sub DgvSearchFieldCurrent_DragDrop( _
       ByVal sender As  Object, ByVal e As DragEventArgs) _
       Handles DgvSearchFieldCurrent.DragDrop
    
    Dim LclDgv As DataGridView = CType(sender, DataGridView)
    
    If dragIndex > -1 AndAlso dragIndex < LclDgv.RowCount -1 Then
    
  2. Second is to set focus to the current row and the first cell:

    LclDgv.Rows.Insert(dragIndex, dragRow)
    
    LclDgv.Rows(fromIndex).Selected = False
    LclDgv.Rows(dragIndex).Selected = True
    
    For Each C As DataGridViewColumn In LclDgv.Columns
      LclDgv(C.Index, fromIndex).Selected = False
    Next
    
    LclDgv(0, dragIndex).Selected = True