Cannot access property value on listbox item - vb.net

I have a listbox with multiple selection activated and i am trying to read the different selected values
I have tried many snippets, here are the latest two :
For i = 0 To ListBox1.SelectedIndices.Count
MsgBox(ListBox1.Items((ListBox1.SelectedIndices(i))).value)
Next
For i = 0 To ListBox1.SelectedItems.Count
MsgBox(ListBox1.SelectedItems(i).value)
Next
For some reason with any approach i choose i can't read any item's value
My listbox is data bound, so i found out on a forum that making it public might fix the issue but it did not
I am hesitating as even Intellisense doesn't show much info, all i get is :
Equals
GetHashCode
GeType
ReferenceEquals
ToString
Any ideas where i went wrong?
Thanks in advance
Edit:

I'm not a big fan of using the Data GUI tools in VB. That said, I think what you are looking for is this:
For Each dvRow As DataRowView In Me.Listbox1.SelectedItems
MessageBox.Show(dvRow("Id").ToString)
Next
If you were interested in the Last Name field, change "Id" to "Last Name".
Also, I used MessageBox.Show instead of MsgBox. MsgBox is a leftover from VB6.

When you use a databound ListBox, the items aren't ListBoxItem objects (which you would expect to have Text and Value properties). Rather they are the type from the data source. The Items collection (and its variations, such as SelectedItems) is defined as a collection of objects, and the runtime type is obtained from the data source. Have you tried something like this?
For i = 0 To ListBox1.SelectedItems.Count
MsgBox(ListBox1.SelectedItems(i).ToString())
Next
In the comments, you indicated that the data source is a DB object containing String objects. If you have a collection of classes, such as Person, you can get it this way:
For i = 0 To ListBox1.SelectedItems.Count
MsgBox(DirectCast(ListBox1.SelectedItems(i), Person).FirstName)
Next
You should also get the same results with a For Each, as long as the selected items collection isn't changed while you are looping.
For Each p As Person in ListBox1.SelectedItems
MsgBox(p.FirstName)
Next
NOTE: This is untested code, as I'm not in front of Visual Studio at the moment.
EDIT: I see from the screenshot that the Value Member is set to a property named Id. If that is a uniqueidentifier column from the database, then the runtime type in the ListBox should be Guid.

Related

Fill a boolean array from checkbox control array

My program creates an array of checkboxes at runtime as shown below:
For Looper = 0 To 36
Dim Ex1ConfigCheck As New CheckBox
frmSetup.Controls.Add(Ex1ConfigCheck) ' Add Control to from
Ex1ConfigCheck.Top = (Looper + 45) + (Looper * 18) ' Set Location
Ex1ConfigCheck.Left = 210
Ex1ConfigCheck.Text = Setup.ExCheckName(Looper) ' Set Text property from strArray
Next
This is where I don't know how to proceed.
I would like to fill a boolean array (ex. MyBoolean(37)) with the value of Ex1configCheck().Checked. The reason I would like to fill another array is because I need to be able to reference the value of the checkboxes in other parts of the code but can't access them until they are created. Also, I plan on saving the array out to a binary file.
Could someone point me in the right direction please?
If there are no other CheckBoxes in the same container as those ones then you can do this:
Dim flags = Me.Controls.OfType(Of CheckBox)().
Select(Function(cb) cb.Checked).
ToArray()
If the controls are in a different container than the form itself, replace Me with that container.
As suggested by #Jimi, you could also create a List(Of CheckBox) and assign that to a field, populating it when you create the controls. You can then use that list instead of creating one on demand:
Dim flags = myCheckBoxList.Select(Function(cb) cb.Checked).
ToArray()
Of course, if you know exactly how many CheckBoxes you are going to be adding, why do you need to wait until run time to create them? Why can't you create them at design time and then modify them at run time? You usually only create controls at run time if you don't know how many there will be until run time, but that seems not to be the case here.
Thanks all for your answers and comments. I always have a fear of being roasted when I ask what some may consider a simple question online.
I have found an alternative way of accomplishing my task. Instead of creating 8 "Arrays" of checkboxes, I have learned of a very simple control available called "CheckedListBox".
I really didn't need to create the checkboxes at runtime but was trying to find an easier way to create 8 groups of 37 checkboxes without having to manually name and set the properties of each one during design. I also wanted to be able to index them in my code to be able to update and read the value using simple loops. I could have done this by creating arrays of CheckBox but again, I would have had to manually initialize the arrays.
Once I found the CheckedListBox, I was able to accomplish what I want very quickly. I only had to set the properties of the 8 "groups" (CheckedListBox's) and fill them using the items property. The ListBox essentially created a List like Jimi suggested automatically and I can index thru each list with a loop as desired. Jimi's suggestion actually lead me to finding the CheckedListBox while I was searching for more information on using "List(of CheckBox)".
Sometimes talking to others helps me find the right questions to ask. Google was able to figure out what I wanted when I searched for "List(of CheckBox)". (:

Load comboboxes with one line of code?

I have around 15 comboboxes on my form, all being loaded with the same information pulled from a table(~150 entries). Currently I am taking the information from the table, then looping through the entries and adding them to each textbox. I'm wondering if there's a more efficient way to load these comboboxes then having to individually add the table entry into each combobox, having to list 15 lines of code within the For loop.
I'm not seeing any performance issues with this, but figured I might as well work with the most efficient way possible rather than stick with what works. :)
You can create a list of the combo boxes, and then just loop through them. For instance:
Dim cbos() As ComboBox = {ComboBox1, ComboBox2, ComboBox3}
For Each cbo As ComboBox In cbos
' Load cbo from table
Next
Alternatively, if they are named consistently, you could find the combo box by name:
For i As Integer = 1 to 15
Dim cbo As ComboBox = DirectCast(Controls("ComboBox" & i.ToString())), ComboBox)
' Load cbo from table
Next
Since Combobox items are a collection, if their elements are the same, you can build and array with the objects you want to insert, and then just insert this array to each ComboBox with the method AddRange() (it's a method which exists inside the Combobox.items).
Getting an example from MSDN:
Dim installs() As String = New String() {"Typical", "Compact", "Custom"}
ComboBox1.Items.AddRange(installs)
Then you would only have to do a loop to add the array to each ComboBox. Of course, you will need to build your array first on your own, instead of this easy string array from the example.
Reference:
MSDN - AddRange
You could also do it this way since you mentioned that you already have a table.
Use a datatable
Change your table object into a datatable, which will assist in binding to the comboboxes. It might help if you add the datatable to a dataset too. That way you can attach all ComboBoxes (which are UI elements that let users see information) to the same DataSource, which is the datatable, in the dataset.
Binding
Now all you need to do is loop through all the comboboxes and set the datasource to the same table, that is if you decide to do it programmatically like so:
ComboBox1.DataSource = ds.Tables(0)
ComboBox1.ValueMember = "au_id"
ComboBox1.DisplayMember = "au_lname"
A further tutorial on this with the example above is found here
You can then also get the user selected value with ComboBox1.selectedValue.
On the other hand, if you did this with C# WPF, you can bind each comboBox in the XAML directly, I am unsure if this can be done in VB.net as I tried to look for the option but did not manage to do so, something you might want to try though.
Some very useful tutorials and guides on Data binding, which you might be interested:
~ denotes recommended reading for your question
MSDN: Connect data to objects
DotNetPerls on DataGridView (note this isn't a combobox, just displaying values)
~ VBNet DataTable Usage from DotNetPerls (this is in relation to 1.)
~ SO Q&A on Binding a comboBox to a datasource
Concepts of Databinding

Get Values from Listbox for if functions

Hey guys very new here.
Have a listbox that gets account names from a specific game server using this command line
Dim apikeyinfo As APIKeyInfo = api.getApiKeyInfo()
lstbxCharacters.DataSource = apikeyinfo.Characters
this code gets all the characters in a single account by displaying it in a listbox.
Now i would like to reference a character from the lisbox but not sure how
Any method such as Listbox.Get to get the value and compare it with something else?
Thanks
you can try something like
lstbxCharacters.SelectedItem
Update
To read the data from the listbox I think there are multiple ways (Assuming that it is readable).
-> Usually listbox display strings, so it should work to read to a string variable
Dim a_string as Strin = lstbxCharacters.SelectedItem
also you may like to add a small check before, assuring that an Item is currently selected:
If lstbxCharacters.SelectedIndex < 0 then return
This jumps out of current sub if no item is selected
And finally, to read the first entry, you can also do it this way:
a_string = lstbxCharacters.Items(0)
if it returns objects, then instead of accessing the object directly, it may work to do
a_string = lstbxCharacters.Items(0).ToString
(most objects allow a .ToString() Function )
Here two ideas for different solutions:
As a user commented, you could access the DataSource directly + the information which listIndex was selected. But if you do so, then maybe it is more easy (if you need to access it anyways, to go with solution 2)
Create a variable of type list(Of some_object) and fill it with the data from the datasource. It will take some time to do this, but if you define for the class some_object a function ToString, then you can fill all objects directly to the lstbxCharacters, and access them without any worries, by doing CType(lstbxCharacters.SelectedItem, some_object)
Update 2
If with reference you mean to access further information from the datasource, then you need to build some kind of query, or set the content of the listbox in relation to another control that shows the database content (in that way the listbox lstbxCharacters would act like a filter)

Searching a bound Combo Box for User Input

Alright Gents,
Im trying to search a vb.net combo box for an item. The combo box is already bound to dataset. The display member is set to display just a single column of the selected record. I had it set initially so the objects displayed in the combo were a customized class. In this class I specified all the properties I wanted to keep track of and that seemed to work well. However now that I am using the combo box in its bound state it is much more difficult to manipulate the data.
Mission:
To have the user type a number, if the number is contained in the ComboBox, the combo box should then move to that record so that all the other items bound to that control will update as well.
Research:
I have looked into the System.Windows.Forms.BingingManagerBase class and that seems to have the information I need. I just can't figure out the bridge between that and what I'm trying to do. I want to throw something together so I tried to simply do a SQL search against the dataset for the text in the combobox. Unfortunately that requires late binding and my targeted version of the .Net compact framework does not support that.
Here is an example of the late binding I was attempting. (Im working with VB.net 2005, Compact Framework 3.5 I believe:
For i as integer = 0 to combobox.items.count - 1
dim Dsr as Dataset.Row
dim dv as dataview
Dsr = DirectCase(Dv.row, Dataset.Row)
If Dsr(i).DesiredColumn = DesiredRow.Desiredcolumn then
'Do such and such code
End If
Next
I want to be able to search the dataset for a specific record matching a query. After I find that row matching the query I want to be able to move my combo box to the row found in the SQL query. The main problem seems to be that the Combo works in Datarowviews and my datasets are mostly cast to rows pertaining to the DS.
Anyone have some insight on this it would be much appreciated.
Thanks again!
If you know the item that should be set as selected in the combobox you can just set the ComboBox.SelectedItem Property
If you actually do really need to loop through all the items bound to the combobox then once you reach the correct one you can set the ComboBox.SelectedIndex Property.
For i As Integer = 0 To ComboBox.Items.Count - 1
Dim drv As System.Data.DataRowView = Nothing
Dim desiredColumn As String = String.Empty
drv = DirectCast(ComboBox.Items.Item(i), DataRowView)
desiredColumn = Convert.ToString(drv("Tag"))
Debug.WriteLine(desiredColumn)
Next
This seems to find the column value for every record in the combo box allowing me to find the correct index of the text I am searching for. Like I said though, if I could find a way to search through the list of items in the combobox without having to address each one, I would be grateful.

Comparing arrays in VB.NET

Let me provide a little detail to explain what I'm trying to accomplish before I get into the nuts and bolts of the question.
I've got two data sources - one is a SQL Server containing part numbers, descriptions, etc. The other is a CAD system that does not have a database in a traditional sense. What I'm trying to do is read out the bill of materials from the SQL Server and compare it to the CAD assembly drawing to ensure that the CAD system contains the same information as the SQL Server.
Getting the data from the SQL Server is fairly straight forward. I query the database and populate a datagrid. Done. Quick. Easy.
Getting the data from the CAD system is a little more involved. I have to load the assembly drawing to get a listing of all the component parts and then load those individual drawings to pull the "Part Number" property from the drawing. This is a somewhat time consuming and slow process (unfortunately) since each of the files must actually be accessed. I load those properties into an array (I guess a list might be more efficient).
So now I have a datagrid and array with part numbers. I need to compare them and colorize the grid accordingly. The grid should remain transparent if the part exists in both, color the row yellow if it only exists in the grid, and add a row colored red if only in the array.
As best I can tell, this means looping through the array on each line of the grid. The thought process is this:
Default the grid to yellow rows.
Loop through the grid and loop through the array to compare. If a match is found, make the row transparent and delete the element from the array.
After step 2 is completed, the array should only contain elements that are not found in the grid. Resize the array to remove the empty elements.
Add the elements of the array to the grid and color those new rows red.
The problems with this logic is that it seems expensive from a performance standpoint. Surely there is a better method? Also, if I modify the grid in some manner (like a resort) I have to go through the process again. I'd really appreciate some advice on this.
Thanks!
Note: written in Visual Studio 2005.
You could load the data from the CAD system in a dictionary (indexed by part number). Then you could go through the grid and check if it exists in the dictionary, which is a fast operation ( O(1) ). You could do exactly as you say, remove the found elements in the dictionary and add the remaining elements in the datagrid.
Here's some code for creating and using a dictionary (used C# style comments to preserve formatting):
//First argument is your key type, second is your item type
Dim cadParts As New Dictionary(Of Integer, Part)
//Add items to the parts dictionary
For Each part As Part In cadPartsArray
cadParts.Add(part.PartNumber,part)
Next
//Check if a part exists
Dim partNumber As Integer = 12345
If cadParts.ContainsKey(partNumber) ...
//Remove a part
cadParts.Remove(partNumber)
//Go through the remaining values
For Each part As Part In cadParts.Values ...
Edit:
1) Yes, if your key (here, part number) is a string, then a Dictionary(Of String,...) would be used.
2) I assumed you had a class named Part, which contained some information about a part. If you just have a part number, and no other info, then you could create a Hashset instead. It is basically the same as a dictionary, but with this structure the value is also your key. You would create a hashset like this:
Dim cadParts As New Hashset(Of String)
I won't go through code examples because it is very close to the Dictionary. ContainsKey becomes Contains, and Add accepts only one argument (which would be your part number here).
3) Yes, loop through them and add them to the hashset.
If part number is unique (it is not repeated in the values you want to search for) then you can use sorted dictionary. Then remove the duplicates and use remaining items.
To compare you can use part number as follows:
if(dictionary.ContainsKey(partNumber))
dictionary.Remove(partNumber)
end if
Given that there will only be a certain number of rows of the data grid visible at one time, it might be quicker to implement some code in the OnPaint method (I'm a little rusty here so apologies if that isn't exactly right) for each of the rows which checks against the array of part information and sets the background color of each row as it becomes visible, perhaps even marking each row as having been processed so the operation doesn't need to be repeated. There might be an initial performance gain here over processing all of the rows at once.
Of no real relevance; but from previous experience this sounds like you are interfacing with AutoDESK Inventor files?
Another solution could be to implement the IComparable(Of T) interface
That would require for you to build a class that you would use for both situation.
Public Class Item
Implements IComparable(Of Item)
Private _Description As String
Public Property Description() As String
Get
Return _Description
End Get
Set(ByVal value As String)
_Description = value
End Set
End Property
Private _PartNo As Integer
Public Property PartNo() As Integer
Get
Return _PartNo
End Get
Set(ByVal value As Integer)
_PartNo = value
End Set
End Property
Public Function CompareTo(ByVal other As Item) As Integer Implements System.IComparable(Of Item).CompareTo
' Your rules for comparing content for objects
If other.PartNo <> Me.PartNo Then Return 1
If other.Description <> Me.Description Then Return 1
' Return 0 if the object are the same
Return 0
End Function
End Class
Here is a small test that works with upper implementation.
Dim item As New Item
item.Description = "Desc"
item.PartNo = 34
Dim item2 As New Item
item2.Description = "Desc"
item2.PartNo = 35
Dim item3 As New Item
item3.Description = "Desc"
item3.PartNo = 36
Dim listFromDatabase As New Generic.List(Of Item)
listFromDatabase.Add(item)
listFromDatabase.Add(item2)
If listFromDatabase.Contains(item2) Then
MessageBox.Show("item2 was found in list")
End If
If Not listFromDatabase.Contains(item3) Then
MessageBox.Show("item3 was NOT found in list")
End If
Hope it helps a bit,
- Dan