Search code examples
vb.netwinformsdatatabledatagridviewdataview

Moving Rows up and down in a sorted DataTable set as the DataSource of a DataGridView does not update the View


I've looked online and I've seen a lot of different posts of similar stuff. My code moves the rows correctly. This is what happens: I press either the UP or Down Buttons, the selected rows moves accordingly.
If I click on the Row that I just moved and I try to move it back, it moves it in the DataTable's DefaultView sort order but the position change is NOT reflected in the DataGridView.

If I select a Row I've never moved, it will move it and the DataGridView will reflect it, but once again if I choose that same row and try to move it back, this change is not reflected in the View, but the DataTable is updated.

I've tried to ResetBindings, DataGridView.Refresh, reset the DataSource: none of this works.

Anyone know why this happens and how i can fix it so it reflects the changes every time?

 Private Sub UpBtn_Click(sender As Object, e As EventArgs) Handles UpBtn.Click
    MoveUpDataRow(StepDGV.CurrentRow.Index)
End Sub

Private Sub MoveUpDataRow(RowIndex As Integer)
    Dim OriginalStepNumber As Integer
    Dim dv As DataView = StepsData.DefaultView

    OriginalStepNumber = CInt(StepDGV.Rows(RowIndex).Cells("StepIDLS").Value)

    If RowIndex = 0 Then
        'dv(RowIndex - 1)("StepIDLS") = OriginalStepNumber + 1
        'dv(RowIndex)("StepIDLS") = OriginalStepNumber - 1
        exit Sub
    Else

        dv(RowIndex - 1).BeginEdit()
        dv(RowIndex - 1)("StepIDLS") = OriginalStepNumber
        'dv(RowIndex)("StepIDLS") = RowIndex-1
        dv(RowIndex - 1).EndEdit()

        dv(RowIndex).BeginEdit()
        dv(RowIndex)("StepIDLS") = OriginalStepNumber - 1
        'dv(RowIndex)("StepIDLS") = RowIndex-1
        dv(RowIndex).EndEdit()

    End If
    dv.Sort = "StepIDLS ASC"
  '  StepDGV.Rows(RowIndex).Selected = true
    'StepDGV.DataSource = StepsData
    'StepDGV.Refresh()
    StepDGV.ResetBindings()
    StepDGV.Refresh()

End Sub


Private Sub downBtn_Click(sender As Object, e As EventArgs) Handles downBtn.Click
    MoveDownDataRow(StepDGV.CurrentRow.Index)
End Sub

Private Sub MoveDownDataRow(RowIndex As Integer)
    Dim OriginalStepNumber As Integer
    Dim dv As DataView = StepsData.DefaultView

    OriginalStepNumber = CInt(StepDGV.Rows(RowIndex).Cells("StepIDLS").Value)

    If RowIndex = dv.Count - 1 Then
        exit Sub
    Else

        dv(RowIndex + 1).BeginEdit()
        dv(RowIndex + 1)("StepIDLS") = OriginalStepNumber
        dv(RowIndex + 1).EndEdit()

        dv(RowIndex).BeginEdit()
        dv(RowIndex)("StepIDLS") = OriginalStepNumber + 1
        dv(RowIndex).EndEdit()

    End If
    dv.Sort = "StepIDLS ASC"
  '  StepDGV.Rows(RowIndex).Selected = true
    'StepDGV.Refresh()
    'StepDGV.DataSource = StepsData
    'StepDGV.Refresh()
    StepDGV.ResetBindings()
    StepDGV.Refresh()

End Sub[![screenshot of datagridview for visual aid when going through code][1]][1]

Solution

  • Since you have a sorted DataView, setting two consecutive values using the same Index reference, may (will) have undefined results, even more if you move in two different directions.

    Method 1:

    It's simpler (and functional) to get the reference of both DataRows from the current DataView and then change the value of a Column using the DataRow reference: even if the DataRow position in the sorted DataView changes, the DataRow reference is the same.

    You don't need to BeginEdit/EndEdit a Cell value, these changes are propagated automatically.

    ► Here, I'm using the same Button.Click handler for both Buttons and determine the direction based on the Button name (it could be anything else that fits).

    ► This line: Dim dView = DirectCast(StepDGV.DataSource, DataTable).DefaultView is not necessary if you have stored a DataTable object (as it looks like).
    If that's the case, of course use the DataTable reference you already have.

    ► It's assumed you have set StepsData.DefaultView.Sort = "StepIDLS ASC" before this point.

    Private Sub btnMoveRowUpDown_Click(sender As Object, e As EventArgs) Handles UpBtn.Click, downBtn.Click
        If StepDGV.CurrentRow Is Nothing Then Return
    
        Dim moveUp As Boolean = DirectCast(sender, Button).Name.Equals("UpBtn")
    
        Dim currentRow As Integer = StepDGV.CurrentCell.RowIndex
        Dim dView = DirectCast(StepDGV.DataSource, DataTable).DefaultView
        Dim rowCurrent = dView(currentRow).Row
        Dim colCurrentValue As Integer = CType(rowCurrent("StepIDLS"), Integer)
    
        If moveUp Then
            If currentRow = 0 Then Return
    
            Dim rowPrevious = dView(currentRow - 1).Row
            Dim colPreviousValue As Integer = CType(rowPrevious("StepIDLS"), Integer)
            rowCurrent("StepIDLS") = colPreviousValue
            rowPrevious("StepIDLS") = colCurrentValue
        Else
            If currentRow = StepDGV.NewRowIndex - 1 Then Return
    
            Dim rowNext = dView(currentRow + 1).Row
            Dim colNextValue As Integer = CType(rowNext("StepIDLS"), Integer)
            rowCurrent("StepIDLS") = colNextValue
            rowNext("StepIDLS") = colCurrentValue
        End If
    End Sub
    

    Method 2:

    You can do something similar setting the DataGridView Cells values.
    In this case, you do need to call DataGridView.EndEdit() to notify the change, so it occurs immediately. Otherwise, the value change would be propagated after the focus leaves the CurrentCell.

    The notes here: Bound DataGridView not updating to display information + sorting issues may also be useful in a similar condition (when you have to directly set DataGridView Cells).

    You can also notice that setting a DataRow Colums value also changes the DataGridView CurrentRow (not just the CurrentCell), while setting the Value of a Cell in a DataGridView, doesn't cause the CurrentRow to change, so you have to do it yourself, setting:

    [DataGridView].CurrentCell = [DataGridView].Rows(currentRow + 1).Cells(currentColumn)
    

    Private Sub btnMoveRowUpDown_Click(sender As Object, e As EventArgs) Handles UpBtn.Click, downBtn.Click
        If StepDGV.CurrentRow Is Nothing Then Return
    
        Dim moveUp As Boolean = DirectCast(sender, Button).Name.Equals("UpBtn")
    
        Dim currentRow As Integer = StepDGV.CurrentCell.RowIndex
        Dim currentColumn As Integer = StepDGV.CurrentCell.ColumnIndex
        Dim currentCellValue As Integer = CType(StepDGV("StepIDLS", currentRow).Value, Integer)
    
        If moveUp Then
            If currentRow = 0 Then Return 
            Dim previousCellValue As Integer = CType(StepDGV("StepIDLS", currentRow - 1).Value, Integer)
    
            StepDGV.Rows(currentRow).Cells("StepIDLS").Value = previousCellValue
            StepDGV.EndEdit()
            StepDGV.Rows(currentRow - 1).Cells("StepIDLS").Value = currentCellValue
            StepDGV.EndEdit()
            StepDGV.CurrentCell = StepDGV.Rows(currentRow - 1).Cells(currentColumn)
        Else
            If currentRow = StepDGV.NewRowIndex - 1 Then Return 
            Dim nextCellValue As Integer = CType(StepDGV("StepIDLS", currentRow + 1).Value, Integer)
    
            StepDGV.Rows(currentRow).Cells("StepIDLS").Value = nextCellValue
            StepDGV.EndEdit()
            StepDGV.Rows(currentRow + 1).Cells("StepIDLS").Value = currentCellValue
            StepDGV.EndEdit()
            StepDGV.CurrentCell = StepDGV.Rows(currentRow + 1).Cells(currentColumn)
        End If
    End Sub