Error When Running VBA Code - excel

I am Trying to run this code but it keeps giving me an error message while compiling
Argument not optional
I am using a function called csvRange
Sub CS()
csvRange()
End Sub
Function csvRange(FirstNum, SecondNum) As Double
i As Integer
For i = FirstNum.Value To SecondNum.Value
csvRange = csvRange & i & "+"
Next
End Function
can anyone help.

you want to call csvRange() but csvRange requires 2 arguments to pass.
And you forgot to save the return value.
for example
Sub CS()
var first = 1
var second = 50
var result
result = csvRange(first, second)
End Sub
Function csvRange(FirstNum, SecondNum) As Double
i As Integer
For i = FirstNum.Value To SecondNum.Value
csvRange = csvRange & i & "+"
Next
End Function

A couple of remarks:
- You call the function csvrange without parametervalues which you did not define as being optional.
- You do nothing with the value the function returns
- the i is an integer and the result a double. it is bad practice to mix them both in your for-next loop

Your code requires a few fixes:
1) Use appropriate arguments for csvRange.
2) Use the return value of csvRange for something (this is not mandatory, but typically required in well designed code).
3) Declare the type of arguments for csvRange.
4) You seem to be using csvRange to get a String. Change code accordingly.
You would use something like
Sub CS()
Dim rng1 As Range, rng2 As Range
Set rng1 = ...
Set rng2 = ...
Set retval As String
retval = csvRange(rng1, rng2)
End Sub
Function csvRange(FirstNum As Range, SecondNum As Range) As String
i As Integer
csvRange = ""
For i = FirstNum.Value To SecondNum.Value
csvRange = csvRange & Cstr(i) & "+"
Next i
End Function
PS1: Your output will end with a trailing "+". Perhaps this is not what you want.
PS2: I do not have a system to test this code.

Related

Writing to a worksheet from a VBA function

I am trying to write some intermediate results in a user defined VBA function to a worksheet. I have tested the function, and it works correctly. I am aware that I cannot modify / write to cells from a UDF, so I tried passing the relevant results to a subroutine which I hoped would then be able to write to the spreadsheet.
Unfortunately my scheme doesn't work, and I am trying to think through this problem.
Public Function f(param1, param2)
result = param1 * param2
call writeToSheet(result)
f = param1 + param2
end
public sub writeToSheet(x)
dim c as range
c = range("A1")
c.value = x
end
I would like to see the product of param1 and param2 in cell A1. Unfortunately, it does not happen - the subroutine just ends abruptly as soon as it attempts to execute the first statement (c = range("A1") ). What am I doing wrong, and how can I fix it?
If it is simply impossible to write to a spreadsheet in this way, is there some other way in which to store intermediate results for later review? My real life problem is a little more complicated that my stylized version above, as I generate a new set of intermediate results each time I go through a loop, and want to store them all for review.
This idea might work for you. The function ParamProduct calls SetProps which writes both parameters to custom document properties (View from File > Properties > Advanced Properties > Custom). Call the function with =ParamProduct(A1, A2) or =ParamProduct(123, 321)
Function ParamProduct(Param1 As Variant, _
Param2 As Variant) As Double
Dim Fun As Double
Dim Param As Variant
Dim i As Integer
Param = Param1
For i = 1 To 2
SetProp "Param" & i, Param
Param = Param2
Next i
ParamProduct = Param1 + Param2
End Function
Private Sub SetProp(Pname As String, _
PropVal As Variant)
' assign PropVal to document Property(Pname)
' create a custom property if it doesn't exist
Dim Pp As DocumentProperty
Dim Typ As MsoDocProperties
If IsNumeric(PropVal) Then
Typ = msoPropertyTypeNumber
Else
Select Case VarType(PropVal)
Case vbDate
Typ = msoPropertyTypeDate
Case vbBoolean
Typ = msoPropertyTypeBoolean
Case Else
Typ = msoPropertyTypeString
End Select
End If
On Error Resume Next
With ThisWorkbook
Set Pp = .CustomDocumentProperties(Pname)
If Err.Number Then
.CustomDocumentProperties.Add Name:=Pname, LinkToContent:=False, _
Type:=Typ, Value:=PropVal
Else
With Pp
If .Type <> Typ Then .Type = Typ
.Value = PropVal
End With
End If
End With
End Sub
Use this UDF to recall the properties to the worksheet.
Function GetParam(ByVal Param As String) As Variant
GetParam = Propty(Param)
End Function
Private Function Propty(Pname As String) As Variant
' SSY 050 ++
' return null string if property doesn't exist
Dim Fun As Variant
Dim Pp As DocumentProperty
On Error Resume Next
Set Pp = ThisWorkbook.CustomDocumentProperties(Pname)
If Err.Number = 0 Then
Select Case Pp.Type
Case msoPropertyTypeNumber
Fun = CLng(Fun)
Case msoPropertyTypeDate
Fun = CDate(Fun)
Case msoPropertyTypeBoolean
Fun = CBool(Fun)
Case Else
Fun = CStr(Fun)
End Select
Fun = Pp.Value
End If
The worksheet function below works (A6 has a value of "Param2")
=GetParam("Param1")*GetParam(A6)
The above code will create a property if it doesn't exist or change its value if it does. The sub below will delete an existing property and do nothing if it's called to delete a property that doesn't exist. You might call it from one of the above subs or functions.
Private Sub DelProp(ByVal Pname As String)
On Error Resume Next
ThisWorkbook.CustomDocumentProperties(Pname).Delete
Err.Clear
End Sub
Thanks a mill everyone. Printing to the immediate window is the easiest by far, but allowed me to print only a single item. I therefore concatenated all 5 items into a single string and printed it in the immediate window:
dummystr = CStr(slope1) & ", " & CStr(intercept1) & ", " & CStr(slope2) & ", " & CStr(intercept2) & ", " & CStr(sse(i))
Debug.Print dummystr

VBA behaving weirdly, can't get the value stored in variable

I am having a very strange problem. I am not able to get the value returned from a simple function as below if the return value is more than one char. Now the second problem is that following code is not assigning "WTH" to sheetName variable. Refer to the screenshot 2. UPDATED AFTER CYRIL'S COMMENTS
Public Sub WTHFormatter()
Dim sheetName As String
sheetName = "WTH"
Dim rng1 As Range
'delete empty rows
lastRowWTH = getLastRow(sheetName, 2)
'Delete rows below the last Row
Worksheets(sheetName).Rows(lastRowWTH + 1 & ":" & Worksheets(sheetName).Rows.Count).Delete
' build first range
Set rng1 = Worksheets(sheetName).Range("B11:F" & lastRowWTH)
Call setCellBorders(rng1)
Set rng1 = Worksheets(sheetName).Range("H11:K" & lastRowWTH)
Call setCellBorders(rng1)
'determine the range for months
For i = 13 To 24
If Cells(7, i) = "" Then
lastCol = i - 1
Exit For
End If
lastCol = i
Next
ColLetter = returnLabel(lastCol)
ColLetter2 = returnLabel(lastCol + 2)
ColLetterX = returnLabel(lastCol + 14)
Set rng1 = Worksheets(sheetName).Range("K17:" & ColLetter & lastRowWTH)
Call setCellBorders(rng1)
Set rng1 = Worksheets(sheetName).Range(ColLetter2 & lastRowWTH & ":" & ColLetter3 & lastRowWTH)
Call setCellBorders(rng1)
End Sub
Function returnLabel(num1 As Long) As String
Dim ColumnLetter As String
ColumnLetter = Split(Cells(1, num1).Address, "$")(1)
returnLabel = ColumnLetter
End Function
The above function returns blank and varTest has nothing after the execution. If I do the line by line execution, I see that test1 in function is not 'Null'.
If I break the execution and probe the variables I see "test1 =" only as per the screen shot below. And this is breaking my code.
Strangely, If I call the function from 'Immediate Window', it returns the expected value.
Things I have already done:
I have tested in a fresh file using simple code as above.
Tested in different PC and the same code is working fine with same version of Windows 10 & Office 365.
Updated / Re-installed MS Office 365
Restarted the PC
If the return value is a single character like "A", the code is working fine.
Failed to understand the reason here. Any help is appreciated.
UPDATE1
I tried it on a fresh file while the code above worked, but the main code is having a new similar problem. This has started happening just now. It's not assigning a string value to the variable. See the attached screenshot.Screenshot of the VBA Code. I am assuming there is some problem with system or some virus.
If the idea is to have a function, that an array, this is possible with the following code:
Function Test1() As Variant
ReDim result(2)
result(0) = "AJ"
result(1) = "A"
Test1 = result
End Function
Sub Main()
Dim varTest As Variant
varTest = Test1(0)
Debug.Print varTest
varTest = Test1(1)
Debug.Print varTest
End Sub
It is questionable why would it be needed, but as a "test-exercise" it is ok.
Going to put my comments into an answer to consolidate and add more explanation.
Pointing out some errors in the code before correcting:
Function test1(num1) 'declare `as variant` to ensure you're returning an array
test1 = "AJ" 'this appears to be saving a single string to var test1
test1 = "A" 'you are now overwriting the above string
End function
Sub test()
varTest = test1(1) 'you have a single string from the function and arrays start at 0, not 1
End sub
You would want to specify the place in the array, after declaring an array, within your function such that:
Function test1() As Variant
Dim arr(2) As Variant 'added array because test1 = BLAH is the final output in a function
arr(0) = "AJ" 'added (1) to call location in array
arr(1) = "A" 'added (2) to call location in array
test1 = arr
End Function
Sub test()
Dim varTest As Variant
varTest = test1(0) 'outputs "AJ" in immediate window
Debug.Print varTest
End Sub
Now you can debug.print your array values, or set to varTest based on the location in the array.
Edit: Tested after my consolidating comments and recognized that there was not an actual output for test1 as an array at the end of the function, so had to go back and add a second array to set test = allowing an array output from a function.
Your code is running as it should.
The test1 function assigns the value AJ to the test1 variable, and then it assigns the value A to the test1 variable.
You could assign the value 50 in your test procedure and it will return A.
I think this is the code you're after:
Function test1(num1) As String
' Dim MyArray As Variant
' MyArray = Array("AJ", "A")
'OR
Dim MyArray(0 To 1)
MyArray(0) = "AJ"
MyArray(1) = "A"
If num1 >= LBound(MyArray) And num1 <= UBound(MyArray) Then
test1 = MyArray(num1)
Else
test1 = "Item not here"
End If
End Function
Sub test()
Dim varTest As String
'Return the second item in the array from the function.
varTest = test1(1)
MsgBox varTest
'Return the first item in the array from the function.
varTest = test1(0)
MsgBox varTest
'Returns "subscript out of range" error as array is only 2 elements in size (0 and 1).
'The error is dealt with in the function using the IF....ELSE...END IF block and returns
'"Item not here" instead.
varTest = test1(2)
MsgBox varTest
End Sub
I solved this by using declaring the variables even when option explicit is not used.
The old code runs without throwing errors even when the variable is not declared and option explicit is also not used. But, for some reasons, it doesn't read / write undeclared variables as expected.
Now as per #cyril suggestion, I declared the variables being used and run the code. This time code ran as expected.
This happened for multiple of variables and at different stages in the code.

Is it possible to pass the fields of an array to a function with variant parameter array?

I am in the following situation: I have a function, that takes a ParamArray of type variant and generates a string from the keywords given in its ParamArray in a special manner by execution mergeToString.
Function function1(ParamArray var() As Variant) As String
For i = LBound(var) To UBound(var)
function1 = mergeToString(function1, CStr(var(i))
Next i
End Function
In another subroutine, I have an array of strings obtained from the Split Function in VBA and want to use it as an input for function1
Sub displayFCTN1()
Dim arr() As String
arr() = Split("foo|bar", "|")
'and here I ran out of ideas...
Debug.Print function1(**???**)
End Sub
The two lines
function1(**???**)
function1("foo","bar")
should be equivalent the first somehow using arr().
In Matlab this is relatively easy - I know, VBA is not Matlab, still this might help as an extended description of my problem:
you could most likely do it by using the colon operator in Matlab
function1(arr(:))
since then the fields of the array arr() count as "free" parameters.
Is there something comparable to this in VBA? I tried ReDim already, that somehow didn't do the job (as far as I tried).
Thank you for your help!
You need to test, whether the first item of array is array:
Sub FFF()
MsgBox Func1("foo", "bar")
MsgBox Func1(Split("foo|bar", "|"))
End Sub
Function Func1$(ParamArray var() As Variant)
Dim s$, x%, args
args = IIf(IsArray(var(0)), var(0), var)
'//Do something
For x = 0 To UBound(args)
s = s & args(x) & "|"
Next
Func1 = Left$(s, Len(s) - 1)
End Function
A workaround as mentioned in the comments above
Sub displayFCTN1()
Dim arr() As String
arr() = Split("foo|bar", "|")
Myhelper arr
End Sub
Sub Myhelper(arr)
Select Case UBound(arr)
Case 0: Debug.Print function1(arr(0))
Case 1: Debug.Print function1(arr(0), arr(1))
Case 2: Debug.Print function1(arr(0), arr(1), arr(2))
Case 3: Debug.Print function1(arr(0), arr(1), arr(2), arr(3))
Case 4: Debug.Print function1(arr(0), arr(1), arr(2), arr(3), arr(4))
'etc up to 29.
Case Else
End Select
End Sub
This does require a change to function1 code, but should still work with orginal.
Sub Test()
Debug.Print function1("foo", "bar")
Dim arr() As String
arr = Split("foo|bar", "|")
Debug.Print function1(arr)
End Sub
Function function1(ParamArray var() As Variant) As String
Dim i As Long
If UBound(var) = 0 Then
For i = LBound(var(0)) To UBound(var(0))
'function1 = Join(var(0), "|")
function1 = mergeToString(function1, CStr(var(0)(i)))
Next i
Else
'Original code.
For i = LBound(var) To UBound(var)
'function1 = Join(var, "|")
function1 = mergeToString(function1, CStr(var(i)))
Next i
End If
End Function

Use comma separated string as function parameters

I have created a comma separated string. I want to use this string as input in an Application.Run procedure.
The string looks exactly like below - with quotes..
MyString = "First_Number", "Second_Number", "Third_Number"
I want to use this string as parameter input:
Application.Run("'" & "Book2.xlsm" & "'!MySub", *MyString*)
It doesn't work.. I get an "argument not optimal".
I build the string with the following code, and the range is just a row with the parameters.
Function csvRange(myRange As Range)
Dim csvRangeOutput
For Each entry In myRange
csvRangeOutput = csvRangeOutput & entry.Value & """, " & """"
Next
csvRange = Left(csvRangeOutput, Len(csvRangeOutput) - 4)
End Function
Hope someone can help.
Evaluate might do the trick. Look at this piece of test-code:
Sub Test()
Dim params As String
params = "3,5"
Debug.Print Evaluate("addNumbers(" & params & ")")
End Sub
Function addNumbers(a As Integer, b As Integer) As Integer
addNumbers = a + b
End Function
It's code that writes code, how exiting! :D
maybe you can use something like this.
String MyString = "First_Number, Second_Number, Third_Number";
String words[] = MyString.split(",");
Then use the individual words as words[0],words[1], etc.
or you can loop through the array words[] and use the individual terms as you like.

function to return all checked items from list box into cell

I would like to get all checked items in a active x list box into a cell on the worksheet. I have this code that exactly does what I want as a sub:
Private Sub CommandButton1_Click()
Dim lItem As Long
Dim outstr As String
For lItem = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(lItem) = True Then
outstr = outstr & ListBox1.List(lItem) & "/"
End If
Next
Range("A1") = Left(outstr, Len(outstr) - 1)
End Sub
This takes all checked items into cell a1.
I convert this into a function like this:
Function CopySelected(lb As MSForms.ListBox) As String
Dim lItem As Long
Dim outstr As String
For lItem = 0 To lb.ListCount - 1
If lb.Selected(lItem) = True Then
outstr = outstr & lb.List(lItem) & "/"
End If
Next
CopySelected = Left(outstr, Len(outstr) - 1)
End Function
But I cannot give the right argument to the function to return the same as the sub. What do I need to put as an argument please?
I tried the following:
=Copyselected(Sheet1.ListBox1)
=Copyselected(Sheet1.ListBoxes("ListBox1"))
=Copyselected(Sheet1.Shapes("ListBox1").OLEFormat.Object)
=Copyselected(Worksheets("Sheet1").ListBox1)
Nothing seem to work. Is the function incorrect or the passing?
Use the following code:
Public Function GetAllSelected(strListBox As String)
Dim lItem As Long
Dim outstr As String
With Worksheets(1).OLEObjects(strListBox).Object
For lItem = 0 To .ListCount - 1
If .Selected(lItem) = True Then
outstr = outstr & .List(lItem) & "/"
End If
Next lItem
End With
GetAllSelected = Left(outstr, Len(outstr) - 1)
End Function
Then call it passing the name of the ListBox as a string:
=GetAllSelected("ListBox1")
Note, that the sheet must be know at this time. If you need to pass the sheet name (as well) to the function then you will have to adjust the code accordingly.
Update:
For the proactive approach as outlined in the comments below using the ListBox1_Change() event the result would be something like this:
Yet, in this case you would have one procedure for each ListBox and the result of the sub would be always written into the same cell (hard-coded).
Using code from #Ralph, you should also be able to use your function as is...
Try...
=Copyselected(Sheet1.OLEObjects("ListBox1").Object)

Resources