Set VBA Variable to Result from Function which is String - excel

I've read a bunch about functions today and they all seem to deal with math/numbers. I'm trying to use a function which returns a string and capture that as a variable in the "main sub" but I can't get it to work. Can anybody point out what I'm doing wrong?
Eg:
Function:
Public Function Test(var) As Variant
Bar = var & "World"
MsgBox Bar
End Function
Sub:
Public Bar As Variant
Public Z As Variant
Sub testing()
Call Test("Hello") ' This displays "HelloWorld" from MsgBox in Function
Test ("Hello") ' This displays "HelloWorld" from MsgBox in Function
Z = Test("Hello") ' This displays "HelloWorld" from MsgBox in Function
MsgBox Z ' This displays an empty MsgBox :*(
End Sub

If you want the function to return a value, populate the function variable and return that in the main sub, like so
Public Function Test(var As String) As String
Test = var & " World"
End Function
Sub testing()
returnstr = Test("Hello")
MsgBox returnstr
End Sub

You aren't returning a value from the function. Also, functions should only be used to return values, not to perform actions that change things (other than variables) or display pop-ups. It also gets confusing having global variables and passing variables to a function. You generally pass local variables to a function. Here's a cleaner example (with your main function first, as is normal convention):
Sub Testing()
Dim Z As String
Z = Test("Hello")
MsgBox Z
MsgBox Test("Hello")
End Sub
Public Function Test(ByRef var As String) As String
Test = var & "World"
End Function

Related

function that erase itself after work. vba

i want to write something like this:
Public Function functionThatEraseHerself()
functionThatEraseHerself = "here is some work"
'ActiveCell.value = ActiveCell.value
End Function
how it now
how it must to be
It is generally not possible to change cell values from a user defined function called by a formula.
Nevertheless there is a nasty workaround using Evaluate
Option Explicit
'=functionThatEraseHerself()
Public Function functionThatEraseHerself()
Dim ReturnValue As String
ReturnValue = "here is some work"
' write the value to the cell that called this function
Application.Caller.Parent.Evaluate "ReplaceWithValue(" & Application.Caller.Address & ", """ & ReturnValue & """)"
End Function
' helper procedure that is called by evaluate
Public Sub ReplaceWithValue(ByVal Cell As Range, ByVal Value As Variant)
Cell.Value2 = Value
End Sub

Passing values from Function to Subroutine

I have been looking around for an answer to that, but have been unable to locate one that explains it well enough that I understand.
I have no experience with functions, and very little with VBA overall. So if I have a subroutine I am executing and then call a function that I pass parameters into, once that function runs how do I get the result back into the main subroutine to be used?
This is for Access where I am pulling a number from one record set then passing that to a function to be used for an insert to create a new number. I then need that number passed back to the subroutine to be used.
Here you have two alternatives.
Run the code using F8 key to see what happens and make sure to activate the Locals Window so you see how variables values change
1- Return the value directly from a function
Run the DoSomethingReturnFromFunction sub
' Return value from function
Public Sub DoSomethingReturnFromFunction()
' Define a parameter
Dim myParameter As String
myParameter = "SomeValue"
' Call a function and store its value into a variable
Dim myResult As Long
myResult = MyFunction(myParameter)
' Print the result variable
Debug.Print myResult
End Sub
Private Function MyFunction(ByVal myParameter as String) As Long
Dim result As Long
Select Case myParameter
Case "SomeValue"
result = 1
Case Else
result = 2
End Select
' Assign the result value to the function
MyFunction = result
End Function
Result: Debug.Print myResult prints to the inmediate window the value returned from the function
2- Change variable value passed ByRef inside another function
Run the DoSomethingReturnFromByRef sub
' Change variable value passed ByRef inside another function
Public Sub DoSomethingReturnFromByRef()
' Call a function
Dim myByRefParameter As Long
MySub myByRefParameter
' Print the result
Debug.Print myByRefParameter
End Sub
Private Sub MySub(ByRef myByRefParameter As Long)
' Change the value of the variable passed ByRef inside the procedure
myByRefParameter = 1
End Sub
Result: Debug.Print myByRefParameter prints to the inmediate window the value that is stored in the myByRefParameter variable originally declared in the DoSomethingReturnFromByRef procedure
Let me know if it's clear

Reference a function with a general expression

In VBA, I want to be able to reference a particuler function with a general expression and a variable within another function, like the following:
Function simpletest()
i = 1
simpletest = doom"i"
End Function
Function doom1()
doom1 = success
End Function
Is this possible in VBA, If so, how should I proceed?
Try this...
Sub SimpleTest()
i = 1
Application.Run "doom" & i
End Sub
This should work:
Function simpletest()
Dim function_name as String
function_name = "doom"
i = 1
function_name = function_name & CStr(i)
Application.Run function_name
End Sub
Function doom1()
doom1 = success
End Function
Let me know if something has to be changed
Function by Expression
Option Explicit
Function SimpleTest(FName As String, FSuffix As String) As String
SimpleTest = FName & FSuffix
End Function
Function Doom1() As String
Doom1 = "Success in Doom1"
End Function
Function Doom2() As String
Doom2 = "Success in Doom2"
End Function
Sub test()
Const cTitle As String = "Doom"
Dim i As Long
' Write the results to the Immediate Window (Ctrl+G).
For i = 1 To 2
Debug.Print Application.Run(SimpleTest(cTitle, CStr(i)))
Next
End Sub

Passing parameter into function to create variable name

I want to use global variables in my workbook and in the ThisWorkbook code. I declared the following varaibles
Public position_1 as string
Public position_2 as string
If I want to see the value of those variables I believe they need to be fully qualified so
Debug.Print ThisWorkbook.position_1
Debug.Print ThisWorkbook.position_2
I have written a UDF which I will pass in an integer to represent which variable I am looking for. I will only be passing in a single number and not a full variable name. I am trying to find a way to use this integer to concatenate with "position_" to display the value of the global variable, ThisWorkbook.position_1, ThisWorkbook.position_2, etc.
Function Test_Global_Var(position as Integer)
Dim variable_name As String
variable_name = "position_" & position
Debug.Print ThisWorkbook.variable_name
End Function
So when I call
Test_Global_Var(1)
my immediate window should display the value of
ThisWorkbook.position_1
The code below produces the following debug output
2 values defined.
ThisWorkbook.Position(0)
First Value
ThisWorkbook.Position(1)
Second Value
It uses a private array in the workbook named m_position. The contents are accessed by a global property ThisWorkbook.Position(index).
In a module have the following code:
Option Explicit
Public Sub Test()
If ThisWorkbook.NoValues Then
ThisWorkbook.FillValues "First Value", "Second Value"
End If
Debug.Print CStr(ThisWorkbook.Count) & " values defined."
Test_Global_Var 0
Test_Global_Var 1
End Sub
Public Sub Test_Global_Var(ByVal index As Long)
' Part of a UDF
Debug.Print "ThisWorkbook.Position(" & CStr(index) & ")"
Debug.Print ThisWorkbook.Position(index)
End Sub
In ThisWorkbook have the following code:
Option Explicit
Private m_position() As Variant
Private Sub Workbook_Open()
Call DefaultValues
End Sub
Public Property Get Position(ByVal index As Long) As Variant
Position = m_position(index)
End Property
Public Sub DefaultValues()
m_position = Array("First", "Second")
End Sub
Public Sub FillValues(ParamArray args() As Variant)
m_position = args
End Sub
Public Property Get Count() As Long
Count = UBound(m_position) - LBound(m_position) + 1
End Property
Public Property Get NoValues() As Boolean
On Error GoTo ArrUndefined:
Dim n As Long
n = UBound(m_position)
NoValues = False
On Error GoTo 0
Exit Sub
ArrUndefined:
NoValues = True
On Error GoTo 0
End Property
PS. In VBA never use Integer, but instead use Long. Integer is a 16bit type, while Long is the standard 32bit type that all other programming languages consider as an integer.
It is possible to consider a global dictionary variable and pass data through it from the UDF.
First add reference to Microsoft Scripting Runtime:
Thus, build the dictionary like this:
Public myDictionary As Dictionary
To initialize the myDictionary variable, consider adding it to a Workbook_Open event:
Private Sub Workbook_Open()
Set myDictionary = New Dictionary
End Sub
Then the UDF would look like this:
Public Function FillDicitonary(myVal As Long) As String
If myDictionary.Exists(myVal) Then
myDictionary(myVal) = "position " & myVal
Else
myDictionary.Add myVal, "position " & myVal
End If
FillDicitonary = "Filled with " & myVal
End Function
And it would overwrite every key in the dictionary, if it exists. At the end, the values could be printed:
Public Sub PrintDictionary()
Dim myKey As Variant
For Each myKey In myDictionary
Debug.Print myDictionary(myKey)
Next
End Sub

Why is VBA function giving compile error for collection but not for string

I am trying to pass a collection to a VBA function but I'm getting a compile error I cannot seem to fix.
This is a simplified example that gives the error.
Sub test()
Dim fooString As String
Dim fooCollection As collection
Set fooCollection = New collection
useString (fooString)
useCollection (fooCollection)
End Sub
Public Function useString(foo As String)
MsgBox ("here")
End Function
Public Function useCollection(foo As collection)
MsgBox ("here")
End Function
I'm not seeing what I'm doing differently than what is shown in examples such as the one shown here: How do I sort a collection?
This is the error I'm getting (Compile Error: Argument not optional):
You're getting this dialog because of the implicit "helpful" default member. In the Object Browser navigate to the Collection in the classes pane and in the members you'll see that Item is the default member, designated by the teal icon.
Here's another example. Ask yourself what it will print and say it out loud before running the code.
Public Sub DontWrapIt()
Dim foo As Range
Set foo = Sheet1.Range("A1")
GiveMeARange foo
GiveMeARange (foo)
End Sub
Private Sub GiveMeARange(ByVal param As Variant)
Debug.Print TypeName(param)
End Sub
Wrapping the object with parentheses causes it to be evaluated which causes an implicit invocation of the "helpful" default member... Which requires an Index parameter that isn't supplied - hence, "parameter not optional" .
I don't know why that particular error (as opposed to a more informative error) is given, but when you call a sub or a function as a sub (not using the return value) you shouldn't have parentheses around the arguments. The following works:
Sub test()
Dim fooString As String
Dim fooCollection As collection
Set fooCollection = New collection
useString fooString
useCollection fooCollection
End Sub
Public Function useString(foo As String)
MsgBox "here"
End Function
Public Function useCollection(foo As collection)
MsgBox "here"
End Function
But -- if your functions don't really return anything, perhaps you could just define them as subs to begin with. In VBA, there really isn't any point in making something which in a language like C would be a void function.
Is just a matter of calling the other sub..
If it doesn't have any parameters, you can call it by name only:
useCollection or Call useCollection
If it does have parameters, then you either call it without parentheses, or you use Call:
useCollection parameter or Call useCollection(parameter)
Sub test()
Dim fooString As String
Dim fooCollection As Collection
Set fooCollection = New Collection
useString (fooString)
fooCollection.Add "test message", "test"
useCollection fooCollection
Call useCollection(fooCollection)
End Sub
Public Function useString(foo As String)
MsgBox ("here")
End Function
Public Function useCollection(foo As Collection)
MsgBox foo(1)
End Function
See some more info about the Call statement:
You are not required to use the Call keyword when calling a procedure. However, if you use the Call keyword to call a procedure that requires arguments, argumentlist must be enclosed in parentheses. If you omit the Call keyword, you also must omit the parentheses around argumentlist. If you use either Call syntax to call any intrinsic or user-defined function, the function's return value is discarded
This works:
Sub test()
Dim fooString As String
Dim fooCollection As VBA.collection
Set fooCollection = New VBA.collection
fooCollection.Add "bar", "bar"
useString (fooString)
useCollection fooCollection
End Sub
Public Function useString(foo As String)
MsgBox ("useString")
End Function
Public Function useCollection(ByRef foo As VBA.collection)
MsgBox ("useCollection")
End Function

Resources