DataGridView Cell Formatting Error - vb.net

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:
When I don't load any data into column, I can see a proper ComboBox selection:
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:
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...

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

You have a DataGridView with some columns set.
Something like this:
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:
For some reason, you are confusing the .Name of the column with it's .DataPropertyName

Related

ComboBoxColumn errors out when trying to load data

I am trying to create a DataGridView with a DataGridViewComboBox
I am using the following code to generate it using 2 function that return DataTables for populating it:
'// Returns a DataTable with 2 Columns of Type Integer and System.String
'// Column 1 are IDs called ID, Column 2 is a String of numbers called CompanyCode
'// Example:
'// 1,'9999'
'// 2,'9998'
'// 3,'9997' etc.
Dim uTableSys As DataTable = GetCompanyCodeLinks()
'// Returns a DataTable with 1 Column of Type System.String
'// The Column is called CC
'// Example: '0001','0002','1234'
Dim CompanyCodes As DataTable = GetListCompanyCodes()
Dim uBindSys As New BindingSource
uBindSys.DataSource = uTableSys
Dim uCol1 As New DataGridViewComboBoxColumn()
uCol1.HeaderText = "Buchungskreis"
'// Points to uTableSys.CompanyCode
uCol1.DataPropertyName = "CompanyCode"
uCol1.DataSource = CompanyCodes
uCol1.ValueType = uTableSys.Columns(1).DataType
'// Points to CompanyCodes.CC
uCol1.ValueMember = "CC"
uCol1.DisplayMember = uCol1.ValueMember
Me.DGV.Columns.Add(uCol1)
Me.DGV.AutoGenerateColumns = False
Me.DGV.DataSource = uBindSys
But as soon as I add uCol1.DataPropertyName = "CompanyCode", which I assume would link the DGV Column to the BindingSource table "CompanyCode" column, it returns with the following error:
"The DataGridViewComboBoxCell value is not valid"
I have checked the types of the DataTable, which are System.String, which the Column is set to via the uCol1.ValueType in the code. For clarification, uTableSys.Columns(1).DataType returns System.String
So why does it return with a "value is not valid" error?
I have figured out the issue:
The value that is written in the cell from the GridView DataSource on loading has to exist ComboBox list as well. If the value does not exist, it will throw the error.

About datagrid-view in vb.net

net.I created a checkbox column in data grid-view and call the checked value.If i unchecked and checked another row it shows error.Need to know how to write loop concept for this.
Here's how you should have a datagridview that has a checkbox column, and how you should access it:
Dim dt as New DataTable
dt.Columns.Add("Name")
dt.Columns.Add("Age", GetType(integer))
dt.Columns.Add("IsActiveMember", GetType(Boolean))
dt.Rows.Add("Siva", 32 , True)
dt.Rows.Add("Siva2", 32 , False)
dt.Rows.Add("Siva3", 33 , True)
yourdatagridview.DataSource = dt
i.e. you create a datatable to hold your data, fill it and bind it. the datagridview will create the columns automatically.
When you want to retrieve the checked boxes:
For Each ro as DataRow in DirectCast(yourdatagridview.DataSource, DataTable).Rows
If DirectCast(ro("IsActiveMember"), Boolean) Then
Messageox.Show(ro("Name") & " is an active member")
Else
Messageox.Show(ro("Name") & " is not an active member")
End If
Next ro
If you get tired of directcasting all the time, add a new DataSet object to your project and use a stringly typed dataset

Bound datagrid not showing values

I have a datagrid which is bound to a binding source which itself is populated from a list.
I can see that the binding source is populated and I can see that the datagrid has the expected number of rows to match the data in the list but i can not get the columns to populate with the data. I have tried setting the datapropertyname for the columns but to no avail. I know this is probably dead simple but I'm going round in circles.
Please can anyone help
Dim cE As New ClsEmail
Dim bs As New BindingSource
Dim dgv = Me.DataGridView1
dgv.AutoGenerateColumns = False
bs.DataSource = cE.GetMail("team#xxxx.xxxx")
With dgv.Columns(0)
.DataPropertyName = "ID"
.HeaderText = "ID"
End With
With dgv.Columns(1)
.DataPropertyName = "Subject"
.HeaderText = "Subject"
End With
dgv.DataSource = bs
When i press the button to populate the datagrid I get four rows but no values in the columns. Clearly I'm not binding the columns correctly but i can't see what I'm missing.
List(Of T) is already bindable. Rearrange your properties in your Email class like this, as the DataGridView will create the columns in property order:
Public Class Email
Public Property ID As Integer
Public Property Subject As String
Public Property DateReceived As DateTime
End Class
Then, just set the datasource like this:
With DataGridView1
.AutoGenerateColumns = True
.DataSource = cE.GetMail("team#xxxx.xxxx")
.Columns(2).Visible = False ' DataReceived (if you really intended to hide this)
End With

In VB.NET I am having problems populating a DataGridView combobox using a datatable

I have a datatable (dsBrands) that I want to display in a datagridview. The combobox in the datagridview gets its selections from a different table(dsGroups). How do I show in the combobox the value that is currently stored in the dsBrands table? Below is my code that works except that the combobox is left blank. The combobox collection does populate correctly from dsGroups. The value that needs to be displayed in the combobox from dsBrands is in the collection. The --> is just to make the line stand out and not in the actual code.
Private Sub FillBrands()
Dim TransCount As Integer
Dim Group As String
If Not ds.Tables.Contains("dsBrands") Then Else ds.Tables("dsBrands").Clear()
conn.Open()
daBrands = New OleDbDataAdapter("Select * from Brands", conn)
daBrands.FillSchema(ds.Tables("dsBrands"), SchemaType.Source)
daBrands.Fill(ds, "dsBrands")
conn.Close()
dgv1.AutoGenerateColumns = False
bs1.DataSource = ds.Tables("dsBrands")
dgv1.DataSource = bs1
Dim colDisplay As New DataGridViewCheckBoxColumn
colDisplay.DataPropertyName = "Display"
colDisplay.HeaderText = "Include?"
colDisplay.Name = "Display"
colDisplay.Visible = True
Dim colGroupList As New DataGridViewComboBoxColumn
colGroupList.HeaderText = "Add to Group"
colGroupList.Name = "GroupList"
colGroupList.Visible = True
colGroupList.DataSource = ds.Tables("dsGroups")
--> colGroupList.DataPropertyName = "BGroup" 'BGroup is a field in dsBrands table.
dgv1.Columns.Add(colDisplay)
dgv1.Columns.Add(colBrand)
dgv1.Columns.Add(colGroup)
dgv1.Columns.Add(colGroupList)
End Sub
Just as when binding a regular ComboBox, you need to set the DisplayMember and ValueMember properties of your column. The DisplayMember specifies the column whose values get displayed and the ValueMember specifies the column whose values correspond to the grid data. ValueMember should specify the PK column of the table bound to the column that corresponds to the FK column of the table bound to the grid.

How to set a data grid column to be a drop down when binding to a table

I am able to create a data table and bind it to a datagridview. If possible I want something more sophisticated. I want to limit the first column to values 1 to 12 and the 2nd column to either AM or PM.
Thanks in advance.
mDataTable = GetTable()
DataGridView1.DataSource = mDataTable
Function GetTable() As DataTable
' Create new DataTable instance.
Dim table As New DataTable
' Create four typed columns in the DataTable.
table.Columns.Add("Hour", GetType(Integer))
table.Columns.Add("AM/PM", GetType(String))
table.Columns.Add("Delete", GetType(Boolean))
For i = 1 To mSchedules.Count
table.Rows.Add(SetAMPMHour(mSchedules(i - 1).ScheduleINetHour), SetAMPM(mSchedules(i - 1).ScheduleINetHour), False)
Next
Return table
End Function
If the combobox column is going to have fixed values, you can manually define those values either at design time or at runtime (althought both ways require you to set AutoGenerateColumns property to False before you bind your data to your grid).
If you want to do it by code, try this:
DataGridView1.AutoGenerateColumns = False
Dim hoursCol, timeOfDayCol As New DataGridViewComboBoxColumn
Dim deleteCol As New DataGridViewCheckBoxColumn
For i As Integer = 1 To 12
hoursCol.Items.Add(i)
Next
timeOfDayCol.Items.Add("AM")
timeOfDayCol.Items.Add("PM")
hoursCol.DataPropertyName = "Hour"
timeOfDayCol.DataPropertyName = "AM/PM"
deleteCol.DataPropertyName = "Delete"
DataGridView1.Columns.Add(hoursCol)
DataGridView1.Columns.Add(timeOfDayCol)
DataGridView1.Columns.Add(deleteCol)
mDataTable = GetTable()
DataGridView1.DataSource = mDataTable
If you want to do it in design time:
Create your Combobox Column...
...set DataPropertyName to the name of the column of the DataTable that will be binded to that GridView column, and in Collection define the values that will populate the ComboBox
Remember that, even if you define the values in design time, you have to programatically set AutoGenerateColumns to False before you set the data source. Also, if you create a ComboBox Column in design time, you'll have to fill it with String values.