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
Related
I'm working on a vb.net Windows forms application. I've got a datagridview that I'm trying to populate by a datatable and cannot get it to show the data. I've tried assigning the datatable directly to datagridview and using a binding source. I've checked the column names and tried to refresh the grid after setting the datasource. If I go back into the code, I can see that the datasource has data, but data will not appear in the grid. What am I doing wrong?
Here's the database table creation:
Dim dsReturns As New DataSet("Returns Dataset")
Dim dtReturns = dsReturns.Tables.Add("Returns Datatable")
dtReturns.Columns.Add("Quantity")
dtReturns.Columns.Add("ItemName")
Dim drReturns As DataRow = dtReturns.NewRow
drReturns("Quantity") = qtyh(key).ToString
drReturns("ItemName") = ItemFunctions.GetItemName(item_id)
dsReturns.Tables("Returns Datatable").Rows.Add(drReturns)
This produces a datatable that looks like this:
Here's the code assigning the datatable to the datagridview. I've tried it both with and without using the binding source.
dgvReturns = new DataGridView()
Dim bsReturns As New BindingSource()
bsReturns.DataSource = dsReturns.Tables("Returns Datatable")
dgvReturns.DataSource = bsReturns
Here's the code from the Designer that creates the datagridview. I didn't write the code, it's all auto generated. I created a new datagridview in Design mode and then added two columns.
Friend WithEvents dgvReturns As DataGridView
Friend WithEvents Quantity As DataGridViewTextBoxColumn
Friend WithEvents ItemName As DataGridViewTextBoxColumn
Me.dgvReturns = New System.Windows.Forms.DataGridView()
Me.Quantity = New System.Windows.Forms.DataGridViewTextBoxColumn()
Me.ItemName = New System.Windows.Forms.DataGridViewTextBoxColumn()
CType(Me.dgvReturns,System.ComponentModel.ISupportInitialize).BeginInit
Me.dgvReturns.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dgvReturns.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() {Me.Quantity, Me.ItemName})
Me.Quantity.DataPropertyName = "Quantity"
Me.Quantity.HeaderText = "Quantity Returned"
Me.Quantity.Name = "Quantity"
Me.ItemName.HeaderText = "Item Name"
Me.ItemName.Name = "ItemName"
Me.dgvReturns.Location = New System.Drawing.Point(13, 13)
Me.dgvReturns.Name = "dgvReturns"
Me.dgvReturns.Size = New System.Drawing.Size(465, 150)
Me.dgvReturns.TabIndex = 1
Me.Controls.Add(Me.dgvReturns)
CType(Me.dgvReturns,System.ComponentModel.ISupportInitialize).EndInit
Here's a screenshot of the table in the Designer:
All I end up with is a blank grid:
Update #1 - Showing all code for the form
Here's the full code for the form showing the constructor.
Public Class frmReturnsDialog
Public Sub New(dsReturns As DataSet)
InitializeComponent()
dgvReturns = new DataGridView()
Dim bsReturns As New BindingSource()
bsReturns.DataSource = dsReturns.Tables("Returns Datatable")
dgvReturns.DataSource = bsReturns
End Sub
End Class
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
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.
I'm having trouble adding dynamically a combo box column to my datagridview (before you ask, yes it must be dynamic and not done in editor).
The main feature is that the combobox cell is different for each row, so it must be done using combo box cell. checkedRows is a datatable.
Name of the datagridview is editCameraTable. It already has a few columns at this point:
'create new column
Dim resComboColumn As New DataGridViewComboBoxColumn _
With {.HeaderText = "Resolution", .ReadOnly = False, .DisplayIndex = 7, .Name = "Resolution", _
.DisplayMember = "Name", .ValueMember = "ID", .DataPropertyName = "ID"}
'add combo box column
EditCameras.editCameraTable.Columns.Insert(17, resComboColumn)
addResCmbBox(checkedRows, resComboColumn)
Pretty straight forward. You'll notice I have the value member, dataproperty name, etc. Here's the addResCmbBox definition:
Public Function addResCmbBox(ByRef DT As DataTable, column As DataGridViewComboBoxColumn)
Dim resolutions As String()
'for each camera
For i As Integer = 0 To DT.Rows.Count - 1
Dim camera As camera = convertDTtoCam(DT, i)
'get the resarray
Select Case DT.Rows(i).Item("Maker").ToString.ToLower
Case "acti"
resolutions = ACTi.GetResArray(camera)
Case Else
resolutions = ACTi.GetResArray(camera)
End Select
'add items to combobox list
Dim comboCell As New DataGridViewComboBoxCell
comboCell.DataSource = resolutions
For j As Integer = 0 To resolutions.Length - 1
'set to current resolution value
If resolutions(j).TrimStart("N") = camera.res Then
comboCell.Value = resolutions(j)
End If
Next
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
EditCameras.editCameraTable("Resolution", i) = comboCell
Next
Return Nothing
End Function
camera is a structure. I have no problems until I get to the displayMember and value member problem, i.e. the last line starting with "editcameras.editcameratable...".
When doing so, the exception of "The Field Name does not exist" pops up. If I don't assign the displayMember and valueMember, I have no problems. But, I can't get the value selected in the comboBox (it comes back as Null). At runtime, the combobox column has the valuemember and display name as "ID" and "Name".
How can I bind this comboboxcell to the row so that I can later get it's selected value?
UPDATE:
I did as was commented, and created a struct/class that was meant to be the resolution property:
Public Class ResolutionStruct
Property Name As String
Property ID As String
End Class
And within the loop I create a list of this class, and assign the values to it:
Dim resolutionList As New List(Of ACTi.ResolutionStruct)
For j As Integer = 0 To resolutions.Length - 1
Dim resClass As New ACTi.ResolutionStruct
resClass.Name = resolutions(j)
resClass.ID = resolutions(j)
resolutionList.Add(resClass)
Next
'set combocell values
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
comboCell.DataSource = resolutionList
EditCameras.editCameraTable("Resolution", i) = comboCell
However, the comboboxCell doesn't show any value when it drops down. So, now I've bound the values but it shows nothing. Is there anything else I should be doing so that I get both the holy combo of seeing the values I'm picking AND having them be bound to the data grid view? :D
UPDATE 2
So, mea culpa. I was adding the combobox cell to the wrong column!
So now, it is showing the values. I click a value, and try to grab the selected value as as string:
Dim cmbbox2 As DataGridViewComboBoxCell = editCameraTable("Resolution", i)
resolution(i) = cmbbox2.Selected.ToString()
But it still says it's a null value! Mid build I checked the combobox props. IN fact "selected" is a boolean as false. It has no value, says it has no items as well. Any ideas on why it says it is null?
UPDATE3:
I recently resorted a different column in the table, and the values from the combo box are cleared! I guess it's really never being attached/bound in the first place.
UPDATE4:
Fixed it!!
Apparently this line:
editCameraTable.Sort(editCameraTable.Columns("ID"), System.ComponentModel.ListSortDirection.Ascending)
Caused the table to freak out! I can now get the value (woohoo!)
Right, I'll try to explain this shortly:
DisplayMember and ValueMember are supposed to be set using properties. For example you create a class containing Name and ID
Public Class Test
Property Name as String
Property ID as String
End Class
Create a few of these objects and put them in a list. Set the list as the datasource to the combobox. Now you can access the DisplayMember and ValueMember as you have written it in your code. Value would be the ID and SelectedItem would be the entire class.
What you are doing now is that you are adding a list of strings to the combobox. A String does not contain the Property Name nor ID, so naturally you can't fetch them. See it like this:
To be able to use Value and/or DisplayMember you need to be able to fetch the Property by yourself. In this case:
resolutions(j).Name
or
resolutions(j).ID
This does not work.
But for example you would be able to do this:
resolutions(j).Length
So You would be able to do this, which would display the Length in the combobox:
Combobox.DisplayMember = "Length"
To currently get the value you would have to do:
Combobox.SelectedItem.ToString()
But since you have it in a combobox column My guess is that this won't cut it since you can't fetch the value from the DataGridView.
EDIT: You are still doing this right?
<DataGridView>.Item("Resolution", i) = comboCell
Otherwise you will have empty comboboxes.
EDIT2: No need to fetch value from Combobox, get it from Grid cell instead:
<DataGridView>.Item("Resolution", i).Value
When creating the columns don't forget to set a defaultvalue to the combobox, otherwise it might be Nothing:
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
comboCell.Value = resolutions(0)
I have these fields on Customer DataTable: ID,title,Name,Addrs,email,Fax and this code to bind the DataGridView:
Dim sql As String = "SELECT * FROM Customers"
Dim daHeader As New SqlDataAdapter(sql, Conn)
daHeader.Fill(dsNota, "Customers")
dgvHeader.DataSource = dsNota.Tables("Customers")
How do I view title,name,addrs data in DataGridView without changing the SQL string to:
"SELECT title,Name,Addrs FROM Customer"
So if you don't want to modify your query string (as #Neolisk noticed this is generally a bad practice to use Select * but this is another debat), and so you get more columns than what you want to display:
Solution1 (Ideal if there are a lot of columns in datatable and you want to display just some of them)
You need to set AutoGenerateColumns property to false.
Default is True, so DataGridView will create a column for all columns in the datatable.
Then, you add a DatagridiviewColumn for each column you want to display.
To avoid to have to add a celltemplate for the DatagriviewColumn (see this) you would prefer to add a strongly typed column (DataGridViewTextBoxColumn for example in order to display String values). In order to bind the column with the source, you set the DataPropertyName property, that needs to match with the ColumnName of the column inDataTable.
So code would be:
dgvheader.AutoGenerateColumns = False
dgvHeader.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Title", .DataPropertyName = "title"})
dgvHeader.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Name", .DataPropertyName = "Name"})
dgvHeader.Columns.Add(New DataGridViewTextBoxColumn() With {.HeaderText = "Adresse", .DataPropertyName = "Addrs"})
dgvHeader.DataSource = dsNota.Tables("Customers")
Solution2 (Ideal if there are a lot of columns in datatable, and you want to Hide just some of them, and so you want to keep the benefit of AutoGenerateColumns)
Set AutoGenerateColumns property to true (or do nothing since
default is True)
Hook the DataGridView.DataBindingComplete Event in order to hide some columns autogenerated:
Private Sub dataGridView1_DataBindingComplete(ByVal sender As Object, ByVal e As DataGridViewBindingCompleteEventArgs) Handles dgvHeader.DataBindingComplete
With dgvHeader
.Columns("Fax").Visible = False
End With
End Sub
If the gridview autogeneratecolumns attribute is set to true change it to false and then do as Raimond suggested. Example:
<asp:GridView ID="gvSearchResults" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="title" HeaderText="Title" />
</Columns>
</asp:GridView>
I know this post goes way back, but I had the same problem in C# and this is how I solved it which works really well.
1 - Build a DataSet with your SQL query
private void LoadDS()
{
// this method gets data from my database
// DS is a DataSet in the properties of my form
DS = LoadData();
}
2 - Get the DataView filtered the way you want
For this I use the RowFilter property of the DataView.
I created a GetFiltersToString() method that formats every filter control I have in a string that matches the RowFilter syntax (more information about the syntax here, and msdn definition of RowFilter here)
public void RefreshDGV()
{
// Get the corresponding dataview
DV = new DataView(DS.Tables[0], rowFilter, "SORTINGCOLUMN Desc", DataViewRowState.CurrentRows);
// Display it in a datagridview
DGV.DataSource = DV;
}
I find that this solution allows user to change the filtering much more easily.
You have to explicitly add the columns to your grid.