Need Suggestions For AutoSuggest For VB.NET ComboBox - vb.net

To provide some context, I have a combobox that the user interacts with to select an insurance company. Unfortunately they don't require just the name; sometimes insurance companies have the same name, and the only way to distinguish between them is to use their address (eg a Medicare office in North Carolina vs. a Medicare office in South Carolina). What I've currently done is use the combobox's DrawItem event to draw a tooltip next to the ComboBox when the dropdown list is displayed. The list itself displays the insurance company names, but the tooltip will display the address of the currently selected company. The combobox is set to DropDownList, so it's impossible for them to pick anything but what's in the list.
Well, now I'm being told to change this. The users are no longer content to have to click the combobox or hit the arrow keys. They want comboboxes that they can type in and have an autosuggest list appear as they type. That's all well and good but this is where I'm running into a wall. My cute little scheme of using a tooltip can't work in that situation because the autosuggest list is a completely separate control. DrawItem doesn't touch it, and I can't find a way to custom draw the autosuggest list. The other problem is that autosuggest doesn't do duplicate entries, so even though I have two different insurance companies only one will appear in the list simply because they share the same name.
The only other idea I've had so far is to somehow scroll the dropdown list to the appropriate item upon the user pressing a key. But I can't figure out how to set the highlighted item in the dropdown list without setting selectedindex. If I use selectedindex, it goes ahead and replaces the text.
Does anyone have any suggestions for how to proceed? Am I on the right track, or do am I trying too hard and need to do something else entirely?

You could use a text field where the user types in their text, and narrow the selection in the dropbox down based on that. To get around the same-name-but-different-company problem, you could list the address of the company after the name in a parenthesis. If the user types in a name that is invalid, put up an error/warning icon next to the text field.
To update the selection at runtime, you can add an event listener to the text field and query the current text to decide whether it is a valid prefix, or not.

We do something similar at the company I'm at. To accomplish it, we utilize a web service through AJAX.
Essentially, you modify a standard textbox with an AJAX AutoCompleteExtender (ACE). This ace references a webservice (which I'll illustrate) that goes and gets the info the customer types in on the fly. It's pretty cool once it's up and running.
Here's an example:
.ascx
<asp:TextBox ID="txtInsuranceCompany" runat="server" TabIndex="520"
AutoComplete="Off"AutoCompleteType="Disabled" CssClass="asbSearch" Width="350px"></asp:TextBox>
<ajax:AutoCompleteExtender ID="aceInsuranceCompany" runat="server" CompletionSetCount="20"
MinimumPrefixLength="0" OnClientShown="resetPosition" ServiceMethod="LookupData"
ServicePath="~/WebLookUpService.asmx" TargetControlID="txtInsuranceCompany" UseContextKey="true">
</ajax:AutoCompleteExtender>
Something subtle is that you have to be sure you set the context key for your autocomplete extender, as well as create some functionality within your webservice to load your values (again, I'll illustrate).
.vb code-behind
Dim yourhardcodedlist As New List(Of String)
yourhardcodedlist.Add("Progressive")
yourhardcodedlist.Add("State Farm")
yourhardcodedlist.Add("USAA")
WebLookUpService.AddLookupValues(txtInsuranceCompany.ID, yourhardcodedlist.ToArray)
aceInsuranceCompany.ContextKey = public_var0 & ":" & public_var1 & ":" & txtInsuranceCompany.ID
Note that the "public_var0" "and public_var1" aren't mandatory. This is just illustrating how you could pass more information to your web service without actually passing it as a parameter (i.e. a colon-delimitted list, that you're web service function can parse out for use in a SQL statement or something).
Now for the webservice...(.asmx)
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<System.Web.Script.Services.ScriptService()> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class WebLookUpService
Inherits System.Web.Services.WebService
<System.Web.Services.WebMethod(), System.Web.Script.Services.ScriptMethod()> _
Public Function LookupData(ByVal prefixText As String, ByVal count As Integer, ByVal contextKey As String) As String()
'Construct SQL statement to pull from database
'parsing the context key as necessary to construct your SQL statement (if necessary)
'Dim somethingForSql As String = contextKey.Split(":")
Dim suggestions As List(Of String) = New List(Of String)
Try
Using cnADO As SqlConnection = New SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("PublicSafetyServer").ToString)
cnADO.Open()
Dim dt As DataTable = New DataTable
Dim da As New SqlDataAdapter
da.SelectCommand = New SqlCommand("<YourSQLStatement>")
da.Fill(dt)
Dim endRow As Integer = dt.Rows.Count
If endRow > count Then
endRow = count
End If
For i As Integer = 0 To endRow - 1
Dim des As String = dt.Rows(i).Item(field)
Dim val As String = dt.Rows(i).Item(field)
suggestions.Add(AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem(des, val))
Next
End Using
Catch ex As Exception
'Throw Error
End Try
suggestions.Sort()
If suggestions.Count = 0 Then
suggestions.Add(AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem(noneFound, ""))
End If
Return suggestions.ToArray()
End Function
What's cool is that you can add values deliberately through 'yourhardcodedlist' that will combine with any values you pull via the web-service. This way, you can add values directly if you can't add values to the database.

Related

VB.NET Combo Box with multiple fields to select from

I'm porting an Access database I wrote over to VB.NET and while I've used Access for 25 years, I've used VB.NET for 2 days so I'm unclear what the capabilities are.
In my Access form, I have a Combo Box which allows the user to select a part record from a table. Using Access I can show multiple columns to the user making a selection - PartID, Description, notes, whatever. However once the part is selected and the combo box is closed, I show only the unique part ID. The other columns help the user choose but make the form look really cluttered.
In my VB.NET form, I set the ValueMember to the PartID. But I only know how to set the DisplayMember to a single field. I created a composite field "PartID | Description | Notes" but then that's what appears even after the selection is made.
Is it possible in VB.NET to show one thing when the box is open and another thing once the selection has been made?
qry = "SELECT PartID, cast(PartID as string) & " | " & [partName] AS PartString
FROM [Part_List]
GROUP BY PartID, cast(PartID as string) & " | " & [partName]
ORDER BY PartID;"
cmd = New SqlCommand(qry, conn)
adapter = New SqlDataAdapter(cmd)
Dim tblPN As New DataTable()
adapter.Fill(tblPN)
ChoosePartNum.DataSource = tblPN
ChoosePartNum.DisplayMember = "PartString"
ChoosePartNum.ValueMember = "PartID"
ChoosePartNum.SelectedIndex = -1
I see what you are trying to do. I was doing that in MS Access too but it was more than 20 years ago.
I agree with Jimi. From a design point of view, trying to reproduce Access-like features in a new environment is not a great idea.
Users are familiar with Winforms controls and have certain expectations in term of UI layout and consistency. If you are building a new UI from scratch you should take the opportunity to redesign it and modernize it. Don't just carry 25-year old habits over. Especially if you are not the only user of that application, because you don't want people to think that your coding methods are stuck in the 20th century...
Is it possible in VB.NET to show one thing when the box is open and
another thing once the selection has been made?
If you really want to achieve this behavior you need to use combo box events. The two events you need would be DropDownClosed and DropDown.
Here is how it can be done:
Private Sub ComboBox1_DropDown(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox1.DropDown
With Me.ChoosePartNum
.DisplayMember = "composite field"
End With
End Sub
Private Sub ComboBox1_DropDownClosed(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox1.DropDownClosed
With Me.ChoosePartNum
.DisplayMember = "PartID"
End With
End Sub
The trick is to change the DisplayMember property whenever the combo box is folded/unfolded by the user. The underlying value remains the same.
I don't know if you require autocomplete from keyboard or have other needs. A treeview or listview sound like obvious alternatives. But you said you want a 'compact' control. Here you go.

VB.Net Read multi column text file and load into ListBox

First, I am not a programmer, I mainly just do simple scripts however there are somethings that are just easier to do in VB, I am pretty much self taught so forgive me if this sounds basic or if I can't explain it to well.
I have run into an issue trying to load a multi-column text file into a list box. There are two separate issues.
First issue is to read the text file and only grab the first column to use in the listbox, I am currently using ReadAllLines to copy the text file to a string first.
Dim RDPItems() As String = IO.File.ReadAllLines(MyDocsDir & "\RDPservers.txt")
However I am having a difficult time finding the correct code to only grab the first Column of this string to put in the listbox, if I use the split option I get an error that "Value of type '1-dimensional array of String' cannot be converted to 'String'"
The code looked like
frmRDP.lstRDP.Items.Add() = Split(RDPItems, ";", CompareMethod.Text)
This is the first hurdle, the second issue is what I want to do is if an item is selected from the List box, the value of the second column gets pulled into a variable to use.
This part I'm not even sure where to begin.
Example data of the text file
Server1 ; 10.1.1.1:3389
Server2 ; 192.168.1.1:8080
Server3 ; 172.16.0.1:9833
.....
When it's working the application will read a text file with a list of servers and their IPs and put the servers in a listbox, when you select the server from the listbox it and click a connect button it will then launch
c:\windows\system32\mstsc.exe /v:serverip
Any help would be appreciated, as I can hard code a large list of this into the VB application it would be easier to just have a text file with a list of servers and IPs to load instead.
The best practise for this would probably be to store your "columns" in a Dictionary. Declare this at class level (that is, outside any Sub or Function):
Dim Servers As New Dictionary(Of String, String)
When you load your items you read the file line-by-line, adding the items to the Dictionary and the ListBox at the same time:
Using Reader As New IO.StreamReader(IO.Path.Combine(MyDocsDir, "RDPservers.txt")) 'Open the file.
While Reader.EndOfStream = False 'Loop until the StreamReader has read the whole file.
Dim Line As String = Reader.ReadLine() 'Read a line.
Dim LineParts() As String = Line.Split(New String() {" ; "}, StringSplitOptions.None) 'Split the line into two parts.
Servers.Add(LineParts(0), LineParts(1)) 'Add them to the Dictionary. LineParts(0) is the name, LineParts(1) is the IP-address.
lstRDP.Items.Add(LineParts(0)) 'Add the name to the ListBox.
End While
End Using 'Dispose the StreamReader.
(Note that I used IO.Path.Combine() instead of simply concatenating the strings. I recommend using that instead for joining paths together)
Now, whenever you want to get the IP-address from the selected item you can just do for example:
Dim IP As String = Servers(lstRDP.SelectedItem.ToString())
Hope this helps!
EDIT:
Missed that you wanted to start a process with it... But it's like charliefox2 wrote:
Process.Start("c:\windows\system32\mstsc.exe", "/v:" & Servers(lstRDP.SelectedItem.ToString()))
Edit: #Visual Vincent's answer is way cleaner. I'll leave mine, but I recommend using his solution instead. That said, scroll down a little for how to open the server. He's got that too! Upvote his answer, and mark it as correct!
It looks like you're trying to split an array. Also, ListBox.Items.Add() works a bit differently than the way you've written your code. Let's take a look.
ListBox.Items.Add() requires that you provide it with a string inside the parameters. So you would do it like this:
frmRDP.lstRDP.Items.Add(Split(RDPItems, ";", CompareMethod.Text))
But don't do that!
When you call Split(), you must supply it with a string, not an array. In this case, RDPItems is an array, so we can't split the entire thing at once. This is the source of the error you were getting. Instead, we'll have to do it one item at a time. For this, we can use a For Each loop. See here for more info if you're not familiar with the concept.
A For Each loop will execute a block of code for each item in a collection. Using this, we get:
For Each item In RDPItems
Dim splitline() As String = Split(item, ";") 'splits the item by semicolon, and puts each portion into the array
frmRDP.lstRDP.Items.Add(splitline(0)) 'adds the first item in the array
Next
OK, so that gets us our server list put in our ListBox. But now, we want to open the server that our user has selected. To do that, we'll need an event handler (to know when the user has double clicked something), we'll have to find out which server they selected, and then we'll have to open that server.
We'll start by handling the double click by creating a sub to deal with it:
Private Sub lstRDP_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles lstRDP.MouseDoubleClick
Next, we'll get what the user has selected. Here, we're setting selection equal to the index that the user has selected (in this case, the first item is 0, the second is 1, and so on).
Dim selection As Integer = lstRDP.SelectedIndex
Lastly, we need to open the server. I'm assuming you want to do that in windows explorer, but if I'm mistaken please let me know.
Dim splitline() As String = Split(RDPItems(selection), ";")
Dim location As String = Trim(splitline(1))
We'll need to split the string again, but you'll notice this time I'm choosing the item whose location in the array is the same as the index of the list box the user has selected. Since we added our items to our listbox in the order they were added to our array, the first item in our listbox will be the first in the array, and so on. The location of the server will be the second part of the split function, or splitline(1). I've also included the Trim() function, which will remove any leading or trailing spaces.
Finally, we need to connect to our server. We'll use Process.Start() to launch the process.
Process.Start("c:\windows\system32\mstsc.exe", "/v:" & location)
For future reference, to first argument for Process.Start() is the location of the process, and the second argument is any argument the process might take (in this case, what to connect to).
Our final double click event handler looks something like this:
Private Sub lstRDP_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles lstRDP.MouseDoubleClick
Dim selection As Integer = lstRDP.SelectedIndex
Dim splitline() As String = Split(RDPItems(selection), ";")
Dim location As String = Trim(splitline(1))
Process.Start("c:\windows\system32\mstsc.exe", "/v:" & location)
End Sub
A final note: You may need to put
Dim RDPItems() As String = IO.File.ReadAllLines(MyDocsDir & "\RDPservers.txt")
outside of a sub, and instead just inside your class. This will ensure that both the click handler and your other sub where you populate the list box can both read from it.

Display members and Value members

Hi I am new to VB platform, Can any one help me to understand the last few lines of code, here I highlight with bold at last, which is not understand or confusing to me. What does that Display member and value number do?
.. cmbcust is the combobox...
Where customer table are having following field.
**Customer_sname** **Customer_code** **Customer_fname**
nokia 1 nokia corp.
samsung 2 samsung corp.
sony 3 sony corp.
Micromax 4 Micromax India corp.
passing custval is nokia, samsung, sony
Public Function customfunc(ByVal custval As String) As DataSet
Try
Dim strSQL As String = "select * from customer where cust_sname in (" & custval & ")"
If Conn.State = ConnectionState.Open Then Conn.Close()
Conn.Open()
Dim Adap As New SqlDataAdapter(strSQL, Conn)
Dim Ds As New DataSet
Adap.Fill(Ds, "customer")
ReadINICustomers = Ds
Catch EXP As Exception
MsgBox("Error Connecting to Server :" & EXP.Message, MsgBoxStyle.Critical)
End Try
End Function
Public Sub Fillcustomer()
Dim Lstcust() As String
Dim Lstcust1 As String
Lstcust1 = ""
Lstcust1 = custINIval
Dim Ds As New DataSet
Ds = objData.ReadINICustomers(Lstcust1)
cmbcust.DataSource = Ds.Tables("customer")
cmbcust.DisplayMember = Ds.Tables("customer").Columns.Item("cust_sname").ToString().Trim()
cmbcust.ValueMember = Ds.Tables("customer").Columns.Item("cust_code").ToString().Trim()
End Sub
cmbcust.DisplayMember =
Ds.Tables("customer").Columns.Item("cust_sname").ToString().Trim()
cmbcust.ValueMember = Ds.Tables("customer").Columns.Item("cust_code").ToString().Trim()
When working in any of the .NET languages, such as VB.NET, the MSDN is your friend. It is the official resource for documentation regarding the languages and all of the types in the .NET Framework. In this case, you are asking about a couple of properties on the ComboBox control. The first thing you should do, then, is to search the MSDN for the ComboBox class. If you do so, you will find this article. It lists all of the members of the class and has a separate article explaining each one. If you scroll down through the list of properties, you will find links to articles for the DisplayMember property and the ValueMember property.
As those articles describe, the ComboBox control can contain any type of objects in its list of items. If you put something simple like a list of strings into the ComboBox, then its obviously easy for it to determine what to display in the list and what to return for its current value. However, when you place complex custom objects in the ComboBox, it's a more difficult proposition.
By default, it will display whatever the ToString method returns for each of the objects in its list. However, by setting the DisplayMember property, you can instruct it to use a particular member (such as a Property or Function) of the objects in the list instead of the ToString method. You do so by setting the DisplayMember property to the string name of the objects' member. It then uses reflection to find the member by that name in each of the objects and retrieve its value.
The ValueMember is very similar, but rather than controlling what gets displayed, it controls what gets returned by the SelectedValue property. By default, the SelectedValue property simply returns the entire object that was selected in the list. However, by setting the ValueMember, you can instruct it to just return the value of one particular member from the object rather than the whole thing.

Is Databinding Textboxes in VB.net with a WHERE clause possible?

Is there a way to add databindings including a WHERE clause to sort which rows in the table get bound to which text boxes?
Serialnametxtbx.DataBinding.Add(New System.Windows.Forms.Binding("Text", Me.CoatingKitsBindingSource, "Serial_Number", True, DataSourceUpdateMode.OnValidation, nullValue As Object, formatString As String)
I'm not sure what one should use in the nullValue As Object location and the formatString As String location?
In the below code I have added a WHERE statement as to what I would like to sort this by but I am not sure where to implement it.
Serialnametxtbx.DataBinding.Add(New System.Windows.Forms.Binding("Text", Me.CoatingKitsBindingSource, "Serial_Number WHERE [Description] LIKE '%Banana%'", True, DataSourceUpdateMode.OnValidation, nullValue As Object, formatString As String)
Any suggestions? :)
The scenario here is there are 7 items to which need to be assigned as a group to say Kit #1. Each of these Items have similarities, Description, Unit Number, Serial Number,Make,Model,Last Calibration, Next Calibration, Calibration Company, Status and Condition. For each of the 7 Items there is different information that the user has input into each of these ten text boxes when they have added the piece of equipment. So I need to bind the piece of equipment's information to individual groups of text boxes on a form for that kit number.
So there are 10 text boxes for each piece of equipment in a kit and there are 7 pieces of equipment in a kit meaning 70 text boxes to the form. In the table there are the 10 columns for each piece and I would like to bind them as they differentiate by their description.
To select the kit number the user would like to edit I used a binding source filter on a combobox:
Me.Coating_KitsTableAdapter.FillBy(MacroQualityDataSet.Coating_Kits)
Me.CoatingKitsBindingSource.Filter = ("Unit_Number LIKE '%" & selectcktxtbx.Text & "%'")
And populated the list items in it with a dataset which is created when the form loads:
Dim ds As New DataSet
Dim cs As String = My.Settings.MacroQualityConnectionString
Dim sql As String = "SELECT DISTINCT Coating_Kits.Unit_Number FROM Coating_Kits"
Dim connection As New SqlConnection(cs)
Dim da As New SqlDataAdapter(sql, connection)
connection.Open()
da.Fill(ds, "Coating_Kits")
connection.Close()
selectcktxtbx.DataSource = ds.Tables(0)
selectcktxtbx.DisplayMember = "Unit_Number"
Can I maybe do a databinding off this dataset? I'm totally lost as usual.. :(
Use the Filter property of the BindingSource; the two mystery parameters to the Binding constructor are irrelevant (they represent DBNull replacements and display formatting strings respectively). Untested:
CoatingKitsBindingSource.Filter = "[Description] LIKE '%Banana%'"
Note also that you'll need multiple binding sources if you need different filters, or one control filtered and one not.

Listbox Control Item - Multiple Names (and/or variables)?

I have a lot of data that's coming from a database and being adding to a listbox. The data that's coming from the database is a unique ID, and a name.
Is there any way I can make the item contain both the ID and name? I know I can append it, that's not what I'm looking to do. I need a way to be able to either get the ID, or get the name, while displaying them both.
I have gotten as far as creating a class:
Class Item
Property ID as Integer
Property Name as String
Sub New(ID as Integer, Name as String)
Me.ID = ID
Me.Name = Name
End Sub
Overrides Function ToString() as String
Return Name
End Function
End Class
That looks like it should do the trick, but I'm having trouble getting the ID, instead of the name. To put it simply, I wish I could do this: listbox1.selecteditem(id) to get the id, or listbox1.selecteditem(name) to get the name.
Any ideas on how to implement this?
You can bind to a List(Of Item) like this:
Dim ItemList = New List(Of Item)
' Fill the list with appropiate values.'
listbox1.DataSource = ItemList
listbox1.DisplayMember = "Name"
listbox1.ValueMember = "ID"
Then listbox1.SelectedValue holds the ID and you can access the name like this:
DirectCast(listbox1.SelectedItem, Item).Name
If you want to show both the ID and the Name, then I suggest you add a property to be the displayed value in the Itemclass:
Public ReadOnly Property DisplayedValue() as String
Get
Return Me.Name & " (" & Me.ID.ToString & ")"
End Get
End Property
Then when binding the list make
listbox1.DisplayMember = "DisplayedValue"
Update:
Based on your comments below I'd say my solution still works. However with this methodology the items must be added to the list and then the list bound to the object. The items can not be added individually and directly to the list box (as you would be separating data from presentation I don't see that as a problem).
To show a message box with the selected item then you just need to do:
MessageBox.Show(DirectCast(listbox1.SelectedItem, Item).ID.ToString))
I think you'll have to write a helper method to do this. If you're using VB 3.5 or newer (part of VS2008 and newer) you can write an extension method so that you can at least get nice syntax. You could write one such that it looked like:
listbox1.SelectByID(123)
listbox1.SelectByName("hello")
In the methods you'd have some search algorithm that went through the items and found the right one.