ListBox DataSource how to update (VB.NET) - vb.net

I have an ArrayList with objects, this ArrayList is used as a DataSource to a ListBox. When new objects are added to the listbox, how can i get the listbox to update?
the code for populating the list looks lite this:
'Form1.ExistingArticles = The ArrayList
'Form1.LB_Articles = The Listbox
Public Sub FillArticleList()
If Form1.ExistingArticles.Count = 0 Then
Exit Sub
End If
Form1.LB_Articles.DataSource = Form1.ExistingArticles
Form1.LB_Articles.ValueMember = "ID"
Form1.LB_Articles.DisplayMember = "ListText"
End Sub
When i add a new object to the Form1.ExistinArticles i can see that the new object is there, but it does not update. Calling FillArticleList() again will not work either.

Firstly, if you're targeting .NET 2.0 or later then you shouldn't be using an ArrayList at all. The List(Of T) basically superseded the ArrayList and is what you should use in most cases.In this case, you can either use a List(Of T) and bind it to a BindingSource and bind that to the ListBox or else use a BindingList(Of T). Both the BindingSource and BindingList(Of T) have methods that you can call when you make changes to the list to update and bound control(s).

Related

DataBinding to List(Of CustomObject) in VB.NET

I have been simple-binding the Text property of a TextBox for some time using a DataTable like so:
Dim dtbData As New DataTable
' populate table
Me.BindingSource.DataSource = dtbData
txtBox.DataBindings.Clear()
txtBox.DataBindings.Add(New Binding("Text", Me.BindingSource, "OrderNumber", True))
BindingNavigator1.BindingSource = Me.BindingSource
However, I am trying to change this to use a List(Of CustomObject) instead. I am simply using the dtbData to populate the List and set the DataSource to the List instead. I have set a breakpoint, and the DataTable and List are both populated. If I comment out the line that adds the Binding to the txtBox, then the BindingNavigator works as expected, but if I leave it alone, the binding does not work.
Here is what I've tried:
Dim lstData As New List(Of CustomObject)
Dim dtbData As New DataTable
' populate table
For Each row As DataRow In dtbData.Rows
Dim obj As New CustomObject
obj.OrderNumber = CInt(row("OrderNumber"))
lstData.Add(obj)
Next
Me.BindingSource.DataSource = lstData
txtBox.DataBindings.Clear()
txtBox.DataBindings.Add(New Binding("Text", Me.BindingSource, "OrderNumber", True))
BindingNavigator1.BindingSource = Me.BindingSource
It seems like it should work the same. Instead of a DataTable, the BindingSource is a List(Of CustomObject) though, so I'm not sure if I have to do something differently. The property names being bound are still the same. I have also tried Me.BindingSource.Current in the Binding but that doesn't work either. Can anyone tell me what I am doing incorrectly?
The CustomObject looks like so:
Public Class CustomObject
Public OrderNumber As Integer
End Class
EDIT
I added a Try/Catch block around the binding, and I see the following error message:
Cannot bind to the property or column OrderNumber on the DataSource. Parameter name: dataMember
I just don't see what is different between binding to the OrderNumber column of a DataTable or the OrderNumber property of a CustomObject.
I realized what the problem was. The OrderNumber wasn't defined as a Property of my CustomObject. I changed the definition to this:
Public Class CustomObject
Public Property OrderNumber As Integer
End Class
And now it allows me to bind the Text property of a TextBox to the OrderNumber property of my CustomObject.

Possible to add some sort of identification onto list items?

I'm just curious if it's possible to gather a list of data and put it into a list or array and easily pull them out using some variable(like a tag property on controls) on each item in the list rather than using the index. An example would be if i have many controls on a form and would like to populate the values using the list but instead of having to cross check a lot of different indicies(would take a lot of time) i could just assign the label to some 'tag like' variable for the list item.
Is this possible?
Lists in .NET, such as the List(Of T) class, only support storing one object per item. However, the beauty of them is, you can store any type of object that you want. If you need to store metadata with your data, simply create a new class that holds all the data for each item. For instance:
Public Class MyControlData
Public Property LabelText As String
Public Property Value As String
End Class
Then you can add the items to the list like this:
Dim dataList As New List(Of MyControlData)()
Dim item As New MyControlData()
item.LabelText = "Name"
item.Value = "Bob"
dataList.Add(item)
And you can read the data from the list like this:
For Each i As MyControlData in dataList
Label1.Text = i.LabelText
TextBox1.Text = i.Value
Next
I would use a Dictionary object so you can use a key to reference your data easily.
You can use LINQ:
Dim myTagControls = From ctrl In Me.Controls.Cast(Of Control)()
Where "your-tag".Equals(ctrl.Tag)
For Each ctrl In myTagControls
Console.WriteLine("Tag:{0} Name:{1}", ctrl.Tag, ctrl.Name)
Next

Cast(Of ?) from UIElement

In silverlight my custom controls are in theUIElementCollection of my StackPanel. I want to get a list of them by a specific value. There only DivElements in the container. It returns Nothing when I know I have one or more. I know I can make a simple loop and cast types inline, but I want to get better with LINQ and Cast(Of TResult). My attempt at casting:
Dim myList = TryCast(spDivs.Children.Where(Function(o) DirectCast(o, DivElement).ElementParent Is bComm).Cast(Of DivElement)(), List(Of DivElement))
The problem is you can't cast into a List(Of DivElement). The collection is a UIElementCollection, not a List(Of T).
You could build a new list, though. This can also be simplified by using OfType instead of casting manually:
Dim myList = spDivs.Children.OfType(Of DivElement)()
.Where(Function(o) o.ElementParent Is bComm)
.ToList()

Sum the counts with Lambda expressions

I have a List(of T) where T has a property that is a list of checkboxes, what I need is a Lambda expression that will count all the checked checkboxes in the list.
I tried with:
Me.list.Sum(Function(objT) objT.CheckBoxes.Where(Function(chk) chk.Checked).Count)
But it didn't do the trick. Any suggestions?
Dim count = (From box In objtCheckBoxes Where box.Checked Select box).Count()
Somehow you must tell VB that T has a property CheckBoxes. Therefore define an interface that the types you add to the list must implement
Public Interface ICheckBoxes
ReadOnly Property CheckBoxes() As List(Of CheckBox)
End Interface
Public Class ClassWithCkeckBoxes
Implements ICheckBoxes
Private m_CheckBoxes As List(Of CheckBox) = New List(Of CheckBox)
Public ReadOnly Property CheckBoxes() As List(Of CheckBox) Implements ICheckBoxes.CheckBoxes
Get
Return m_CheckBoxes
End Get
End Property
End Class
Specify that T must implement ICheckBoxes with the type parameter (Of T As ICheckBoxes)
Class CountCheckBoxes(Of T As ICheckBoxes)
Public Sub Test(ByVal list As List(Of T))
Dim count As Integer = list _
.SelectMany(Function(t) t.CheckBoxes) _
.Count(Function(checkBox) checkBox.Checked)
End Sub
End Class
SelectMany flattens the list. I.e., it converts the list of list of checkboxes into a simple list of checkboxes.
You'll need to use SelectMany. Something like this may work.
Me.list.SelectMany(Function(objT) objT.Checkboxes)
.Count(Function(chk) chk.Checked)
At the end there was a problem populating the list which translated in an always 0 answer. Fixing the method that populated the list gave me the correct answer, which means the lambda expression per se was right all along.
P.S. sorry for all those other answers, I did read them all so it wasn't time wasted guys ;)
Dim b As New List(Of Boolean)
b.AddRange({True, False, True})
Dim n = b.Sum(Function(x)
Return If(x, 1, 0)
End Function)
n now contains 2.

Generic Lists copying references rather than creating a copiedList

I was developing a small function when trying to run an enumerator across a list and then carry out some action. (Below is an idea of what I was trying to do.
When trying to remove I got a "Collection cannot be modified" which after I had actually woken up I realised that tempList must have just been assigned myLists reference rather than a copy of myLists. After that I tried to find a way to say
tempList = myList.copy
However nothing seems to exist?? I ended up writing a small for loop that then just added each item from myLsit into tempList but I would have thought there would have been another mechanism (like clone??)
So my question(s):
is my assumption about tempList receiving a reference to myList correct
How should a list be copied to another list?
private myList as List (Of something)
sub new()
myList.add(new Something)
end sub
sub myCalledFunction()
dim tempList as new List (Of Something)
tempList = myList
Using i as IEnumerator = myList.getEnumarator
while i.moveNext
'if some critria is met then
tempList.remove(i.current)
end
end using
end sub
By writing tempList = myList you don't make a copy oh the collection, you only make tempList reference myList. Try this instead : dim tempList as new List (Of Something)(myList)
I think if you called myCalledFunction(byVal aListCopy as Something) you can let the framework do the work.
If your list consists of value types you can just create a new list with the old list passed in the constructor. If you are going to be doing a deep copy of a reference object your best bet is to have your reference type implement ICloneable (example). You can then loop through and clone each object or you could add an extension method (like this c# example).
Try this - use LINQ to create a new list from the original, for example:
Sub Main()
Dim nums As New List(Of Integer)
nums.Add(1)
nums.Add(2)
nums.Add(3)
nums.Add(4)
Dim k = (From i In nums _
Select i).ToList()
For Each number As Integer In nums
k.Remove(number)
Next
End Sub
k will then be a new list of numbers which are not linked to the source.