Sort items in a listbox which contain integer from highest to lowest for VB.NET - vb.net

(I have about 3 weeks of knowledge so sorry if I can't use the right naming conventions (like string for words)).
I would recommend reading this and then looking at the image I've provided. Also anything red ignore it and blue shows where data travels
To sum it up, I'm entering data, for example, a most football goals challenge and will enter the persons name how many points they achieved (1-100) and the event (series 1,2 or 3) this will then be entered into the 'solo' listbox and repeated until 20 participants have been added into the listbox. Then I will click the 'Add to Rank' button and all the items within solo listbox will be added to the 'RANK', listbox.
Now this is the tricky part, all I'm trying to do is sort the listbox by descending points (most to least, 100-1) which is why I put the points first, but I don't know how to use classes or arrays.

Can you maybe share you code? would be nice too see what you are doing.
Dim List As New List(Of Integer)
'Add ListBox Items to List
For Each ListBoxItem In ListBox1.Items
List.Add(ListBoxItem)
Next
'Sort the List and Clear the ListBox
List.Sort()
ListBox1.Items.Clear()
'Add to ListBox Ascending
For Each ListItem In List
ListBox1.Items.Add(ListItem)
Next
'Add to ListBox Descending
For i = List.Count - 1 To 0 Step -1
ListBox1.Items.Add(List(i))
Next
small example here for you

Don't store the data in the UI. Store it in data structures in code. Here is some code you can use to do that.
First of all, make a class to hold a Solo. This is a fundamental of object oriented programming. It will give you easy access to the properties of the solo, importantly the score which you can use to sort. Then it doesn't matter in what order you display it.
Public Class Solo
Public Sub New(name As String, score As Integer, series As String)
Me.Name = name
Me.Score = score
Me.Series = series
End Sub
Public Property Name As String
Public Property Score As Integer
Public Property Series As String
Public ReadOnly Property Text As String
Get
Return $"{Name}, {Series}, {Score} Points"
End Get
End Property
End Class
In your form, you can use this code. It will bind the data to the ListBoxes. You still have the data in your code to do other things with.
Private serieses As List(Of String)
Private solos As List(Of Solo)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
serieses = New List(Of String)() From {"Series 1", "Series 2", "Series 3"}
solos = New List(Of Solo)()
Me.SeriesComboBox.DataSource = serieses
End Sub
Private Sub AddButton_Click(sender As Object, e As EventArgs) Handles AddButton.Click
Dim score As Integer
If Not String.IsNullOrEmpty(NameTextBox.Text) AndAlso Integer.TryParse(ScoreTextBox.Text, score) Then
solos.Add(New Solo(NameTextBox.Text, score, SeriesComboBox.Text))
bindSolos()
End If
End Sub
Private Sub ShowRankButton_Click(sender As Object, e As EventArgs) Handles ShowRankButton.Click
bindRanks()
End Sub
Private Sub bindSolos()
SoloListBox.DataSource = Nothing
SoloListBox.DataSource = solos
SoloListBox.DisplayMember = "Text"
End Sub
Private Sub bindRanks()
RankListBox.DataSource = Nothing
RankListBox.DataSource = solos.OrderByDescending(Function(s) s.Score).ToList()
RankListBox.DisplayMember = "Text"
End Sub
Note the series are populated in Form_Load, not in the designer. In fact there is no data in the designer, as it should be.
You can rethink the rank button. You don't need it at all and can just bind the sorted ranks when you add, like this
Private Sub bindSolos()
SoloListBox.DataSource = Nothing
SoloListBox.DataSource = solos
SoloListBox.DisplayMember = "Text"
RankListBox.DataSource = solos.OrderByDescending(Function(s) s.Score).ToList()
RankListBox.DisplayMember = "Text"
End Sub
And really, you can just automatically just sort the solo ListBox and get rid of the second ListBox, so when you Add, they are always displayed in the solo ListBox sorted.
Private Sub bindSolos()
SoloListBox.DataSource = Nothing
SoloListBox.DataSource = solos.OrderByDescending(Function(s) s.Score).ToList()
SoloListBox.DisplayMember = "Text"
End Sub

Related

Make the input of two combo boxes search for, read and display data from csv file

Look this question is probably going to be unanswered or really confusing. This is all in Visual Studio (VB Code is preferred).
I have this program, and basically when the program loads, I want the combo boxes to be populated with data from a CSV file(I have done this.)
Then once I choose a value from the section box, I want the competitor number combo box to only show the numbers that have the selected value from the section box (i.e dependent combo boxes). Then based of the two values chosen, I want a name to be displayed.
So if Section 1, competitor 1 is chosen, I want their name to be displayed. But if I change it to section 1 competitor 2, the name should change.
Build a class for your competitor object.
Public Class Person
Public Property ComboBox1Value As String
Public Property ComboBox2Value As String
Public Property Name As String
End Class
Then declare a collection to hold the list of objects somewhere. I added it to the top of Form1.vb. Now when you hit your Search_For_Name sub, just loop through the collection until the combobox values match those in one of the objects.
Public Class Form1
Dim MyCollection As New Collection
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
'Populate your collection here from your csv by declaring a new
'person for each row of your file, and then adding it to the
'collection.
End Sub
Private Sub ComboBox1_SelectedValueChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedValueChanged
Search_For_Name()
End Sub
Private Sub ComboBox2_SelectedValueChanged(sender As Object, e As EventArgs) Handles ComboBox2.SelectedValueChanged
Search_For_Name()
End Sub
Private Sub Search_For_Name()
For Each P As Person In MyCollection
If P.ComboBox1Value = ComboBox1.Text And P.ComboBox2Value = ComboBox2.Text Then
TextBox1.Text = P.Name
Exit For
End If
Next
End Sub
End Class

Storing text box input into an array and displaying it on to a list box in visual basic

What i need to do is take input 10 times from a text box, store it in an array and then display the array onto a list box.
I came up with this for loop but it doesn't work, all it does is display the first input and then the rest are just blank:
For i As Integer = 0 To 9
ArrNames(i) = txtUserInput.Text
txtUserInput.Clear()
Next i
and i have a different button to display the array and i used this:
lstDisplay.DataSource = ArrNames
can anyone help me? iv'e looked everywhere and nothing worked for me
Edit 1:
So i changed it a lot and came up with this:
Dim I As Integer
If sender Is btnEnter Then
I = I + 1
End If
ArrNames(I) = txtUserInput.Text
txtUserInput.Clear()
is what it does is if the button is pressed it increases I by one which makes it so the input goes to the right index right?
but now that i make this it stopped displaying anything at all so this:
Private Sub btnShow_Click(sender As Object, e As EventArgs) Handles btnShow.Click
lstDisplay.DataSource = ArrNames
stopped working
Hopefully that I variable is declared at class level in the same place as the array.
To force the ListBox to refresh you have to set the .DataSource to Nothing and then back to your array again:
Public Class Form1
Private I As Integer
Private ArrNames(9) As String
Private Sub btnAddName_Click(sender As Object, e As EventArgs) Handles btnAddName.Click
If I < ArrNames.Length Then
If txtUserInput.Text.Trim.Length > 0 Then
ArrNames(I) = txtUserInput.Text
I = I + 1
lstDisplay.DataSource = Nothing
lstDisplay.DataSource = ArrNames
txtUserInput.Clear()
txtUserInput.Focus()
Else
MessageBox.Show("Enter a name first!")
End If
Else
MessageBox.Show("The array is full!")
End If
End Sub
End Class
I would first recommend you to use typed Lists instead of using the old fashioned array (discussions incoming.. but..).
You can also make use of the nice feature of Enumerable.Range().
I don't have my editor right now but it should look like:
Dim list = new List(Of string)
For i In Enumerable.Range(1, 10)
list.Add(txtUserInput.Text)
Next
txtUserInput.Clear()
lstDisplay.DataSource = list
If you plan to modify your list afterwards and e.g. keep it on class scope, then you should use a BindingList(Of String) which supports notifying about changes.
Edit 1:
You are calling txtUserInput.Clear() inside the loop after the first time your input has been received from the textbox. That means in each following iteration the textbox is cleared.

Hyperlinks inside ListBox

Hello, can someone help me with my project that I'm working on? Anything is appreciated!
So, what do I need? Its simple, I need link and name bars to actually create a hyperlink into the LSB listbox. Can someone please help me??
PS: Could you please keep it simple for me, I just started with this language.
OK This isn't as simple as I would like it to be for you but here goes. I'll try to explain as I go along
OK all the bits code below goes in your Form1 class as one lump - without my explanations that is. What I've done here is create a new class called Hyperlink
It has a couple of properties called Name and Link and a constructor called New
Class HyperLink
Private friendlyName As String
Private link As String
Public Property Name As String
Get
Return friendlyName
End Get
Set(ByVal value As String)
friendlyName = value
End Set
End Property
Public Property URL As String
Get
Return link
End Get
Set(value As String)
link = URL
End Set
End Property
Public Sub New(nm As String, ln As String)
friendlyName = nm
link = ln
End Sub
End Class
Here I'm creating list called linkList - this will hold the list of hyperlinks - I'm doing this so that later on the listbox will be set to use this as it's source of list items
Dim linklist As New List(Of HyperLink)
This sub called addLinkToListbox actually does several things. The important bits are that it tells you program to temporarily stop responding when the index of the listbox changes(which happens even when you're changing the contents off the listbox)
Then it adds a new **hyperlink* to the list of hyperlinks taking data from the link textbox and the name textbox.
To actually refresh the data shown in the textbox, I have to change the listbox datasource to nothing and then back to to the linkList
The next two lines tell the listbox to display the Name property inn the list box and when the item is actually clicked, to return the URL property.
Finally, I tell the program to start responding again when the index of the listbox index changes.
Private Sub addLinkToListbox(linkName As String, linkURL As String)
RemoveHandler ListBox1.SelectedIndexChanged, AddressOf ListBox1_SelectedIndexChanged
linklist.Add(New HyperLink(linkName, linkURL))
ListBox1.DataSource = Nothing
ListBox1.DataSource = linklist
ListBox1.DisplayMember = "Name"
ListBox1.ValueMember = "URL"
AddHandler ListBox1.SelectedIndexChanged, AddressOf ListBox1_SelectedIndexChanged
End Sub
These last two bits of code are hopefully self-explanatory
Private Sub btnAddLink_Click(sender As Object, e As EventArgs) Handles btnAddLink.Click
addLinkToListbox(txtName.Text, txtLink.Text)
End Sub
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
MessageBox.Show(ListBox1.SelectedValue.ToString)
End Sub

Delete line of structure array and update it in tab vb.net

So I have a listbox, filled with informations from a structure tab as follows :
Private Sub Modifier_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i As Integer = 0 To frmConnecter.TabPolyFlix.Length - 1
ListBox1.Items.Add(frmConnecter.TabPolyFlix(i).strTitre)
Next
End Sub
And I want the user to choose to delete the TabPolyFlix(ListBox1.SelectedIndex) and it has to get updated in the original Tab and thus in the Listbox
P.S I tried this but it only updates it in the listbox, not in the original tab
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim Temp As New List(Of ListViewItem)
For i As Integer = 0 To ListBox1.Items.Count - 1
If ListBox1.SelectedIndices.Contains(i) = False Then
Temp.Add(ListBox1.Items(i))
End If
Next
ListBox1.Items.Clear()
For i As Integer = 0 To Temp.Count - 1
ListBox1.Items.Add(Temp(i))
Next i
End Sub
Arrays have a fixed size. Better use a List(Of ListViewItem) for TabPolyFlix. Then assign the List directly to the DataSource of the listbox:
'Fill the listbox
ListBox1.DisplayMember = "strTitre" 'You can also set this in the property grid.
ListBox1.DataSource = frmConnecter.TabPolyFlix
You can also override the ToString method of ListViewItem in order to display anything you want in the listbox and keep ListBox1.DisplayMember empty.
Now you can directly delete the item in the list:
frmConnecter.TabPolyFlix.Remove(ListBox1.SelectedItem)
or
frmConnecter.TabPolyFlix.RemoveAt(ListBox1.SelectedIndex)
or if you want to delete several items at once
For Each i As Integer In ListBox1.SelectedIndices.Cast(Of Integer)()
frmConnecter.TabPolyFlix.Remove(ListBox1.Items(i))
Next
and re-display the list:
ListBox1.DataSource = Nothing
ListBox1.DataSource = frmConnecter.TabPolyFlix
The moral of the story is: don't use controls (the ListBox in this case) as your primary data structure. Use it only for display and user interaction. Perform the logic (the so called business logic) on display-independent data structures and objects (so called business objects) and when finished, display the result.

VB.NET datagridview one-to-one mapping of combobox

I have a datagridview with two textbox columns and one combobox column. The combobox's DataSource is bound to enums for the values of the dropdown. The datagridview's DataSource is bound to a custom class with datatypes of string, string and enum.
The first two columns are pre-populated with values and in the third column the user must select a value from the dropdown. All this is working excellent so far except....
The combobox field should be a one-to-one kind of mapping, meaning no two comboboxes should have the same value. I am really not sure how to implement this kind of behavior. Should the chosen value be removed from the remaining dropdowns? should the chosen value remain in the dropdown and just give an error when two of the same are selected?
Any ideas and how to implement these ideas will be of great help.
Thanks
note the only acceptable value that can be used more than once is 'None'
I have an idea based off your intent to possibly remove the chosen value from the remaining dropdowns.
I have a class called Runner which has a similar setup to your example.
Public Class Runner
Public Property FirstName As String
Public Property LastName As String
Public Property Placement As Result
Public Sub New(fn As String, ln As String)
FirstName = fn
LastName = ln
Placement = Result.None
End Sub
End Class
I also have an enum called Result which will get populated into the ComboBoxCell:
Public Enum Result
None = 0
Bronze = 1
Silver = 2
Gold = 3
End Enum
When I create data objects and bind them to the DataGridView, I do so like this (note - Placements is a global List(Of Result) and Runners is a global List(Of Runner):
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Placements.Add(Result.None)
Placements.Add(Result.Bronze)
Placements.Add(Result.Silver)
Placements.Add(Result.Gold)
Runners.Add(New Runner("John", "Smith"))
Runners.Add(New Runner("Jane", "Doe"))
Runners.Add(New Runner("Bill", "Jones"))
Column1.DataPropertyName = "FirstName"
Column2.DataPropertyName = "LastName"
Column3.DataPropertyName = "Placement"
Column3.DataSource = Placements
DataGridView1.DataSource = Runners
End Sub
Now, whenever the value of a cell in the ComboBoxColumn changes, you remove the new value from the list that contains the enums:
Private Sub DataGridView1_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
If (e.RowIndex > -1 And e.ColumnIndex = 2) Then
Dim currentvalue As Result = DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value
If currentvalue <> Result.None Then
Placements.Remove(currentvalue)
End If
End If
End Sub
Be careful when changing drop down selections... as per this Discussion, you have to trick the DataGridView into committing values as soon as the ComboBox value changes. I used an answer from that discussion and did something like this:
Private Sub DataGridView1_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles DataGridView1.CurrentCellDirtyStateChanged
Dim col As DataGridViewColumn = DataGridView1.Columns(DataGridView1.CurrentCell.ColumnIndex)
If col.Name = "Column3" Then
DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
Dim selected As DataGridViewCell = DataGridView1.CurrentCell
DataGridView1.CurrentCell = Nothing //This line and the next one simply hide
DataGridView1.CurrentCell = selected //an odd display effect that occurs
//because we remove a value and change the
//selection at the same time
End If
End Sub
Finally, you want to handle the DataError event for the DataGridView and leave it blank so that you don't get Exceptions thrown at you when removing values from your list of enums.
This gets you about 90% of the way there. It will not re-add items to the list when you change a value. For example if I change from Gold to Silver, Gold should be added back to the list. You can probably figure out what events to handle to get the old value and the new one, and insert the old value back into the list at the correct index based on its enum value.
I decided to use the CellValidating event of the DataGridView to check whether or not the same value is selected more than once. If it is then a error message is displayed in the row header column.
The combobox in error will not lose focus until the error is resolved.
Private Sub DataGridView1_CellValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
Dim headerText As String = DataGridView1.Columns(e.ColumnIndex).HeaderText
'Abort validation if cell is not in the Mapping column.
If Not headerText.Equals("Column Mapping") Then Return
'Clear error on current row.
DataGridView1.Rows(e.RowIndex).ErrorText = Nothing
e.Cancel = False
Dim newMappingValue As XmlElement = DirectCast([Enum].Parse(GetType(XmlElement), e.FormattedValue), XmlElement)
' Abort validation if cell value equal XmlElement.None
If newMappingValue.Equals(XmlElement.None) Then Return
For Each dgvRow As DataGridViewRow In DataGridView1.Rows
Dim currentMappingValue As XmlElement = dgvRow.Cells.Item(headerText).Value
If dgvRow.Index <> e.RowIndex Then
If currentMappingValue.Equals(newMappingValue) Then
DataGridView1.Rows(e.RowIndex).ErrorText = "Value already selected, please select a different value."
e.Cancel = True
End If
End If
Next
End Sub