Resizing an array at runtime in VB.NET - vb.net

In my Windows Forms application at runtime I will be resizing an array each time I add an element. So first I must resize to the size + 1, and then add a member to this index. How do I do this?

You could use the ReDim statement, but this really isn't your best option. If your array will be changing sizes often, especially as it sounds like you're just appending, you should probably use a generic List(Of T) or similar collection type.
You can use it just like you use an array, with the addition that adding an item to the end is as easy as MyList.Add(item)
To use a generic list, add Imports System.Collections.Generics to the top of the file. Then, you would declare a new integer list like this:
Dim MyList As New List(Of Integer)()
or a string list like this:
Dim MyList As New List(Of String)()
You should get the idea.

The suggested ReDim's need the Preserve keyword for this scenario.
ReDim Preserve MyArray(n)

Using a generic list is (as suggested) the best idea. If you however want to change the size of an Array, you can use Array.Resize(ByRef arr, newSize).
ReDim is not a good (pretty bad) idea (VB specific legacy, extremely slow).

I would prefer some type of collection class, but if you WANT to use an array do it like this:
Dim arr() As Integer
Dim cnt As Integer = 0
Dim ix As Integer
For ix = 1 To 1000
cnt = cnt + 1
ReDim arr(cnt)
arr(cnt - 1) = ix
Next

You can also make your own collection class. A good programming exercise for new programmers.
Public Class MyList
Private Items() As String
Private No As Integer = 0
Public Sub Add(ByVal NewItem As String)
''Create a temporary new string array
Dim CopyString(No) As String
''Copy values from Global Variable Items() to new CopyString array
For i As Integer = 0 To No - 1
CopyString(i) = Items(i)
Next
''Add new value - NewItem - to CopyString
CopyString(No) = NewItem
''Increment No to No + 1
No += 1
''Copy CopyString to Items
Items = CopyString
'Discard CopyString
CopyString = Nothing
End Sub
Public Sub Show(ByVal index As Integer)
MsgBox(Items(index))
End Sub
End Class
''Now create a form with a TextBox name - txt, Button1 and Button2
Public Class Form1
''Declare txts as a new MyList Class
Private txts As New MyList
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
''Add text to txts which is a MyList Class
txts.Add(txt.Text)
txt.Text = ""
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
''Display value at a specific index
txts.Show(Convert.ToInt16(txt.Text))
txt.Text = ""
End Sub
End Class

Use the ReDim command to specify the new size.
ReDim MyArray(MyArray.Length + 1)

As Joel says, use a list.
Dim MyList As New List(Of String)
Don't forget to change Of String to be Of whichever datatype you're using.

This work for me
Dim Table1 As New DataTable
' Define columns
Table1.Columns.Add("Column1", GetType(System.String))
Table1.Columns.Add("Column2", GetType(System.Int32))
Table1.Columns.Add("Column3", GetType(System.Int32))
' Add a row of data
Table1.Rows.Add("Item1", 44, 99)
Table1.Rows.Add("Item2", 42, 3)
Table1.Rows.Add("Item3", 42, 3)
Table1.Rows.Add("Item4", 42, 3)
Dim arr(-1) As String
For Each dr As DataRow In Table1.Rows
ReDim Preserve arr(arr.Length)
arr(arr.Length - 1) = dr("Column1")
Next

Related

Is there a way to retreive the collection items from Textbox - AutoCompleteCustomSource in vb.net

On executing the below code, I am successfully able to insert an array values into a Textbox AutoCompleteCustomSource.
But I need to be able to read all data back from the AutoCompleteCustomSource too, and put it into an array.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox1.AutoCompleteMode = AutoCompleteMode.Suggest
TextBox1.AutoCompleteSource = AutoCompleteSource.CustomSource
Dim Arr1 As String() = {"Hello", "How", "Are", "You"}
'Below line of code puts all the array values into TxtBox.AutoCompleteCustomSource
TextBox1.AutoCompleteCustomSource.AddRange(Arr1)
'-------------Need Help on Below-------
'How to read all data from TextBox1.AutoCompleteCustomSource and bring it into an array
Dim MyArr1
MyArr1 = TextBox1.AutoCompleteCustomSource??????????????????????????????????????
End Sub
You can throw a bit of LINQ at it:
Dim items = TextBox1.AutoCompleteCustomSource.Cast(Of String)().ToArray()
The AutoCompleteCustomSource is type AutoCompleteStringCollection, which implements IEnumerable but not IEnumerable(Of T), although every item is guaranteed to be a String. That means that you can call the Cast(Of String) extension method to get an IEnumerable(Of String) and then call ToArray on that to get a String array.
Other options include this:
Dim source = TextBox1.AutoCompleteCustomSource
Dim items(source.Count - 1) As String
source.CopyTo(items, 0)
or you can go really old-school with this:
Dim source = TextBox1.AutoCompleteCustomSource
Dim upperBound = source.Count - 1
Dim items(upperBound) As String
For i = 0 To upperBound
items(i) = source(i)
Next

Listbox Sorting Issue

I am having trouble sorting a listbox the way I am expecting it to work even using the sorted = true property. Assuming i have files named as such, when i sort by "Name" in a Windows Folder view (outside of vb) it sorts like so:
1180741
1179715
1162371
1141511
1131750
1117362
1104199
1082698
1062921
1043875
991514
970621
963154
952954
948067
917669
904315
899902
892398
882024
This is how i need it to be sorted in my list. However, with the sorted = true property, vb.net decides to sort like this:
1043875
1062921
1082698
1104199
1117362
1131750
1141511
1162371
1179715
1180741
882024
892398
899902
904315
917669
948067
952954
963154
970621
991514
I cannot understand why windows sorts the list correctly but VB does not. Any help would be most appreciated.
*This example assumes that each object in the listbox is a string representation of an integer, if in the case that it is not, the item will be skipped, you can modify this to handle such a case in another way if you want....
Example:
Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
Dim r As New Random(Now.Millisecond)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'We simulated a populated listbox
For I As Integer = 0 To 1000
Dim n As Integer = r.Next(0, Integer.MaxValue)
ListBox1.Items.Add(n.ToString)
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SortListBoxNumbers(ListBox1)
End Sub
Sub SortListBoxNumbers(listbox As ListBox)
Dim numbers As New List(Of Integer)
For Each item As Object In listbox.Items
Dim n As Integer = 0
If item.GetType = GetType(String) Then
If Integer.TryParse(DirectCast(item, String), n) Then
numbers.Add(n)
End If
End If
Next
listbox.Items.Clear()
numbers = SortNumbers(numbers)
For Each n As Integer In numbers
listbox.Items.Add(n.ToString)
Next
End Sub
Function SortNumbers(numbers As List(Of Integer)) As List(Of Integer)
Dim result As New List(Of Integer)
Dim count As Integer = numbers.Count
Do
Dim highN As Integer = Integer.MinValue
For Each n As Integer In numbers
If n > highN Then highN = n
Next
numbers.Remove(highN)
result.Insert(result.Count, highN)
Loop Until result.Count = count
result.Reverse()
Return result
End Function
End Class

Adding multiple items to combo box only adds LAST ITEM

I'm trying to add multiple items to a combobox but only the last item is showing.
Example:
Dim i as Integer
For i = 0 to 3
AddItemToComboBox(i, i)
Next
Sub AddItemToComboBoxMod(ByVal itmValue, ByVal itmData)
Dim comboSource As New Dictionary(Of String, String)()
comboSource.Add(itmValue, itmData)
cbComboBox.DataSource = New BindingSource(comboSource, Nothing)
cbComboBox.DisplayMember = "Value"
cbComboBox.ValueMember = "Key"
Dim key As String = DirectCast(cbComboBox.SelectedItem, KeyValuePair(Of String, String)).Key
Dim value As String = DirectCast(cbComboBox.SelectedItem, KeyValuePair(Of String, String)).Value
End Sub
BUT at the end, only the last item "3" will be there. 0,1,2 will be missing.
Why?
You're making this too complicated.
Populate the values you want in the list.
THEN set the DataSource property ONCE. In above code, you are setting the DataSource property in each iteration of the loop.
Below is a sample event handler that I put together to demonstrate the concept.
Private Sub cmdPopulate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdPopulate.Click
Dim i As Integer
Dim comboSource As New Dictionary(Of String, String)()
For i = 0 To 3
comboSource.Add(i, i)
Next
cbComboBox.DataSource = New BindingSource(comboSource, Nothing)
cbComboBox.DisplayMember = "Value"
cbComboBox.ValueMember = "Key"
End Sub
It looks to me like you are creating a new Dictionary every time you call AddItemToComboBoxMod(). You might want to instantiate it and bind it somewhere else. The add item method should only be appending the key value pair.

Selecting variable by name

I'm not sure of the appropriate search term so please close and point me to a duplicate if that's the case.
I have a structure with has multiple variables. I've created an array of the type of the structure. I've added data to a few indexes of the array. I now want to select the a specific variable based on the name of the variable.
He's an example of the structure:
structure struc
dim name as string
dim lvl as integer
dim capacity as integer
end structure
And the deceleration of the array:
dim vills(3) as struc
You should be using public fields, not variables Actually you have the terminology correct according to MSDN, the only thing you are missing is the access modifier i.e.
Public Structure Struc
Public Name As String
Public Lvl As Integer
Public Capacity As Integer
End Structure
At the moment your variables are private which means they aren't accessible from outside your Structure.
Looking at your comments it looks like you are trying to access the property by name dynamically rather than knowing it at compile time. There are a few ways of doing this, most of which involve some Reflection.
You should perhaps have a look at ExpandObject - it's effectively a key/value dictionary with the characteristics of a normal class-type object so you get the best of both worlds e.g.
Dim struct As Object = New ExpandoObject()
struct.name = "SomeValue"
struct.lvl = 3
struct.capacity = 100
Console.WriteLine(struct["name"])
Console.WriteLine(struct["lvl"])
As Tony Hopkinson mentioned, it is possible via Reflection:
Public Class Form1
Structure struc
Dim name As String
Dim lvl As Integer
Dim capacity As Integer
End Structure
Dim vills(3) As struc
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i As Integer = 0 To vills.Length - 1
vills(i).lvl = i * 10
Next
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim fi As Reflection.FieldInfo
Dim fieldName As String = "lvl"
For i As Integer = 0 To vills.Length - 1
fi = vills(i).GetType.GetField(fieldName, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Public)
If Not IsNothing(fi) Then
Dim value As Object = fi.GetValue(vills(i))
Debug.Print(i & ": " & value.ToString)
End If
Next
End Sub
End Class
Up to you to decide if it's worth it...
I really think that if this is an important and frequent requirement (find an element by key) then you should think twice and change your array to a Dictionary(Of string, struc) and use something like this
Dim vills = new Dictionary(of String, struc)
Dim s = new struc()
s.name="k1"
s.lvl=1
s.capacity=1
z.Add(s.name, s)
....
struc c = vills("k1")
if(c IsNot Nothing) Then
Console.WriteLine(c.lvl.ToString())
End If
but if you still want to use an array you could search your struc array by name using Linq
Structure struc
Public Dim name as string
Public Dim lvl as integer
Public Dim capacity as integer
End Structure
Sub Main
Dim vills(3) as struc
....
Dim c as struc = wills.Where(Function(x) (x.name="keyName")).Single()
if(c.name IsNot Nothing) then
Console.WriteLine("Found")
End If
End Sub

ByVal does work not correctly in VB.NET

I have a code in VB.NET like this:
' This code will sort array data
Public Sub SelectionSort(ByVal array as ArrayList)
For i as Integer = 0 To array.Count -1
Dim index = GetIndexMinData(array, i)
Dim temp = array(i)
array(i) = array(index)
array(index) = temp
Next
End Sub
Public Function GetIndexMinData(ByVal array As ArrayList, ByVal start As Integer) As Integer
Dim index As Integer
Dim check As Integer = maxVal
For i As Integer = start To Array.Count - 1
If array(i) <= check Then
index = i
check = array(i)
End If
Next
Return index
End Function
' This code will sort array data
Public Sub SelectionSortNewList(ByVal array As ArrayList)
Dim temp As New ArrayList
' Process selection and sort new list
For i As Integer = 0 To array.Count - 1
Dim index = GetIndexMinData(array, 0)
temp.Add(array(index))
array.RemoveAt(index)
Next
End Sub
Private Sub btnProcess_Click(sender As System.Object, e As System.EventArgs) Handles btnProcess.Click
Dim data as new ArrayList
data.Add(3)
data.Add(5)
data.Add(1)
SelectionSort(data)
SelectionSortNewList(data)
End Sub
When I run this code, in the btnProcess event click, variable "data" is array = {3,5,1}. By SelectionSort(data) procedure, variable data is changed. Item in variable data have sorted by that procedure, so when SelectionSortNewList(data) is run, array "data" have sorted became {1,3,5}. Why does it happen?
Although I have used "Byval parameter" in SelectionSort and SelectionSortNewList, I don't want variable data to be changed when it pass to SelectionSort.
Even if you use a ByVal on an object, the properties of the object can be modified.
The instance of the object cannot be modified but not its properties.
Example:
Public Class Cars
Private _Make As String
Public Property Make() As String
Get
Return _Make
End Get
Set(ByVal value As String)
_Make = value
End Set
End Property
End Class
If I pass the class as ByVal;
Private sub Test(ByVal MyCar as Car)
MyCar.Make = "BMW"
End Sub
The value of the property will change as you're pointing to the same object and modifying its property.