I am trying to use a bindingsource as the datasource for a combobox. The display and value members of the combobox will be the field names in my bindingsource's datasource.
Currently I use a process of populating a datatable and assigning it to the datasource of the combobox. Because I already have the bindingsource populated with data it would make sense to just set the binding up rather than continue to use this code below:
Dim dtfields As New DataTable
dtfields = mySqlref.sqlobj.SelectData(String.Format("select column_name from information_schema.columns where table_name = '{0}' order by ordinal_position", mydata.Table), SqlLibrary.SqlLibrary.SelectType.datatable)
cboField.DataSource = dtfields
cboField.ValueMember = "column_name"
cboField.DisplayMember = "column_name"
Can someone point me in the right direction? Thanks for reading.
The sort of answer I was looking for here was:
if your fieldnames are already in your bindingsource then you can use linq to create an array of those fieldnames instead of using a seperate query to call the database for field names
Here's the code I ended up using:
Dim arraynames = (From x As DataColumn In mydata.Table.Columns Select x.ColumnName).ToArray()
cboField.DataSource = arraynames
in this example "mydata" is a dataview. It is the object upon which my binding source is created. I extract column names into an array using the datatable object inside that dataview.
Related
I am trying to get the results of a SQL Server stored procedure in VB.NET, parse them into a DataSet, then use the DataSet to fill a Windows Forms ListBox.
I have found many, many tutorials. The only ones that have gotten me close to a solution are ones that depend on the ListBox's DataBind() method. However:
myListBox.DataBind()
' ERROR: 'DataBind' is not a member of 'ListBox'
This is contrary to every tutorial I've found on the topic (approx a dozen so far).
Here is more code context:
Dim connection As New SqlConnection(myConnectionSecret)
Dim command As New SqlCommand("myStoredProcedureName")
command.Parameters().Add("#myParam", SqlDbType.Int).Value = myParamValue
command.Connection = connection
command.CommandType = CommandType.StoredProcedure
Dim adapter As New SqlDataAdapter(command)
adapter.SelectCommand.CommandTimeout = 300
'Fill the dataset
Dim dataSet As New DataSet
adapter.Fill(dataSet)
connection.Close()
myListBox.DataSource = dataSet
myListBox.DataBind() ' This method not found
A dataset is a collection of datatables. Typically you would bind your listbox to only one datatable. You can set the DataSource property to a dataset but you'll then also need to set the DataMember property to the name of the table in the dataset so the binding knows which table to rummage in
myListBox.DataSource = dataSet
myListBox.DataMember = "Person" 'whatever your table name is
Or set it to refer to the table directly:
myListBox.DataSource = dataSet.Tables("Person")
As other answers have commented you should then set the DisplayMember (chooses what text appears in the control) and ValueMember (chooses what value is emitted by the listbox.SelectedValue property) properties to strings representing the column names. If I want to show my persons name in the list but have their email be the selected value:
myListBox.DisplayMember = "FullName" 'fullname is a column in the datatable
myListBox.ValueMember = "Email"
If you don't set a ValueMember the whole DataRow of the selected person is returned from SelectedValue. (Clarification: Actually, it's a DataRowView because binding to a datatable actually happens to a DataView exported by the .DefaultView property - more on this later)
So, we've bound our list.DataSource to a datatable, and set the diplay/valuemember properties. How to we get the details of what Person is selected currently? Put a button on the form that has this handler code:
MessageBox.Show(DirectCast(listbox.SelectedValue, string)) 'shows e.g. john.smith#hotmail.com
Run the program, click "John Smith" in the list then click the button. The messagebox will show the selected person's email address
I mentioned earlier that databinding happens to a DataView object exported by the table's .DefaultView property. A DataView is a collection of DataRowView objects, and a DataRowView is a thin wrapper around a DataRow. DataRows exist in various versions such as original or updated values. A DataRowView is a way of selecting one of these versions for presentation, by default the Current version. You can address it like you would a DataRow:
'accessing the email address of a datatable row
Dim myDataRow = myPersonTable.Rows(0)
Dim email as String = DirectCast(myDataRow("EmailAddress"), string)
'accessing the email address of the dataview exported by the table defaultview
Dim myDataRowView = myPersonTable.DefaultView(0)
Dim email as String = DirectCast(myDataRowView("EmailAddress"), string)
As you can see, there isn't a lot of difference - as noted, the view just shows one of the various versions a datarow can exist in. If you want to get access to the underlying row you can do it via the Row property:
Dim myDataRowView = myPersonTable.DefaultView(0) 'or however you ended up holding a DataRowView object
Dim dr as DataRow = myDataRowView.Row
If you're using strongly typed datatables (discussed below), and want the strongly typed row:
Dim dr as PersonDataRow = DirectCast(myDataRowView.Row, PersonDataRow)
A useful aspect of the list binding to the .DefaultView DataView is that it can have its own filtering and sorting setup:
Dim dv = dataSet.Tables("Person").DefaultView
dv.Sort = "[FullName] ASC"
dv.RowFilter = "[FullName] LIKE 'J*'"
For more info on these see the documentation for dataview
Now, sea change: you don't have to do any of this by hand. All this can be linked up and done by visual studio and there are compelling reasons for doing so. For the same reason you don't write your form codes by hand, manually laying out all your controls etc, you can visually design and maintain your data access layer
Add a new dataSet file to your project, open it, right click the design surface and choose to add a tableadapter, go through the wizard setting your connection string and choosing that it's a stored procedure that gives you the data. At the end of it you'll see a datatable representation and all the columns returned by your stored procedure
If you now show the data sources window when you're in your forms designer you'll see a node representing your table from your dataSet, and you can either drag that node onto the form to create a DataGridview that is hooked up leafy, or you can expand the node in the data sources window to see individual columns, you can change the type of control to create for that property and you can drop them on the form. (I can't remember if listbox is one of them, but I know that ComboBox is). Dropping them on the form simply creates a control, already named and wired up with the right DataSource, Member and DisplayMember properties set, and you can change these and other properties like the value member in the properties grid.
Most critically of a difference, the controls the designer creates are all bound through a device called a bindingsource - this tracks the row in the datatable you're currently looking at, and keeps data bound controls in sync. If one control such as a grid or list is capable of showing multiple rows, clicking on different rows changes the Current property of the binding source, causing other controls (that only render one data row's data) such as textboxes to update to the new Current row values
Thus list controls may operate in one or two modes: they either serve as a device that can navigate a datatable's rows allowing you to pick one of a few rows so that you can edit the values using other textboxes etc, or lists serve as a way of showing a set of values for the user to cope from and cause another datarow's property to update to that chosen value. The differentiation in these two modes comes from whether the selectedvalue property is bound to something else or not. As a simple example in the context I've been discussing already, we could have a dataSet with two tables; person and gender; the person table has a gender column single char M or F, and the gender table has two columns one is a single char M or F and the other a text column of Male or Female (I won't get into the cases for other genders right now but the system is easy to extend by adding more rows). You could then have a form where the person table plus its binding source is causing a list of people to show in the first list box and click in different items in the list causes all the other textboxes (FullName) on the form to change so you can edit those details. You can also have a second listbox bound to the gender table via its own bindingsource (or direct, doesn't matter) that has its DisplayMember set to the "Male/female" column, its value member set to the "m/f" column and it's SelectedValue property bound to the main Person bindingsource's Person.gender column. When you choose a person from the first list, the current char value of their Gender will be used to set the current item selected in the gender list but if you change the value in the gender combo/list then it will write the new selectedvalue back into the person.gender column
That's the 101 of Windows forms binding; I recommend adding a dataset to your project because it then gives you datatables that are specifically typed with named properties. Your code looks like this:
ForEach PersonDataRow r in myDataset.Person
r.Age += 1
Instead of this:
ForEach DataRow r in myDataset.Tables("Person").Rows
r("Age") = DirectCast(r("Age"), Integer) +1
I was mid-answer when jmcilhinney commented: DataBind() is specific to ASP.Net Web Forms server controls.
Therefore, the DataBind() call isn't necessary, simply setting the DataSource property on a Windows Forms ListBox is enough.
As for parsing your results from your SQL Server, don't forget to set the ListBox's DisplayMember and ValueMember properties to correctly display your data to the user.
Here's the documentation on the DataSource property. It has a very decent example.
For filling a list box, you usually don't need DataSets or DataAdapters. Just a DataTable table will do.
The Using...End Using blocks ensure that your database objects are closed and disposed.
You can pass the connection directly to the constructor of the command.
Set the list box data source to the DataTable. Then you can use the names of the fields in the Select statement to set the .DisplayMember and .ValueMember. The display and value can be the same.
Private Sub FillListBox(myParamValue As Integer)
Dim dt As New DataTable
Using connection As New SqlConnection("myConnectionSecret")
'Pretend your stored procedure has a statement like "Select DeptarmentID, DepartmentName From Departments Where SupervisorID = #myParam;"
Using command As New SqlCommand("myStoredProcedureName", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters().Add("#myParam", SqlDbType.Int).Value = myParamValue
connection.Open()
dt.Load(command.ExecuteReader)
End Using
End Using 'Closes and disposes the connection
ListBox1.DataSource = dt
ListBox1.DisplayMember = "DepartmentName"
ListBox1.ValueMember = "DepartmentID"
End Sub
I have a DataGridView and have set the DataSource (dataset DsAll) and columns in the DataGridView properties.
Now i want to update the source/dataset. That is, I want to be able to limit or expand the data records shown in the grid, by putting a value in a TextBox and searching for only those records where that value is in a particular field (for instance employee id or department number, etc...)
I got this to work when I manually set the DataSource/bindings but I'am having issues when using the properties to set the DataSource.
Any help appreciated.
To elaborate on my comment, I suggest adding a BindingSource in the designer and then binding something like this:
myDataAdapter.Fill(myDataTable)
myBindingSource.DataSource = myDataTable
myDataGridView.DataSource = myBindingSource
and then filtering something like this:
myBindingSource.Filter = String.Format("MyColumn LIKE '%{0}%'", myTextBox.Text)
I am using a combo box that is bound to a data table by the primary key. By selecting a value from the dropdown I am able to populate several textboxes in a form. I can easily add a new record to the data table. Occasionally I need to edit an existing record but I cannot discover the "index" of the row corresponding to the selection in the combo box even though I can populate the textboxes. Are there any methods to use with "datarow" to determine what record the "Pointer" is pointing to? If so perhaps then I could reference that datarow and write new data to the datatable, then update using a dataadapter... Any help would be greatly appreciated.
You're trying to solve a problem that doesn't exist. You don't need to know the index of the row or the row for that matter. What you should be doing is:
Binding your DataTable to a BindingSource, which you add in the designer.
Binding your BindingSource to your ComboBox AND your TextBoxes.
Select records in the ComboBox and edit in the TextBoxes as you like.
When it's time to save, call EndEdit on the BindingSource to ensure any pending edit is committed to the DataTable.
Use the same data adapter to save the changes from your DataTable as you used to populate it in the first place.
The binding would look something like this:
myDataAdapter.Fill(myDataTable)
myBindingSource.DataSource = myDataTable
With myComboBox
.DisplayMember = "DisplayColumn"
.ValueMember = "PKColumn"
.DataSource = myBindingSource
End With
myTextBox.DataBindings.Add("Text", myBindingSource, "EditColumn")
The saving would look something like this:
myBindingSource.EndEdit()
myDataAdapter.Update(myDataTable)
At no point do you have to care what rows were edited and where they are in the DataTable because Update will just save all changes.
I have a main DataSet which contains several DataTables. These different DataTables are bound to their DataGridView's DataSource respectively.
My problem is whenever I modify something in the display area such as the description textbox below and then click on save....
<<< To >>>
The DataGridView doesn't have the replicated changes, as seen here:
I need a way to update the DataTable ... My save button successfully saves the information. I am quite new to DataSets and DataTables so this is my first time trying to update a DataTable. I doubt I need to reload the information in the DataTable, there must be something more efficient?
Updating a DataTable With Unknown Indexes
For more information: How to: Edit Rows in a DataTable
In order to edit an existing row in a DataTable, you need to locate the DataRow you want to edit, and then assign the updated values to the desired columns.
Update existing records in typed datasets (Row index not known)
Assign a specific DataRow to a variable using the generated FindBy method, and then use that variable to access the columns you want to edit and assign new values to them.
Dim Description As String = "Hello World Modified"
'Update DataTable
Dim Row As DataSet1.DataTableRow
Row = DataSet1.DataTableRow.FindByPrimaryKey(PK)
Row.Description = Description
Update existing records in untyped datasets (Row index not known)
Use the Select method of the DataTable to locate a specific row and assign new values to the desired columns
Dim Description As String = "Hello World Modified"
'Update DataTable
Dim Row() As Data.DataRow
Row = DataSet1.Tables("Table1").Select("PrimaryKey = '10'")
Row(0)("Description") = Description
Once this is done, I don't need to refresh anything else - my DataGridView has the latest information.
How do I add new record to DataGridView control in VB.Net?
I don't use dataset or database binding. I have a small form with 3 fields and when the user clicks OK they should be added to the DataGridView control as a new row.
If you want to add the row to the end of the grid use the Add() method of the Rows collection...
DataGridView1.Rows.Add(New String(){Value1, Value2, Value3})
If you want to insert the row at a partiular position use the Insert() method of the Rows collection (as GWLlosa also said)...
DataGridView1.Rows.Insert(rowPosition, New String(){value1, value2, value3})
I know you mentioned you weren't doing databinding, but if you defined a strongly-typed dataset with a single datatable in your project, you could use that and get some nice strongly typed methods to do this stuff rather than rely on the grid methods...
DataSet1.DataTable.AddRow(1, "John Doe", true)
I think you should build a dataset/datatable in code and bind the grid to that.
The function you're looking for is 'Insert'. It takes as its parameters the index you want to insert at, and an array of values to use for the new row values. Typical usage might include:
myDataGridView.Rows.Insert(4,new object[]{value1,value2,value3});
or something to that effect.
When I try to cast data source from datagridview that used bindingsource it error accor cannot casting:
----------Solution------------
'I changed casting from bindingsource that bind with datagridview
'Code here
Dim dtdata As New DataTable()
dtdata = CType(bndsData.DataSource, DataTable)
If you want to use something that is more descriptive than a dumb array without resorting to using a DataSet then the following might prove useful. It still isn't strongly-typed, but at least it is checked by the compiler and will handle being refactored quite well.
Dim previousAllowUserToAddRows = dgvHistoricalInfo.AllowUserToAddRows
dgvHistoricalInfo.AllowUserToAddRows = True
Dim newTimeRecord As DataGridViewRow = dgvHistoricalInfo.Rows(dgvHistoricalInfo.NewRowIndex).Clone
With record
newTimeRecord.Cells(dgvcDate.Index).Value = .Date
newTimeRecord.Cells(dgvcHours.Index).Value = .Hours
newTimeRecord.Cells(dgvcRemarks.Index).Value = .Remarks
End With
dgvHistoricalInfo.Rows.Add(newTimeRecord)
dgvHistoricalInfo.AllowUserToAddRows = previousAllowUserToAddRows
It is worth noting that the user must have AllowUserToAddRows permission or this won't work. That is why I store the existing value, set it to true, do my work, and then reset it to how it was.
If your DataGridView is bound to a DataSet, you can not just add a new row in your DataGridView display. It will now work properly.
Instead you should add the new row in the DataSet with this code:
BindingSource[Name].AddNew()
This code will also automatically add a new row in your DataGridView display.