Phil
Phil

Reputation: 167

Setting a DataGridView source nullifies all existing DataGridViewRow objects

I've got a project with a DataGridView showing data from a flat file database. There are 7 buttons that load an SQL querys result to the DGV, as well as adding a checkbox to select rows.

When a user selects a checkbox, that particular DataGridViewRow (DGVR) gets added to a temporary List(Of DGVR) for later use.

Then there's a final button that shows a second form, doing a comparison of the two entries selected, passing the temporary list to the second form.

Users will be able to select 1 from 2 different selections (hence the separate lists).

The problem is, if you click button 1, and choose a row, when you click a second button, the line that resets the datasource to the new query results apparently turns all existing DGVRs into null pointers.

IE: the references to the DGVR are lost.

If you pick one from the first button, and then one from the second button, then try and pass the two selected items, the first one will be null, but the second won't.

Code:

    Dim con As New OleDb.OleDbConnection
    Dim dbProvider As String
    Dim dbSource As String

    currentWeapon = "Sniper"

    Dim ds As New DataSet
    Dim da As New OleDb.OleDbDataAdapter
    Dim sql As String

    dbProvider = "PROVIDER=Microsoft.ACE.OLEDB.12.0;"
    dbSource = "Data Source = Resources/Battlefield 4 Weapons.accdb"
    con.ConnectionString = dbProvider & dbSource

    con.Open()

    sql = "SELECT * FROM [Battlefield 4 Weapons]"
    da = New OleDb.OleDbDataAdapter(sql, con)
    da.Fill(ds, "Battlefield 4 Weapons.accdb")

    con.Close()

    Dim dt As DataTable = ds.Tables(0)
    Dim dr As DataRow() = dt.Select("[Weapon type] = 'Sniper'", "Weapon Name")
    Dim miniDT As New DataTable

    miniDT = dr.CopyToDataTable()
    DataGridView1.DataSource = miniDT
    DataGridView1.Sort(DataGridView1.Columns("Weapon Name"), System.ComponentModel.ListSortDirection.Ascending)

    DataGridView1.MultiSelect = True

The line that nullifies the entries when a new button is pressed is the line:

        DataGridView1.DataSource = miniDT

Then, when the checkbox gets clicked, this code runs (with a case for each weapon type). AsList is the list designed to hold the rows temporarily before passing them over:

    DataGridView1.EndEdit()
    Select Case currentWeapon
        Case "Assault"

            Assaultlabel.Visible = True
            AsList.RemoveRange(0, AsList.Count)
            For i = 0 To DataGridView1.Rows.Count - 1
                If DataGridView1.Rows(i).Cells(14).Value = True Then
                    If Not AsList.Contains(DataGridView1.Rows(i)) Then
                        AsList.Add(DataGridView1.Rows(i))
                    End If
                End If
            Next

I've tried cloning the row, to create a copy that is separate, but that doesn't work. How can I separate it from the data source so that I can load a new set of data, but SAVE the selected rows?

Upvotes: 0

Views: 208

Answers (1)

The DataGridView will remove all rows whenever the DataSource is changed. You always need to reference the underlying data. I think it's time to understand how a DataTable is bound to the grid.

When you set a new data source the grid will see if the type of the new object implements the IListSource interface. A DataTable implements this interface. It returns the DefaultView and it's actually this instance that the grid use as its source. The grid create new rows and each row are bound to a DataRowView (which in turn is bound to a DataRow).

DataGridView > DataView > DataTable

DataGridViewRow > DataRowView > DataRow

You can obtain a reference to a DataRowView through the DataBoundItem property of the DataGridViewRow:

Dim gridRow As DataGridViewRow = Me.DataGridView1.Rows(0)
Dim viewRow As DataRowView = DirectCast(gridRow.DataBoundItem, DataRowView)
Dim tableRow As DataRow = viewRow.Row

Instead of copying a data table, just create a new data view:

Dim view1 As New DataView(mytable)
Dim view2 As New DataView(mytable)

You can sort and filter a data view.

view1.Sort = "Name ASC"
view1.RowFilter = "Name = 'Smith' And Age >= 18"

Now you can use this DataView as the data source instead the default view.

Me.DataGridView1.DataSource = view1

Upvotes: 1

Related Questions