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
Related
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...
see the basic function structure
function test (first as range)
whatever I do inside
end function
calling the function and first is the address of the first cell, for example, B2, like this:
first=ActiveCell.address (0,0)
test (first)
calling the function results with a run time error 424 "object required"
no idea what I'm doing wrong
You just want the first cell's address? There are lots of ways to do this. Here is one.
Sub test(first As Range)
Dim r As Range
For Each r In first
Debug.Print r.Address
Exit Sub
Next
End Sub
OR as a function that returns the address:
Function GetFirstAddress(first As Range) As String
Dim r As Range
For Each r In first
GetFirstAddress = r.Address
Exit Function
Next
End Function
Another approach allowing optional external references might be:
Function GetFirstAddress(rng As Range, Optional IsQualified As Boolean = True) As String
GetFirstAddress = rng.Parent.cells(rng.Row, rng.Column).Address(external:=IsQualified)
End Function
where rng.Parent qualifies the needed worksheet reference.
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.
Hi Please help me with the below,
Sub movedata()
Call select_data(.Range("B6:B12"))
End Sub
Function select_data(C As Range)
Worksheets("sheet1").Range("I6:I16") = Worksheets("Sheet1").Range(C).Value
End Function
I can't see where I am going wrong,
Thanks,
C already is a Range object so there's no need to pass it to the Range() function (which expects a string anyway) to create it.
Change:
Worksheets("sheet1").Range("I6:I16") = Worksheets("Sheet1").Range(C).Value
To:
Worksheets("sheet1").Range("I6:I16") = C
You are confusing the range as a Range object with its Address property.
Method 1:
Sub movedata()
Call select_data(Range("B6:B12").address)
End Sub
Function select_data(C As string)
with Worksheets("sheet1")
.Range("I6:I16") = .Range(C).Value
end with
End Function
Method 2:
Sub movedata()
Call select_data(Worksheets("sheet1").Range("B6:B12"))
End Sub
Function select_data(C As range)
C.parent.Range("I6").resize(C.rows.count, C.columns.count) = C.Value
End Function
fwiw, there is going to be some difficulties stuffing B6:B12's values into I6:I16's cells. There seem to be 4 missing values.
I have a button someone can click. This button will create a range and pass it to another function that changes the value of that range.
Sub CommandButton21_Click()
Dim example As Range
Set example = Range("A1")
test (example)
End Sub
This function does not work. For some reason the range cannot be used by the other function.
Function test(x As Range)
x.Value = "changed"
End Function
Any help? The error says "Object required". I have tried to pass stuff like [A1] or making it a variant with no luck.
You can't "Call" a function, you Call a sub.....try this:
Sub CommandButton21_Click()
Dim example As Range
Set example = Range("A1")
MsgBox test(example)
End Sub
Function test(r As Range) As String
r.Value = "Changed"
test = "O.K."
End Function
Use Call:
Call test(example)
If you will not return any value, use Sub rather than Function.