Move up or down small, complex rows of a Multi-select DataGridView

I have a DataGridView like this:

enter image description here

Now, in C # , or VB.Net , using a button, I would like to move up or down one position at a time the selected rows, how could I do that ?. Multiple choice requires making it harder for me.

The files that appear in the DataGridView will be merged into one executable file, then the files will be executed in the order of the Order column , so " Order " should be unavailable when moving rows up or down.

I am not using any data source.


I tried to parse this sample from MSDN , it consists of some extension methods, but it requires a data source and data source, I don't have any problems using a data source and data source, but I just don't know how to adapt the code sample for mine DataGridView. The sample doesn't support multiple choice anyway:

Move rows up / down and remember data binding order of DataGridView and ListBoxs

I've also seen some C # questions about this on StackOverflow, but they ask for a single row selection:

How to move selected grid row up / down by pressing KeyUp or Keydown key

Selected row DataGridView Move UP and DOWN

Then I don't have a starting point, just these methods move the SINGLE string, which also does not store the Order value , if someone can help me extend the functionality for my needs: Private Sub Button_MoveUp_Click (sender as object, e As EventArgs) _ Button_MoveUp handles. Click

    Me.MoveUpSelectedRows(Me.DataGridView_Files)

End Sub

Private Sub Button_MoveDown_Click(sender As Object, e As EventArgs) _
Handles Button_MoveDown.Click

    Me.MoveDownSelectedRows(Me.DataGridView_Files)

End Sub

Private Sub MoveUpSelectedRows(ByVal dgv As DataGridView)

    Dim curRowIndex As Integer = dgv.CurrentCell.RowIndex
    Dim newRowIndex As Integer = curRowIndex - 1

    Dim curColIndex As Integer = dgv.CurrentCell.ColumnIndex
    Dim curRow As DataGridViewRow = dgv.CurrentRow

    If (dgv.SelectedCells.Count > 0) AndAlso (newRowIndex >= 0) Then

        With dgv
            .Rows.Remove(curRow)
            .Rows.Insert(newRowIndex, curRow)
            .CurrentCell = dgv(curColIndex, newRowIndex)
        End With

    End If

End Sub

Private Sub MoveDownSelectedRows(ByVal dgv As DataGridView)

    Dim curRowIndex As Integer = dgv.CurrentCell.RowIndex
    Dim newRowIndex As Integer = curRowIndex + 1

    Dim curColIndex As Integer = dgv.CurrentCell.ColumnIndex
    Dim curRow As DataGridViewRow = dgv.CurrentRow

    If (dgv.SelectedCells.Count > 0) AndAlso (dgv.Rows.Count > newRowIndex) Then

        With dgv
            .Rows.Remove(curRow)
            .Rows.Insert(newRowIndex, curRow)
            .CurrentCell = dgv(curColIndex, newRowIndex)
        End With

    End If

End Sub

      


UPDATE

I'm trying to use @Plutonix's method (with a little modification), the only problem is it doesn't move the correctly selected rows in the UP direction.

Steps to fix the problem:

  • Select two rows that are together (e.g. row index 2 and row index 3, NOT row index 2 and row index 4)

  • Try to move the lines UP.

How can I fix this?

Public Enum MoveDirection As Integer
    Up = -1
    Down = 1
End Enum

Private Sub MoveRows(ByVal dgv As DataGridView, ByVal moveDirection As MoveDirection)

    Dim rows As DataGridViewRowCollection = dgv.Rows

    ' row index
    Dim thisRow As DataGridViewRow

    ' put selection back
    Dim selectedRows As New List(Of Integer)

    ' max rows
    Dim lastRowIndex As Integer =
        If(dgv.AllowUserToAddRows,
           rows.Count - 2,
           rows.Count - 1)



    For n As Integer = lastRowIndex To 0 Step -1

        If Not rows(n).IsNewRow Then

            If rows(n).Selected Then

                selectedRows.Add(n)

                MsgBox(n)

                Select Case moveDirection

                    Case Main.MoveDirection.Down
                        If ((n + moveDirection) <= lastRowIndex) AndAlso (n + moveDirection >= 0) AndAlso rows(n + moveDirection).Selected = False Then

                            selectedRows(selectedRows.Count - 1) = (n + moveDirection)
                            thisRow = rows(n)
                            rows.Remove(thisRow)

                            rows.Insert(n + moveDirection, thisRow)

                        End If

                    Case Main.MoveDirection.Up

                        If ((n + moveDirection) <= lastRowIndex) Then

                            MsgBox(selectedRows(selectedRows.Count - 1))
                            selectedRows(selectedRows.Count - 1) = (n + moveDirection)
                            thisRow = rows(n)
                            rows.Remove(thisRow)

                            rows.Insert(n + moveDirection, thisRow)

                        End If

                End Select

            End If

        End If

    Next n

    ' reselect the original selected rows
    For n As Integer = 0 To lastRowIndex

        dgv.Rows(n).Selected = selectedRows.Contains(n)

        ' renumber the order (optional & unknown, but trivial)
        dgv.Rows(n).Cells(0).Value = (n + 1)

    Next n

End Sub

      

+3


source to share


2 answers


(Updated)
You need to iterate through the collection of strings, check if they are selected, and then swap the strings. In order for two selected rows to switch with each other when they hit the top or bottom, a separate Up and Down method is needed.

' list of hash codes of the selected rows
Private Function GetSelectedRows() As List(Of Integer)
    Dim selR As New List(Of Integer)

    ' have to clear selected so the NEXT row 
    ' doesnt cause odd behavior
    For n As Integer = 0 To dgv.Rows.Count - 1
        If dgv.Rows(n).IsNewRow = False AndAlso dgv.Rows(n).Selected Then
            selR.Add(dgv.Rows(n).GetHashCode)
            dgv.Rows(n).Selected = False
        End If
    Next
    Return selR
End Function

' restore original selected rows
Private Sub SetSelectedRows(selRows As List(Of Integer))

    For n As Integer = 0 To dgv.Rows.Count - 1
        If dgv.Rows(n).IsNewRow = False Then
            dgv.Rows(n).Selected = selRows.Contains(dgv.Rows(n).GetHashCode)
            ' reset Order col:
            dgv.Rows(n).Cells(0).Value = n + 1
        End If
    Next
End Sub

Private Sub MoveRowsUp()
    ' short ref
    Dim rows As DataGridViewRowCollection = dgv.Rows
    ' row index
    Dim thisRow As DataGridViewRow
    ' put selection back
    Dim selectedRows As List(Of Integer)
    ' max rows
    Dim LastRow = If(dgv.AllowUserToAddRows, rows.Count - 2, rows.Count - 1)

    selectedRows = GetSelectedRows()

    For n As Int32 = 0 To LastRow
        If rows(n).IsNewRow = False Then

            If (selectedRows.Contains(rows(n).GetHashCode)) AndAlso (n - 1 >= 0) AndAlso
                (selectedRows.Contains(rows(n - 1).GetHashCode) = False) Then

                thisRow = rows(n)
                rows.Remove(thisRow)
                rows.Insert(n - 1, thisRow)
            End If
        End If
    Next

    SetSelectedRows(selectedRows)

End Sub

Private Sub MoveRowsDn()
    Dim rows As DataGridViewRowCollection = dgv.Rows
    Dim thisRow As DataGridViewRow
    Dim selectedRows As New List(Of Integer)
    Dim LastRow = If(dgv.AllowUserToAddRows, rows.Count - 2, rows.Count - 1)

    selectedRows = GetSelectedRows()

    For n As Int32 = LastRow To 0 Step -1

        If rows(n).IsNewRow = False Then
            If (selectedRows.Contains(rows(n).GetHashCode)) AndAlso (n + 1 <= LastRow) AndAlso
                         (selectedRows.Contains(rows(n + 1).GetHashCode) = False) Then
                thisRow = rows(n)
                rows.Remove(thisRow)
                rows.Insert(n + 1, thisRow)
            End If
        End If
    Next

    SetSelectedRows(selectedRows)

End Sub

      

Using:

 MoveRowsUp()
 ' move down:
 MoveRowsDn()

      

Moving around the lines causes the dgv to be reset. The methods first go through and get a list of HashCodes from the selected strings, and then at the end resets each property Row.Selected

based on that.



The code changes the value of Cell (0) to match the order and order of the order (which means that the value of the Order column has been changed).

Move checks to see both if this line is at the end OR if a line at destination is also selected. This prevents the strings from moving from under the swap when they hit the top or bottom.

Before:
enter image description here

After:
enter image description here

Note that when Zalgo got to the bottom (meaning 3 and 5 were selected), the other could still move down one by one, so this happened while Ziggy stayed in place. The ability of "Ziggy (3)" to move down or not is based on the next line / index (only) - the next line is not behind the bottom AND is not selected, so it can still move down 1, whereas "Zalgo (5))" is not managed.

+2


source


This is my final code, all thanks to @Plutonix, I just translated the logic into extension methods and also extended the original functionality to automate the saving of cells by providing a set of cell indices to store their values:



#Region " Members Summary "

' ยท Public Methods
'
'     MoveSelectedRows(direction)
'     MoveSelectedRows(direction, preserveCellsIndex)

#End Region

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Imports "

Imports System.Diagnostics
Imports System.Runtime.CompilerServices
Imports System.Windows.Forms

#End Region

''' <summary>
''' Contains sofisticated extension methods for a <see cref="DataGridView"/> control.
''' </summary>
''' <remarks></remarks>
Public Module DataGridViewExtensions

#Region " Enumerations "

    ''' <summary>
    ''' Specifies a direction for a move operation of a rows collection.
    ''' </summary>
    Public Enum RowMoveDirection As Integer

        ''' <summary>
        ''' Move row up.
        ''' </summary>
        Up = 0

        ''' <summary>
        ''' Move row down.
        ''' </summary>
        Down = 1

    End Enum

#End Region

#Region " Public Extension Methods "

    ''' <summary>
    ''' Moves up or down the selected row(s) of the current <see cref="DataGridView"/>.
    ''' </summary>
    ''' <param name="sender">The <see cref="DataGridView"/>.</param>
    ''' <param name="direction">The row-move direction.</param>
    <DebuggerStepThrough>
    <Extension>
    Public Sub MoveSelectedRows(ByVal sender As DataGridView,
                                ByVal direction As RowMoveDirection)

        DoRowsMove(sender, direction)

    End Sub

    ''' <summary>
    ''' Moves up or down the selected row(s) of the current <see cref="DataGridView"/>.
    ''' </summary>
    ''' <param name="sender">The <see cref="DataGridView"/>.</param>
    ''' <param name="direction">The row-move direction.</param>
    ''' <param name="preserveCellsIndex">A sequence of cell indexes to preserve its cell values when moving the row(s).</param>
    <DebuggerStepThrough>
    <Extension>
    Public Sub MoveSelectedRows(ByVal sender As DataGridView,
                                ByVal direction As RowMoveDirection,
                                ByVal preserveCellsIndex As IEnumerable(Of Integer))

        DoRowsMove(sender, direction, preserveCellsIndex)

    End Sub

#End Region

#Region " Private Methods "

    ''' <summary>
    ''' Moves up or down the selected row(s) of the specified <see cref="DataGridView"/>.
    ''' </summary>
    ''' <param name="dgv">The <see cref="DataGridView"/>.</param>
    ''' <param name="direction">The row-move direction.</param>
    ''' <param name="preserveCellsIndex">Optionally, a sequence of cell indexes to preserve its cell values when moving the row(s).</param>
    <DebuggerStepThrough>
    Private Sub DoRowsMove(ByVal dgv As DataGridView,
                           ByVal direction As RowMoveDirection,
                           Optional ByVal preserveCellsIndex As IEnumerable(Of Integer) = Nothing)

        ' Keeps tracks of a cell value to preserve, to swap them when moving rows.
        Dim oldCellValue As Object
        Dim newCellValue As Object

        ' Short row collection reference.
        Dim rows As DataGridViewRowCollection = dgv.Rows

        ' Keeps track of the current row.
        Dim curRow As DataGridViewRow

        ' The maximum row index.
        Dim lastRowIndex As Integer =
            If(dgv.AllowUserToAddRows,
               rows.Count - 2,
               rows.Count - 1)

        ' List of hash codes of the selected rows.
        Dim selectedRows As New List(Of Integer)

        ' Get the hash codes of the selected rows
        For i As Integer = 0 To (rows.Count - 1)
            If (rows(i).IsNewRow = False) AndAlso (rows(i).Selected) Then
                selectedRows.Add(rows(i).GetHashCode)
                rows(i).Selected = False
            End If
        Next i

        ' Move the selected rows up or down.
        Select Case direction

            Case RowMoveDirection.Up
                For i As Integer = 0 To lastRowIndex

                    If Not rows(i).IsNewRow Then

                        If (selectedRows.Contains(rows(i).GetHashCode)) AndAlso
                           (i - 1 >= 0) AndAlso
                           (Not selectedRows.Contains(rows(i - 1).GetHashCode)) Then

                            curRow = rows(i)
                            rows.Remove(curRow)
                            rows.Insert(i - 1, curRow)

                            If preserveCellsIndex IsNot Nothing Then

                                For Each cellIndex As Integer In preserveCellsIndex
                                    oldCellValue = curRow.Cells(cellIndex).Value
                                    newCellValue = rows(i).Cells(cellIndex).Value

                                    rows(i).Cells(cellIndex).Value = oldCellValue
                                    curRow.Cells(cellIndex).Value = newCellValue
                                Next cellIndex

                            End If

                        End If

                    End If

                Next i

            Case RowMoveDirection.Down
                For i As Integer = lastRowIndex To 0 Step -1

                    If Not rows(i).IsNewRow Then

                        If (selectedRows.Contains(rows(i).GetHashCode)) AndAlso
                           (i + 1 <= lastRowIndex) AndAlso
                           (Not selectedRows.Contains(rows(i + 1).GetHashCode)) Then

                            curRow = rows(i)
                            rows.Remove(curRow)
                            rows.Insert(i + 1, curRow)

                            If preserveCellsIndex IsNot Nothing Then

                                For Each cellIndex As Integer In preserveCellsIndex
                                    oldCellValue = curRow.Cells(cellIndex).Value
                                    newCellValue = rows(i).Cells(cellIndex).Value

                                    rows(i).Cells(cellIndex).Value = oldCellValue
                                    curRow.Cells(cellIndex).Value = newCellValue
                                Next cellIndex

                            End If

                        End If

                    End If

                Next i

        End Select

        ' Restore selected rows.
        For i As Integer = 0 To (rows.Count - 1)

            If Not rows(i).IsNewRow Then
                rows(i).Selected = selectedRows.Contains(rows(i).GetHashCode)
            End If

        Next i

    End Sub

#End Region

End Module

      

+1


source







All Articles