Trouble with object array as object property in VBA Excel 2010 - excel

Private oiCustoms() As CCustomClass
Public Property Get Partners() As CCustomClass()
Set Partners() = oiCustoms()
End Property
Public Property Set Partners(values() As CCustomClass)
ReDim oiPartners(values.Count)
Set oiCustoms() = values()
End Property
When I try to run I get a Compile error:
Definitions of property procedures for the same property are inconsistent, or property procudure has n optional parameter, a ParamArray, or an invalid Set final parameter.
What's wrong here? I have done some looking, and it looks like I can't use an array as a property parameter. Is this correct? Any good workarounds. I will need to have this data for the object stored in an array for use elsewhere.

You are confusing arrays with objects.
Private oiCustoms() As CCustomClass
Public Property Get Partners() As CCustomClass()
Partners = oiCustoms
End Property
Public Property Let Partners(values() As CCustomClass)
ReDim oiPartners(LBound(values) To UBound(values))
oiCustoms = values
End Property

Related

Create class with multiple indexed properties

I'm trying to create a class with multiple properties that are indexed by the same number I pass into the property. I've tried several configurations of code, my latest failure being this:
'dataTypeClass class module
Private ap() As String
Private dt() As String
Public Property Get apos(index As Long) As Variant
Set apos = ap(index)
End Property
Public Property Get dataType(index As Long) As Variant
Set dataType = dt(index)
End Property
Public Property Let apos(index As Long, apVal As String)
ap(index) = apVal
End Property
Public Property Let dataType(index As Long, dtVal As String)
dt(index) = dtVal
End Property
I get this error code mostly:
Expecting to use below to get "INT" and "" for item 1 in sub below.
Sub classTest()
Dim d As New dataTypeClass
d(1).dataType = "INT"
d(1).apos = ""
Debug.Print d(1).dataType & d(1).apos
End Sub
What several things am I doing wrong?
EDIT:
Taking suggestions below (such as editing my question rather than commenting), I'm setting the variants to strings, and using let instead of set. Since I did indeed get a subscript out of range, I need to initialize the class, but do I need to redim index? How might I do that if the index will have a different maximum each time I run the script?
The apos property1 gets a Variant at the specified index; the implementation suggests that the encapsulated ap(index) is an Object, so the return type should probably be Object instead of Variant:
Public Property Get apos(index As Long) As Variant
Set apos = ap(index)
End Property
The setter uses Let assignment, which is hackish, but allowed given the value is Variant - oh wait no, it's a String!
Public Property Let apos(index As Long, apVal As String)
ap(index) = apVal
End Property
You're getting this error because, as the error states, the property definition is inconsistent. If ap(index) is a String, then the getter should look like this:
Public Property Get apos(index As Long) As String
apos = ap(index)
End Property
If ap(index) is an Object, then the setter should look like this:
Public Property Set apos(index As Long, apVal As Object)
Set ap(index) = apVal
End Property
...and the getter like this:
Public Property Get apos(index As Long) As Object
Set apos = ap(index)
End Property
Or like this:
Public Property Get apos(index As Long) As Variant
Set apos = ap(index)
End Property
Public Property Set apos(index As Long, apVal As Variant)
Set ap(index) = apVal
End Property
In other words:
The RHS/value parameter of Property Let/Property Set needs to be the same type as the return type of the same-name Property Get member.
If the type is an object type, use Property Set for assignments.
If the type is a value type, use Property Let for assignments.
Avoid Variant if possible.
Do not use Set to assign anything other than an object reference.
1All of this is also true for the dataType property.
You have a variant for the return type of your Get but your Let additional param is using a string. Perhaps you need dtVal As Variant? As mentioned in comments and other answer there are additional things to consider; some will be implementation specific.
Quote:
The parameters for Property Get, Property Let, and Property
Setprocedures for the sameproperty must match exactly, except that the
Property Let has one extra parameter, whose type must match the return
type of the corresponding Property Get, and the Property Set has one
more parameter than the corresponding Property Get, whose type is
either Variant, Object, a class name, or an object library type
specified in an object library. This error has the following causes
and solutions:
The number of parameters for the Property Get procedure isn't one less
than the number of parameters for the matching Property Let or
Property Set procedure. Add a parameter to Property Let or Property
Set or remove a parameter from Property Get, as appropriate.
The parameter types of Property Get must exactly match the
corresponding parameters of Property Let or Property Set, except for
the extra Property Set parameter. Modify the parameter declarations in
the corresponding procedure definitions so they are appropriately
matched.
The parameter type of the extra parameter of the Property Let must
match the return type of the corresponding Property Get procedure.
Modify either the extra parameter declaration in the Property Let or
the return type of the corresponding Property Get so they are
appropriately matched.
The parameter type of the extra parameter of the Property Set can
differ from the return type of the corresponding Property Get, but it
must be either a Variant, Object, class name, or a validobject library
type.
Make sure the extra parameter of the Property Set procedure is either
a Variant, Object, class name, or object library type.
You defined a Property procedure with an Optional or a ParamArray
parameter. ParamArray and Optional parameters aren't permitted in
Property procedures. Redefine the procedures without using
thesekeywords.

Issue Creating Class with Boolean

I'm having an issue creating this class and it's stating that the Set parameters are invalid and error on the below code. I've triple checked this and can't seem to find the issue! Any idea why this error is occurring?
CODE:
Option Explicit
Private pIsChargeable As Boolean
Public Property Set IsChargeable(value As Boolean)
pIsChargeable = value
End Property
Public Property Get IsChargeable() As Boolean
IsChargeable = pIsChargeable
End Property
ERROR:
Compile Error:
Definitions of property procedures for the same property are inconsistent, or property procedure has an optional parameter, a ParamArray, or an invalid Set final parameter
Property Set is for Object type variables. You're looking for Property Let.
It might be helpful to read Chip Pearson's explanation of classes as well.

VBA Error: Definitions of property procedures for the same property are inconsistent, or property procedure has an optional parameter

I have a very simple Class definition.
Class Sheetwriter is defined as follows:
Option Explicit
'Works off of the ActiveCell
'Helps you write data into the cells
Private pCornerCell As String
Public Property Get CornerCell()
CornerCell = pCornerCell
End Property
Public Property Let CornerCell(Value As String)
pCornerCell = Value
Range(Value).Select
End Property
I get a compile error that I don't understand.
Definitions of property procedures for the same property are inconsistent, or the property procedure has an optional parameter.
What am I doing wrong?
Public Property Get CornerCell()
That's returning an implicit Variant, since no return type was specified.
Public Property Get CornerCell() As String
That's returning the String that the compiler is seeing in the Property Let member, and fixes your problem.
FWIW, that Range(Value).Select statement doesn't belong in there at all, and you don't want to work off the active cell and sprinkle Select and Activate statements everywhere.
See How to avoid using Select in Excel VBA macros for some tips about avoiding that.

How to emulate the ScriptingContext's "ASPTypeLibrary.Application" Object

I have been tasked with modifying some legacy ActiveX DLLs written in Visual Basic 6. One of the things I need to do is to emulate the "ScriptingContext" object, (so that we can support other mechanisms for running the DLLs other than IIS without having to re-write large chunks of the code).
Something that has been causing me some grief is the "ASPTypeLibrary.Application" object which has two very different ways to access its stored values, eg:
.Application("KeyName")
or
.Application.Value("KeyName")
How can I create my own VB6 class which supports both of these access mechanisms? I can do one or the other but not both?
(a simple code example would be great thanks, I'm not a VB6 programmer)
I have found a way to do this, see the code snippet below taken from two classes, "clsContext" and "clsContextApp". The latter implements the ".Value" functionality and the former has the ".Application" property...
I have now discovered an even more difficult problem. The ScriptingContext's "ASPTypeLibrary.Request" object has three different ways to access its ".Request.QueryString" property:
.Request.QueryString("KeyName")
or
.Request.QueryString.Value("KeyName")
or
.Request.QueryString
The last method returns a string comprised of all the Key/Value pairs concatenated by "&" characters. I have no idea how to implement this?
' clsContext
Public ContextApp As clsContextApp
Public Property Get Application(Optional ByRef Key As Variant = Nothing) As Variant
If (Key Is Nothing) Then
Set Application = ContextApp
Else
If (Not ContextApp.p_Application.Exists(Key)) Then
Application = ""
Else
Application = ContextApp.p_Application.Item(Key)
End If
End If
End Property
Public Property Let Application(ByRef Key As Variant, ByVal Value As Variant)
If (VarType(Key) = vbString) Then
If (VarType(Value) = vbString) Then
If (Not ContextApp.p_Application.Exists(Key)) Then
ContextApp.p_Application.Add Key, Value
Else
ContextApp.p_Application.Item(Key) = Value
End If
End If
End If
End Property
' clContextApp
Public p_Application As Scripting.Dictionary
Public Property Get Value(Key As String) As String
If (Not p_Application.Exists(Key)) Then
Value = ""
Else
Value = p_Application.Item(Key)
End If
End Property
Public Property Let Value(Key As String, Value As String)
If (Not p_Application.Exists(Key)) Then
p_Application.Add Key, Value
Else
p_Application.Item(Key) = Value
End If
End Property
Well I've managed to answer the additional question regarding ScriptingContext's "ASPTypeLibrary.Request" object which has three different ways to access its ".Request.QueryString" property.
I've included a code snippet below that is based on the code from my previous answer for the "ASPTypeLibrary.Application" object. If I add a new Property to the "clsContextApp" class and make it the default property for that class, then it will be called when the ".Application" property is called without any qualification eg:
MyString = Context.Application
Setting a particular property as the default property in VB6 is a little obscure, but I followed the directions I found here.
' clsContextApp Default Property
Property Get Values(Optional ByVal Index As Integer = -1) As String ' This is the Default Value for clsContextApp
Attribute Values.VB_UserMemId = 0
Dim KeyName As String, Value As String
Values = ""
If (Index < 0) Then
For Index = 0 To p_Application.Count - 1
KeyName = p_Application.Keys(Index)
Value = p_Application.Item(KeyName)
If (Index > 1) Then
Values = Values + "&"
End If
Values = Values + KeyName + "=" + Value
Next Index
Else
If (Index < p_Application.Count) Then
KeyName = p_Application.Keys(Index)
Value = p_Application.Item(KeyName)
Values = KeyName + "=" + Value
End If
End If
End Property
Adding a reference to Microsoft Active Server Pages Object Library, and to COM+ Services Type Library, and then using the object browser reveals some basic things you seem to be missing.
GetObjectContext is a global method in COMSVCSLib with no arguments used to retrieve the current ObjectContext as its return value.
ObjectContext is a Class. It has a read-only default property, named Item that takes a String argument and is of type Variant.
Passing "Application" as an argument to Item returns the current instance of the Application Class.
ScriptingContext is a Class. It is obsolete.
Application is another Class. It has a default property named Value that takes a String argument and is of type Variant.
Value is a property of the Application Class and provides access to a read-write key/value pair store where keys are always Strings. Since it is of type Variant you can store objects as well as simple values and arrays of various types.
None of this looks difficult to replicate in VB6. The key/value store could be a Collection or Scripting.Dictionary.

VBA Passing Object into another Objects Collection

I have a set of owners, who each have their own set of opportunities.
I have two class modules, ClmOpportunity which has a bunch of properties, and ClmOwner which has a single name property and a Collection storing ClmOpportunity Objects:
Public name As Variant
Private opps As New collection
Public Function addOpportunity(opp As ClmOpportunity)
opp.ID = opps.Count + 1
opps.Add opp, opps.Count + 1
End Function
These owner objects are also being stored in a collection in my main module. When I try to use the function addOpportunity as shown below:
Dim item As New ClmOpportunity
item.name = "test"
owners.item(overallOwner).addOpportunity (item)
I get the error:
"object doesn't support this property or method"
I am quite new to VBA and I don't understand why this is, I am passing in a ClmOpportunity, so it should be fine right?
Any help would be greatly appreciated!
You don't use parentheses if there's no return value...
owners.item(overallOwner).addOpportunity item
...then you'll get a "type mismatch" error because a collection expects a string value as a key, so you'll need to adjust your addOpportunity function (which should probably be a Sub if you don't intend adding a returned value)

Resources