1

I am trying to programmatically add a new row to a binding source . I know calling the bsSource.AddNew() adds a new row which I cast as a DataRowView and I set my values. My problem is this - the DataRowView.Row shows a RowState of detached. I do not want it to be detached ; I believe it should show added - I also do NOT want it to commit the change to the database (There is a very valid reason for this). I want to pick the time for that later. My code is as follows:

private Sub AddToRelationSource(binID As Integer, gradeID As Integer, IsChecked As Boolean)
            Dim drv As DataRowView = DirectCast(bsBinGrades.AddNew(), DataRowView)


            drv.Row("IsSelected") = IsChecked
            drv.Row("BinID") = binID
            drv.Row("GradeID") = gradeID
    ' I tried drv.EmdEdit(0 drv.Row.EndEdit() - Row State still shows detached

End Sub
Ken
  • 2,518
  • 2
  • 27
  • 35

3 Answers3

6

The BindingSource AddNew method does not actually add a new record to the underlying datasource , it simply adds it to the bindingsource as a detached item. When using the datatabel as a datasource I needed to get the datatable and use the AddRow() method - this properly set the value in my bindingsource to added so that when the changes would be committed to the database on bindingsource.Update() method.

The code I used:

    Dim drv As DataRowView = DirectCast(bsData.AddNew(), DataRowView)

    drv.BeginEdit()

    drv.Row.BeginEdit()

    drv.Row("IsSelected") = IsChecked

    drv.Row.EndEdit()

    drv.DataView.Table.Rows.Add(drv.Row)

The last line is what actually added the item to the datasource - I misunderstood BindingSource.AddNew() .

Ken
  • 2,518
  • 2
  • 27
  • 35
2

The following may be in the right direction. First I used a few language extension methods e.g.

Public Module BindingSourceExtensions
    <Runtime.CompilerServices.Extension()>
    Public Function DataTable(ByVal sender As BindingSource) As DataTable
        Return CType(sender.DataSource, DataTable)
    End Function
    <Runtime.CompilerServices.Extension()>
    Public Sub AddCustomer(ByVal sender As BindingSource, ByVal FirstName As String, ByVal LastName As String)
        sender.DataTable.Rows.Add(New Object() {Nothing, FirstName, LastName})
    End Sub
    <Runtime.CompilerServices.Extension()>
    Public Function DetachedTable(ByVal sender As BindingSource) As DataTable
        Return CType(sender.DataSource, DataTable).GetChanges(DataRowState.Detached)
    End Function
    <Runtime.CompilerServices.Extension()>
    Public Function AddedTable(ByVal sender As BindingSource) As DataTable
        Return CType(sender.DataSource, DataTable).GetChanges(DataRowState.Added)
    End Function
End Module

Now load ID, FirstName and LastName into a DataTable, Datatable becomes the DataSource of a BindingSource which is the BindingSource for a BindingNavigator and are wired up to a DataGridView.

Keeping things simple I mocked up data, has no assertions e.g. make sure we have valid first and last name, instead concentrate on the methods.

First use a extension method to add a row to the underlying DataTable of the BindingSource.

bsCustomers.AddCustomer("Karen", "Payne")

Now check to see if there are detached or added rows

Dim detachedTable As DataTable = bsCustomers.DetachedTable
If detachedTable IsNot Nothing Then
    Console.WriteLine("Has detached")
Else
    Console.WriteLine("No detached")
End If
Dim AddedTable As DataTable = bsCustomers.AddedTable
If AddedTable IsNot Nothing Then
    Console.WriteLine("Has added")
Else
    Console.WriteLine("None added")
End If

Since we are not talking to the database table, the primary key is not updated as expected and since you don't want to update the database table this is fine. Of course there is a method to get the primary key for newly added records if you desire later in your project.

Addition

Private Sub BindingSource1_AddingNew(ByVal sender As System.Object, ByVal e As System.ComponentModel.AddingNewEventArgs) Handles BindingSource1.AddingNew
  Dim drv As DataRowView = DirectCast(BindingSource1.List, DataView).AddNew()
  drv.Row.Item(0) = "some value"
  e.NewObject = drv
  ' move to new record
  'BindingSource1.MoveLast()
End Sub
Karen Payne
  • 4,341
  • 2
  • 14
  • 31
  • 1
    I already know if the item is detached or not, the part I was missing was that even though I used BindingSource.AddNew() method it does not addnew to the datatable I had to add that DataRowView.Row to the dataTable then it was no longer detached. (which a reaosnable person would assume AddNew on the binding source would do naturally. – Ken Aug 15 '16 at 14:00
  • 1
    I just added additional code sample at end of my last reply, see if that is helpful or not. – Karen Payne Aug 15 '16 at 14:52
0

'This routine takes the AddForm with the various fields that the user 'fills in and calls the TableAdapter's Insert method. 'After that is done, then the table has be be reflected back to the 'various components.

Private Sub AddRecord()
    'The following line did not work because I could not get
    'the bs definition down.
    'Tried the BindingSource but in gave an error on 
    'DataRowView so I came up with an alternate way of 
    'adding the row.
    'Dim drv As DataRowView = DirectCast(bsData.AddNew(), DataRowView)
    'Dim drv As DataRowView = DirectCast(RecTableBindingSource.AddNew(), DataRowView)

    'drv.BeginEdit()
    'drv.Row.BeginEdit()
    'drv.Row("Title") = "Order, The"
    'drv.Row.EndEdit()
    'drv.DataView.Table.Rows.Add(drv.Row)

    RecTableTableAdapter.Insert(pAddForm.tTitle.Text,
                                pAddForm.tCast.Text,
                                pAddForm.tAKA.Text,
                                pAddForm.tRelated.Text,
                                pAddForm.tGenre.Text,
                                pAddForm.tRated.Text,
                                pAddForm.tRelease.Text,
                                pAddForm.tLength.Text)

    Validate()
    RecTableBindingSource.EndEdit()
    RecTableTableAdapter.Update(VideoDBDataSet.RecTable)
    RecTableAdapterManager.UpdateAll(VideoDBDataSet)
    RecTableTableAdapter.Fill(VideoDBDataSet.RecTable)
    VideoDBDataSet.AcceptChanges()
End Sub

'Here is my Delete Record routine
Private Sub DeleteRecordToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles DeleteRecordToolStripMenuItem.Click
    Dim RowIndex As Int32

    If (dgvRec.SelectedRows.Count > 0) Then
        RowIndex = dgvRec.SelectedRows(0).Index
        'Now we have to delete the record
        dgvRec.Rows.RemoveAt(RowIndex)

        dgvRec.CommitEdit(RowIndex)
        dgvRec.EndEdit()
        Validate()
        RecTableBindingSource.EndEdit()
        RecTableTableAdapter.Update(VideoDBDataSet.RecTable)
        RecTableAdapterManager.UpdateAll(VideoDBDataSet)
        RecTableTableAdapter.Fill(VideoDBDataSet.RecTable)
        VideoDBDataSet.AcceptChanges()
    Else
        'No row selected to work with
    End If
End Sub

'The pAddForm MUST be open for this routine to work
Private Sub UpdateGridFromForm()
    Dim RowIndex As Int32
    Dim Index As Int32
    Dim RecIndex As Int32
    Dim dt As DataTable

    If ((pAddForm Is Nothing) = False) Then
        RowIndex = pAddForm.GridIndex
        If (RowIndex >= 0) Then
            Index = pAddForm.Index
            If (Index = dgvRec.Rows(RowIndex).Cells(constRecGridColIndex).Value) Then
                'OK, we have a match so we are good to go
                Call PopulateGridFields(RowIndex)
            Else
                MsgBox("Unable to save data back to the Grid because the record is no longer the same")
            End If
        Else
            'This must be a NEW record
            Call AddRecord()
        End If
    Else
        'No form to work with
    End If
End Sub

'Populate the dgvRec fields from pAddForm
Private Sub PopulateGridFields(RowIndex As Int32)
    dgvRec.Rows(RowIndex).Cells(constRecGridTitle).Value = pAddForm.tTitle.Text
    dgvRec.Rows(RowIndex).Cells(constRecGridCast).Value = pAddForm.tCast.Text
    dgvRec.Rows(RowIndex).Cells(constRecGridAKA).Value = pAddForm.tAKA.Text
    dgvRec.Rows(RowIndex).Cells(constRecGridRelated).Value = pAddForm.tRelated.Text
    dgvRec.Rows(RowIndex).Cells(constRecGridGenre).Value = pAddForm.tGenre.Text
    dgvRec.Rows(RowIndex).Cells(constRecGridRated).Value = pAddForm.tRated.Text
    dgvRec.Rows(RowIndex).Cells(constRecGridRelease).Value = pAddForm.tRelease.Text
    dgvRec.Rows(RowIndex).Cells(constRecGridLength).Value = pAddForm.tLength.Text

    dgvRec.CommitEdit(RowIndex)
    dgvRec.EndEdit()
    Validate()
    RecTableBindingSource.EndEdit()
    RecTableTableAdapter.Update(VideoDBDataSet.RecTable)
    RecTableAdapterManager.UpdateAll(VideoDBDataSet)
    RecTableTableAdapter.Fill(VideoDBDataSet.RecTable)
    VideoDBDataSet.AcceptChanges()
End Sub

'This all works great.
'The only problem I have now is that the DataGridView will 'always'Repopulate the grid (including any changes with 'Add/Delete/Modify) sending the active 'row back to the top of the grid

'I will work on a solution to this now that I have the rest working