VBA excel passing an array to sub fired by button - arrays

As in title I would like to pass an array to the sub which is executed by a button (That's why I don't know ho to pass it to the sub). Array is calculated in the Sheet before you can exectue sub with the button.
I've tried using:
Option Explicit On
Public My_array(1 to 10)
But I have received an error that I can not do that command with array, const etc.
What is the correct way to do it?

I think there is some terminology we can try to clear up: you're not actually passing the variable to the button's procedure. A publicly scoped variable is going to be available within that procedure and does not need to be "passed".
Note: I'm not sure what you mean by "Array is calculated in the Sheet". Unless you are using some event procedure to calculate an array in memory (probably you are not, because you don't have the array scoped or declared properly to do this...), this statement can't possibly be true.
Your declaration should be like:
Public myArray(1 To 10) As Variant 'or As String, As Double, etc.
You can omit the As ... part and it will be type Variant. if you need more strongly-typed array, then you will need to specify.
Also, On is not a legal keyword after the Option Explict statement. So, remove that. But keep Option Explicit in all of your modules.
The error message you receive hints at the solution: You can't put public declarations in object modules. An object module is a Worksheet, Workbook, UserForm, or Class module in the VBE.
You can only put these declarations in a normal code module.
Solution: Move the declaration of myArray to a normal code module.

Related

Excel VBA: Passing Array between subs [duplicate]

VBA question. I'm using it to make Solidworks macros, but that's not important.
Can someone explain the syntax to pass an array (1-Dimensional of type Double, with length of three) to a subroutine or function so I can modify the contents. I know that they have to be passed by reference. I'm talking exact syntax, because everything I try I get type mismatch error on the subroutine/function call.
All I'm looking for is the correct Dim statement for the array of Doubles, initialize statement to make all array values zero, then the subroutine call statement and subroutine header that I would need. Please be specific if I need to use variant type or dynamic array, even when I already know the type and size of the array. Use a function or a sub, I don't care which.
My code works fine as of now, but I'm tired of avoiding function and subroutine calls when I'm using arrays. I've looked at countless documentation and similar questions on here, but I just cannot figure it out. Thanks a lot.
This is fairly trivial, using the ByRef keyword in the function signature will pass your array by reference rather than by value. This means that manipulations to that array will be preserved and bubble up to the calling scope. This is probably an oversimplification, but think of it this way:
ByRef: you're passing a reference to the thing (array, Object, etc.) and any transformations to that thing will be apparent anywhere that thing exists.
ByVal: you're passing the value representation of the thing. Essentially, you're passing a "copy" of it. You can manipulate this object, but it is not the same object from the calling scope, it's just a copy. So when the enclosing scope ends, you're still left with only the original thing.
Initialize your array as a numeric type and that will give you default 0 values.
Example as follows:
Option Explicit
Sub foo()
Dim myArray(1 To 3) As Double 'The array is initialized with zero-values
Call bar(myArray)
MsgBox myArray(3)
End Sub
Sub bar(ByRef dblArray() As Double)
dblArray(1) = 123
dblArray(2) = 345
dblArray(3) = 33456
End Sub
By default, arrays are passed by reference. I've seen in a few places that passing "ByVal" throws an exception. However, I have been able to use ByVal without issue in O365.

VBA Pass Array By Reference and Modify Contents

VBA question. I'm using it to make Solidworks macros, but that's not important.
Can someone explain the syntax to pass an array (1-Dimensional of type Double, with length of three) to a subroutine or function so I can modify the contents. I know that they have to be passed by reference. I'm talking exact syntax, because everything I try I get type mismatch error on the subroutine/function call.
All I'm looking for is the correct Dim statement for the array of Doubles, initialize statement to make all array values zero, then the subroutine call statement and subroutine header that I would need. Please be specific if I need to use variant type or dynamic array, even when I already know the type and size of the array. Use a function or a sub, I don't care which.
My code works fine as of now, but I'm tired of avoiding function and subroutine calls when I'm using arrays. I've looked at countless documentation and similar questions on here, but I just cannot figure it out. Thanks a lot.
This is fairly trivial, using the ByRef keyword in the function signature will pass your array by reference rather than by value. This means that manipulations to that array will be preserved and bubble up to the calling scope. This is probably an oversimplification, but think of it this way:
ByRef: you're passing a reference to the thing (array, Object, etc.) and any transformations to that thing will be apparent anywhere that thing exists.
ByVal: you're passing the value representation of the thing. Essentially, you're passing a "copy" of it. You can manipulate this object, but it is not the same object from the calling scope, it's just a copy. So when the enclosing scope ends, you're still left with only the original thing.
Initialize your array as a numeric type and that will give you default 0 values.
Example as follows:
Option Explicit
Sub foo()
Dim myArray(1 To 3) As Double 'The array is initialized with zero-values
Call bar(myArray)
MsgBox myArray(3)
End Sub
Sub bar(ByRef dblArray() As Double)
dblArray(1) = 123
dblArray(2) = 345
dblArray(3) = 33456
End Sub
By default, arrays are passed by reference. I've seen in a few places that passing "ByVal" throws an exception. However, I have been able to use ByVal without issue in O365.

VBA Passing Class Arrays to Functions

Apologies if I am making some amateur mistakes, but I am new to VBA.
I'm trying to populate an array declared inside of a class as a property, but something's going wrong. After much searching I have two questions that I can't seem to find anywhere else:
1 - Is there a simpler way to accomplish saving data to an array-like structure that I can pass to functions from a sub? The array-like structure needs to be resizable because I will not know how many components I will add until each iteration of a loop checks those conditions.
2 - How do I correctly accomplish the passing of the array property in my class correctly to another function? It's frustrated me enough that I would like to know how it can be done if only to understand what I was doing wrong or, perhaps, what misunderstanding I had of the way VBA works.
Code structure follows:
I have declared a Segments property inside of a CTask class like this:
Private pSegments() As CSegment
Public Property Get Segments() As CSegment()
Segments = pSegments()
End Property
Public Property Get segment(index As Integer) As CSegment
segment = pSegments(index)
End Property
Public Property Let Segments(Value() As CSegment)
pSegments() = Value()
End Property
I pass the CTask from a Sub, where it is defined to populateTasks using this code:
Dim tasks() As CTask
ReDim tasks(1 To 10)
Call populateTasks(tasks)
When I do this, the populateTasks code receives it using the following code:
Function populateTasks(ByRef tasks() As CTask)
I then try to call another function from populateTasks called populateSegments like this:
Call populateSegments(tasks(icount).Segments)
I receive segments array inside populateSegments like this:
Function populateSegments(ByRef Segments() As CSegment)
The last two code snippets are where the problem resides. The segments array is populating correctly inside the populateSegments function, but when I check the array to see if it is empty just below the call to populateSegments there isn't anything in the tasks(icount).segments array. Thanks in advance for any help, and please let me know if more information is required.
Couldn't you make populatesegments a method of cTask? That avoids the problem and is a better design

Array variable is used before assigned a value

When I rebuild the solution I get the warning:
"Variable 'aMyArray' is used before it has been assigned a value."
A function in VB.NET uses an array which is dynamically populated.
Example:
Function MyArray()
Try
Dim aMyArray()
For i = 0 to 100
ReDim Preserve aMyArray(i)
Next
Catch ex As Exception
End Try
End Function
How should I declare a dynamically populated array to eliminate this warning?
I like to use the CreateInstance to avoid possible errors:
Dim aMyArray() as string = Array.CreateInstance(GetType(String),0)
A. Your function returns nothing, so you should also have a warning about that
B. You seriously need to turn on Option Strict and let the compiler point out other errors like aMyArray has no Type.
C. Never use and empty Try/Catch; if there is an exception, you want to know when something goes wrong (and where) so you can fix it.
D. Avoid arrays like the plague. ReDim Preserve aMyArray(i) creates a new array and then copies all the data to the new structure. This can be costly in terms of performance if it is a large array of something like strings. Net has several very nice collections such as List(of T) which do not need to be explicitly resized:
Private myList As New List(of String) ' or Integer, Decimal or even MyClassObject
...
myList.Add("Hello")
myList.Add("Lists...")
myList.Add("Goodbye")
myList.Add("Arrays")
D(1). The result of using a List would mean that entire procedure can be done away with. Just add new things to the list as needed.
E. The code posted cant actually result in the warning because you dont ever add a value to it. Adding: aMyArray(2) = 2 after the loop will result in the warning. This is because you have never fully declared the array (size and type) as in:
Dim aMyArray(100) As String
ReDim simply resizes the array which isnt the same thing.
F. Since there is nothing in your new array, there is no reason to use ReDim Preserve because there is nothing to preserve. I'll assume that is supposed to be some calculation. On the other hand, if you are trying to resize an existing array, it should be a Sub.
We are all left to guess if it really is meant to modify an existing array (based on ReDim Preserve) or return a new array of stuff (considering the array is declared in the procedure).
Again, none of this is needed using a List, Dictionary or other Collection Type.

VBA Excel - Returning an array of ListBoxes from a function

I am currently working on a module that is meant to populate three ActiveX ListBoxes. Each will be populated with the same values: choices for axes titles on a graph (X-axis, Primary Y-Axis and Secondary Y-Axis). I have a function called "FillBox" that takes a ListBox as an argument and populates it.
Originally I used the following to accomplish this:
Sub FillAllBoxes()
FillBox X_BOX
FillBox Y1_BOX
FillBox Y2_BOX
End Sub
I was informed that I cannot use a Union to compact this code because of the MSForms.ListBox object type. There are many other subs that perform a repeated operation on each box, so it appears that some sort of array would be best to use.
So far, the only way I have found to define this array of ListBoxes is the following, though I am unsatisfied with the fact that the return value is a Variant but I can't seem to get it to work if I define it as Function BOX_ARRAY() As MSForms.ListBox:
Function BOX_ARRAY() As Variant
Dim Temp(1 To 3) As MSForms.ListBox
Set Temp(1) = X_BOX
Set Temp(2) = Y1_BOX
Set Temp(3) = Y2_BOX
BOX_ARRAY = THIS_ARRAY
End Function
I can then use this sub, though I needed to explicitly define the argument to FillBox as ByVal, because BOX_ARRAY is a variant.
Sub FillAllBoxes()
For j = LBound(BOX_ARRAY) To UBound(BOX_ARRAY)
FillBox BOX_ARRAY(j)
Next j
End Sub
A) Is there any way to have a function return an array that is not a Variant?
B) Would it be wise to stick with my original method of just repeating the function explicitly for each list box or is it worth using arrays for this type of operation?
C) In the function BOX_ARRAY is there a way around defining a Temp array and just populating the returned array right off the bat?
Here goes:
There are many other subs that perform a repeated operation on each box
Simply pass the ListBox to a function that operates on listboxes, e.g.:
Sub MyExample()
EnableListBoxes Array(Me.ListBox1, Me.ListBox2, Me.ListBox3)
End Sub
Sub EnableListBoxes(boxes as Variant)
Dim lbox as variant
For each lbox in boxes
lbox.Enabled = True
Next
End Sub
Alternatively you can pass the entire form/worksheet/whatever contains the listboxes, and iterate over the collection containing them (e.g., UserForm.Controls, etc.) You'd need to do some additional logic to make sure you're operating on the right type of form control, but that is relatively easy to do.
I can't seem to get it to work if I define it as Function BOX_ARRAY() As MSForms.ListBox
Of course not, because an MSForms.ListBox is a single object. You can't expect to return an array of anything in to a function that is designed to return an object.
Is there any way to have a function return an array that is not a Variant?
Perhaps use a subroutine and pass the arguments ByRef. You don't need to return a value if you are operating on the reference directly, rather than a local copy of it. See Chip Pearson for some detailed explanations/examples:
http://www.cpearson.com/excel/passingandreturningarrays.htm
Sub TestByRef()
Dim i as Integer
i = 1
ChangeValue i
Debug.Print i
End Sub
Sub ChangeValue(ByRef x as Integer)
x = 3
End Sub
In the function BOX_ARRAY is there a way around defining a Temp array and just populating the returned array right off the bat?
Not that I can think of. Perhaps with a class module and the class _Initialize method, you could automatically assign one of the class properties. But I think that ultimately is the same method: creating a temporary array that defines the return value. It is hard to tell for sure since you don't provide enough code (e.g., where are the variables? are they even declared?

Resources