VBA : How to change the cell's value in a function? - vba

I'm a new vba programmer and I have some trouble.
This is my function :
Function CopiePaste(CASEREF As Range, REF)
Dim o As Range
For Each o In CASEREF
o.Value = REF
Next
End Function
I want define multiple cell's value in a function but my code doesn't work and I don't understand why ?
Thanks in advance

In VBA, you would use a Sub rather than a Function
Here is one way that a Sub could use a Function to accomplish this:
Sub MAIN()
Dim MSG As String
MSG = CopiePaste(Range("A1:A10"), 123)
MsgBox MSG
End Sub
Function CopiePaste(CASEREF As Range, REF) As Variant
Dim o As Range
For Each o In CASEREF
o.Value = REF
Next
CopiePaste = "Mission Accomplished!"
End Function
Note: the custom function does not actually appear in a cell, but rather is called from the sub.

I will make some suggestions but I am not an expert either :-)
First: a function is supposed to return a value to the place in the code where the function is called from.
Here, you want to do something instead of returning a value, so you should use a sub() instead of a function.
Second, I think you should also declare the "REF" variable for it to work, (unless if it's a public variable).

Related

Error In Function Name

I get an error when trying to use the below function. The strange part is that the error occurs in the name
Code:
Function DATECHECK(rng As Range, date_time As Date) As Boolean
For Row = 1 To rng.Rows.Count
'Stuff'
Next Row
End Function
Syntax:
DateCheck(Sheet2!A2:B561, Sheet1!A2)
Error:
#NAME
#NAME(Sheet2!A2:B561, Sheet1!A2)
Make sure the function is in a public module; not a private worksheet code sheet.
DateCheck should return something. Add DateCheck = 1 before End Function.
You cannot manipulate values of other cells with a worksheet UDF. You decided that showing the relevant code wasn't important; it was important.
In your function's context, Row is a variable. You need to declare it as a long (e.g. dim row as long) if you are using Option Explicit.

Using a function to clean data in VBA

I am familiar with this post: How to Return a result from a VBA Function but changing my code does not seem to help.
I want to write a simple function in VBA that allows to lowercase an input sentence. I wrote this:
Private Function Converter(inputText As String) As String
Converter = LCase(inputText)
End Function
Sub test()
Dim new_output As String
new_output = Converter("Henk")
MsgBox (new_output)
End Sub
I tried following the advice I found at another stackoverflow post. I made me change this:
Private Function Converter(inputText As String)
Set outputText = LCase(inputText)
End Function
Sub test()
Dim new_output As String
Set new_output = Converter("Henk")
MsgBox (new_output)
End Sub
However, now I get an error that an object is required. Why does it require an object now? I dont get it...
Set outputText = LCase(inputText)
The Set keyword is reserved for Object data types. Unlike VB.NET, String in VBA is a basic data types.
So you dont Set a variable to a string. Drop the second version of your code altogether. It doesn't make sense. That "advice" was probably in another context.
To fix your first version
1- Assign the returned result to the name of the function Converter
2- It would be beneficial to specify explicitly the return type, as String. Currently it is a Variant that always embeds a String, so better make it explicit:
' vvvvvvvvv
Private Function Converter(inputText As String) As String
Converter = LCase(inputText) ' <------------ assign return to name of function
End Function

How can I assign a Variant to a Variant in VBA?

(Warning: Although it might look like one at first glance, this is not a beginner-level question. If you are familiar with the phrase "Let coercion" or you have ever looked into the VBA spec, please keep on reading.)
Let's say I have an expression of type Variant, and I want to assign it to a variable. Sounds easy, right?
Dim v As Variant
v = SomeMethod() ' SomeMethod has return type Variant
Unfortunately, if SomeMethod returns an Object (i.e., a Variant with a VarType of vbObject), Let coercion kicks in and v contains the "Simple data value" of the object. In other words, if SomeMethod returns a reference to a TextBox, v will contain a string.
Obviously, the solution is to use Set:
Dim v As Variant
Set v = SomeMethod()
This, unfortunately, fails if SomeMethod does not return an object, e.g. a string, yielding a Type Mismatch error.
So far, the only solution I have found is:
Dim v As Variant
If IsObject(SomeMethod()) Then
Set v = SomeMethod()
Else
v = SomeMethod()
End If
which has the unfortunate side effect of calling SomeMethod twice.
Is there a solution which does not require calling SomeMethod twice?
In VBA, the only way to assign a Variant to a variable where you don't know if it is an object or a primitive, is by passing it as a parameter.
If you cannot refactor your code so that the v is passed as a parameter to a Sub, Function or Let Property (despite the Let this also works on objects), you could always declare v in module scope and have a dedicated Sub solely for the purpose of save-assigning that variable:
Private v As Variant
Private Sub SetV(ByVal var As Variant)
If IsObject(var) Then
Set v = var
Else
v = var
End If
End Sub
with somewhere else calling SetV SomeMethod().
Not pretty, but it's the only way without calling SomeMethod() twice or touching its inner workings.
Edit
Ok, I mulled over this and I think I found a better solution that comes closer to what you had in mind:
Public Sub LetSet(ByRef variable As Variant, ByVal value As Variant)
If IsObject(value) Then
Set variable = value
Else
variable = value
End If
End Sub
[...] I guess there just is no LetSet v = ... statement in VBA
Now there is: LetSet v, SomeMethod()
You don't have a return value that you need to Let or Set to a variable depending of its type, instead you pass the variable that should hold the return value as first parameter by reference so that the Sub can change its value.
Dim v As Variant
For Each v In Array(SomeMethod())
Exit For 'Needed for v to retain it's value
Next v
'Use v here - v is now holding a value or a reference
You could use error trapping to reduce the expected number of method calls. First try to set. If that succeeds -- no problem. Otherwise, just assign:
Public counter As Long
Function Ambiguous(b As Boolean) As Variant
counter = counter + 1
If b Then
Set Ambiguous = ActiveSheet
Else
Ambiguous = 1
End If
End Function
Sub test()
Dim v As Variant
Dim i As Long, b As Boolean
Randomize
counter = 0
For i = 1 To 100
b = Rnd() < 0.5
On Error Resume Next
Set v = Ambiguous(b)
If Err.Number > 0 Then
Err.Clear
v = Ambiguous(b)
End If
On Error GoTo 0
Next i
Debug.Print counter / 100
End Sub
When I ran the code, the first time I got 1.55, which is less than the 2.00 you would get if you repeated the experiment but with the error-handling approach replaced by the naïve if-then-else approach you discussed in your question.
Note that the more often the function returns an object, the less function calls on average. If it almost always returns an object (e.g. that is what it is supposed to return but returns a string describing an error condition in certain cases) then this way of doing things will approach 1 call per setting/ assigning the variable. On the other hand -- if it almost always returns a primitive value then you will approach 2 calls per assignment -- in which case perhaps you should refactor your code.
It appears that I wasn't the only one with this issue.
The solution was given to me here.
In short:
Public Declare Sub VariantCopy Lib "oleaut32.dll" (ByRef pvargDest As Variant, ByRef pvargSrc As Variant)
Sub Main()
Dim v as Variant
VariantCopy v, SomeMethod()
end sub
It seems this is similar to the LetSet() function described in the answer, but I figured this'd be useful anyway.
Dim v As Variant
Dim a As Variant
a = Array(SomeMethod())
If IsObject(a(0)) Then
Set v = a(0)
Else
v = a(0)
End If

VBA Excel: How to pass one parameter of different types to a function (or cast Int/String to Range)?

I'm writing some VBA functions in Excel that compute word values and cross sums of the input.
I'm passing the input as Public Function cross_sum(myRange As Range) As Integer to them so that they take cell references as input, e.g. =cross_sum(A1). Works fine.
However when I try to chain two functions like =cross_sum(word_value(A1)) I run into th VALUE error because word_value() returns an Integer value and not the Range cross_sum() is set to expect. However I did not find a way to cast an Integer (or String) into a Range.
As Excel's built-in functions support chaining as well as Range input I wonder how.
Unfortunately this is my first VBA project so I wonder if and how to cast or what type to choose to get this working both ways.
Any pointers appreciated!
TIA,
JBQ
You can pass Variant to a function and the function can determine the type of input:
Public Function Inputs(v As Variant) As String
If TypeName(v) = "Range" Then
MsgBox "you gave me a range"
Else
MsgBox "you gave me a string"
End If
Inputs = "done"
End Function
Sub MAIN()
Dim st As String
Dim rng As Range
st = "A1"
Set rng = Range(st)
x = Inputs(st)
x = Inputs(rng)
End Sub
Without your code, it is hard to know what you could change. That being said...
There is not a way to convert an integer to a range. You would have to create a function to do so if that is what you desired.
You could create a converter function, maybe titled IntegerToRange, that takes an integer and after some logic (maybe 1 = "A1", 2 = "A2" or something), will return a range. Your cell formula would then be =cross_sum(IntegerToRange(word_value(A1))
Alternatively, you could modify your word_value function to return a range instead of an integer. Your cell formula would then be =cross_sum(word_value(A1).

Assigning the value of a worksheet cell to a constant

I am trying to assign the value of a worksheet cell to a constant variable in a VBA macro. The logic behind that action is that the end user is supposed to enter the current week in a specified cell before running the Macro. Since this value is going to be reused throughout the macro and I wanted to play it safe, I tried to declare it as a public constant:
private const thisWeek as Integer = Range("B1")
However I get an error message about a constant value being needed. So, is it even possible to declare a constant like this in VBA?
No it is not possible. As the word suggest it should be Constant.
Workaround:
Public Const weekRange As String = "$B$1"
Then in your code:
Sub Something()
Dim thisWeek As Integer: thisWeek = Range(weekRange).Value
'~~> some codes here
End Sub
From help: You can't use variables, user-defined functions, or intrinsic Visual Basic functions (such as Chr) in expressions assigned to constants.
In your case you have to use a variable.
I know this is old but I was looking to do this myself and came up with this. I use a function:
Function insertPNA(strSource As Long) As String
Dim strResult As String
strResult = Replace(strSource, strSource, ThisWorkbook.Sheets("Audits & Actuals").Range("pnacode").Value)
insertPNA = strResult
End Function
Whenever I want the pna code I just type insertpna(1). Hope someone finds this useful. (It doesn't have to be "1" obvs, I just used it to minimize typing.)