How to pass a dynamic array into a VBA object. Compile error: Invalid use of property - vba

I'm trying to pass an array into a custom class for storage and further use within that object. The class object has the following definition:
' Class Name: MBRMCurves
Implements ICurves
Private m_lInterpDates() As Long
Public Property Get InterpDates() As Long()
InterpDates = m_lInterpDates
End Property
Public Property Let InterpDates(lInterpDates() As Long)
m_lInterpDates = lInterpDates
End Property
The module that calls this code looks like this:
Dim objResult As New MBRMCurves
'Store the forward prices
Dim fx_fwd() As Double
'Store the interpolation dates
Dim int_dates() As Long
'initially there are no people
Dim NumberTenors As Integer
NumberTenors = 0
Dim cell As range
' Create ranges of Dates
Dim range As range
Dim range_topcell As range
' TODO Pri1 Create the Curves Obj
With Worksheets("test")
' Populate the dates of the FWD rates.
Set range_topcell = .range("B5")
Debug.Print range_topcell.Value
Set range = .range(range_topcell, range_topcell.End(xlDown))
Debug.Print range.Count
' Add more columns to the FWD array
ReDim fx_fwd(0 To range.Count - 1, 0 To 3)
ReDim int_dates(0 To range.Count - 1)
' Set the counter
NumberTenors = 0
' Populate the dates of the FWD rates into the first column of the dates array.
For Each cell In range
NumberTenors = NumberTenors + 1
int_dates(NumberTenors - 1) = cell.Value
Next cell
' Add interpolation dates to Curves object
objResult.InterpDates int_dates
The last line in the above code is giving me the compile error: Invalid use of property.
I believe that the syntax of me Let function is correct, but I might be missing a more subtly nuanced oversight.
Can anyone see what I'm doing wrong? I'm working with Excel 2003 and VBA 6.5 on Windows XP.
Any suggestions would be greatly appreciated.
Thanks,
Christos

a property is not a method call and you need to set it equal to your array:
objResult.InterpDates = int_dates
There still might be an issue with the array your passing in but this is a first step.

Related

VBA Excel Invalid ReDim/Expected Array with Array of Ranges Module

I have a module that stores an array of Range objects that is called in other modules. While this module is functional, it's sloppy, and I would like the code to be easy to read/edit for future developers. Ideally this would not only be easy to read/edit but also be a range array (as opposed to variant array).
How the module is called(ideally would be 'As Range'):
Sub CallModule()
'...
Dim rangeArray As Variant
'...
rangeArray = RngArr()
'...
Call AnotherModule(rangeArray(count))
End Sub
Current Module:
Public Function RngArr() As Variant
RngArr = Array(Range("'ActivityTracker'!B12"), Range("'ActivityTracker'!H12"), Range("'ActivityTracker'!B26"), Range("'ActivityTracker'!H26"), Range("'ActivityTracker'!B39"), Range("'ActivityTracker'!H39"), Range("'ActivityTracker'!B53"))
End Function
I am getting a couple of errors when I attempt to put it together,
Returns 'expected array':
Public Function RngArr() As Range
ReDim RngArr(0 To 6) '<---Expected Array
Set RngArr(0) = Range("'ActivityTracker'!B12")
Set RngArr(1) = Range("'ActivityTracker'!H12")
Set RngArr(2) = Range("'ActivityTracker'!B26")
Set RngArr(3) = Range("'ActivityTracker'!H26")
Set RngArr(4) = Range("'ActivityTracker'!B39")
Set RngArr(5) = Range("'ActivityTracker'!H39")
Set RngArr(6) = Range("'ActivityTracker'!B53")
End Function
Returns 'Invalid ReDim':
Public Function RngArr() As Variant
ReDim RngArr(0 To 6) As Range '<---Invalid ReDim
Set RngArr(0) = Range("'ActivityTracker'!B12")
Set RngArr(1) = Range("'ActivityTracker'!H12")
Set RngArr(2) = Range("'ActivityTracker'!B26")
Set RngArr(3) = Range("'ActivityTracker'!H26")
Set RngArr(4) = Range("'ActivityTracker'!B39")
Set RngArr(5) = Range("'ActivityTracker'!H39")
Set RngArr(6) = Range("'ActivityTracker'!B53")
End Function
I don't know VBA well enough to know exactly what's going on with these errors and I have a number of these modules that need to be fixed. So if someone could give a quick explanation of why I am getting these errors and how to fix them I would really appreciate it!
EDIT: The purpose of this module is to give global access to the locations of various tables in the worksheet so the locations themselves are what matter, not the values in the cells. But this array is used a few times in the workbook because other modules need access to the tables in order to be able to work properly. Also I know you can reference the tables directly but there are many cases in this particular program that would make referencing tables individually much harder than it needs to be.
Public Function RngArr() As Range()
Dim rv(0 To 6) As Range
Set rv(0) = Range("'ActivityTracker'!B12")
Set rv(1) = Range("'ActivityTracker'!H12")
Set rv(2) = Range("'ActivityTracker'!B26")
Set rv(3) = Range("'ActivityTracker'!H26")
Set rv(4) = Range("'ActivityTracker'!B39")
Set rv(5) = Range("'ActivityTracker'!H39")
Set rv(6) = Range("'ActivityTracker'!B53")
RngArr = rv
End Function
Sub Tester()
Debug.Print RngArr()(2).Address()
End Sub
It's not clear what you're trying to do here.
The following code works though:
Public Function testArr() As Variant
Dim newArr() As Range
ReDim newArr(1 To 5) As Range
Set newArr(1) = Sheets("Sheet1").Range("A1")
testArr = newArr
End Function
Public Sub test()
Dim myArr As Variant
myArr = testArr()
End Sub
myArr is still going to be a variant when it gets returned, not a range array if you do it this way, but this seems to match your intent.

Excel VBA refer to chart seriescollection by string name

I have a need to refer to a seriescollection by name. The MSDN object model page for seriescollection states either an index or a name can be used. However when I try to use a string variable I get error 1004 'Invalid parameter':
Dim sSeriesName As String
dim aRootCats() as string
Dim cSeriesCol As SeriesCollection
For x = 0 To UBound(aRootCats)
sSeriesName = aRootCats(x)
Set cSeriesCol = cChart.ChartGroups(1).SeriesCollection(sSeriesName)
Next x
I've tried the following too:
sSeriesName = CStr(aRootCats(x)) ' just in case, because I filled this array with variant data type earlier
Set cSeriesCol = cChart.ChartGroups(1).SeriesCollection(sSeriesName)
and
Set cSeriesCol = cChart.ChartGroups(1).SeriesCollection(aRootCats(x))
and
Set cSeriesCol = cChart.ChartGroups(1).SeriesCollection("Product Support")
UPDATE: I'm also experiencing the same error when looping on the seriescollection:
For c = 1 To cChart.ChartGroups(1).SeriesCollection.Count
Set cSeriesCol = cChart.ChartGroups(1).SeriesCollection(c)
Next c
Any ideas?
Many thanks.
Because you are using a ChartGroup not a Chart, you have to use the Item property of the SeriesCollection and you have to pass a variant. Use either:
cChart.ChartGroups(1).SeriesCollection.Item(1)
or:
cChart.ChartGroups(1).SeriesCollection.Item(CVar(sSeriesName))
(or declare the variable as Variant rather than String)

Edit Value In Excel Range

I have an excel sheet with values in a range which I want to perform some calculations on in vb.net. I pass that range to vb.net using COM. When I try editing the values of the range, nothing happens.
My question seems to be very similar to these questions, but I can't quite figure out what step I am missing.
How to edit cell value in VB.net - Using .Interop.Excel
VB.net Office Solution - Accessing value in named Range in a Worksheet
VBA CODE:
Function MyTestRange (Byref myrng as range)
Set classLib = New VBProject.CClass
MyTestRange = classLib.MyTestRange(myrng)
End Function
VB.NET code
Imports Microsoft.Office.Interop.Excel
Public Class CClass
Function MyTestRange(ByRef myrng As Range) As Double
Dim newrng As Range
Dim b As Integer = myrng.Rows.Count
Dim i As Integer
newrng = myrng
For i = 1 To b
newrng.Value2(i, 1) = myrng.Value2(i, 1) + 1
Next i
MyTestRange = newrng.Value2(1, 1)
End Function
End class
While this code doesn't generate an error, it doesn't change the values in newrng.
Edit:
I have tried many iterations based on the link provide, but always get the same error :
An exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll but was not handled in user code
Additional information: Exception from HRESULT: 0x800A03EC
This is the simplest way to generate the error:
Dim c As Object
c = myrng.Value
myrng.Value = c
Hence amending the values to something new, seems out of reach!
Edit 2:
After chatting with user Mat's Mug, I tried to edit a value in a range, purely in VBA.
This sub works perfectly:
Sub rangesub()
Dim example As Range
Set example = Range("A1:A4")
example.Value = Application.Transpose(Array(1, 2, 3, 4))
End Sub
Excel functions also seem able to handle passing ranges:
Function SimpleCopyRange(myrng)
SimpleCopyRange = myrng
End Function
A very simple combination of this code works:
Function EditRange(myrng)
Dim example As Range
Set example = Range("A1:A4")
EditRange = example
End Function
However there is an unspecified error if I try to edit the range:
Function EditRange(myrng)
Dim example As Range
Set example = Range("A1:A4")
example.Value = Application.Transpose(Array(1, 2, 3, 4))
EditRange = example
End Function
The application.transpose part obviously adds a layer of complexity, and can be replaced with example.Value = 8 without any changes in results.
Looking at this question/answer:
Excel VBA: Iterating over range parameter and change cell values
It appears that a UDF can only change the calling cell.
I think therefore this method is impossible.
The workaround is to to rewrite the code to work with an array, assign the range to an array, edit the array and pass it to the new function.

When one should use Set [e.g for SpecialCells return value]?

I am afraid I misunderstand the documentation of VBA for excel, I have this line which seems to be an error:
Range a = Selection.SpecialCells(xlCellTypeConstants, 23)
But this one is just fine:
Set a = Selection.SpecialCells(xlCellTypeConstants, 23)
The documentation claims:
Returns a Range object that represents all the cells that match the specified type and value.
But it actually returns a byRef object and that is why I have to use Set.
What do I miss here?
Here is Range.SpecialCells method help in Excel:
Range a = Selection.SpecialCells(xlCellTypeConstants, 23)
This is not valid VBA, regardless of data type. You don't declare variable type in front of variable name, as you would do in C#, and you don't initialize variable at the point of declaration, as you would do in VB.NET.
You can do:
Dim a As Range
Set a = Selection.SpecialCells(xlCellTypeConstants, 23)
This will save a reference to the range into a.
You can also do:
Dim a As Variant
a = Selection.SpecialCells(xlCellTypeConstants, 23)
This will save in a a 2D array of values of cells in the range.
Returns a Range object that represents all the cells that match the specified type and value.
But it actually returns a byRef object and that is why I have to use Set.
There are no byval objects in VBA. All objects are byref, and when you want to copy a reference to an object, you always use Set. The reason why you need Set is default properties. Each object can have a default property that is requested when only object name is provided. This creates ambiguity, so you need to say Set when you need to manipulate the object reference itselt, and omit Set when you want the default property of an object. The default property of Range is Value.
Object variables are assigned using the Set keyword. Non-object variables (let's ignore variants for now) do not use the Set keyword
dim a as int
dim b as string
dim c as boolean
a = 1
b = "hello"
c = false
dim a as Range
dim b as Worksheet
dim c as PivotTable
set a = ActiveSheet.Range("a1")
set b = ActiveSheet
set c = ActiveSheet.PivotTables(1)
Range.SpecialCells Method returns a Range object that represents all the cells that match the specified type and value.
set keyworkd is used to assign a reference to an object.
There are two flavors of assignments in VBA: one for ordinary variables, which use Let, and one for object variables, which use Set.
An ordinary (string, logical, numeric) variable is one that points to the location in memory where the variable is stored.
An object variable (all the things you find in the language reference under Objects) is one that points to a structure in memory (a VTable) that in turn contains pointers to the object's properties and methods.
Reference http://www.excelforum.com/excel-programming-vba-macros/693357-when-to-use-the-keyword-set.html
Do not know it this matters, but Value is not default property of Range. Default property of Range is _Default.
It is defined like this
_Default([in, optional] VARIANT RowIndex, [in, optional] VARIANT ColumnIndex) and it represents the array of values of particular range.
Sub test()
Dim rng As Range
Set rng = ActiveSheet.Range("A1:C3")
' all cells in range rng become value of 1
rng.Value = 1
' all cells in range rng become now value of 2
rng.[_Default] = 2
' first cell in range rng become value of 3
rng.[_Default](1, 1) = 3
' nothing changes
rng.Value()(1, 1) = 4
Dim a1, a2
' however both Value and _Default return same array of variants
a1 = rng.Value
a2 = rng.[_Default]
End Sub

Syntax options creating errors in VBA Macro for Excel

I'm having some trouble with syntax options while writing a VBA Macro for Excel. In VBA you can call a method on an object in two different ways:
foo.bar(arg1, arg2)
or
foo.bar arg1, arg2
I absolutely detest the second sort of syntax because I find it lacks any sort of clarity, so I normally adhere to the first option. However, I've come across a situation where using the first option creates an error, while the second executes fine. (This may perhaps be an indicator of other problems in my code.) Here is the culprit code:
Function GetFundList() As Collection
Dim newFund As FundValues
Range("A5").Select
Set GetFundList = New Collection
While Len(Selection.Value)
Set newFund = New FundValues
' I set the fields of newFund and move Selection
The problem is in this next line:
GetFundList.Add newFund
Wend
End Function
FundValues is a class I created that is essentially just a struct; it has three properties which get set during the loop.
Basically, when I call GetFundList.Add(newFund) I get the following error:
Run-time error '438':
Object doesn't support this property or method
But calling GetFundList.Add newFund is perfectly fine.
Does anyone understand the intricacies of VBA well enough to explain why this is happening?
EDIT: Thanks much for the explanations!
Adding items to a collection is not defined as a function returning a value, but as a sub routine:
Public Sub Add( _
ByVal Item As Object, _
Optional ByVal Key As String, _
Optional ByVal { Before | After } As Object = Nothing _
)
When calling another sub routine by name and sending arguments (without adding the "Call" statement), you are not required to add parentheses.
You need to add parentheses when you call a function that returns a value to a variable.
Example:
Sub Test_1()
Dim iCnt As Integer
Dim iCnt_B As Integer
Dim iResult As Integer
iCnt = 2
iCnt_B = 3
fTest_1 iCnt, iResult, iCnt_B
End Sub
Public Function fTest_1(iCnt, iResult, iCnt_B)
iResult = iCnt * 2 + iCnt_B * 2
End Function
Sub Test_2()
Dim iCnt As Integer
Dim iCnt_B As Integer
Dim iResult As Integer
iCnt = 2
iCnt_B = 3
iResult = fTest_2(iCnt, iCnt_B)
End Sub
Public Function fTest_2(iCnt, iCnt_B)
fTest_2 = iCnt * 2 + iCnt_B * 2
End Function
Let me know if not clear.
This Daily Dose of Excel conversation will be helpful.
When you use the parentheses you are forcing VBA to evaluate what's inside them and adding the result to the collection. Since NewFund has no default property - I assume - the evaluation yields nothing, so can't be added. Without the parentheses it evaluates to the instance of the class, which is what you want.
Another example. This:
Dim coll As Collection
Set coll = New Collection
coll.Add Range("A1")
Debug.Print coll(1); TypeName(coll(1))
and this ...
coll.Add (Range("A1"))
Debug.Print coll(1); TypeName(coll(1))
... both yield whatever is in A1 in the debug.window, because Value is Range's default property. However, the first will yield a type of "Range", whereas the type in the 2nd example is the data type in A1. In other words, the first adds a range to the collection, the 2nd the contents of the range.
On the other hand, this works:
Dim coll As Collection
Set coll = New Collection
coll.Add ActiveSheet
Debug.Print coll(1).Name
... and this doesn't:
coll.Add (ActiveSheet)
Debug.Print coll(1).Name
because ActiveSheet has no default property. You'll get an runtime error 438, just like in your question.
Here's another way of looking at the same thing.
Let assume that cell A1 contains the string Hi!
Function SomeFunc(item1, item2)
SomeFunc = 4
End Function
Sub Mac()
' here in both of the following two lines of code,
' item1 will be Variant/Object/Range, while item2 will be Variant/String:
SomeFunc Range("A1"), (Range("A1"))
Let i = SomeFunc(Range("A1"), (Range("A1")))
'this following is a compile syntax error
SomeFunc(Range("A1"), (Range("A1")))
' while here in both the following two lines of code,
' item1 will be Variant/String while item2 will be Variant/Object/Range:
SomeFunc ((Range("A1")), Range("A1")
Let j = SomeFunc((Range("A1")), Range("A1"))
'this following is a compile syntax error
SomeFunc((Range("A1")), Range("A1"))
Set r = Range("A1") ' sets r to Variant/Object/Range
Set r = (Range("A1")) ' runtime error 13, type mismatch; cannot SET r (as reference) to string "Hi!" -- Strings are not objects in VBA
Set r = Range("A1").Value ' runtime error (same)
Let r = Range("A1") ' set r to "Hi!" e.g. contents of A1 aka Range("A1").Value; conversion to value during let = assignment
Let r = (Range("A1")) ' set r to "Hi!" e.g. contents of A1 aka Range("A1").Value; conversion to value by extra ()'s
Let r = Range("A1").Value ' set r to "Hi!" by explicit use of .Value
End Sub
I only add this to help illustrate that there are two things going on here, which could be conflated.
The first is that the () in an expression that converts the item to its Value property as stated above in other answers.
The second is that functions invoked with intent to capture or use the return value require extra () surrounding the whole argument list, whereas functions (or sub's) invoked without intent to capture or use the return value (e.g. as statements) must be called without those same () surrounding the argument list. These surrounding () do not convert the argument list using .Value. When the argument list has only one parameter, this distinction can be particularly confusing.