Listbox.selected index change variable assignment - vb.net

Hi there
What is the correct way of assigning the selected index's value from a listbox to a variable? The user selects an item in a listbox and then the output changes depending on their selection.
I use:
variablename = listbox.text
in the listBox_SelectedIndexChanged event and this works.
When I use the button_click event I use:
variablename = listbox.selectedindex
But this does not work in the listbox_selectedindexchanged event.
Please could you let me know if it is okay to use it like I did above or if I will run into problems and why you cannot use the selectedindex method.
Thanks!

A. It sounds like your variable is a string, and yet you are trying to assign to it the value returned by the SelectedIndex Property, which is an integer.
B. If you are trying to retrieve the value of the item associated with the SelectedINdex of the Listbox, use the Index to return the Object itself (the listbox is a list of Objects, which are often, but not always, going to be strings).
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
'THIS retrieves the Object referenced by the SelectedIndex Property (Note that you can populate
'the list with types other than String, so it is not a guarantee that you will get a string
'return when using someone else's code!):
SelectedName = ListBox1.Items(ListBox1.SelectedIndex).ToString
MsgBox(SelectedName)
End Sub
THIS is a little more direct, using the SelectedItem Property:
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
'This returns the SelectedItem more directly, by using the SelectedItem Property
'in the event handler for SelectedIndexChanged:
SelectedName = ListBox1.SelectedItem.ToString
MsgBox(SelectedName)
End Sub

Well it depends what you want to achieve from the listbox's selected item.
There are a couple of possible ways, let me try to explain some of these for your homework.
Suppose you have a data table with two columns, and their rows...
ID Title
_________________________
1 First item's title
2 Second item's title
3 Third item's title
And you bind this data table to your list box as,
ListBox1.DisplayMember = "ID";
ListBox1.ValueMember = "Title";
If user selects second item from the list box.
Now if you want to get the display value (Title) of the selected item, then you can do
string displayValue = ListBox1.Text; // displayValue = Second item's title
OR even this to get same results.
// displayValue = Second item's title
string displayValue = ListBox1.SelectedItem.ToString();
And to get the value member against the selected item, you need to do
string selectedValue = ListBox1.SelectedValue; // selectedValue = 2
Now there are situations when you want to allow user to select more than one item from the list box, so you then set
ListBox1.SelectionMode = SelectionMode.MultiSimple;
OR
ListBox1.SelectionMode = SelectionMode.MultiExtended;
Now suppose if user selects two items; second and third.
So you can get the display values by simply iterating through the SelectedItems
string displayValues = string.Empty;
foreach (object selection in ListBox1.SelectedItems)
{
displayValues += selection.ToString() + ",";
}
// so displayValues = Second item's title, Third item's title,
And if you want to get ID's instead of Title's then...
I am also looking through it, I will post if found.
I hope your understandings build.
Good Luck!

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. ;)

Combo Box items - Display Member for List(Of String)?

My project is in Visual Basic. I am trying to create a custom & savable "filter" for a DataGridView using several TextBoxes. Right now, any List(Of String) that is added to the Combo Box is displayed in the box as (Collection). I want my users to be able to select the one they created, so I would like the Lists to have a display name that can be selected in the Combo Box. Here is some of the code.
Dim savedFilter As New List(Of String)
savedFilter.Add(NameTextBox.Text)
savedFilter.Add(AgeTextBox.Text)
savedFilter.Add(NotesTextBox.Text)
ComboBoxSavedFilters.Items.Add(savedFilter)
Is it possible to add a display name for a List?
Or if you are lazy use buid-in generic class Tuple From MSDN.
Create collection of Tuple(Of String, List(Of String)) and use approach suggested by #Plutonix for binding collection to ComboBox
Dim savedFilter As New List(Of Tuple(Of String, List(Of String)))()
savedFilter.Add(
Tuple.Create("default",
New List From {"filter1", "filter2", "filter3"}))
savedFilter.Add(
Tuple.Create("Blue ones",
New List From {"filter4", "filter5"}))
savedFilter.Add(
Tuple.Create("Old ones",
New List From {NameTextBox.Text, AgeTextBox.Text, NotesTextBox.Text}))
With ComboBoxSavedFilters
.DisplayMember = "Item1" 'Name of first property in Tuple type
.ValueMember = "Item2" 'Name of second property in Tuple type -List
.DataSource = savedFilter
End With
Then SelectedValue will contain currently selected filter's collection,
which can be accessed like that
Dim filter As List(Of String) =
DirectCast(Me.ComboBoxSavedFilters.SelectedValue, List(Of String))
You could setup under My.Settings a StriingCollection
Initializing (you can omit the items added if so desired)
If My.Settings.Filters Is Nothing Then
My.Settings.Filters = New StringCollection() From {"One", "Two"}
End If
Setup items in a ComboBox
ComboBox1.Items.AddRange(My.Settings.Filters.Cast(Of String).ToArray)
Adding an item
My.Settings.Filters.Add(Now.ToShortDateString)
You can remove and clear items too.
Provide a Display Member for List(Of String)
Apparently, these are less a collection of filters than a collection of criteria or clauses for one Filter:
I condensed the code in the question, but there are 14 fields that can be filtered and there are multiple filters that can be applied on one field.
For the multiples per field, I am not sure I would want to store those individually, but keep the field criteria together. So, if you want to apply a name to these, a class would not only do that but could help manage the filter elements:
Public Class SuperFilter
Public Property Name As String
Public Property Elements As SortedList
Public ReadOnly Property FilterText As String
Get
Return GetFilterText()
End Get
End Property
Public Sub New(n As String)
Name = n
Elements = New SortedList
End Sub
Public Sub AddItem(filter As String)
Elements.Add(Elements.Count, filter)
End Sub
Public Sub InsetAt(index As Int32, filter As String)
Elements.Add(index, filter)
End Sub
Private Function GetFilterText() As String
Dim els(Elements.Count - 1) As String
Elements.Values.CopyTo(els, 0)
Return String.Join(" ", els)
End Function
Public Overrides Function ToString() As String
Return String.Format("{0} ({1})", Name, Elements.Count.ToString)
End Function
End Class
You would need to add methods and properties like Remove and Count but this should be enough to demonstrate. I am not sure about the SortedList, a Dictionary using the field name might be better, but something to control the order seems worthwhile. I am also unsure I would expose the Elements collection - managing it might be better left to the class.
Hopefully, the Combo displaying a set of these (as opposed to the filter elements/clauses) is the goal.
Private filters As New List(Of SuperFilter)
Add filter items to the list:
Dim item As New SuperFilter("Default")
item.AddItem("Id = 7")
filters.Add(item)
item = New SuperFilter("Blue Ones")
item.AddItem("Color = Blue")
filters.Add(item)
item = New SuperFilter("Complex")
item.AddItem("[Name] like %Bob% OR [Name] like %Alice%")
item.AddItem("AND Color = 'Blue'")
item.AddItem("AND Active=True")
item.AddItem("AND AccessRequired < 3")
item.AddItem("AND DateAdded > #2/11/2010#")
item.AddItem("AND CreatedBy = 'ziggy'")
filters.Add(item)
cbo1.DataSource = filters
cbo1.DisplayMember = "Name"
cbo1.ValueMember = "FilterText"
The value member could be the Elements - the collection of filter clauses, or it could be the query text. The GetFilterText method joins them together for you as part of what a filter manager class could/should:
For n As Int32 = 0 To filters.Count - 1
Console.WriteLine("Name: {0} Count: {1}{2}Text:{3}", filters(n).Name,
filters(n).Elements.Count,
Environment.NewLine, filters(n).FilterText)
Next
Result:
Name: Default Count: 1
Text:Id = 7
Name: Blue Ones Count: 1
Text:Color = Blue
Name: Complex Count: 6
Text:[Name] like %Bob% OR [Name] like %Alice% AND Color = 'Blue' AND Active=True AND AccessRequired < 3 AND DateAdded > #2/11/2010# AND CreatedBy = 'ziggy'
If you use "Elements" as the ValueMember you will get back the collection.
The combo displays the Name for the user. On the right, a label displays the ValueMember in this case, it is the FilterText or joined Elements. As I said, you could get back the actual collection as the SelectedValue instead, but that is available as part of SelectedItem.
If savable means beyond the life of the application instance, that is another question, but these are very easily serialized.

Winforms combobox bug - 2 items with the same value but different key

Is this truly a bug in Winforms in 2015 or am I just doing something wrong...
1) Create a new winforms project (.net 4.0) and add a combobox to the main form.
2) Use this for your form load code:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim items As New Dictionary(Of Integer, String)
items.Add(1, "Value 1")
items.Add(2, "Value 2")
items.Add(3, "Value 3")
items.Add(4, "Value 3")
Dim dataSource As New BindingSource(items, Nothing)
ComboBox1.DataSource = dataSource
ComboBox1.DisplayMember = "Value"
ComboBox1.ValueMember = "Key"
End Sub
Notice how items 3 & 4 have the same value, but different keys and that the display and value members are set correctly (unless I am going crazy, which is possible). When you run the application, open the combobox and select the last item. Now, open the combobox back up and you will notice that the 2nd to last item is now selected. That is a problem.
Any thoughts?
Thanks!
EDIT: I added an Infragistics UltraComboEditor to the form and placed the following code in the form load event:
For Each item As KeyValuePair(Of Integer, String) In items
UltraComboEditor1.Items.Add(New ValueListItem With {.DataValue = item.Key, .DisplayText = item.Value})
Next
UltraComboEditor1.SelectedIndex = 0
UltraComboEditor1.AutoComplete = True
The Infragistics control allows me to autocomplete and enter my own text and it is not changing my selection when I select an item with the same text as the item above it. The Winforms control should not be changing my selection like that.
When the ComboBox allows the text portion to be edited, then it will pattern match and highlight the first prefix text that matches. This has the side effect that when the listbox is closed, the selected item is updated.
When the ComboBox's DropDownStyle == DropDownList mode, then the item previously selected will be highlighted in the dropdown list.
You can change the behavior by assigning a NativeWindow to the list window and then listen for the LB_SETCURSEL Msg.
You can use this thread as a starting point: Prevent AutoSelect behavior of a System.Window.Forms.ComboBox (C#)
Add an int index field to the Data object. Then in the Register method add:
combo.SelectedIndexChanged += delegate {
data.index = combo.SelectedIndex;
};
Then pass the Data to the native window, which keeps track of the previously selected index.
private class NW : NativeWindow {
Data data;
public NW(IntPtr handle, Data data) {
this.AssignHandle(handle);
this.data = data;
}
private const int LB_FINDSTRING = 0x018F;
private const int LB_FINDSTRINGEXACT = 0x01A2;
private const int LB_SETCURSEL = 0x0186;
protected override void WndProc(ref Message m) {
if (m.Msg == LB_FINDSTRING)
m.Msg = LB_FINDSTRINGEXACT;
if (m.Msg == LB_SETCURSEL)
m.WParam = (IntPtr) data.index;
base.WndProc(ref m);
}
}

VB.net DataBindingSource: bind an Integer into a Combobox

I have a populated combobox that I would like to bind to an Integer.
The wanted behaviour is that when the datasource changes to a new object, the combobox selected value changes accordingly to the Integer binded to it.
The problem is that the default binding(I simply dragged and dropped my Integer from the datasource pannel over the combobox) binds the text property of the combobox, not the SelectedIndex.
Right now I kinda make it work this way:
BindingSourceName.DataSource = Object
cmbType.SelectedIndex = Object.Type
But I have to manually manage it also while saving the binded object upon User modifications.
Is there a proper way to change the default bindig bahaviour to make it automatically bind the Integer to the SelectedIndex of my combobox?
The closest I can get to what you want is if I use a ValueMember for the index. For example:
I fill my Combobox with a couple of Items like this:
Public Class NameValue
Property Name as String
Property Type as Integer
Public Sub New(ByVal pName as String, ByVal pVal as Integer)
Name = pName
Type = pValue
End Sub
End Class
Dim cmbList As New List(Of NameValue)
cmbList.Add(New NameValue("Name",1)
cmbList.Add(New NameValue("Name2",2)
cmbList.Add(New NameValue("Name3",3)
cmbType.Items = cmbList
cmbType.ValueMember = "Value"
cmbType.DisplayMember = "Type"
Now the first stage is complete. The Combobox contains three items with a name and a value bound together. Next step is to setup what you are asking for: Bind the ComboboxValue to the Object class.
cmbType.DataBindings.Add(New Binding("SelectedValue", BindingSourceName, "Type", False, DataSourceUpdateMode.OnPropertyChanged))
As soon as the BindingSource "BindingSourceName.DataSource" changes, the combobox should be updated. And if you change the combobox, the Object.Type will change to the selected Value.

vb.net Set listbox items equal to an integer

I have a list box with lots of options (198 to be exact) and they are names of items.
I need to convert each name to be equal to an integer (the item id) so I can write that number to a file.
For example, if they selected the first item, then the integer would be set equal to 3000 but if they picked the second item, it would be 3001 and so on.
I hope you can understand, wasn't sure how to word it. Thanks.
Create an ENUMERATION and assign each item a value such as below
Public Enum MyCountryCodes As Integer
drzCOUNTRY_UNKNOWN = 0
drzCOUNTRY_AFGHANISTAN = 1
drzCOUNTRY_ALBANIA = 2
drzCOUNTRY_ALGERIA = 3
drzCOUNTRY_AMERICANSAMOA = 4
drzCOUNTRY_ANDORRA = 5
... etc etc
drzCOUNTRY_YEMEN = 241
drzCOUNTRY_ZAMBIA = 242
drzCOUNTRY_ZIMBABWE = 243
End Enum
You mean like this ?
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
MsgBox(ListBox1.SelectedIndex + 3000)
End Sub
Assign a dataTable to the ItemSource of your listBox. The DataTable can obviously have more than one field in it. You'll have to configure the properties of the list box to set one of it's fields (the string to display) as visible, then have the other field as visible = false. In one of the events (such as selectedItemChange i think) access the item's SelectedItem that corresponds to the id field.