I have a public Variant variables declared in a UserForm called MainForm
Public increaseArray As Variant
Public countryArray As Variant
Then in sub on button click for the MainForm:
Sub testButton_Click()
Dim country As Variant
Set countryArray = Module1.callSomeFunctionThatReturnsVariant(1)
Set increaseArray = Module1.callSomeFunctionThatReturnsVariant(2)
For Each country In countryArray
Call Module1.createPage(country)
Next country
End Sub
In Module1 I have:
Function callSomeFunctionThatReturnsVariant(ByVal testInt As Integer) As Variant
.... do something when testInt = 1
.... do something when testInt = 2
callSomeFunctionThatReturnsVariant = someVariant
End Function
Public Sub createPage(ByVal country As String)
Dim testInt As Integer
... do something
testInt=insertSection(country, MainForm.increaseArray)
End Sub
Function insertSection(ByVal country As String, arr as Variant) As Integer
Dim arrCountry As Variant
For Each arrCountry In arr
If country = "France" Then
...do something
insertSection = 1
Exit Function
End If
Next arrCountry
insertSection = 2
End Function
I get ByRef argument type mismatch error when passing MainForm.increaseArray to insertSection() function. I've tried using Function insertSection(ByVal country As String, ByVal arr as Variant) As Integer but I get same error.
If I try to define a Variant variable in createPage sub Dim testArray As Variant and get the increaseArray from its getter function Set testArray = MainForm.getterForIncreaseArray I get type mismatch error...
If I pass getter function directly to caller of insertSection function I get ByRef argument type mismatch...
Please help :)
this simple code works fine.
declaring public array in userform not allowed (so using variant as disguise was good idea).
But then, Functions dont want to accept passing it as argument as a legit array, so i used a temporary 'legit' array.
on UserForm1 :
Option Explicit
Public a As Variant 'i would usually declare it like this : Public a() as variant, but public arrays not allowed in userforms (throws error)
'Private a() as variant , would not throw error (inside userform)
Private Sub UserForm_Initialize()
Dim i&
ReDim a(1 To 2) 'absolutely needed, it shows a is actually an array type
a(1) = 1
a(2) = 2
End Sub
Private Sub UserForm_Terminate()
Erase a
End Sub
in a module :
Option Explicit
Sub test()
Load UserForm1
Dim b&
Call get_value(1, UserForm1.a, b)
Unload UserForm1
MsgBox b
End Sub
Sub get_value(ByVal i&, ByRef arr As Variant, ByRef answer As Long) ' function won't let it through, i used a sub with aditionnal variable as Byref.
answer = arr(i)
End Sub
Launch it by calling TEST.
Note : i didn't succeed in passing argument in a Function, so did it in a SUB by adding an argument called Answer, wich is Byref.
Note2 : i looked back at my older code, and it would seem that you can pass a byref Array (disguised as variant) in a function , but maybe because this one is declared not with () or whatever, it don't want to work through a function.
Note 3 : after thurther digging into it, i found a solution using function, and as i thought, the array-declaring was the troublemaker :
'in a module (use the same userform as before)
Sub test()
Load UserForm1
Dim b&
Dim i& 'counter
Dim Temp_Array() As Long 'as variant works too, but i filled it with numbers so as long is ok too
ReDim Temp_Array(LBound(UserForm1.a) To UBound(UserForm1.a))
'Temp_Array = UserForm1.a 'damn, i first thought this would work, in the same way you can fill a listbox in one simple line (wich would be a 3rd solution passing an array from the userform to a module)
For i = LBound(UserForm1.a) To UBound(UserForm1.a)
Temp_Array(i) = UserForm1.a(i)
Next i
b = get_value(1, Temp_Array)
Erase Temp_Array
Unload UserForm1
MsgBox b
End Sub
Function get_value(ByVal i&, ByRef arr As Variant) As Long
get_value = arr(i)
End Function
As per findwindow's comment. You can't make use of things in module 1 from within the form as it's beyond its scope. Instead, try putting all the code from module1 into a new class module. Instantiate an instance of that from within your form and it should work ok.
Related
I have a VBA script that uses a Private Sub Open_Workbook() to initialize some arrays from one of the excel sheets to be used as lookup tables later. The Private Sub Open_Worbook() is placed in the ThisWorkbook module and it appears to do it's job when the excel workbook is open.
The arrays in the Open_Workbook() sub are Dim as Variants but I have not been able to "pass" them along to Sheet1 for example to be used in the Sub for that sheet. I have tried using an accessor similar to what was suggested at the following link:
Create and assign variables on Workbook_open, pass it to Worksheet_change
Here is my code following that suggestion.
Following code is in the ThisWorkbook module of the excel workbook:
Option Explicit
Private Test_Array() As Variant
Private Sub Workbook_Open()
' Code here to redim preserve Test_array and set elements of the array and other code that
' Added snippits of code to
Dim Test_Array() As Variant
For i = 1 To UBound(TestRange,2)
ReDim Preserve Test_Array(i - 1)
Test_Array(i-1) = TestRange(1,i)
Next i
End Sub
Public Property Get TestArray() As Variant
TestArray = Test_Array()
End Property
I was hoping that I would be able to use Thisworkbook.TestArray in Sheet1 to do some calculations on it but when I have a Msgbox ThisWorkbook.TestArray(0), I am getting a Subscript out of range error. I debugged the code and it appears that the Public Property Get TestArray(), Test_Array() is empty. What am I doing wrong? Can I not use Variant with Public Get Property?
I did confirm that the Test_Array in the Workbook_Open() Sub is indeed populated with the expected elements.
Edited: Added code for populating Test_Array
You have a Dim Test_Array() As Variant on module level and in the Private Sub Workbook_Open(). So you never populate Test_Array on the module level.
The following code is working for me
Workbook class module:
Option Explicit
Private Test_Array() As Variant
Private Sub Workbook_Open()
' Code here to redim preserve Test_array and set elements of the array and other code that
' Added snippits of code to
'Dim Test_Array() As Variant
Dim testrange
Dim i As Long
testrange = Sheet1.Range("A1:A4").Value
For i = 1 To UBound(testrange, 2)
ReDim Preserve Test_Array(i - 1)
Test_Array(i - 1) = testrange(1, i)
Next i
End Sub
Public Property Get TestArray() As Variant
TestArray = Test_Array()
End Property
Normal module
Option Explicit
Sub testit()
Debug.Print ThisWorkbook.TestArray(0)
End Sub
Reading on Lifetime of variables
In Python, we normally pass arguements in the function when we have to call them multiple times as below:
def function_add(a,b):
print(a+b)
function_add(4,5)
function_add(5,7)
function_add(10,4)
function_add(4,6)
Do we have a similar way to implement it in VBA too? I tried to implement it but I couldn't make it. Below is my code.
Private Sub SearchAndInsertRows()
Dim rng As Range
Dim cell As Range
Dim search As String
Dim kk As String
Set rng = ActiveSheet.Columns("A:A")
search = ""
Call searchGetKey(search)
Set cell = rng.Find(What:=search, LookIn:=xlFormulas, LookAt:=xlWhole, MatchCase:=False)
If cell Is Nothing Then
MsgBox "Not Found"
Else
kk = ""
Call searchSetKey(kk)
cell.Value = kk
End If
End Sub
Sub searchGetKey(ByRef getKey As String)
getKey = "a"
End Sub
Sub searchSetKey(ByRef setKey As String)
setKey = "b"
End Sub
Sub searchGetKey and searchSetKey modifies one cell but I need to do the same for number of cells. Is there any other ways to do it?
Please fell free to optimize the code wherever necessary.
Thank you very much and much appreciated. :)
A function in VBA must return something. Otherwise, you should use a Sub:
Function function_add(a As Long, b As Long) As Long
function_add = a + b
End Function
Sub TestFunction()
MsgBox function_add(3, 5)
End Sub
You can use a function without arguments, just returning according to a specific calculation algorithm. For instance:
Function tomorrow_Date() As Date
tomorrow_Date = Date + 1
End Function
It can be called as:
Sub testTommorrow_Date()
MsgBox tomorrow_Date
End Sub
Or a Sub which by default takes arguments ByRef, if not specified ByVal:
Sub Sub_add(a As Long, b As Long, c As Long)
c = a + b
End Sub
And test it as:
Sub TestSub_Add()
Dim c As Long
Sub_add 3, 2, c
MsgBox c
End Sub
Of course, a and b may be declared in the testing Sub and used like arguments, but I wanted saying that they are not relevant against c which was updated after the call...
I am trying to have a sub in VBA to call for another sub by passing the i value using the for loop. The problem is when u run the forLoop() sub, there is an error ByRef Type Mismatch. I am just trying out this example method to see if it works so that i can put into my bigger macro.
I tried to add the ByVal method but the error still remains. Someone please help me.
The forLoop sub
Public Sub forLoop()
For i = 1 To 3
Call Fetch_data(i)
Next i
End Sub
The Fetch_data() sub
Sub Fetch_data(num As Integer)
Dim data As String
Dim myCellValue As range
Dim myCellValue1 As range
Dim sht As String
Set sht = "mySheet" & num
Set myCellValue = sht.range("J6")
Set myCellValue1 = sht.range("J8")
myCellValue1.Value = myCellValue.Value
End Sub
The issue is that
Sub Fetch_data(num As Integer)
is the same as
Sub Fetch_data(ByRef num As Integer)
so Call Fetch_data(i) gives i by reference. But since in forLoop() the variable i is not declared it is Variant by default and not Integer therefore the type mismatch. Make sure you use Option Explicit and declare all your variables properly:
Public Sub forLoop()
Dim i As Integer
For i = 1 To 3
Fetch_data i
Next i
End Sub
Also you can make the parameter ByVal as there is no need to have it ByRef (otherwise your sub Fetch_data could mess up the loops counter in forLoop:
Sub Fetch_data(ByVal num As Integer)
Dim data As String
Dim myCellValue As range
Dim myCellValue1 As range
Dim sht As Worksheet
Set sht = ThisWorkbook.Worksheets("mySheet" & num)
Set myCellValue = sht.range("J6")
Set myCellValue1 = sht.range("J8")
myCellValue1.Value = myCellValue.Value
End Sub
I am in the middle of switching my code over from using .active and .select to something more reliable at the recommendation of the internet. I was wondering about the functionality of with statements, and if they follow into a created function.
'---Which way is more proper?
'1.
Sub TestCase()
With ThisWorkbook.Worksheets("TestWorksheet")
TestFunction()
End With
End Sub
Function TestFunction()As Integer
Dim I As Integer
I = .cells(1,1)
End function
'--2.
Sub TestCase()
With ThisWorkbook.Worksheets("TestWorksheet")
TestFunction()
End With
End Sub
Function TestFunction()As Integer
Dim I As Integer
With ThisWorkbook.Worksheets("TestWorksheet")
I = .cells(1,1)
End With
End function
Unfortunately, With statements do not follow to called functions. For that functionality, you'd need to pass an argument to the function. Here's an example that replicates your provided psuedo-code in a valid manner:
Sub TestCase()
MsgBox TestFunction(ThisWorkbook.Worksheets("TestWorksheet"))
End Sub
Function TestFunction(ByRef ws As Worksheet) As Double
With ws
TestFunction = Val(.Cells(1, 1).Value)
End With
End Function
Note that the Sub is calling the Function and passing the argument which is a worksheet object. Then in the function you can use the With statement on the passed argument.
Most proper (building on #BigBen's very correct comment):
Sub TestCase()
'Integer variable to catch the return of the function
Dim somVar as Integer
'Call the function, pass the worksheet.
somVar = TestFunction(ThisWorkbook.Worksheets("TestWorksheet"))
'Do something with the return
Debug.print somVar
End Sub
Function TestFunction(ws as Worksheet) As Integer
Dim I As Integer
I = ws.cells(1,1)
'Return the integer to the caller
TestFunction = I
End function
I've simplified my question to make it as generic as possible.
Say I would like to create a Sub, which takes a collection, and a datatype of objects expected to be contained in that collection, and prints a specified parameter to the immediate window. The sub would need to take as inputs a collection (e.g. ThisWorkbook.Worksheets), a datatype (e.g. Worksheet), and I would think optionally a property (e.g. Name)
The sub would look something like:
Sub PrintMembers(ByVal myCol as Collection, ByVal datatype as ????, ByVal property as ????)
dim myObj as datatype?
for each in myCol
debug.print myObj.property?
next
End Sub
Which I could then call with:
Call PrintMembers(ThisWorkbook.Worksheets, Worksheet, "Name")
or something along those lines, which would output:
Sheet1
Sheet2
Sheet3
I'm not exactly sure what you're asking but it seems as though you might be looking for the CallByName() function:
Public Sub RunMe()
PrintMembers ThisWorkbook.Worksheets, "Name"
End Sub
Private Sub PrintMembers(col As Object, prop As String)
Dim item As Object
For Each item In col
Debug.Print CallByName(item, prop, VbGet)
Next
End Sub