Excel: Visual Basic: range as function input doesn't work - excel

I'm starting with VB in Excel; so far I can't figure the following;
I've got the following function:
Function testGetRange(myRange As Range)
Dim weekStart As Integer
Dim weekEnd As Integer
weekStart = myRange(1).Value
weekEnd = myRange(2).Value
End Function
If I try to execute it like that:
Sub CreationRapport()
Dim testRange1 As Range
Set testRange1 = Range("A5:B5")
testGetRange (testRange1)
End Sub
I've got an error like "object needed" (sorry the error message is in French: "objet requis"), stopping right when I try to execute the function.
So: the range is created, the function takes a range as input; don't know why this doesn't work...

You are calling a function and the parantheses signify that you want the function to return something:
testGetRange (testRange1)
But your function doesn't return anything. You can fix this by adding this to testGetRange:
testGetRange ="My return output"
...And you don't put the output anywhere. You can fix this by changing in CreationRapport:
MyOutput = testGetRange (testRange1)
msgbox MyOutput

When you call a function but don't want a return value you need to either leave off the parenthesis
Sub CreationRapport()
Dim testRange1 As Range
Set testRange1 = Range("A5:B5")
testGetRange testRange1
End Sub
Or use call
Sub CreationRapport()
Dim testRange1 As Range
Set testRange1 = Range("A5:B5")
Call testGetRange (testRange1)
End Sub
For the why you can see how VBA handles transferring control to a sub or function here on MSDN
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 use
either Call syntax to call any intrinsic or user-defined function, the
function's return value is discarded.

OK, so after testing the different answers, this worked:
Function testGetRange(myRange As Range) As String
Dim weekStart As String
Dim weekEnd As String
weekStart = myRange(1)
weekEnd = myRange(2)
testGetRange = weekStart
End Function
And in the Sub:
Sub CreationRapport()
Dim myOutput As String
Dim testRange1 As Range
Set testRange1 = Range("A5:B5")
myOutput = testGetRange(testRange1)
MsgBox myOutput
End Sub
The MsgBox is not mandatory, but the part myOutput = testGetRange(testRange1) is!
So, as advised by Doug, need to work more on the VB to see why it is. Thank you all :)

Related

How to pass a ListColumn to a function or Sub?

My code is pretty simple, I am looking for a particular word in a column from a different worksheet. I made a "find" function to return True or false depending on whether or not it finds this word. The find function has two parameters, the particular column in the worksheet and the word it is looking for. See code below:
Public Function FoundExp(expID As String, ByVal lsCol As listColumn) As Boolean
Dim exp As String
Dim listCol As ListColumn
exp = expID
Set listCol = lsCol
FoundExp = Not lsCol.Range.find(exp) Is Nothing 'changed per BigBen <--error thrown here
End Function
When I call this function from another sub I get the error saying the object hasn't been set. Also, when I checked the datatype it comes out as a string.
This is the calling line:
Any idea on what I am doing wrong?
Edit: It might have something to do with the way I am instantiating the original listColumn that I am passing.
I am calling the function from a separate sub, in this case in a different module, but the final position of the sub will on the same module as the find function.
Calling sub looks like:
Sub Foo()
Dim tbl2 as ListObject
Dim lsCol as ListColumn
Set tbl2 = ws.ListObjects.("TableName")
Set lsCol = tbl2.ListColumns(2)
Debug.Print lsCol 'this reads column header text
Debug.Print VarType(lsCol) 'this reads as 8
FoundExp "foo", lsCol
End Sub

Argument not optional when calling sub-procedure that passes dict as a value

My question is a little different here because I am trying to call a sub-procedure that has passed a dictionary as a parameter and it keeps returning the error 'Argument not optional'. Please help!
Sub Code1()
Call sub_input
End Sub
Sub sub_input (dicDat as Dictionary)
Dim ws As Worksheet: Set ws =ActiveSheet
Dim i As Integer
Dim j As Integer
Dim vTemp As Variant
Range("rInputStart").Parent.Calculate
vTemp =Range(Range("rInputStart").Offset(1),_
Range("rInputStart").End(xlDown).Offset(0,2)).value
Dim price as Long
Dim currency As String: currency = vbNullString
Dim exchangeRate as String: exchangeRate = vbNullString
Dim remark as String: remark = vbNullString
For j =1 To 10
price = price & dicDat ("price" & CStr (j))&"|"
price = price ("rPriceManual").value
currency = currency & dicDat("dl_currency"&CStr(j))&"|"
exchangeRate =(exchangeRate & _
dicDat("exchange_rate"&CStr(j))&"|")/100
Remark= remark & dicDat("remarks"&CStr(j))&"|"
For i =LBound(vTemp,1)ToUBound(vTemp,1)
If vTemp(i,1)="currency"And dicDat(dl_currency)<> vbNullString _
Then
vTemp(i,3)= currency
Endif
If vTemp(i,2)="remark"Then
vTemp(i,3)=Remark
EndIf
If vTemp(i,2)="exchangeRate"Then
vTemp(i,3)= exchangeRate
EndIf
Next i
Next j
End Sub
Try creating a scripting.dictionary object to pass over to the sub.
Option Explicit
Sub Code1()
Dim dict As New Scripting.Dictionary
dict.Item(10) = "abc"
dict.Item(11) = "bcd"
dict.Item(12) = "cde"
sub_input dict
End Sub
Sub sub_input(dicDat As Scripting.Dictionary)
Dim k As Variant
For Each k In dicDat.keys
Debug.Print k & " - " & dicDat.Item(k)
Next k
End Sub
If you prefer late-binding, use dim dict as object then set dict = createobject("scripting.dictionary").
To use this code, go into the VBE's Tools, References then locate Microsoft Scripting Runtime and put a check beside it to include this library in your project. Library references like this are on a project-to-project basis, not a computer-to-computer basis. If you run your workbook on another computer, it will be carried across.
You have called the sub sub_input but you are calling sub_book also sub_input requires a parameter sub_input(dicDat as Dictionary) but you are not adding a parameter to your call code.
For example:
if you called a sub sub Test but then add (name as string) next to it to make Sub Test(Name as string) you are making a variable that is necessary to run the sub. If you wanted to call this sub you would need to call it with a value to give the Name variable as it is a string you would need to surround that with "". as an example one way you could call this is call Test("Geoff") "Geoff" being the name string
The error you are getting is because you have not called your sub with nol value to the dicDat parameter. your code should look like: `call sub_input(TestValue) then that gives your 'dicDat' a value
For a more detailed explanation of argument not optional errors see here.
My suggestion is at the top of every module/class/sheet where you are going to add code type option explicit at the top and then you will find any typos on names or subs
Hope this helps

Excel VBA reference issues

I was following some examples online in which im trying to pass a value from one sub into a another sub in VBA but get the error:
Compile Error:
Procedure declaration does not match description of event or procedure having the same name.
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Call set_button_Click(nextsat)
End Sub
Sub set_button_Click(ByRef nextsat As String)
......
End Sub
Change the sub name in something else like SetButtonOnClick.
The _Click keyword is reserved by excel for the Click event on buttons if you have a button called with the same name.
You can't change the parameters for an event handler (except for the parameter name). That also means you can't add any parameters if none are expected. Not even Optional ByRef nextsat As String will work.
There are three ways to pass a value between event handlers in a UserForm:
Using a global variable (not recommended, ever);
Via the UserForm.tag property (recommended for simple values such as strings). Obviously cannot be used if it already has a permanent use;
Via one or more hidden controls (recommended for multiple or complex values as well as simple ones).
I've used the second method:
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = Weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Me.Tag = nextsat
End Sub
Sub set_button_Click()
Dim nextsat As String
nextsat = Me.Tag
......
End Sub
A better solution in your case might be to have a visible TextBox in which you store the calculated date when the user clicks next_sat, so that the user can see it. Then in your set_button handler, grab it from TextBox.Text.

Can I Evaluate An Excel VB Constant That Is In String Format?

Is it possible to Evaluate a String which contains a valid Excel VB Constant's Name
to return that Constant's Value?
eg
Dim ConstantName as String
Dim ConstantValue as Long
ConstantName="xlValues"
ConstantValue= UnknownFunction(ConstantName)
'would set ConstantValue=-4163
Fun!
Option Explicit
Function getConstantValue(constStr As String) As Variant
Dim oMod As VBIDE.CodeModule
Dim i As Long, _
num As Long
Set oMod = ThisWorkbook.VBProject.VBComponents("Module1").CodeModule
For i = 1 To oMod.CountOfLines
If oMod.Lines(i, 1) = "Function tempGetConstValue() As Variant" Then
num = i + 1
Exit For
End If
Next i
oMod.InsertLines num, "tempGetConstValue = " & constStr
getConstantValue = Application.Run("tempGetConstValue")
oMod.DeleteLines num
End Function
Function tempGetConstValue() As Variant
End Function
All code must be in a module called Module1. That can be changed pretty simply by changing the text "Module1" in the routine.
You'll need to add a reference to Microsoft Visual Basic for Applications Extensibility x.x
There are a number of ways this could fail. Let me know if you have any problems with it :)
Instead of using constants, you could use a dictionary
Dim dict As Object
Sub InitialiseDict()
Set dict = CreateObject(Scripting.Dictionary)
dict("xlValues") = -4163
dict("const1") = value1
...
dict("constN") = valueN
End Sub
ConstValue = dict("xlValues")
Is using the string value necessary?
Dim anyConstant as Long
anyConstant = xlValues
msgbox anyConstant
Set anyConstant to any xl constant you please, they are all enumerated Long values.
The first solution offered is indeed much more fun however.

Excel UDF detecting page breaks?

I'm trying to write a UDF that returns whether the cell is at a page break.
So far I have this:
Function pbreak() As Boolean
' Application.Volatile
pbreak = False
Dim ra As Range
Set ra = Application.Caller
With ra
For i = 1 To .Worksheet.HPageBreaks.Count
If .Worksheet.HPageBreaks(i).Location.Row = .Row Then
pbreak = True
End If
Next
End With
End Function
This returns a #VALUE error. I've tried debugging it, HPageBreaks.Count returns 3 (and there are 3 page breaks), but HPageBreaks(i) yields an "index out of range"-error for all pagebreaks that are below the current cell .
Is this a bug (ie .Count is wrong), or is there some special behavior with page breaks that I am missing?
Is there a way to fix this (preferably without resorting to on error resume next)?
Thanks
Martin
Option Explicit
Function pbreak() As Boolean
' Application.Volatile
Dim i As Integer 'the missing line
pbreak = False
Dim ra As Range
Set ra = Application.Caller
With ra
For i = 1 To .Worksheet.HPageBreaks.Count
If .Worksheet.HPageBreaks(i).Location.Row <= .Row Then
If .Worksheet.HPageBreaks(i).Location.Row = .Row Then
pbreak = True
'exit the function once a page break is found.
Exit Function
End If
Else
Exit Function
End If
Next
End With
End Function
EDIT: Always use Option Explicit & compile the code before using it.
Use of Exit Function inside the loop is to prevent the code from running it further, once the result is known.

Resources