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.
Related
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.
Not sure if its possible, and I can't find a specific question that shows how to do this. I am basically looking to write a function that would work like the SUM (or any number of functions in Excel). The user can call the function from the spreadsheet and select a variably sized, one dimensional set of numbers from the SS as an argument. For illustrative purposes I've tried writing the following test function to learn how to do this:
Function Test(tester() As Double) As Double
MsgBox "array position 1 = " + tester(1)
Test = tester(1)
End Function
This is just one manifestation of the things I've tried to get this to work, but anytime I choose data from the SS as an argument to my function, it makes the cell go "#Value". I'm thinking I need to do something clever with ranges but am very new to this. It is worth noting that I can pass arrays around function behind the scenes in code, but can't master allowing the user to select the data from the SS. Any help is appreciated!
Try something like this
Public Function TestFunc(TestRange As Range) As Variant
Dim vArray As Variant
vArray = TestRange.Value2
If IsArray(vArray) Then
TestFunc = vArray(1, 1)
Else
TestFunc = vArray
End If
End Function
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.
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.
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?