Add object to listbox and use different string representations depending on the listbox? - vb.net

I have a coupple of listboxes with the same object type in all of them, the problem i have is that i dont want the object displayed with the same ToString() method in all of the listboxes. Is there a way to solve this?
At the moment im adding strings to the listboxes and then i use the selected string to search a list of objects for the correct one but i dont like that solution at all.

Suppose to have a class for Employees like this one:
Public Class Employee
Public Property ID As Integer
Public Property FirstText As String
Public Property SecondText As String
' and go on with other properties
....
End Class
Now when you populate your listboxes, you set the DisplayMember and ValueMember of your listboxes to two different property of Employee
Dim myList As ArrayList = New ArrayList()
myList.Add(New Employee() With {.ID = 1, .FirstText = "John Doe", .SecondText = "Doe John"})
myList.Add(New Employee() With {.ID = 2, .FirstText = "Mark Ross", .SecondText = "Ross Mark"})
ListBox1.DataSource = myList
ListBox2.DataSource = myList
ListBox1.ValueMember = "ID"
ListBox1.DisplayMember = "FirstText"
ListBox2.ValueMember = "ID"
ListBox2.DisplayMember = "SecondText"

Related

Retrieve list that is saved in a datatable

I created a datatable containing the list of notes for songs:
Private Table As New DataTable
Public Sub New()
With Table
.Columns.Add("Name")
.Columns.Add("NoteList")
.Rows.Add("GOT", GOT)
.Rows.Add("Yesterday", Yesterday)
End With
End Sub
GOT and Yesterday are lists of notes (notes is a class containing note, duration etc..)
On the form I then assign the datatable to a combobox:
ComboSongs.DisplayMember = Songs.DataTable.Columns(0).ColumnName
ComboSongs.ValueMember = Songs.DataTable.Columns(1).ColumnName
ComboSongs.DataSource = Songs.DataTable
I try to get the list of notes like this:
Dim songToPlay As List(Of Note) = CType(ComboSongs.SelectedValue, List(Of Note))
When I try to get the list I get the error:
System.InvalidCastException: 'Unable to cast object of type 'System.String' to type 'System.Collections.Generic.List`1[test.Note]'.'
Now I am unsure where I am getting it wrong. What would be the correct way to do this?
Your ValueMember is what is returned through the ComboBox.SelectedValue. So since you set the ValueMember like this
ComboSongs.ValueMember = Songs.DataTable.Columns(1).ColumnName
you only get the ColumnName. I assume that's a string and, well, the error message tells you it is one
... Unable to cast object of type 'System.String' ...
I guess that should be "NoteList", since that would be returned by Songs.DataTable.Columns(1).ColumnName
But this all doesn't make much sense, as I guess you are selecting a song there, either "Yesterday" or "GOT". At the point you're at it's so convoluted to return the DataTable rows, and index them. You will need to find the row by name and that is just too complicated when you could just create a class with strong names. I'll give you a class based solution but I'm not sure if you can make that change.
Private Class Song
Public Property Name As String
Public Property NoteList As List(Of Note)
End Class
Private Class Note
' your implementation
End Class
Dim songs As New List(Of Song)()
songs.Add(New Song() With {.Name = "GOT", .NoteList = New List(Of Note)})
songs.Add(New Song() With {.Name = "Yesterday", .NoteList = New List(Of Note)})
' need to populate those NoteLists first
ComboSongs.DisplayMember = "Name"
ComboSongs.DataSource = songs
Dim songToPlay = songs.SingleOrDefault(Function(s) s.Name = ComboSongs.SelectedValue)
Dim noteList = songToPlay.NoteList

Two way databinding for WinForms ComboBox with separate source for Items and SelectedItem

I hope my title makes sense. I want to do the following:
Bind ComboBox Items to a list
Bind the SelectedItem to a property (separate from the list)
Update the SelectedItem of the ComboBox once something in the object changes (for example, once the property that is bound to SelectedItem changes programatically, the SelectedItem of the ComboBox also changes accordingly)
I have already achieved to do the first two things but I am stuck on point 3. The databinding only works one-way. Meaning, when I select a new Item from the ComboBox, the object in code behind changes accordingly. But if I change the object in code behind or if it has an initial value, the SelectedItem of the ComboBox is not updated/preselected.
cbErledigungsArt.DataSource = _erledigungsArten.ToArray()
cbErledigungsArt.DisplayMember = "Beschreibung"
cbErledigungsArt.ValueMember = "ID"
cbErledigungsArt.DataBindings.Add("SelectedItem", _feststellung, "ErledigungsArt")
Festellung is a custom type:
Public Class FeststellungDTO
Public Property Jahr() As Integer
Public Property ErledigungsArt() As ErledigungsArtDTO
End Class
Erledigungsart is the property that is bound to the ComboBox
Public Class ErledigungsArtDTO
Public Property ID() As Integer
Public Property Beschreibung() As String
End Class
I want to be able to say, for example:
Dim _feststellung As New FeststellungDTO() With {
.Jahr = 2015,
.ErledigungsArt = New ErledigungsArtDTO() With {.ID = 0, .Beschreibung = "Bla"}
}
Dim _erledigungsArten As New List(Of ErledigungsArtDTO)(
{
New ErledigungsArtDTO() With {.ID = 0, .Beschreibung = "Bla"},
New ErledigungsArtDTO() With {.ID = 1, .Beschreibung = "Blu"}
}
)
cbErledigungsArt.DataSource = _erledigungsArten.ToArray()
cbErledigungsArt.DisplayMember = "Beschreibung"
cbErledigungsArt.ValueMember = "ID"
cbErledigungsArt.DataBindings.Add("SelectedItem", _feststellung, "ErledigungsArt")
'SelectedItem will become "Bla"
_feststellung.ErledigungsArt = New ErledigungsArtDTO() With {.ID = 1, .Beschreibung = "Blu"}
'SelectedItem will become "Blu"
'User now selects "Bla" from the ComboBox and the value of _festellung.ErledigungsArt will change accordingly
Is this possible?

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.

Finding Element in a Linked List of Type Structure (VB.NET)

I have declared a structure:
Public Structure MyStructure
Public name As String
Public dataType As String
Public address As String
End Structure
Then a Linked List:
Private MyList As New LinkedList(Of MyStructure)
What's the best way to find an element in the list given the value of an element in the struct. For example if I want to find the instance of MyStruct where the field name is "readings" in the list, how should I do it? Is there a way to avoid looping through the linked list elements?
You'd probably be better off using a class instead of a Structure:
Public Class MyFoo
Public Property name As String
Public Property dataType As String
Public Property address As String
End Class
This allows the following:
Dim lst As New LinkedList(Of MyFoo)
lst.AddFirst(New MyFoo With {.name = "Ziggy", .address = "here", .dataType = "foo"})
lst.AddFirst(New MyFoo With {.name = "Zoey", .address = "there", .dataType = "bar"})
lst.AddFirst(New MyFoo With {.name = "Curly", .address = "nowhere", .dataType = "any"})
Dim item = lst.FirstOrDefault(Function(x) x.name = "Ziggy")
If item IsNot Nothing Then
' do something
End If
There really isnt a way to avoid looping - something somewhere has to iterate the collection to find "Ziggy" (or "readings"); here, we just don't have to write code for it.
FirstOrDefault will return Nothing if the item cannot be found. Since a Structure is a value type, it cannot ever be Nothing (though it can contain Nothing). As a result, the line If item IsNot Nothing results in a syntax error.
I suppose with a Structure, you could use String.IsNullOrEmpty to test if the name is filled in, but I would just use a class.

ObjectListView adding items to it

I am a bit lost and I would like to add some items to a Fast ObjectListView. what I have is not working, and I cant seem to find anything online with vb.net samples
Dim LvItm As BrightIdeasSoftware.OLVListItem = lstMain.Items.Add("title")
With LvItm
.SubItems.Add("name")
.SubItems.Add("last")
.SubItems.Add("phone")
.SubItems.Add("address")
.EnsureVisible()
End With
ObjectListView works completely different from normal ListView, usually you dont add individual items.
In short:
- create columns
- set aspect names of created columns to property names of your objects
- point objectlistview to list of objects
See example below:
Imports BrightIdeasSoftware
Public Class Person
Public Property name As String
Public Property last As String
Public Property phone As String
Public Property address As String
End Class
Dim LvItm As New Person With {.name = "John",
.last = "Smith",
.phone = "555-69997-44",
.address = "Main Str. 1"}
Dim LvLst As New List(Of Person)
LvLst.Add(LvItm)
ObjectListView1.View = View.Details
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Name",
.AspectName = "name"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Last Name",
.AspectName = "last"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Phone",
.AspectName = "phone"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Address",
.AspectName = "address"})
ObjectListView1.SetObjects(LvLst)
With everything set-up you can add items to the list or manipulate in any way,
hitting ObjectListView1.SetObjects(LvLst) again to refresh view.
You can also add items to ObjectListView directly:
Dim p As New Person
p.name = "Steve"
p.last = "Wilson"
p.phone = "777-888-9987"
p.address = "First Str. 1"
ObjectListView1.AddObject(p)
Remember that items added directly were not added to your List(Of Person)