Question Drag and Drop between two grids

mlsrinivas

New member
Joined
Aug 9, 2012
Messages
2
Programming Experience
Beginner
Hello,

I am trying to design a form with two datagridviews on it and enable drag and drop between. The user should be able to drag and drop rows from grid1 to grid2 and vice versa. When a row is drag and dropped it should be removed from the source grid and added to the target grid.

One way dragdrop is happening but when trying toway method, mouse click on the grid is firing dragdrop event and creating problems. I am posting my code below please help me complete the task.

Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

DataGridView1.Rows.Add(1, "Emp1", "Team Lead", "Y")
DataGridView1.Rows.Add(2, "Emp2", "Team Lead", "N")
DataGridView1.Rows.Add(3, "Emp3", "Team Lead", "Y")
DataGridView1.Rows.Add(4, "Emp4", "Team Lead", "Y")
DataGridView1.Rows.Add(5, "Emp5", "Team Lead", "N")
DataGridView1.Rows.Add(6, "Emp6", "Team Lead", "Y")
End Sub

Private Sub DataGridView1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseDown
If e.Button = Windows.Forms.MouseButtons.Left Then
If DataGridView1.SelectedRows.Count <> 0 Then
Dim i As Integer

For i = DataGridView1.SelectedRows.Count - 1 To 0 Step -1
If DataGridView1.SelectedRows(i).Cells("Flag").Value = "Y" Then

DataGridView1.DoDragDrop(DataGridView1.SelectedRows(i), DragDropEffects.All)

End If
Next
End If
End If
End Sub
Private Sub DataGridView2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView2.MouseDown
If e.Button = Windows.Forms.MouseButtons.Left Then
If DataGridView2.SelectedRows.Count <> 0 Then
Dim i As Integer

For i = DataGridView2.SelectedRows.Count - 1 To 0 Step -1
If DataGridView2.SelectedRows(i).Cells("DestFlag").Value = "Y" Then

DataGridView2.DoDragDrop(DataGridView2.SelectedRows(i), DragDropEffects.All)

End If
Next
End If
End If
End Sub
Private Sub DataGridView2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView2.DragDrop

Dim s As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))

DataGridView2.Rows.Add(s.Cells(0).Value, s.Cells(1).Value, s.Cells(2).Value, s.Cells(3).Value)
DataGridView2.Sort(DataGridView2.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
DataGridView1.Rows.RemoveAt(GetRowIndex(DataGridView1, s.Cells(0).Value))

End Sub
Private Sub DataGridView1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragDrop

Dim s As DataGridViewRow = e.Data.GetData(GetType(DataGridViewRow))

DataGridView1.Rows.Add(s.Cells(0).Value, s.Cells(1).Value, s.Cells(2).Value, s.Cells(3).Value)
DataGridView1.Sort(DataGridView2.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
DataGridView2.Rows.RemoveAt(GetRowIndex(DataGridView2, s.Cells(0).Value))
End Sub
Private Sub DataGridView2_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView2.DragEnter
e.Effect = DragDropEffects.Copy
End Sub
Private Sub DataGridView1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragEnter
e.Effect = DragDropEffects.Copy
End Sub
Private Function GetRowIndex(ByRef SourceGrid As DataGridView, ByVal StrSearchString As String) As Integer

Dim iRowIndex As Integer = 0
For Each Row As DataGridViewRow In SourceGrid.Rows
If SourceGrid.Rows(iRowIndex).Cells(0).Value.ToString = StrSearchString Then
Exit For
End If
iRowIndex += 1
Next Row
Return iRowIndex

End Function
End Class


Thanks

Srinivas
 
Sub DataGridView1_DragDrop
DataGridView1.Sort(DataGridView2.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
The problem with this line is that you can't sort on a column belonging to another DGV. This is the corrected code:
DataGridView1.Sort(DataGridView1.Columns(0), System.ComponentModel.ListSortDirection.Ascending)

You have quite a bit of duplicate and redundant code there;
  • Did you know you that the event sender parameter returns the reference to the class instance (the control) that raised the event? This allows for dynamic handling of multiple objects events using a single event handler.
  • In DragDrop event you are also referencing the control that started the drag operation, and while you can always store that information somewhere, you don't need to. DoDragDrop is a function method, return value will tell you if and how data was dropped, knowing that the operation completed you can remove the source row here.
  • The GetRowIndex method is also completely unnecessary. A DataGridViewRow has a Index property and the DataGridViewRowCollection has a Remove(column) method, both valid alternatives that do without that extra code.
  • SelectedRows property returns a readonly snapshot, so its safe to do a For Each loop over that while removing each row. For Each loops usually makes code simpler and easier to read than indexed For Next loops.
  • Pay some attention to the DragDropEffects values, you're doing move operations so use the Move value.
So a more compressed version of that code is this:
    Private Sub DataGridViews_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseDown, DataGridView2.MouseDown
        Dim handler = CType(sender, DataGridView)
        If e.Button <> Windows.Forms.MouseButtons.Left OrElse handler.SelectedRows.Count = 0 Then Return

        For Each row As DataGridViewRow In handler.SelectedRows
            If CStr(row.Cells(3).Value) = "Y" Then
                If handler.DoDragDrop(row, DragDropEffects.Move) = DragDropEffects.Move Then
                    handler.Rows.Remove(row)
                End If
            End If
        Next
    End Sub

    Private Sub DataGridViews_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragEnter, DataGridView2.DragEnter
        e.Effect = DragDropEffects.Move
    End Sub

    Private Sub DataGridViews_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragDrop, DataGridView2.DragDrop
        Dim handler = CType(sender, DataGridView)
        Dim row = CType(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)
        handler.Rows.Add(row.Cells(0).Value, row.Cells(1).Value, row.Cells(2).Value, row.Cells(3).Value)
        handler.Sort(handler.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
    End Sub

The Rows.Add(each value) is tedious, there are many ways to work around it, one alternative is you can pass the source DGV and move things around like below, removing the row from source control first allows you to simply add it to target control after:
    Private Sub DataGridViews_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseDown, DataGridView2.MouseDown
        Dim handler = CType(sender, DataGridView)
        If e.Button <> Windows.Forms.MouseButtons.Left OrElse handler.SelectedRows.Count = 0 Then Return
        handler.DoDragDrop(handler, DragDropEffects.Move)       
    End Sub

    Private Sub DataGridViews_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragEnter, DataGridView2.DragEnter
        e.Effect = DragDropEffects.Move
    End Sub

    Private Sub DataGridViews_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragDrop, DataGridView2.DragDrop
        Dim handler = CType(sender, DataGridView)
        Dim source = CType(e.Data.GetData(GetType(DataGridView)), DataGridView)
        For Each row As DataGridViewRow In source.SelectedRows
            If CStr(row.Cells(3).Value) = "Y" Then
                source.Rows.Remove(row)
                handler.Rows.Add(row)
            End If
        Next
        handler.Sort(handler.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
    End Sub

A difference between these two alternatives is that the former will only initiate the DnD operation if there is actually "Y"-rows that can be moved, the latter will initiate the DnD operation as long as there are selected rows.
 
Worked like a charm, but new problem arised

Hi John,

Thanks for your help, your code worked like a charm. But a small problem started after putting your code. When I select multiple rows on the grid (select a row, hold shift and select a row below) first row is getting deselected.

Can you give any idea why this could be happening.

Thanks

Srinivas
 
But a small problem started after putting your code.
To be precise, it is not my code. It is still 'your code', just refactored a bit to get rid of duplicate and superfluous code.
The issue is that by adding behaviour to MouseDown event you're battling the standard selection behaviours. One way to mitigate this is to handle DGV KeyDown and KeyUp event and capture Shift/Control keys state (e.Shift/e.Control) and use this info in MouseDown event handler to not start DnD when user is currently doing Shift/Control selections.
 
Back
Top