Accessing Public Variant Array from Module - arrays

Say I have two files Form1.frm and Module1.bas in a Visual Basic 6 project.
Form1.frm:
Public myArray As Variant
Private Sub Form_Load()
ReDim myArray(2)
Call PopulateArrays
End Sub
Module1.bas:
Public Sub PopulateArrays()
Form1.myArray(0) = Array(1, 2, 3)
Form1.myArray(1) = Array(4, 5, 6)
Form1.myArray(2) = Array(7, 8, 9)
End Sub
The issues is that once the code runs, 'myArray' remains empty.
If I put the PopulateArrays Sub within the main Form1.frm the arrays do populate.
Any thoughts?

There are certain things you can't declare in an object module (userforms are just special object modules) and one of them is arrays. If you had declared
Public myArray() As Variant
Then you would have got a compile error that said as much and would have known. But since you declared as Variant, the compiler didn't complain, but the assignment just doesn't work.
One option is to make the Variant array private and use property statements (this is VBA, but should be the same for VB6). In the userform
Private mmyArray() As Variant
Public Property Get myArray() As Variant
myArray = mmyArray
End Property
Public Property Let myArray(lmyArray As Variant)
mmyArray = lmyArray
End Property
Private Sub UserForm_Click()
MsgBox Join(Me.myArray(0), "_")
End Sub
Private Sub UserForm_Initialize()
ReDim mmyArray(2)
PopulateArrays
End Sub
And in a standard module
Public Sub PopulateArrays()
Dim ar(0 To 2) As Variant
ar(0) = Array(1, 2, 3)
UserForm1.myArray = ar
End Sub

I see you've used a workaround to let your Form1 hold a public array. However, I think a better solution would be: (A) move the array to the module and make it public (B) create accessor/mutator functions for it.
If you aren't keeping multiple instances of the form at the same time, then (A) is the simplest solution.
Here is the code I tested for (A),
Form:
Private Sub UserForm_Initialize()
Call PopulateArrays
Call displayArray
End Sub
Private Sub displayArray()
Dim v1 As Variant
Dim s As String
For Each v1 In myArray
s = s + Join(v1, ", ") + vbNewLine
Next v1
MsgBox s
End Sub
Module:
Dim form1 As UserForm1
Public myArray As Variant
Public Sub start()
Set form1 = New UserForm1
End Sub
Public Sub PopulateArrays()
ReDim myArray(2)
myArray(0) = Array(1, 2, 3)
myArray(1) = Array(4, 5, 6)
myArray(2) = Array(7, 8, 9)
End Sub

Related

UserForm variable scope: transfer 2D array values from userform2 to userform1

I have a problem transferring a 2D array between two userforms.
When I click on a CommandButton in userform1, it will open userform2. Then I click on CommandButton in userform2 for creating a 2D array. After this I terminate userform2 and want to transfer my 2D array into userform1.
My best try is calling a function in userform1 click event. I put this function into the userform2 module. But my userform2's function doesn't see 2D array from another subs in userform2. Private Sub userform_terminate() can see this 2D-array which was created in Private Sub CommandButton1_Click() but my function doesn't.
userform1:
Private Sub CommandButton1_Click()
dim results()
results = userform2.get2dArray()
End Sub
userform2:
Private myArray()
Private Sub CommandButton1_Click()
ReDim myArray(1 To 2, 1 To 2)
myArray(1, 1) = "arg1"
myArray(2, 1) = "arg2"
myArray(1, 2) = "arg3"
myArray(2, 2) = "arg4"
End Sub
Private Sub userform_terminate()
'here i can see every args in myArray
...
end sub
Function get2dArray()
'that function I called from userform1
userform2.show vbModal
get2dArray = myArray 'but myArray is empty
End Function
I want to transfer myArray from userform2 back to the main form userform1.
The main problem is userform2.get2dArray doesn't see the private variable myArray in userform2 module.
Making myArray global is also impossible.
Use a public function in a standard module (not in a userform) which takes an optional parameter (your 2D array).
The parameter is then stored in the function as a static variable. The next time the function is called, if the parameter is missing, then return the stored static variable. Here is the example:
Public Function store2DArray(Optional my2DArray As Variant) As Variant
Static storedArray As Variant
If IsMissing(my2DArray) Then
store2DArray = storedArray
Else
storedArray = my2DArray
End If
End Function
The usage would then be like this to store the array:
Sub Userform2Button1_Click()
store2DArray myArray
End Sub
This is how you would retrieve the array:
Sub Userform1Button2_Click()
myArray = store2DArray
End Sub

VBA - Public Array Error - Subscript out of range

I want to declare a public array, create it and then use it in another sub.
This is exapmle of what I wrote:
Public array1() As String
Sub Create_Array()
Dim array1(1 To 4) As String
array1(1) = "1"
array1(2) = "2"
array1(3) = "A"
array1(4) = "B"
End Sub
Sub Show_Some_Index()
Dim a As String
a = array1(1)
MsgBox (a)
End Sub
I get Error 9: "Subscript out of range".
Couldn't find an answer, what am I doing wrong?
Variable array1() in Sub Create_Array is scoped to that procedure - basically it's a local variable that's only ever accessible within that procedure, and it happens to have the same name as another public field declared elsewhere, so what's happening is that Show_Some_Index is working off an array that hasn't been initialized yet.
Dim is used for declaring variables. If you mean to re-dimension an array that's in-scope, use the ReDim keyword.
A better approach would be to use a function that returns the array, instead of relying on global variables.
I want to declare a public array, create it and then use it in another sub.
In that case remove the Dim Statement from your code. Further to what Mat explained, here is another way to make your code work
WAY 1
Public array1(1 To 4) As String
Sub Create_Array()
array1(1) = "1"
array1(2) = "2"
array1(3) = "A"
array1(4) = "B"
Show_Some_Index
End Sub
Sub Show_Some_Index()
Dim a As String
a = array1(1)
MsgBox (a)
End Sub
WAY 2
Public array1(1 To 4) As String
Sub Create_Array()
array1(1) = "1"
array1(2) = "2"
array1(3) = "A"
array1(4) = "B"
End Sub
Sub Show_Some_Index()
Create_Array
Dim a As String
a = array1(1)
MsgBox (a)
End Sub
Once you initialize it, you should be able to use it in other procedures.

Making an array of string in VB

I'm trying to make an array of string, but can't get it to run correctly. Here is what I have.
Public Class Form1
Dim wordArray() As String
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'increase the size of string array by one, by setting the new upperBound at the current Length
'use Preserve so that string currently in the array are not overwritten with Nothing
ReDim Preserve wordArray(wordArray.Length)
'use an TextBox to get the name of the new string from the user
'assign this name (which is a String) to the last element of the string array
wordArray(wordArray.GetUpperBound(0)) = TextBox2.Text
End Sub
End Class
Any help will be appreciated, thank you.
How about a List(Of String)? No ReDim required here.
Private wordList As New List(Of String)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
wordList.Add(textbox2.Text)
End Sub
For Fixed-size array:
Dim strCDRack(0 to 2) As String
strCDRack(0) = "Deftones"
strCDRack(1) = "Tool"
strCDRack(2) = "Disturbed"
For Dynamic array:
Dim strCDRack() As String
ReDim strCDRack(0 to 2) As String
strCDRack(0) = "Deftones"
strCDRack(1) = "Tool"
strCDRack(2) = "Disturbed"
For expanding the Dynamic array:
Dim strCDRack() As String
ReDim strCDRack(0 to 2) As String
strCDRack(0) = "Deftones"
strCDRack(1) = "Tool"
strCDRack(2) = "Disturbed"
ReDim Preserve strCDRack(0 to 3) As String
strCDRack(3) = "Charlotte Church"
For more info about VB arrays checkout this link..
Use a list!
Dim StringArray As New List(Of [String])()
And in the click handler:
StringArray.Add(TextBox1.Text)
You should use Collections here like List(Of String), it grows as the number of element grows. No need to maintain size yourself. Also the problem might at your end is, when you ReDim try to increase your array size.
ReDim Preserve wordArray(wordArray.Length + 1)

Get all posible combinations of array of arrays in vb.net

I have several arrays in VB.net. These arrays have different lengths and I need the combination between them. The simple solution is using nested loop as in the example:
Dim Array1= {10, 11, 12}
Dim Array2= {15}
Dim Array3= {1,2,3}
Dim array(2) As Object
array(0) = Array1
array(1) = Array2
array(2) = Array3
for (a = 1 to < Array1.Length - 1)
for (b = 1 to < Array2.Length - 1)
for (c = 1 to < Array3.Length - 1)
'Get combination
Next
Next
Next
Output: {10,15,1},{10,15,2},{10,15,3},{11,15,1},{11,15,2},...
But the real problem is that the number of arrays is not a fixed parameter (could be 3 as in the example or any other number) and therefore nested loops is not a solution.
Any idea?
Here's a quick rewrite of some code I wrote almost a decade ago while still using VB.Net 2003...what great memories lying around in my hard drive!
Hope you find its output useful:
10,15,1
10,15,2
10,15,3
11,15,1
11,15,2
11,15,3
12,15,1
12,15,2
12,15,3
The code that generated it:
Public Class Form1
Private WithEvents FC As ArrayCombinations = Nothing
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If IsNothing(FC) Then
FC = New ArrayCombinations
FC.AddArray(New Integer() {10, 11, 12})
FC.AddArray(New Integer() {15})
FC.AddArray(New Integer() {1, 2, 3})
FC.GenerateCombos()
End If
End Sub
Private Sub FC_combinations(combos As System.Collections.Generic.List(Of System.Collections.Generic.List(Of Integer))) Handles FC.Combinations
For Each combo As List(Of Integer) In combos
Debug.Print(String.Join(",", combo.Select(Function(i) i.ToString())))
Next
FC = Nothing
End Sub
End Class
Public Class ArrayCombinations
Private Arrays As New List(Of Integer())
Private WithEvents BGW As New System.ComponentModel.BackgroundWorker
Public Event Combinations(ByVal combos As List(Of List(Of Integer)))
Public Sub New()
BGW.WorkerReportsProgress = True
End Sub
Public Sub AddArray(ByVal values() As Integer)
If Not BGW.IsBusy Then
Arrays.Add(values)
End If
End Sub
Public Sub GenerateCombos()
If Not BGW.IsBusy Then
BGW.RunWorkerAsync()
End If
End Sub
Private Sub BGW_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BGW.DoWork
Dim sizes As New List(Of Integer)
Dim combinations As Integer
Dim remainder As Integer
Dim quotient As Integer
Dim combination As List(Of Integer)
Dim combos As New List(Of List(Of Integer))
If Arrays.Count > 0 Then
combinations = 1
For Each factor() As Integer In Arrays
sizes.Add(factor.Count)
combinations = combinations * factor.Count
Next
For i As Integer = 0 To combinations - 1
combination = New List(Of Integer)
For j As Integer = 1 To Arrays.Count
combination.Add(Nothing)
Next
quotient = i \ sizes.Item(sizes.Count - 1) ' Integer Division
remainder = i Mod sizes.Item(sizes.Count - 1)
combination(Arrays.Count - 1) = Arrays.Item(Arrays.Count - 1)(remainder)
For j As Integer = (sizes.Count - 2) To 0 Step -1
combination.Item(j) = Arrays.Item(j)(quotient Mod sizes.Item(j))
quotient = quotient \ sizes.Item(j) ' Integer Division
Next
combos.Add(combination)
Next
e.Result = combos
End If
End Sub
Private Sub BGW_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BGW.RunWorkerCompleted
RaiseEvent Combinations(e.Result)
End Sub
End Class
You can create a list to hold the combined values from all the arrays and then push those values back into an array, like this:
Dim listCombined = New List(Of Integer)
listCombined.AddRange(array1)
listCombined.AddRange(array2)
listCombined.AddRange(array3)
Dim arrayCombined As Integer() = listCombined.ToArray()
Note: This example assumes you have integer arrays, but you can change the type to whatever the arrays actually hold. You can also loop through the arrays instead of listing them one by one for the AddRange calls.

Null Reference Exception - Read from CSV

I have to code a WPF application for college which reads from a csv file. I get a null reference exception when I want to output the parts of the CSV lines into arrays. You can find the line where the error happens in commentary. Here is the code.
Imports System.Windows.Forms
Imports System.IO
Imports System.Globalization
Class MainWindow
Private foldername As String
Private arrGemeenten As String()
Private arrOppervlakte As Double()
Private arrInwoners As Integer()
Private arrDeelgemeenten As Integer()
Private Sub cboProvincie_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles cboProvincie.SelectionChanged
CSVInlezen()
End Sub
Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
FileBrowserAanmaken()
comboBoxVullen()
End Sub
Private Sub comboBoxVullen()
For Each file As String In IO.Directory.GetFiles(foldername)
If file.EndsWith(".csv") Then
Dim filenaam As String = System.IO.Path.GetFileNameWithoutExtension(file)
cboProvincie.Items.Add(filenaam)
End If
Next
End Sub
Private Sub FileBrowserAanmaken()
'folderbrowserdialog aanmaken
Dim fbd As New FolderBrowserDialog
fbd.SelectedPath = AppDomain.CurrentDomain.BaseDirectory
' Show the FolderBrowserDialog.
Dim result As DialogResult = fbd.ShowDialog()
If (result = Forms.DialogResult.OK) Then
foldername = fbd.SelectedPath
End If
End Sub
Private Sub CSVInlezen()
Dim filepath As String = foldername & "\" & cboProvincie.SelectedValue & ".csv"
If File.Exists(filepath) Then
fileInlezenHulpMethode(filepath)
End If
End Sub
Private Sub fileInlezenHulpMethode(ByVal path As String)
'declarations
Dim sr As New StreamReader(path)
Dim iTeller As Integer = 0
Dim arrLijn As String()
Dim culture As New System.Globalization.CultureInfo("nl-BE")
'eerste lijn meteen uitlezen, dit zijn kolomkoppen en hebben we niet nodig
'read out first line, these are titles and we don't need them
sr.ReadLine()
Do While sr.Peek <> -1
Dim lijn As String = sr.ReadLine()
arrLijn = lijn.Split(";")
arrGemeenten(iTeller) = Convert.ToString(arrLijn(0)) 'HERE I GET THE ERROR!
arrOppervlakte(iTeller) = Double.Parse(arrLijn(2), NumberStyles.AllowDecimalPoint, culture.NumberFormat)
arrInwoners(iTeller) = Integer.Parse(arrLijn(3), NumberStyles.Integer Or NumberStyles.AllowThousands, culture.NumberFormat)
arrDeelgemeenten(iTeller) = Convert.ToString(arrLijn(4))
Loop
End Sub
End Class
You haven't created the array, you have only created a reference for it. To create the array you need to specify a size, for example:
Private arrGemeenten As String(100)
However, to specify the size, you need to know the size when you create the array. (Well, actually you put all data in the first item, so just the size 1 would keep it from crashing, but I don't thing that's what you intended.) You probably want to use lists instead:
Private gemeenten As New List(Of String)()
Then you use the Add method to add items to the list:
gemeenten.Add(Convert.ToString(arrLijn(0)))
Also, consider putting the data in a single list of a custom object, instead of having several lists of loosely coupled data.

Resources