1
votes

DataGridView caught me again, I cannot figure out, why I get cell formatting error with following code. None of the solutions I found on the net wouldn't work/fit either. On load I set:

    Dim dtAlign As New DataTable  ' creating and filling a DataTable
    dtAlign.Columns.Add("ID", GetType(Int32))
    dtAlign.Columns.Add("Text", GetType(String))
    dtAlign.Rows.Add(1, "Left")
    dtAlign.Rows.Add(2, "Right")
    dtAlign.Rows.Add(3, "Center")

    Dim cola As DataGridViewComboBoxColumn = CType(Me.dgList.Columns("ColAlign"), DataGridViewComboBoxColumn)
    cola.DisplayMember = "Text"
    cola.ValueMember = "ID"
    cola.DataSource = dtAlign  ' assign datatable as a combobox col. datasource

The columns are set via VS GUI (this column: Name = 'ColAlign', Width = 100, DataPropertyName = 'ColAlign', column type = DataGridViewComboBoxCell, etc.). It, works, at a breakpoint just before filling data into DGV, I see a valid datatable is provided:

enter image description here

When I don't load any data into column, I can see a proper ComboBox selection:

enter image description here

However, if I add data in the datasource for this column, I get cell formatting error (saying the value is not valid) and the value is shown as display member text:

enter image description here

The database column is not nullable (at this point always 1), and is INT. I even tried to CAST it, to be sure it's not mixed up i.e. as string:

CAST(ColAlign as INT) as ColAlign

Still, I get the error and I have no more ideas what can be wrong, but ovbiously the 1 in the datatable is not the same as 1 in the database result set. In past I had a problem with Int16 not matching INT, but Int32 always worked agaist INT in database. And even:

CAST(1 as INT) as ColAlign

...doesn't work. By the way, I assign the data simply this way:

Me.dgList.DataSource = ds.Tables(0)

As usually, it has to be something really simple, what I'm missing. I would appreciate even hints how to furthermore debug such an issue.

EDIT:

I also tried creating a button and in onClick wrote a code:

Me.dgList.Rows(1).Cells("ColAlign").Value = 1

This works well.

EDIT 2:

I inserted a DataTable between DataSet and DataSource with fixed datatypes columns (int32 in case of the ComboBoxColumns), which should rule out that wrong datatypes are provided and it still doesn't work...

                Dim dtGrid As New DataTable
                dtGrid.Columns.Add("ID", GetType(Int32))
                dtGrid.Columns.Add("FormID", GetType(Int32))
                dtGrid.Columns.Add("ColName", GetType(String
                dtGrid.Columns.Add("IsVisible", GetType(Boolean))
                dtGrid.Columns.Add("ColWidth", GetType(String))
                dtGrid.Columns.Add("ColAlign", GetType(Int32))

                dtGrid = ds.Tables(0)
                Me.dgList.AutoGenerateColumns = False
                Me.dgList.DataSource = dtGrid

EDIT 3

Another debugging:

    Dim dtTemp As DataTable
    Dim dgwcb As DataGridViewComboBoxColumn = CType(Me.dgList.Columns("ColAlign"), DataGridViewComboBoxColumn)
    dtTemp = CType(dgwcb.DataSource, DataTable)
    MsgBox("ValueMember name = " & dgwcb.ValueMember.ToString & vbCrLf &
           "DisplayMember name = " & dgwcb.DisplayMember.ToString & vbCrLf &
           "DataPropertyName = " & dgwcb.DataPropertyName.ToString & vbCrLf &
           "Value = " & Me.dgList.Rows(2).Cells("ColAlign").Value.ToString & vbCrLf &
           "ToString = " & dgwcb.ToString)

dtTemp in DataSet Visualizer looks good and everything checked in MsgBox is es expected (ValueMember = "ID", DisplayMember = "Text", DataPropertyName = "ColAlign", Value = 2 /I set it to 2 for a change/).

WORKAROUND:

The only way it works is to leave columns with ComboBoxCell empty and fill them manually after the DGV datasource is set, narrowing them to Int32 along (note that they were narrowed both in SQL and dtGrid already):

    For ir = 0 To dtGrid.Rows.Count - 1
        Me.dgList.Rows(ir).Cells("ColAlign").Value = CInt(dtGrid.Rows(ir)("ColAlign"))
    Next

Since I'm using DGV with ComboBoxCell extremely rarely, I can live with this "solution".

EDIT 4 / WORKAROUND 2

I created a 2nd, identical (structure) dtGrid2 and copied values row-by-row and cell-by-cell into the 2nd and voila - it works with the dtGrid2 and doesn't with the original dtGrid! So when I pull data from database, the original correct datatypes of columns are re-typed to something wrong. I suspect this is only happening with SQLiteDataAdapter, because I didn't experienced it with SqlDataAdapter in past. A simplified code to explain:

    Dim dtGrid As New DataTable
    Dim dtGrid3 As New DataTable

    dtGrid.Columns.Add("ID", GetType(Int32))
    dtGrid.Columns.Add("ColName", GetType(String))
    dtGrid.Columns.Add("ColAlign", GetType(Int32))
    ' load data from DB. ...and corrupt the dtGrid...
    dtGrid = ds.Tables(0).DefaultView.ToTable  

    dtGrid3.Columns.Add("ID", GetType(Int32))          ' identical columns
    dtGrid3.Columns.Add("ColName", GetType(String))    ' identical columns
    dtGrid3.Columns.Add("ColAlign", GetType(Int32))    ' identical columns

    For ir = 0 To dtGrid.Rows.Count - 1  ' copy all values to new identical table
        dtGrid3.Rows.Add({dtGrid(ir)("ID"), dtGrid(ir)("ColName"), dtGrid(ir)("ColAlign")})
    Next

    Me.dgList.DataSource = dtGrid3  ' works!!! Doesn't with dtGrid...
2
The point of a CBO column is to Display thing and use another. Display Text and Save integer. If the ValueMember is ID then the DGV column datatype must be integer - Ňɏssa Pøngjǣrdenlarp
I understand that and don't get what are pointing out at. I have integeres everywhere, didn't I? That's a first thing I checked as thouroughly as I am able. - Oak_3260548
Most of what you have seems correct in the code but the post is very very confusing. At the start you tell you you have a cell formatting error with following code but thats not where the problem is and there is no minimal reproducible example and no sample data to reproduce the problem. Make sure the DB columns map to the DGV columns like you think and that the datatypes in fact match. You could also use AutoGenerateColumns = true and just swap out that one column - Ňɏssa Pøngjǣrdenlarp
I would do better if I know how, but I believe sample data is provided (= value 1 as shown). The post also contains a complete code to do this task and a debugging code I so far did. With last added code, if one fills column ColAlign with 1 in dtGrid, then it's 100% what I have. However, if you miss anything particular, please let me know, I will try to complete the post. - Oak_3260548

2 Answers

0
votes

Try this:

Dim cola As DataGridViewComboBoxColumn = CType(Me.dgList.Columns("ColAlign"), DataGridViewComboBoxColumn)
cola.DataSource = dtAlign  
cola.DataPropertyName = "ID"
cola.DisplayMember = "Text"
cola.ValueMember = "ID"

You need to set DataPropertyName if you are binding to DataSource like DataTable

0
votes

You have a DataGridView with some columns set.
Something like this:

enter image description here

But could be anything else.
Let's create a sample DataSource using a List(Of Class):

Public Class MyDataSourceRow

    Private _ID As Integer
    Private _Text As String

    Public Sub New()

    End Sub

    Public Property ID() As Integer
        Get
            Return Me._ID
        End Get
        Set(ByVal value As Integer)
            Me._ID = value
        End Set
    End Property

    Public Property Text() As String
        Get
            Return Me._Text
        End Get
        Set(ByVal value As String)
            Me._Text = value
        End Set
    End Property

End Class


Creeate a new DataGridViewComboBoxColumn, set it's basic properties, specify a DataSource and add it to the DataGridView:

    Dim MyDataSource = New List(Of MyDataSourceRow)
    MyDataSource.Add(New MyDataSourceRow With {.ID = 1, .Text = "Left"})
    MyDataSource.Add(New MyDataSourceRow With {.ID = 2, .Text = "Right"})
    MyDataSource.Add(New MyDataSourceRow With {.ID = 3, .Text = "Center"})

    Dim MyColumn As DataGridViewComboBoxColumn = New DataGridViewComboBoxColumn
    MyColumn.Width = 150
    ' Name of the new Column. Will also be the text of the Header
    MyColumn.Name = "ComboAlignment"

    'Define its DataSource 
    MyColumn.DataSource = MyDataSource
    ' The DataPropertyName is the field of your DataSource to which your column value is bound
    MyColumn.DataPropertyName = "ID"
    ' This is the value you want the User to see
    MyColumn.DisplayMember = "Text"
    ' ValueMember is the value passed to the DataGrid when a User selects an entry in the DropDown
    MyColumn.ValueMember = "ID"

    'Insert the column in the first position -> Index(0)
    Me.DataGridView1.Columns.Insert(0, MyColumn)


Now, this is the result:

enter image description here

For some reason, you are confusing the .Name of the column with it's .DataPropertyName