How do I read data in drop down? - vb.net

I can view the data in textbox but the problem is, if I change it to drop down to show multiple data, it will give an empty data ..
Private Sub textbox2_SelectionChangeCommitted(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox2.KeyPress
Try
cmd = New Odbc.OdbcCommand("SELECT maker FROM pcba_info.tblvendorpartnumber WHERE partnumber ='" & Trim(TextBox2.Text.TrimEnd()) & "'", con)
dr = cmd.ExecuteReader
If dr.Read Then
TextBox55.Text = dr("maker").ToString ----------> return single data
TextBox12.SelectedIndex = dr("maker").ToString -----------> no data
dgvcertifiedoperator.DataSource = dt
dgvcertifiedoperator.Update()
dgvcertifiedoperator.Refresh()
End If
Catch ex As Exception
Debug.WriteLine("Plz check the parts" & ex.Message)
End Try
End Sub

I am assuming that your TextBox12 represents a dropdown. In this case assigning SelectedIndex to a string is incorrect (just think about it for a second - an index cannot be a string, i.e. AAA is not an index). Use Option Strict On to prevent such errors.
See, by default, VB.NET does not prevent you from doing it, because theoretically a string can be an integer, so this statement can work. But it does not mean it will work 100% of the time. Option Strict validates type conversion to ensure it always works, or your code does not compile.
Back to your question, one way to do it is to accumulate values in a list, then make that list a data source for your dropdown, like this:
Dim lst As New List(Of String)
While dr.Read Then
lst.Add(dr("maker").ToString)
End While
TextBox12.DataSource = lst
You can then retrieve current value by using TextBox12.SelectedItem, cast to string:
DirectCast(TextBox12.SelectedItem, String)
A side note - make sure your controls are named accordingly. TextBox is usually reserved for, well, text boxes. Dropdowns are usually MakerComboBox, MakerDropDown etc. Avoid naming your controls like TextBox56 and ComboBox188, because those numbers are meaningless for another developer. Even if you are the only one on the project, consider us helping you with it.

Related

Filtering binding source

This should be easy but I am having a headbanging of a time trying to get this to work! I have done a search and tried all most EVERY SINGLE ONE. Nothing works. I have a datagrid with a binding source. A user will type text into a textbox and the grid is SUPPOSED to change to only show records that contain what user typed in the name. Simple right? NOPE! Not for me! What am I doing wrong? Code below.
Private Sub SearchButton_Click(sender As Object, e As EventArgs) Handles SearchButton.Click
Dim Found As Boolean = False
Dim StringToSearch As String = ""
Dim ValueToSearchFor As String = "%" & SearchTextBox.Text.Trim.ToLower & "%"
Dim CurrentRowIndex As Integer = 0
Try
If ReferencesGrid.Rows.Count = 0 Then
CurrentRowIndex = 0
Else
CurrentRowIndex = ReferencesGrid.CurrentRow.Index + 1
End If
If CurrentRowIndex > ReferencesGrid.Rows.Count Then
CurrentRowIndex = ReferencesGrid.Rows.Count - 1
End If
If ReferencesGrid.Rows.Count > 0 Then
For Each gRow As DataGridViewRow In ReferencesGrid.Rows
StringToSearch = gRow.Cells(1).Value.ToString.Trim.ToLower
If InStr(1, StringToSearch, LCase(Trim(SearchTextBox.Text)), vbTextCompare) Then
TrainingItemBindingSource.Filter = String.Format("Name LIKE '{0}'", ValueToSearchFor)
Exit For
End If
Next
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
You should get rid of pretty much all that code. If you want to filter the data then just filter the data. There's no conditional statements required and loops required. Just set the Filter property and any records that don't match the filter will be hidden:
Private Sub SearchButton_Click(sender As Object, e As EventArgs) Handles SearchButton.Click
TrainingItemBindingSource.Filter = $"Name LIKE '%{SearchTextBox.Text.Trim()}%'"
End Sub
As you can see, it is simple. I've no real idea what you were actually trying to achieve with the rest of that code. That will exclude any records where the Name column does not contain the search text.
Note that there is no need to try to force case-insensitivity by using ToLower or the like. Just like in real SQL, comparisons done this way in a DataTable are case-insensitive by default. You have to explicitly set the CaseSensitive property of the DataTable or its parent DataSet to True to make such comparisons case-sensitive.
I should also point out that the ability to filter is predicated on the data source implementing certain interfaces. If the data source is a DataTable then you have those interfaces for free. If you have actually bound to something else, e.g. a List(Of T), then you won't be able to filter this way because the required members do not exist.

Database Lookup From ComboBox selection

I have a question about database values and how to determine the id of a value that has been changed by the user at some point.
As it is currently set up there is a combobox that is populated from a dataset, and subsequent text boxes whose text should be determined by the value chosen from that combobox.
So let's say for example you select 'Company A' from the combobox, I would like all the corresponding information from that company's row in the dataset to fill the textboxes (Name = Company A, Address = 123 ABC St., etc.,)
I am able to populate the combobox just fine. It is only however when I change the index of the combobox that this specific error occurs:
An unhandled exception of type 'System.Data.OleDb.OleDbException'
occurred in System.Data.dll
Additional information: Data type mismatch in criteria expression.
Here is the corresponding code:
Imports System.Data.OleDb
Public Class CustomerContact
Dim cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|datadirectory|\CentralDatabase.accdb;")
Dim da As New OleDbDataAdapter()
Dim dt As New DataTable()
Private Sub CustomerContact_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
cboVendorName.Items.Add(r("Name").ToString)
cboVendorName.ValueMember = "ID"
Next
cn.Close()
End Sub
Private Sub cboVendorName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboVendorName.SelectedIndexChanged
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers WHERE id='" & cboVendorName.SelectedValue & "'", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
txtNewName.Text = "Name"
txtAddress.Text = "Address"
Next
cn.Close()
End Sub
The error is caught at Line 24 of this code, at the second da.Fill(dt) . Now obviously from the exception I know that I am sending in a wrong datatype into the OleDbCommand, unfortunately I am a novice when it comes to SQL commands such as this. Also please keep in mind that I can't even test the second For loop, the one that is supposed to fill the Customer information into textboxes (for convenience I only copied the first two textboxes, of which there are nine in total). I am think I could use an If statement to determine if the row has been read, and from there populate the textboxes, but I will jump that hurdle when I can reach it.
Any guidance or suggestions would be much appreciated. Again I am a novice at managing a database and the code in question pertains to the project my current internship is having me write for them.
Since you already have all the data from that table in a DataTable, you dont need to run a query at all. Setup in form load (if you must):
' form level object:
Private ignore As Boolean
Private dtCust As New DataTable
...
Dim SQL As String = "SELECT Id, Name, Address, City FROM Customer"
Using dbcon = GetACEConnection()
Using cmd As New OleDbCommand(SQL, dbcon)
dbcon.Open()
dtCust.Load(cmd.ExecuteReader)
End Using
End Using
' pk required for Rows.Find
ignore = True
dtCust.PrimaryKey = New DataColumn() {dtCust.Columns(0)}
cboCust.DataSource = dtCust
cboCust.DisplayMember = "Name"
cboCust.ValueMember = "Id"
ignore = False
The ignore flag will allow you to ignore the first change that fires as a result of the DataSource being set. This will fire before the Display and Value members are set.
Preliminary issues/changes:
Connections are meant to be created, used and disposed of. This is slightly less true of Access, but still a good practice. Rather than connection strings everywhere, the GetACEConnection method creates them for me. The code is in this answer.
In the interest of economy, rather than a DataAdapter just to fill the table, I used a reader
The Using statements create and dispose of the Command object as well. Generally, if an object has a Dispose method, put it in a Using block.
I spelled out the columns for the SQL. If you don't need all the columns, dont ask for them all. Specifying them also allows me to control the order (display order in a DGV, reference columns by index - dr(1) = ... - among other things).
The important thing is that rather than adding items to the cbo, I used that DataTable as the DataSource for the combo. ValueMember doesn't do anything without a DataSource - which is the core problem you had. There was no DataSource, so SelectedValue was always Nothing in the event.
Then in SelectedValueChanged event:
Private Sub cboCust_SelectedValueChanged(sender As Object,
e As EventArgs) Handles cboCust.SelectedValueChanged
' ignore changes during form load:
If ignore Then Exit Sub
Dim custId = Convert.ToInt32(cboCust.SelectedValue)
Dim dr = dtCust.Rows.Find(custId)
Console.WriteLine(dr("Id"))
Console.WriteLine(dr("Name"))
Console.WriteLine(dr("Address"))
End Sub
Using the selected value, I find the related row in the DataTable. Find returns that DataRow (or Nothing) so I can access all the other information. Result:
4
Gautier
sdfsdfsdf
Another alternative would be:
Dim rows = dtCust.Select(String.Format("Id={0}", custId))
This would return an array of DataRow matching the criteria. The String.Format is useful when the target column is text. This method would not require the PK definition above:
Dim rows = dtCust.Select(String.Format("Name='{0}'", searchText))
For more information see:
Using Statement
Connection Pooling
GetConnection() method aka GetACEConnection

Auto update access tables Cells with random numbers from vb.net

I searched many forums but didn't find any solution. I want to update access table cells from Vb.net. My table has fields:
[PanelNumber],[Date], [PVValue]
In Panel number field, there is some text like "Panel 1", "Panel 2" etc..
from vb, i will select that "Panel 1" after clicking a button, i need to fill that "PVValue" field with random numbers in given range, plz check my code below, when i try with this code, i am always getting same number in all rows
but need separate number (may be repeated in some rows)
LogTable2 is my table name
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=LoggedData.accdb;Jet OLEDB:Database Password=GodavarthiSuresh;"
myNewConnection.ConnectionString = connString
myNewConnection.Open()
Dim UpdateString As String = "update LogTable2 set [pvvalue]= #rndVal1 where panelnumber='" & panelnametxt.Text & "'"
Dim UpdateCmd As New OleDb.OleDbCommand(UpdateString, myNewConnection)
UpdateCmd.Parameters.Clear()
Randomize()
UpdateCmd.Parameters.AddWithValue("#rndVal1", GetRandom())
Try
UpdateCmd.ExecuteNonQuery()
UpdateCmd.Dispose()
myNewConnection.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
' this is the function to get random number in given range
Public Function GetRandom() As Integer
Static Generator As System.Random = New System.Random()
Return Generator.Next(825, 850)
End Function
If you have multiple rows for each panel and you want them to have different values, you need to update them individually. Its is not true that update command will be "called" 5 times if there are five rows associated. It will be executed once per click event.
To do what it sounds like you want, you need a unique identifier for each such as an AutoIncrement ID column.
Private RNG As New Random()
Private Sub btnUpdate_Click(etc...
Dim sql = "SELECT ID FROM LogTable2 WHERE panelnumber = #pnl"
Dim pnlList As New List(Of Int32)
Using con As OleDbConnection = GetACEConnection()
Using cmd As New OleDbCommand(sql, con)
con.Open()
cmd.Parameters.AddWithValue("#pnl", panelnametxt.Text)
' get affected row IDs into a list;
Using rdr As OleDbDataReader = cmd.ExecuteReader
While rdr.Read
pnlList.Add(Convert.ToInt32(rdr.Item("ID")))
End While
End Using ' close, dispose of reader
End Using ' dispose of cmd
' not sure you need a new command object
sql = "UPDATE LogTable2 SET pvvalue = #rVal WHERE ID = #id"
Using pcmd As New OleDbCommand(sql, con)
' loop thru ID list and update each row with
' new random value 825-849 inclusive
For n As Int32 = 0 To pnlList.Count - 1
pcmd.Parameters.AddWithValue("#rVal", RNG.Next(825, 850))
pcmd.Parameters.AddWithValue("#id", pnlList(n))
pcmd.ExecuteNonQuery()
' clear for next iteration
pcmd.Parameters.Clear()
Next
End Using ' close and dispose of pcmd
End Using ' close and dispose of connection
End Sub
I dont like scattering the connection string in every method which opens a connection, so a method for that is nice to have.
Notes:
This depends on a unique ID column which is AutoIncrement (PK). If you have some other unique identifier, use it but you have to have some way to identify rows individually.
Rather than a method to create a random value, since it is just one line, it might be easier to just use your RNG directly as shown.
I cant test the code, but it should be close.
Use Using blocks to close and dispose of DBObjects like connections, command and reader otherwise you can run out of resources.
You can also initialize Command objects with the SQL and COnnection when you declare it rather than setting them as properties. It makes the code a little more compact and less likely that you forget them.
Randomize does nothing - it is meant to be used with the old VB6 Rnd(). You only need to [Escape] keywords in SQL, not every column name and pvvalue is not a keyword.
A DataTable instead of a Reader could be used to get the rows but I am not sure it is any simpler.
Finally, elements of a SQL WHERE clause can also be parameterized; there is no need to concat them just because it is a where rather than a column value.
you can do this in database level,add auto increment value to database field

I need to know how to avoid the System.Data.DataRowview in vb 2010

I have an issue with my code; and would appreciate some assistance getting her to do what I want without an annoying error. Read the databound listbox strings, find the user selected string and write said string to a file.
Private Sub btnContinue_Click(sender As System.Object, e
As System.EventArgs) Handles btnContinue.Click
' 1st file and mySQL Update to testing table
If txtLocation.Text.ToString <> "" And txtUnitTested.Text.ToString <> "" Then
Dim filewriter As New System.IO.StreamWriter(
"C:\Users\OER\Documents\Visual Studio 2010\Projects\frmTest1_0.txt")
Dim curItem = lstCustomer.SelectedItem
Dim now As DateTime = DateTime.Now
With filewriter
.WriteLine(vbCrLf + (now.ToString("F"))) ' Write the DateTime to the file
End With
For Each objDataRowView As DataRowView In lstCustomer.SelectedItems
Dim item As String
For Each item As DataRowView In Me.lstCustomer.Items.Item("customer")
item = String.Parse(lstCustomer.SelectedValue)
WriteLine(curItem(item))
Next item
Next ' THIS IS THE FOR LOOP I AM HAVING ISSUES WITH!!!!!!!
With filewriter
.WriteLine(vbCrLf + txtLocation.Text + vbCrLf + txtUnitTested.Text)
End With
filewriter.Close()
frmTest1.Show()
Else
MsgBox("Please Type the Location and Unit Tested!!!", vbCritical)
txtLocation.Clear()
txtUnitTested.Clear()
txtLocation.Focus()
End If
These loops:
For Each objDataRowView As DataRowView In lstCustomer.SelectedItems
Dim item As String
For Each item As DataRowView In Me.lstCustomer.Items.Item("customer")
item = String.Parse(lstCustomer.SelectedValue)
WriteLine(curItem(item))
Next item
Next
Are going to cause you some problems. You have declared a loop variable with the same name as another variable. And then you are assigning a value to it.
Also, I am not very clear on what the purpose of the two loops are. I am somewhat confused about what you are trying to accomplish in this section of code. I would start by renaming the variable in this line:
Dim item as String
I am going to use this for an example:
Dim result as string
Make that something else, and you cannot then change the value of item (since that is the loop variable) in the loop. That will cause an error.
Changing the code inside the loop to:
result = String.Parse(lstCustomer.SelectedValue)
Odds are this still won't get you to where you are trying to go, but it is a good start. Unless each item in your lstCustomer contains multiple items to iterate through I think you probably only need that first, outer loop, anyway.
If your loop starts like this:
For Each objDataRowView As DataRowView In lstCustomer.SelectedItems
Then I think you will want:
objDataRowView.Item("customer")
as what is output. I think that will be the value of the customer field from your table.
You are basically saying that this row has a column called customer.... I would like that value please.

VB.NET Combobox - Auto-complete behaviour for numeric values

I have a problem with the auto-complete behaviour of comboboxes in VB.NET (with the .NET framework 2.0).
I am using a combobox to type in numeric values, and its DropDown list to suggest possible numeric values. This list is sorted in ascending order, for example {"10","92", "9000", "9001"}.
The combobox properties are set as follow:
AutoCompleteMode: SuggestAppend
AutoCompleteSource: ListItems
DropDownStyle: DropDown
Sorted: False
The DropDown list is simply filled like this:
myCombobox.Items.Add("10")
myCombobox.Items.Add("92")
myCombobox.Items.Add("9000")
myCombobox.Items.Add("9001")
When I don't type anything, the order of values of the DropDown list is correct, in original/ascending order. However, when I start typing something, the suggested values in the DropDown list get sorted (alphanumerically): if I type "9", the list of suggestions becomes {"9000", "9001", "92"}.
I would like to prevent this behaviour to get the values of the list in the original/ascending order. I can't figure out how...
A possible work-around would be to pad with zeroes the values in the list, e.g. {"0010", "0092", "9000", "9001"} but I would like to avoid this.
Edit:
As suggested by bendataclear, one can use a list box to display the suggestions.
This will work for small lists but doesn't scale well to large lists. It may be useful for some applications. Based on the code given by bendataclear, I made it work this way:
Private Sub ComboBox1_KeyUp(sender As System.Object, e As System.Windows.Forms.KeyEventArgs) Handles ComboBox1.KeyUp
Dim cursorPos As Integer = ComboBox1.SelectionStart
ListBox1.Items.Clear()
For Each s In ComboBox1.Items
If s.StartsWith(ComboBox1.Text) Then
ListBox1.Items.Add(s)
End If
Next
If ListBox1.Items.Count > 0 And ComboBox1.Text.Length > 0 Then
ComboBox1.Text = ListBox1.Items(0)
ComboBox1.SelectionStart = cursorPos
ComboBox1.SelectionLength = 0
End If
End Sub
The code has not been thoroughly tested and can be improved, but the main idea is there.
Edit 2:
Using DataGridView leads to better performance; it was sufficient for me. Thanks bendataclear.
Just out of curiosity, any other answer is welcomed :)
Seems to be an issue when the combo box displays the data, as even if you set a custom source it re-orders alphabetically:
ComboBox1.Items.Add("10")
ComboBox1.Items.Add("92")
ComboBox1.Items.Add("9000")
ComboBox1.Items.Add("9001")
ComboBox1.AutoCompleteCustomSource.Add("10")
ComboBox1.AutoCompleteCustomSource.Add("92")
ComboBox1.AutoCompleteCustomSource.Add("9000")
ComboBox1.AutoCompleteCustomSource.Add("9001")
ComboBox1.AutoCompleteSource = AutoCompleteSource.CustomSource
I think the only way I can think of is to create your own autocomplete something like (untested):
Dim cbotxt As String = ComboBox1.Text
Dim key As String
key = ChrW(e.KeyCode)
ListBox1.Items.Clear()
For Each i In ComboBox1.Items
Dim s As String = i.ToString()
If s.StartsWith(ComboBox1.Text & key) Then
ListBox1.Items.Add(s)
End If
Next
If ListBox1.Items.Count > 0 Then
ListBox1.Visible = True
ComboBox1.Text = ListBox1.Items(0)
End If
Edit:
A good approach for many items (I'm using for 10000+ in an application):
First change from a list box to a datagridview.
Then declare a list of strings and fill with values you want to autocomplete
Dim Numberlist as List<Of String>
' Fill List using Numberlist.Add("String")
Then in the text change property:
Filter = NumberList.FindAll(AddressOf checkNum)
DataGridView1.DataSource = Filter
And add the function to check the strings.
Function checkNum(ByVal b As String) As Boolean
If b.StartsWith(ComboBox1.Text) Then
Return True
Else
Return False
End If
End Function
This method runs on my machine with 10k items faster than I can type.