Search code examples
vb.netdatagridviewbindingsource

AddNew(), problems deleting this row


I have a DataGridView bound to a BindingSource. I then have a Button to add a new record:

Private Sub btnNew_Click(sender As Object, e As EventArgs) Handles btnNew.Click
    _bsStaff.AddNew()
End Sub

This does create a new row, also following it with the "next new/blank" row. This works fine, except if I immediately press my Delete button to try and remove this newly added row:

Private Sub btnDelete_Click(sender As Object, e As EventArgs) Handles btnDelete.Click
    If MessageBox.Show("Delete staff member record?", "Confirm", MessageBoxButtons.YesNo, _
                       MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
        _bsStaff.RemoveCurrent()
    End If
End Sub

This removes the new row, but also the one before it!

I tried adding a line before RemoveCurrent, _bsStaff.EndEdit() and also If _bsStaff.Position + 1 = _bsStaff.Count Then, but the behaviour persists.

Could someone explain this behaviour and offer a solution?


Added: I thought I had it beat with this code, checking the row state:

Private Sub btnDelete_Click(sender As Object, e As EventArgs) Handles btnDelete.Click

    Dim state = CType(_bsStaff.Current, DataRowView).Row.RowState
    Select Case state
        Case DataRowState.Added
            _bsStaff.RemoveCurrent()
        Case DataRowState.Deleted
            MessageBox.Show("Row already deleted.", "Delete")
        Case DataRowState.Detached
            _bsStaff.CancelEdit()
        Case DataRowState.Modified
            If MessageBox.Show("Delete staff member record?", "Confirm", MessageBoxButtons.YesNo, _
                               MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                _bsStaff.RemoveCurrent()
            End If
        Case DataRowState.Unchanged
            If _bsStaff.Position + 1 > _bsStaff.Count Then
                'do nothing, the new row
            ElseIf MessageBox.Show("Delete staff member record?", "Confirm", MessageBoxButtons.YesNo, _
                               MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                _bsStaff.RemoveCurrent()
            End If
        Case Else
            'do nothing
    End Select
End Sub

However, looking at the screenshot,

enter image description here

when I click into the new row, and then click my button, it is in an Unchanged state, but it is the previous row that has the current record pointer (and that the Unchanged state probably refers to), it is this row that will be deleted.

Essentially, I think I need a way to detect that the new row has just been clicked into, but nothing has been typed, so that I can abandon the attempt to delete.


Solution

  • In the meantime, and for comparison and comment (and criticism), I'll post my second, improved, solution here.

    Here is my current full code block for the Delete method:

    Private Sub btnDelete_Click(sender As Object, e As EventArgs) Handles btnDelete.Click
        If _bsStaff.Current Is Nothing Then
            Exit Sub        'nothing to consider deleting
        End If
        Dim currentRow = CType(_bsStaff.Current, DataRowView).Row
        Dim sMemberMessage As String
    
        Dim state = currentRow.RowState
        Select Case state
            Case DataRowState.Added
                'newly added, just remove it
                _bsStaff.RemoveCurrent()
            Case DataRowState.Deleted
                MessageBox.Show("Row already deleted.", "Delete")
            Case DataRowState.Detached
                _bsStaff.CancelEdit()
            Case DataRowState.Modified, DataRowState.Unchanged
                If dgvStaff.SelectedCells.Count > 0 AndAlso _
                        dgvStaff.SelectedCells(0).RowIndex = dgvStaff.NewRowIndex Then
                    'a new, blank, row, is attempted to be deleted
                    '(the current row is the one before this)
                    _bsStaff.CancelEdit()
                    Exit Sub
                End If
                sMemberMessage = String.Format("Delete record ({0} {1} {2}) ?", _
                        _bsStaff.Current("StaffID"), _bsStaff.Current("FirstName"), _bsStaff.Current("LastName"))
                If MessageBox.Show(sMemberMessage, "Confirm", MessageBoxButtons.YesNo, _
                                   MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                    _bsStaff.RemoveCurrent()
                End If
            Case Else
                'do nothing
        End Select
    End Sub
    

    The key component is here:

            Case DataRowState.Modified, DataRowState.Unchanged
                If dgvStaff.SelectedCells.Count > 0 AndAlso _
                        dgvStaff.SelectedCells(0).RowIndex = dgvStaff.NewRowIndex Then
                    'a new, blank, row, is attempted to be deleted
                    '(the current row is the one before this)
                    _bsStaff.CancelEdit()
                    Exit Sub
                End If
    

    Looking at the screenshot in my question, although the current row is the one just above the new row, there is still a SelectedCell in the new row. With dgvStaff.SelectedCells(0).RowIndex = dgvStaff.NewRowIndex I am confirming that this cell is in the new (blank) row. Now this will only be the case if the user has clicked into this new row, so it is in an Unchanged state, and immediately pressed the Delete button, which is the exact situation I am trying to detect. If they had typed anything in this new row, then it would no longer be the new row (the test condition would not be met), nor would it be in an unchanged state. In these circumstances, the earlier cases would already have caused the row to be abandoned.

    It is a little clumsy (I'm still hoping for an improvement upon this) but it is also a very specific circumstance that I am trying to capture.