Getting Data from WinForms ListView Control - vb.net

I need to retrieve my data from a ListView control set up in Details mode with 5 columns.
I tried using this code:
MessageBox.Show(ManageList.SelectedItems(0).Text)
And it works, but only for the first selected item (item 0). If I try this:
MessageBox.Show(ManageList.SelectedItems(2).Text)
I get this error:
InvalidArgument=Value of '2' is not valid for 'index'. Parameter name: index
I have no clue how I can fix this, any help?
Edit: Sorry, should have said, I'm using Windows.Forms :)

Right, from what I've tested:
Private Sub Button1Click(ByVal sender As Object, ByVal e As EventArgs)
For index As Integer = 0 To Me.listView1.SelectedItems.Count - 1
MessageBox.Show(Me.listView1.SelectedItems(index).Text)
Next
End Sub
(items added like this:)
For i As Integer = 0 To 99
Me.listView1.Items.Add(String.Format("test{0}", i))
Next
It just works.
So are you sure you have selected more than 1 item?
Could you please show us more code? :)

Related

List of currently displayed rows in DataGridView

I have a List(Of String) which I want to contain a list of primary keys based on if the associated row in dgv is displayed.
I need to both add and remove values from the list as they are displayed/hidden.
Currently I am doing this:
Private Sub dgv_RowStateChanged(sender As Object, e As DataGridViewRowStateChangedEventArgs) Handles dgv.RowStateChanged
If e.StateChanged = DataGridViewElementStates.Displayed Then
If Not VisibleRows.Contains(e.Row.Cells("SQ").Value.ToString) Then
VisibleRows.Add(e.Row.Cells("SQ").Value.ToString)
End If
End If
End Sub
However this will just add an item to my list when a new row is displayed without removing the hidden row's primary keys.
I can remove a value from the list using VisibleRows.Remove(e.Row.Cells("SQ").Value.ToString) however I don't know how to identify that a row is no longer displayed.
What is the result of e.StateChanged when a row is no longer displayed?
Hmmm.
DataGridViewElementStates is an enum containing flags. You might want to check for it like this:
If e.StateChanged And DataGridViewElementStates.Displayed = DataGridViewElementStates.Displayed Then
...
End If
I don't know if the event gets triggered for rows that become invisible. But then again, I would not want to keep track of such a list of strings. Smells not so well.
Personally (if I really need a list of strings containing the visible items), I would do the following:
fill the list only when I need it (for example by using a readonly property that refreshes the list if it is invalid).
invalidate (or simply dispose of) the list in the dgv_RowStateChanged event handler (or perhaps in a more appropriate event handler; I would need to check)
Something like this:
Private _visibleRows As List(Of String) 'Only use this inside the VisibleRows property and the dgv_RowStateChanged event handler. For all other usage of the list, use the VisibleRows property!
Private ReadOnly Property VisibleRows As List(Of String)
Get
If _visibleRows Is Nothing Then
_visibleRows = New List(Of String)
For Each row As DataGridViewRow In dgv.Rows
If row.Displayed Then
_visibleRows.Add(row.Cells("SQ").Value.ToString)
End If
Next
End If
Return _visibleRows
End Get
End Property
Private Sub dgv_RowStateChanged(sender As Object, e As DataGridViewRowStateChangedEventArgs) Handles dgv.RowStateChanged
_visibleRows = Nothing
End Sub
But it still does not smell right. Depending on the rest of your code, this might also have a dramatically bad performance.
Edit:
You might replace the For-loop in the VisibleRows property with the following code:
Dim index As Integer = dgv.FirstDisplayedScrollingRowIndex
Dim row = dgv.Rows(index)
While (row.Displayed)
_visibleRows.Add(row.Cells("SQ").Value.ToString)
index += 1
row = dgv.Rows(index)
End While
This might be faster...
Edit 2:
The code in my first edit has a bug: you might get an index-out-of-range-exception when you scroll down to the bottom of the datagrid. So you also need to check inside the While-loop if the increased index value is valid before you try to fetch that next row or exit the loop otherwise. But I'm not going to correct this. I don't like this entire "solution" at all. ;)

Adding ListItems to Session

There are examples here on Stackflow on how to add ListBox items to the session and then repopulate the items back to the ListBox but for some reason it is not working.
Here's the code snippet
Private Sub btnSelect_Click(sender As Object, e As EventArgs) Handles btnSelect.Click
If lstFields.SelectedIndex >= 0 Then
For i As Integer = 0 To lstFields.Items.Count - 1
If lstFields.Items(i).Selected Then
If Not arrayFields.Contains(lstFields.Items(i)) Then
arrayFields.Add(lstFields.Items(i))
Session("items") = arrayFields
End If
End If
Next
For i As Integer = 0 To arrayFields.Count - 1
If Not lstSelected.Items.Contains((CType(arrayFields(i), ListItem))) Then
lstSelected.Items.Add((CType(arrayFields(i), ListItem)))
End If
lstFields.Items.Remove((CType(arrayFields(i), ListItem)))
Next
lstSelected.SelectedIndex = -1
End Sub
When I try to repopulate the items back to ListBox using the For Each loop, the error I kept getting using VS 2015 that shows:
An exception of type 'System.InvalidCastException' occurred in FocusVB.dll but was not handled in user code
Additional information: Unable to cast object of type 'System.Web.UI.WebControls.ListBox' to type 'System.Collections.IEnumerable'.
Here's the snippet of the for each loop:
For Each item As ListItem In Session("item")
lstSelected.Items.Add(New ListItem(item.Text, item.Value))
Next
Am I missing somewhere in the code?
Just remove ListItem from the line For each item as listitem ..
For Each item In Session("item")
lstSelected.Items.Add(New ListItem(item.Text, item.Value))
Next
Don't ask me why ,rather read this .Take a look at the namespace of it,it'll answer you(if you are not talking about System.Windows.Documents.listItem)
Your enumerated list contains Session("items") = arrayFields
However, your reference is item, not items. Change to items.
If Not Session("items") Is Nothing Then
For Each item As ListItem In Session("items")
lstSelected.Items.Add(New ListItem(item.Text, item.Value))
Next
End If

VB.net Using a string to change something.Text

I need to work in VB.net for school and have encountered a problem.
The program works like this:
I've got a store page with buttons to buy them, they each have a name with the item and "_buy" after it. So if I wanted to buy a laptop, I'd press a button named laptop_buy.
What I want then is to make the event call the fuction Buy(laptop), so I can use the laptop ID later on.
I also have things named like laptop_level and laptop_price. I want to change them when I click the button. So I created this function:
Private Sub laptop_buy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles laptop_buy.Click
Buy("laptop")
End Sub
And
Function Buy(ByVal item)
Dim itemlevel As String = item + "_level.Text"
itemlevel += 1
End Function
So this should change the laptop_level.Text when I click laptop_buy.
But it isn't working.
Help is greatly appriciated!
You can use ControlCollenction.Find method to get your label.
Then you need to cast it's text value to Integer (I recommend using Int32.TryParse method for that) and then add 1 and return the result to the label text. Something like this should do the trick.
Sub Buy(ByVal item As String)
Dim level As Inetger
Dim ItemLabel as Label;
Dim ChildControls As Control() = Me.Controls.Find(item & "_level", True)
If ChildControls.Length > 0 Then
ItemLabel = ChildControls(0)
End If
If not isNothing(ItemLabel) AndAlso Int32.TryParse(ItemLabel.Text, level) Then
ItemLabel.Text = (Level + 1).ToString
End If
End Sub
Note: Code was written directly here, and it's been a long time since my vb.net days, so there might be some mistakes in the code.
Your trying to add 1 to a string.
A 'String' is a series of text characters, so the '1' in your text box is the character '1' not the number. You need to first conver the text into an 'integer'
Try:
dim itemlevel as integer = TryCast(item, integer)
If IsNothing(itemlevel) = false then
itemlevel = itemlevel + 1
Return itemlevel
end if
MSDN TryCast

MediaPlayer.MediaPlayer: How do I use the Volume property?

The Volume property from MediaPlayer.MediaPlayer is really confusing me. I googled the definition and found this which I think is the right class. It says the Volume property is a Double that excepts any number between 0 and 1, and it's default is 0.5. However, when I set it to 0.5 or any other number within 0 and 1, it says it does not fall within the expected range.
Here is how it is defined and how I tried to adjust the volume. I have the event as MouseUp because I could not get ValueChanged or Scroll to work properly. Any tips on that would be greatly appreciated.
Dim Player1 As MediaPlayer.MediaPlayer = New MediaPlayer.MediaPlayer
Private Sub SndMasterSlider_MouseUp(sender As Object, e As EventArgs) Handles SndMasterSlider.MouseUp
Player1.Volume = SndMasterSlider.Value / 100
Debug.Print(Player1.Volume)
End Sub

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.