Trying to populate a set of data from datatable to Combobox on the basis of text entered in Combobox. But getting a System.Data.DataRow error in the Select method of datatable.
Following is the code which binds the datatable on Form Load and rebinds data on the call of Search method.
Note that the search has to be on Tab press not AutoComplete
Imports System.Data.SqlClient
Public Class Form1
Dim connection As SqlConnection = New SqlConnection()
Dim table As New DataTable
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
LoadComboBox()
End Sub
Private Sub LoadComboBox()
Dim adp As SqlDataAdapter = New SqlDataAdapter("select stage from sample", connection)
adp.Fill(table)
ComboBox1.DataSource = New BindingSource(table, Nothing)
ComboBox1.DisplayMember = "stage"
End Sub
Private Sub Search()
Dim filteredTable As New DataTable
Dim filterRow As DataRow()
Dim str As String = ComboBox1.Text.Trim
filterRow = table.Select("stage like '%" & ComboBox1.Text.ToString & "%'")
'**Error in above table(datatable)**
For Each rw As DataRow In filterRow
filteredTable.ImportRow(rw)
Next
ComboBox1.DataSource = New BindingSource(filteredTable, Nothing)
ComboBox1.DisplayMember = "stage"
End Sub
Private Sub ComboBox1_PreviewKeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles ComboBox1.PreviewKeyDown
If e.KeyCode = Keys.Tab Then
Search()
End If
End Sub
End Class
in your binding to combo box you have a data table already why are you using a binding source
why not just say ComboBox1.DataSource = filteredTable
Also i would advice you to use Key Value Pair rather than using Like easier binding
Like is regex search at SQL Server this is killing your Database and you are creating overhead over nothing
Key | Value >>
ID | Name
so taking this from another stack over flow for fast reference
gridview bind dropdownlist to List<keyvaluePair<int, string>>
you can bind a dictionary , datatable , dataview does not matter.
if you want to get text just say your dropdown.text for the ID value dropdown.value if you want to do something later to in the database.
You can make your drop down read only and when user types basically what he is typing is filtering according to what you have binded that way your text search can be made
var dictionary = new Dictionary<int, string>();
dictionary.Add(1, "Home");
dictionary.Add(2, "Work");
dictionary.Add(3, "Mobile");
dictionary.Add(4, "Fax");
dropDown.DataTextField = "Value";
dropDown.DataValueField = "Key";
dropDown.DataSource = dictionary; //Dictionary<int, string>
dropDown.DataBind();
Tried it with a bit of Linq.
Private Sub Search()
Dim filteredTable As New DataTable
Dim str As String = ComboBox1.Text.Trim
Dim filterRows = (From row As DataRow In Table.AsEnumerable
Where row.Field(Of String)("Name").Contains(ComboBox1.Text)
Select row.Field(Of String)("Name")).ToList
ComboBox1.DataSource = filterRows
End Sub
Just substitute your column name for "Name"
Related
I have a ComboBox that I use on multiple WinForms. Instead of dropping a ComboBox on each WinForm and then filling the ComboBox with data from a DataTable on each individual WinForm, couldn't I create a User Control (ComboBox) that has the data populated already and just use that UC on my Winforms?
Below is how I fill the data for each individual combobox now. (I have a public class for the sql stuff)
The Variable SQL comes from a Class called SQLControl. the Class has all the sql connection stuff.
Public Sub Fillcombobox()
sql.AddParam("#ExaminerType", 3)
sql.ExecQuery("MyStoredProcedure")
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = sql.DBDT
End Sub
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Call Fillcombobox()
End Sub
You can put a small Class Examiner
Public Class Examiner
Public Property Examiner_ID As Integer
Public Property Last_Name As String
Public Sub New(ID As Integer, lname As String)
Examiner_ID = ID
Last_Name = lname
End Sub
End Class
Then, when the first form loads, get the data in a list declared in a module so it can be accessed from any form in the application. Of course, you may have other things in the Module.
Module Module1
Public ExaminerData As New List(Of Examiner)
End Module
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillExaminerList()
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = ExaminerData
End Sub
Any other form that needs the data to fill a combo box can use ExaminerData. You only call FillExaminerList once at the beginning of the application. There is only a single hit on the database.
Private OPConStr As String = "Your connection string."
Private Sub FillExaminerList()
Dim dt As New DataTable
Using cn As New SqlConnection(OPConStr),
cmd As New SqlCommand("MyStoredProcedure", cn)
cmd.Parameters.Add("#ExaminerType", SqlDbType.Int).Value = 3
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
For Each row As DataRow In dt.Rows
Dim ex As New Examiner(CInt(row("Examiner_ID")), row("Last_Name").ToString)
ExaminerData.Add(ex)
Next
End Sub
Currently, my code is like this:
Private Sub btnAlaCarte_Click(sender As Object, e As EventArgs) Handles btnAlaCarte.Click
Dim item(2) As String
Dim description(2) As String
Dim price(2) As String
conn.Open()
sql = "SELECT name, description, price FROM Food WHERE id LIKE 'F%';"
cmd = New SqlCommand(sql, conn)
dr = cmd.ExecuteReader
If dr.HasRows Then
For count = 1 To 3 Step 1
dr.Read()
lklblItem1.Text = dr.GetValue(dr.GetOrdinal("name"))
lblDescription.Text = dr.GetValue(dr.GetOrdinal("description"))
lblPrice.Text = dr.GetValue(dr.GetOrdinal("price"))
Next
End If
dr.Close()
conn.Close()
End Sub
I have 3 set of label in my design. Each of the set consist of 1 linklabelItem, 1 labelDescription and 1 labelPrice. All of them will display different output from database. I manage to extract all of the data that i want. But now the problem is the FOR....NEXT loop only display the output at the first set.
Is there any possibility for me to use For....NEXT Loop to achieve my concept?
Hopes my question is clear enough.
In your case DataGridView control will do the job without explicit For .. Next loop.
' Create a class to represent a food item
Public Class AlaCarteItem
Public Property Name As String
Public Property Description As String
Public Property Price As Decimal
End Class
' Extract loading data into dedicated function with properly disposed sql connection
Private Function GetAlaCarte() As List(Of AlaCarteItem)
Dim query As String = "SELECT name, description, price FROM Food WHERE id LIKE 'F%';"
Using connection As New SqlConnection(connectionString)
Using command As New SqlCommand(query, connection)
connection.Open()
Dim items = new List(Of AlaCarteItem)()
Using reader AS SqlDataReader = command.ExecuteReader()
While reader.Read()
Dim item As New AlaCarteItem With
{
.Name = reader.GetString(0),
.Description = reader.GetString(1),
.Price = reader.GetDecimal(2),
}
items.Add(item)
End While
End Using
Return items
End Using
End Using
End Function
Add DataGridView control to the form
Private btnAlaCarte_Click(sender As Object, e As EventArgs) Handles btnAlaCarte.Click
Me.DataGridView.DataSource = GetAlaCarte()
' Make all columns read only
For Each column As DataGridViewColumn in Me.DataGridView.Columns
column.ReadOnly = True
End With
End Sub
You can create DataGridView with predefined columns and make them readonly in design time, then you don't need to do this in the code.
Start in the form designer.
In the toolbox drag a BindingNavigator to the form. Reference https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/bindingnavigator-control-overview-windows-forms
Remove the buttons that you will not use by right clicking on the buttons and selecting delete. You will end up with a strip that looks like this.
Then, in the code
Fill a DataTable
Create a BindingSource
Set the DataSource of the BindingSource to the DataTable
Finally set the BindingSource property of the BindingNavigator control to the the BindingSource.
Next your will add DataBindings to each of your controls.
The .Add method takes 3 parameters in this overload.
1.The property to bind to, in this case we are binding the Text property
2. The BindingSource (bindSrc)
3. The member of the DataTable to bind to.
Note: The name of my Form is BindingNavigator
Private Sub BindingNavigator_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt = LoadData()
Dim bindSrc As New BindingSource
bindSrc.DataSource = dt
BindingNavigator1.BindingSource = bindSrc
lblItem.DataBindings.Add(New Binding("Text", bindSrc, "name"))
lblDescription.DataBindings.Add(New Binding("Text", bindSrc, "description"))
lblPrice.DataBindings.Add(New Binding("Text", bindSrc, "price"))
End Sub
Private Function LoadData() As DataTable
Dim dt As New DataTable
Using conn As New SqlConnection("Your connection string"),
cmd As New SqlCommand("SELECT name, description, price FROM Food WHERE id LIKE 'F%';", conn)
conn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
You will then be able to move through the records with the BindingNavigator toolbar.
You are replacing your labels' texts in each iteration. I recommend to use a datagrid instead. However, if you want to use labels anyway, update your loop as this:
lklblItem1.Text += vbNewLine + dr.GetValue(dr.GetOrdinal("name"))
lblDescription.Text += vbNewLine + dr.GetValue(dr.GetOrdinal("description"))
lblPrice.Text += vbNewLine + dr.GetValue(dr.GetOrdinal("price"))
I have two forms. One is the Main.vb which the datagridview is located and the other one is the EditPage.vb which consist a set of textboxes. If I click a row in datagridview and click the edit button in the datagridview in Main.vb it will go in EditPage.vb. Then, the data of the clicked row will show in the textboxes in EditPage.vb... I'll try to public my variables and set it in the other form(EditPage.vb) but still the data of the selected row didn't shown up. What's something wrong in my code even it has no error?
Here's the code of my datagridview Cell Content Click in Main.vb
Private Sub tblAttendance_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles tblAttendance.CellContentClick
Dim row As DataGridViewRow = tblAttendance.CurrentRow
Try
id = row.Cells(0).Value.ToString()
firstname = row.Cells(1).Value.ToString
lastname = row.Cells(2).Value.ToString
birthdate = row.Cells(3).Value.ToString
position = row.Cells(4).Value.ToString
sex = row.Cells(5).Value.ToString
address = row.Cells(6).Value.ToString
contact_num = row.Cells(7).Value.ToString
email = row.Cells(8).Value.ToString
Catch ex As Exception
MessageBox.Show("Input Data Properly!", "Error Message")
End Try
End Sub
Here's the code of my Edit Button in Main.vb
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim result As Integer = MessageBox.Show("Are you sure you want to Edit the Info?", "Validation", MessageBoxButtons.YesNoCancel)
If DialogResult.Yes Then
Edit_Page.Show()
ElseIf DialogResult.No Then
MessageBox.Show("Edit Cancelled", "Message")
End If
End Sub
And Here's my Public Variables in Main.vb
Public id As Integer
Public firstname As String
Public lastname As String
Public birthdate As String
Public position As String
Public sex As String
Public address As String
Public contact_num As String
Public email As String
My Public Variables in EditPage.vb to get the public variables in Main.vb
Public id_edit As Integer
Public firstname_edit As String
Public lastname_edit As String
Public birthdate_edit As String
Public position_edit As String
Public sex_edit As String
Public address_edit As String
Public contact_num_edit As String
Public email_edit As String
The code in the of the form of Edit.Page.vb or called Edit_Page_Load
id_edit = Main.id
firstname_edit = Main.firstname
lastname_edit = Main.lastname
birthdate_edit = Main.birthdate
position_edit = Main.position
sex_edit = Main.sex
address_edit = Main.address
contact_num_edit = Main.contact_num
email_edit = Main.email
firstname_txtbox.Text = firstname_edit
lastname_txtbox.Text = lastname_edit
DateTimePicker1.Text = birthdate_edit
position_txtbox.Text = position_edit
sex_combo.Text = sex_edit
address_txtbox.Text = address_edit
contact_mask.Text = contact_num_edit
email_txtbox.Text = email_edit
AGAIN, MY PROBLEM IS I CAN'T GET THE DATA OF THE SELECTED ROW OF MY DATAGRIDVIEW IN MAIN.VB AND SHOW IT IN EDITPAGE.VB'S TEXTBOXES.
Declare a class (Form level) level Public variable for a binding source.
Public bind As New BindingSource()
Bind to your DataGridView
Private Sub FillGrid()
Dim dt As New DataTable
Using cn As New SqlConnection(My.Settings.CoffeeConnectionString)
Dim strSQL As String = "Select * From Coffees;"
Using cmd As New SqlCommand(strSQL, cn)
'dr As SqlDataReader
cn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader
dt.Load(dr)
End Using
End Using
End Using
bind.DataSource = dt
DataGridView1.DataSource = bind
End Sub
Then on the second form use the BindingSource from the first form.
Private Sub TestBindingSource_Load(sender As Object, e As EventArgs) Handles MyBase.Load
txtCoffeeName.DataBindings.Add("Text", ExcelToDGV.bind, "Name")
End Sub
Could someone help here?
I need to extract data from a Database into a combolistbox in VB.net. I have got the data, but now find that The first and the 'x' line need to be removed from the combolistbox (they are validation entries for another software) and shouldn't be selected for this application.
I tried to simply remove the offending entries from lists by using :- cbCubeARivet.Items.RemoveAt(index), but had an error letting me know I cannot use "Items" with a DataSource.
I decided to send the data to a listbox, and then try to transfer the entries to the combolistbox. This then lead me to getting multiple entries of System.Data.DataRowView in the combolist box. To demonstrate my problem I include an example code modified from MSDN.
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Collections
Imports System.Data.SqlClient
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Populate the list box using an array as DataSource.
Dim SQLConnectionString As String = "Data Source=HL605\RIVWARE;Database=RIVWARE;Integrated Security=true;"
Dim mySQLConnection As New SqlConnection(SQLConnectionString)
mySQLConnection.Open()
Dim SQLDataTable As New System.Data.DataTable
'Create new DataAdapter
'Use DataAdapter to fill DataTable
Dim mySQLDataAdapter = New SqlDataAdapter("SELECT * FROM [Rivware].[dbo].[RivetTypes]", mySQLConnection)
mySQLDataAdapter.Fill(SQLDataTable)
ListBox1.DataSource = SQLDataTable
ListBox1.DisplayMember = "RivetType"
'original code from MSDN
'Dim USStates As New ArrayList()
'USStates.Add(New USState("Alabama", "AL"))
'USStates.Add(New USState("Washington", "WA"))
'USStates.Add(New USState("West Virginia", "WV"))
'USStates.Add(New USState("Wisconsin", "WI"))
'USStates.Add(New USState("Wyoming", "WY"))
'ListBox1.DataSource = USStates
' Set the long name as the property to be displayed and the short
' name as the value to be returned when a row is selected. Here
' these are properties; if we were binding to a database table or
' query these could be column names.
' Bind the SelectedValueChanged event to our handler for it.
AddHandler ListBox1.SelectedValueChanged, AddressOf ListBox1_SelectedValueChanged
' Ensure the form opens with no rows selected.
ListBox1.ClearSelected()
End Sub 'NewNew
Private Sub ListBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As EventArgs)
If ListBox1.SelectedIndex <> -1 Then
TextBox1.Text = ListBox1.SelectedValue.ToString()
' If we also wanted to get the displayed text we could use
' the SelectedItem item property:
' Dim s = CType(ListBox1.SelectedItem, USState).LongName
End If
End Sub
End Class 'ListBoxSample3
Public Class USState
Private myShortName As String
Private myLongName As String
Public Sub New(ByVal strLongName As String, ByVal strShortName As String)
Me.myShortName = strShortName
Me.myLongName = strLongName
End Sub 'NewNew
Public ReadOnly Property ShortName() As String
Get
Return myShortName
End Get
End Property
Public ReadOnly Property LongName() As String
Get
Return myLongName
End Get
End Property
End Class 'USState
I may not get this correct so here goes, the following uses mocked up data to display a string in both a ListBox and ComboBox where a integer is available by casting the current item as a DataRowView, access Row then access the data via Row.Field(Of Integer)("ID").
In the code I use a copy of the underlying DataTable for the ComboBox as using the same data table for listbox and combobox will cause one to traverse when is generally unwanted. The cast aspect would be done in another event but wanted to stay simple. Again I may not be on track here, let me know and can adjust to better suit your question.
Code using Option Infer On for the Linq anonymous statement which could be strongly typed too.
Dim dt As New DataTable
dt.Columns.Add(New DataColumn With {.ColumnName = "ID", .DataType = GetType(Integer)})
dt.Columns.Add(New DataColumn With {.ColumnName = "Name", .DataType = GetType(String)})
Dim data =
(
From M In System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.MonthNames
Where Not String.IsNullOrEmpty(M)).ToList.Select(
Function(monthName, index) New With
{
.ID = index, .Name = monthName
}
).ToList
For Each item In data
dt.Rows.Add(New Object() {item.ID, item.Name})
Next
ListBox1.DataSource = dt
ListBox1.DisplayMember = "Name"
ComboBox1.DataSource = dt.Copy
ComboBox1.DisplayMember = "Name"
Dim theTable As DataTable = CType(ComboBox1.DataSource, DataTable)
Dim theRow As DataRow = theTable.AsEnumerable _
.Where(
Function(row) row.Field(Of String)("Name") = "September") _
.FirstOrDefault()
If theRow IsNot Nothing Then
theTable.Rows.Remove(theRow)
End If
Thanks again #Karen Payne,
I used your code to lead me in the right direction.
I created a new application and added a textbox, and two Listboxes, then paste the code. To run you will need to point to your own Server, Database, and Table.
This is what I came up with. It is useful as this will give you the actual data in a useable form:-
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Collections
Imports System.Data.SqlClient
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' get the data
Dim SQLConnectionString As String = "Data Source=HL605\RIVWARE;Database=RIVWARE;Integrated Security=true;"
Dim mySQLConnection As New SqlConnection(SQLConnectionString)
' Populate the list box using an array as DataSource.
mySQLConnection.Open()
Dim SQLDataTable As New System.Data.DataTable
'Create new DataAdapter
Dim mySQLDataAdapter = New SqlDataAdapter("SELECT * FROM [Rivware].[dbo].[RivetTypes]", mySQLConnection)
mySQLDataAdapter.Fill(SQLDataTable)
ListBox1.DataSource = SQLDataTable
ListBox1.DisplayMember = "RivetType"
' remove validation data from list
Dim Count As Integer
Dim X As Integer
'get the column of data to search for
For Count = 0 To ListBox1.DataSource.Columns.Count - 1
X = InStr(ListBox1.DataSource.Columns.Item(Count).ToString, "Name")
If X <> 0 Then Exit For
Next
' now search for any invalid rows, in that column. those containing "---"
Dim TheTable As DataTable = CType(ListBox1.DataSource, DataTable)
Dim theRow As DataRow() = TheTable.Select()
Dim RowNumber As Integer
For RowNumber = 0 To UBound(theRow) - 1
If theRow(RowNumber).Item(Count).ToString <> "---" Then
' data is OK so transer it to the other listbox
ListBox2.Items.Add(theRow(RowNumber).Item(Count - 1).ToString)
End If
Next
ListBox2.ClearSelected()
End Sub 'NewNew
Private Sub ListBox2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox2.SelectedIndexChanged
TextBox1.Text = ListBox2.SelectedItem.ToString()
End Sub
End Class 'ListBoxSample3
I have a small requirement and that is:
There are two combo boxes on a form and for populating the employee names and roles. I am populating the combo boxes as follows:
I have created a class called "DbConnect" and in that there are 02 functions as:
Public Function getEmployees() As DataTable
Dim employeeDS As New DataSet
Dim employeeDA As New SqlDataAdapter("prc_emp_list", conn)
employeeDA.Fill(employeeDS, "employees")
Return employeeDS.Tables("employees")
End Function
Public Function getRoles() As DataTable
Dim roleDS As New DataSet
Dim roleDA As New SqlDataAdapter("prc_role_list", conn)
roleDA.Fill(roleDS, "roles")
Return roleDS.Tables("roles")
End Function
Have designed a form with two combo boxes and am populating data into them as:
Public Sub employees()
accessFunction.Open()
cboEmployees.DataSource = accessFunction.getEmployees
cboEmployees.DisplayMember = "emp_name"
cboEmployees.ValueMember = "login_id"
End Sub
Public Sub roles()
accessFunction.Open()
cboRoles.DataSource = accessFunction.getRoles
cboRoles.DisplayMember = "role_name"
cboRoles.ValueMember = "role_id"
End Sub
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
employees()
roles()
End Sub
The data is getting populated into the combo boxes correctly and my requirement is that when I select and employee from the first combo, his corresponding role should get selected in the second combo.
Anyone, please help me on this requirement.
Regards,
George
You need to add a binding source and a data relationship to get this to work. Consider this walk through, it is for datagridviews but the concept is the same.
I did a quick mock up to give you an idea. Remember that "EmpTable" is the name that you assign to your datatable and "EmpColumn" is the parent column, similarly apply the same logic to the Roles table. The key change to your code is that both tables must be in the same dataset with a datarelationship.
Dim dtEmp as Datatable
Dim dtRole as Datatable
''//fill tables here
Dim ds as New Dataset()
ds.Tables.add(dtRole)
ds.Tables.add(dtEmp)
Dim dr as New DataRelation( _
ds.Tables("EmpTable").Columns("EmpColumn"),
ds.Tables("RoleTable").Columns("RoleColumn"))
''//create binding sources
Dim bsEmp as New BindingSource
Dim bsRole as New BindingSource
bsEmp.Datasource = ds
bsEmp.DataMember = "EmpTable"
bsRole.Datasource = bsEmp
bsRole.DataMeber = "RoleTable"
''//bind the binding sources to the appropriate comboboxes
cboEmployee.Datasource = bsEmp
cboRole.Datasource = bsRole
Good luck.