For each loop, compare to next item on the list - vb.net

I am writing an application where I bring change history of items from the database and place them on the table using For Each loop. I would, however, like to show in table what information has changed in each edit. Is it possible to compare variables of each item to the variables of next loop in For Each loop?
Something like:
For Each k As Examplemodel In Model
'Find next item on the loop after current one somehow
Dim nextItem = Model.Item(k+1) 'something like this
If k.ItemsName <> nextItem.Itemsname 'if the name has changed in edit
'show result in bold
Else
'show result in normal font weight
End If
Next
Is this possible and if not, what's the best way to achieve this?

You can't do it in a foreach loop directly.
If your Model class have indexers you can easily convert it into a for loop:
If Model.Count > 1 Then
For i as Integer = 0 to Model.Count - 2 ' Note the -2 here !!!
Dim Item As Examplemodel = Model(i)
Dim NextItem As Examplemodel = Model(i + 1)
if Item.ItemsName <> NextItem.ItemsName then
'show result in bold
else
'show result in normal font weight
end if
Next
'show result of NextItem here, since the last item doesn't get shown in the loop
Else
'show result of only item here
End If
If not, you can use a workaround like this:
Dim PrevItem as Examplemodel = Nothing ' Assuming a reference type
For Each k As Examplemodel In Model
If Not IsNothing(PrevItem) AndAlso k.ItemsName <> Prev.Itemsname 'if the name has changed in edit
'show result (of PrevItem!!!) in bold
Else
'show result (of PrevItem!!!) in normal font weight
End If
PrevItem = k
Next
'show result (of PrevItem (currently the last item in Model) in normal font weight

You should use a normal for loop:
Dim numbers() As Integer = New Integer() {1, 2, 4, 8}
Sub Main()
For index As Integer = 0 To numbers.Length - 2
Dim currentInt As Integer = numbers(index)
Dim nextInt As Integer = numbers(index + 1)
Console.WriteLine(currentInt)
Console.WriteLine(nextInt)
Next
End Sub

Another approach to use LINQ Aggregate extension method, which use first item of collection as initial value. So every item will have access to previous one.
Public Class ItemChanges
Public Property Item As ExampleModel
Public Property Changes As New Hash(Of String)
End Class
Public Function Check(previous As ItemChanges, current As ItemChanges) As ItemChanges
If current.Item.Name <> previous.Item.Name Then
current.Changes.Add(Nameof(current.Name))
End
Return current
End Function
' assume model is collection of items
Dim itemWithChanges =
model.Select(Function(m) New ItemChanges With { .Item = m })
.Aggregate(AddressOf(Check))
.ToList()
Then you can use calculated result as you want - every item will have a hash of property names which had changed
If checkedItem.Changes.Contains(Nameof(checkedItem.Item.Name)) Then
' use bold font or different color
End

Related

Listbox returns index as integer, but I want the current string value

In this case, I want to store in the txtCountryQuery variable the currently selected country with the currentCountry variable name as String, for example, Brazil. But not, the number of the list index, I want to store it but as text, returning the corresponding country. Attached is part of my code.
Code:
'other subroutine
lbCountry.List = regionArray
lbCountry.ListIndex = 0
End Sub
'''Main code:
Private Sub lbCountry_Click()
Dim currentCountry As String
currentCountry = lbCountry.ListIndex
rsRegion.MoveFirst
rsRegion.Move (currentCountry)
txtCountryQuery = currentCountry
End Sub
You will need to cycle through the items in the list box. You grab the actual content, by reading out the columns that are there. Here an example that would add the items to a dictionary object:
For i = 0 To lbCountry.ListCount - 1
If Not dict.Exists(lbCountry.Column(1, i)) Then
dict.Add lbCountry.Column(1, i), i
End If
Next i

Using selected values in an Excel list box to formulate the legend

I am attempting to pass the selected values from a list box in Excel to legend in a chart. Specifically, I have data of certain companies in the following format
And I also have a list box, globalList, which contains the names of companies that can be selected. Selected companies' data will then be passed onto a chart using VBA.
However, the problems I encounter are in the following sections:
Initialising a variable to hold values selected in the globalList
listMax = globalList.ListCount - 1
`this creates the upper bound for the list box
For i = 0 To (globalList.ListCount - 1)
If globalList.Selected(i) = True Then
companiesSelected = companiesSelected + 1
End If
If i = listMax Then
Exit For
End If
Next i
`the above is used to retrieve the number of companies that have been selected by a user - whether =0 or > 0
Dim myLegend() As String
ReDim myLegend(0 To (globalList.ListCount - 1))
For i = 0 To (globalList.ListCount - 1)
If globalList.Selected(i) = True Then
myLegend(i) = globalList.List(i)
End If
If i = listMax Then
Exit For
End If
Next i
`this is the array object in which I intend to store company names selected in the list box.
The problem is that even though the above creates the myLegend string array, it also contains empty array items for the companies that may not have been selected by the user in the list box.
And even if I am able to remove these empty items from the array, the following problem occurs
Passing the held values from my variable to my chart
For i = 1 To companiesSelected
myChart.SeriesCollection(i).Name = myLegend(i)
Next i
Problem here is that myLegend array starts from 0, while SeriesCollection seems to start from 1. So I am unable to pass the string values for selected items to the legend of my chart's.
Could somebody please point out how to circumvent these problems?
Many thanks in advance!
Here is a code to extract the selected items into an one-based array of String (without empty items):
Dim i As Integer
Dim iCount As Integer
Dim myLegend() As String
iCount = 0
With globalList
ReDim myLegend(1 To .ListCount)
For i = 0 To .ListCount - 1
If .Selected(i) = True Then
iCount = iCount + 1
myLegend(iCount) = .List(i)
End If
Next i
End With
If iCount > 0 Then
ReDim Preserve myLegend(1 To iCount)
Else
ReDim myLegend(1 To 1)
myLegend(1) = "Nothing here!"
End If

String.contains not working

I'm trying to filter a list based on input from a textbox. If the item doesn't contain the string, it is deleted from the list. Here is my subroutine:
Sub filterlists(filter As String)
Dim removalDifferential As Integer = 0
For colE As Integer = 0 To RadListView1.Items.Count
Try
Dim itemEpp As ListViewDataItem = Me.RadListView1.Items(colE)
Dim jobname As String = itemEpp(0)
If Not jobname.Contains(filter) Then
' MsgBox(jobname & " Contains " & filter)
RadListView1.Items.RemoveAt(colE - removalDifferential)
removalDifferential = removalDifferential + 1
End If
Catch
End Try
Next
End Sub
Currently this is not deleting the correct items. The TRY is there because when you delete an item the list index changes (which means the for loop length is wrong and will throw outofbounce errors). Any other loop options that will work here?
Assuming you really do want to delete any LVI which simply contains the filter text, you should loop backwards thru the items (any items, not just Listview items) so the index variable will in fact point to the next correct item after a deletion:
For n As Integer = RadListView1.Items.Count-1 to 0 Step -1
If radListView1.Items(n).Text.Contains(filter) Then
RadListView1.Items.RemoveAt(n)
End If
Next

Concatenate Variable Names in VB

I have a large window with a little under 200 controls/variables to worry about. Many of them are similar, so I am wondering if instead of repeatedly calling each one individually I can concatenate their names.
I'll make an example:
I have 5 pieces of data that I'm worried about: Red, Orange, Yellow, Green, Blue.
Each one of these will have a label that needs to be made visible, a textbox that needs to be made visible, and a string that contains the text in the textbox:
lblRed.Visible = True
txtRed.Visible = True
strRed = txtRed.Text
Instead of listing this for every one of those 5 pieces of data, is there a way that I could make some sort of array to loop through that could concatenate these variable names?
Dim list As New List(Of String)(New String() {"Red", "Orange", "Yellow", "Green", "Blue"})
Dim i As Integer = 0
Do while i < list.count
lbl + list(i) + .Visible = True
txt + list(i) + .Visible = True
str + list(i) = txt + list(i) + .Text
i = i+1
Loop
I know that the above code doesn't work, but I wanted to give you the basic idea of what I wanted to do. Does this look feasible?
http://msdn.microsoft.com/en-us/library/7e4daa9c(v=vs.71).aspx
Using the controls collection:
Dim i As Integer
i = 1
Me.Controls("Textbox" & i).Text = "TEST"
so
Me.controls("lbl" & list(i)).Visible = true
Keep in mind that when concatenating items, '+' will work on strings and not integers. You might want to always use '&' when concatenating
Another way is to use a select-case block for each type of control. Something like this:
Private Sub EnableControls()
For Each c As Control In Me.Controls
Select Case c.GetType
Case GetType(TextBox)
c.Visible = True
Select Case c.Name.Substring(3)
Case "Red"
strRed = c.Text
Case "Orange"
strOrange = c.Text
Case "Yellow"
'and so on
End Select
Case GetType(Label)
c.Visible = True
End Select
Next
End Sub
Check out the information here:
http://msdn.microsoft.com/en-us/library/axt1ctd9.aspx
I'm sure someone may find something slightly more eloquent than this, but this should achieve your desired result. You'll need the below import:
Imports System.Reflection
If you use properties to access your variables, you can use reflection to grab them by name:
'Define these with getters/setters/private vars
Private Property strRed as String
Private Property strOrange as String
Private Property strYellow as String
Private Property strGreen as String
Private Property strBlue as String
For each color as String in list
If Me.Controls.Count > 1 Then
'Should really check for existence here, but it's just an example.
Me.Controls("lbl" & color).Visible = True
Dim tbx as TextBox = Me.Controls("txt" & color)
tbx.Visible = True
Dim propInfo as PropertyInfo = Me.GetType.GetProperty("str" & color)
propInfo.SetValue(Me, tbx.Text, Nothing)
End If
Next

Multi Select List Box

I have a list box on a form and it works fine for what I want to do.
I am wanting to edit items on the form, this means populating the listbox and then selecting the relevant items.
My listbox contains a list of item sizes, i want to select the sizes which belong to the item being edited.
PLease can someone give me some pointers.
I tried me.lstItemSizes.SetSelected(i,true) but this only works for a single item.
Any help wil be much appreciated.
My Code:
Private Sub SelectItemSizes(ByVal itemID As Integer)
Dim itemSizes As IList(Of ItemSize) = _sizeLogic.GetItemSizes(itemID)
Me.lstItemSizes.SelectionMode = SelectionMode.MultiExtended
If (itemSizes.Count > 0) Then
For i As Integer = 0 To Me.lstItemSizes.Items.Count - 1
For x As Integer = 0 To itemSizes.Count - 1
If (CType(Me.lstItemSizes.Items(i), PosSize).SizeID = itemSizes(x).SizeID) Then
Me.lstItemSizes.SetSelected(i, True)
Else
Me.lstItemSizes.SetSelected(i, False)
End If
Next
Next
End If
End Sub
Did you set the selectionmode to multi?
You need to specify that in order to allow multiple selections.
Then you can do:
Dim i as Integer=0
For i=0 To Me.listBox.SelectedItems.Count -1
'display the listbox value
next i
Here is a screen shot:
After you set the property on the listbox then call setselected based on the values you want selected.
me.lstItemSizes.SetSelected(3,true)
me.lstItemSizes.SetSelected(4,true)
me.lstItemSizes.SetSelected(9,true)
Here you can add 20 numbers and only select the even.
Dim i As Integer
'load the list with 20 numbers
For i = 0 To 20
Me.ListBox1.Items.Add(i)
Next
'now use setselected
'assume only even are selected
For i = 0 To 20
If i Mod 2 = 0 Then
Me.ListBox1.SetSelected(i, True)
End If
Next
3rd edit
Look at the way you are looping, lets assume I create a list of integers, my vb.net is rusty I mainly develop in C#. But assume you did this:
Dim l As New List(Of Integer)
l.Add(2)
l.Add(6)
l.Add(20)
You only have three items in your list, so first loop based on the items on your list, then within the items in your listbox, you have it vice versa. Look at this:
Dim i As Integer
Dim l As New List(Of Integer)
l.Add(2)
l.Add(6)
l.Add(20)
'load the list with 20 numbers
For i = 0 To 20
Me.ListBox1.Items.Add(i)
Next
Dim lCount As Integer = 0
For lCount = 0 To l.Count - 1
For i = 0 To 20
If i = l.Item(lCount) Then
Me.ListBox1.SetSelected(i, True)
Exit For
End If
Next
Next
In the code my l is a list of just 3 items: 2, 6, and 20.
I add these items to l which is just a list object.
So now I have to loop using these 3 numbers and compare with my listbox. You have it the opposite you are looping on your listbox and then taking into account the list object.
Notice in my for loop that once the item in my list is found I no longer need to loop so I exit for. This ensures I dont overdue the amount of looping required. Once the item is found get out and go back to the count of your list object count.
After running my code here is the result
You have to change the ListBox.SelectionMode property in order to enable multiple-selection.
The possible values are given by the SelectionMode enum, as follows:
None: No items can be selected
One: Only one item can be selected
MultiSimple: Multiple items can be selected
MultiExtended: Multiple items can be selected, and the user can use the Shift, Ctrl, and arrow keys to make selections
So, you simply need to add the following line to the code you already have:
' Change the selection mode (you could also use MultiExtended here)
lstItemSizes.SelectionMode = SelectionMode.MultiSimple;
' Select any items of your choice
lstItemSizes.SetSelected(1, True)
lstItemSizes.SetSelected(3, True)
lstItemSizes.SetSelected(8, True)
Alternatively, you can set the SelectionMode property at design time, instead of doing it with code.
According to MSDN, SetSelected() can be used to select multiple items. Simply repeat the call for each item that needs to be selected. This is the example they use:
' Select three items from the ListBox.
listBox1.SetSelected(1, True)
listBox1.SetSelected(3, True)
listBox1.SetSelected(5, True)
For reference, this is the MSDN article.
Because my code had the following loops:
For i As Integer = 0 To Me.lstItemSizes.Items.Count - 1
For x As Integer = 0 To itemSizes.Count - 1
If (CType(Me.lstItemSizes.Items(i), PosSize).SizeID = itemSizes(x).SizeID) Then
Me.lstItemSizes.SetSelected(i, True)
Else
Me.lstItemSizes.SetSelected(i, False)
End If
Next
Next
The first loop loops through the available sizes and the second loop is used to compare the item sizes.
Having the following code:
Else
Me.lstItemSizes.SetSelected(i, False)
End If
Meant that even if item i became selected, it could also be deselected.
SOLUTION:
Remove Me.lstItemSizes.SetSelected(i, False) OR Include Exit For