Object doesn't support this property or method, If I declare variable with correct type - excel

I declare 2 ClassModules:
Class1
Public MyProp As Class2
Public Sub MyMethod(val As Class2)
Set MyProp = val
MsgBox MyProp.Foo
End Sub
Class2
Public Foo As String
Sheet1
Private Sub CommandButton1_Click()
Dim a As Class1
Dim b As Class2
Set a = New Class1
Set b = New Class2
b.Foo = "Bar"
a.MyMethod (b)
End Sub
When I call a.MyMethod (b), I get "Object doesn't support this property or method"
but if I write the code as following, everything works as expected:
Private Sub CommandButton1_Click()
Dim a As Variant
Dim b As Variant
Set a = New Class1
Set b = New Class2
b.Foo = "Bar"
a.MyMethod (b)
End Sub
What Am I doing wrong?

Related

Custom class methods in Excel

I'm trying to create my own custom class module to handle custom class object.
Let's say i want to create a method witch would double the result of object property...
Class1:
Public Property Get MyObject() As Workbooks
Set MyObject = Application.Workbooks
End Property
When i create code like this:
Module1:
Function test()
Dim clstest As New Class1
Debug.Print "Result is: " & clstest.MyObject.Count
End Function
will give me
Result is: 1
since there is one worksheet in my object.
What i`m trying to achive is to make a custom function like this:
test.MyObject.DoubleResult '<- to make the result equals 2
I can`t get the idea how to pass the object to the new class function.
Class 2
Public Function DoubleResult() As Integer
DoubleResult = (The_Object_I_Created_earlier.Count) * 2
End Function
How i cant refer to the object "The_Object_I_Created_earlier"?
As far as I know you have to keep the count in an External variable.
You declare the public Long variable in a Module
Public ClassCount As Long
And on your class initialize and terminate you add and remove from it.
Private Sub Class_Initialize()
ClassCount = ClassCount + 1
End Sub
Private Sub Class_Terminate()
ClassCount = ClassCount - 1
End Sub
The design intent is not clear at all. But I can offer a simple primer with custom classes in VBA, and how one can contain the other.
Here is the code
MyClass1
Private m_value As Long
Private Sub Class_Initialize()
m_count = 0
End Sub
Public Sub InitializeWithCount(ByVal n As Long)
m_count = n
End Sub
Public Property Get Count() As Long
Count = m_count
End Property
MyClass2
Private m_obj As MyClass1
Private Sub Class_Initialize()
Set m_obj = New MyClass1
End Sub
Public Sub InitializeWithObject(ByVal x As MyClass1)
Set m_obj = x
End Sub
Public Sub InitializeWithValue(ByVal n As Long)
Set m_obj = New MyClass1
m_obj.InitializeWithCount n
End Sub
Public Property Get MyResult() As MyClass1
Set MyResult = m_obj
End Property
Module
for testing the above
Public Sub SO_Test()
Dim t1 As MyClass1
Dim t2 As MyClass2
Set t1 = New MyClass1 ' New Class1
t1.InitializeWithCount 100 ' Set value once to Long
Set t2 = New MyClass2 ' New Class2
t2.InitializeWithObject t1 ' Set value once to Class1
Debug.Print t2.MyResult.Count
' 100
t2.InitializeWithValue 200
Debug.Print t2.MyResult.Count
' 200
End Sub

How do you use a public variable in a Class Module?

I'm using a Class Module to make a collection of save buttons all do the same thing. But when I try to get them to run a sub that requires a variable I can't get the variable passed to them.
Edited using #Teasel's advice about properties. The problem seems to be the Let Property is not allowing me to set the variable from Module1.
Class1
Public WithEvents SaveBtn As MSForms.CommandButton
Dim currentrow As Long
Private Sub SaveBtn_Click()
SendMessage
`Even if I just have it Msgbox currentrow it returns 0
End Sub
Property Let GetRow(myrow As Long)
currentrow = myrow
End Property
Property Get GetRow() As Long
GetRow = currentrow
End Property
Module1
`Trying to send the value into the Class using Let
Private Sub SendRow_Click()
Module1.GetRow = 22
End Sub
`Trying to Get the value back from the Class
Public Sub SendMessage()
Dim therow As Long
therow = Module1.GetRow
`I get the "Method or Data Member not found" error in the line above
MsgBox therow
End Sub
UserForm1
`This part works fine
Dim colSaveButtons As New Collection
Private Sub UserForm_Initialize()
Dim i As Long
Dim ctl As MSForms.Control
Dim obEvents As Class1
For Each ctl In Me.Controls
If TypeOf ctl Is MSForms.CommandButton Then
For i = 0 To 5
If ctl.Name = "btnSavePage" & i Then
Set obEvents = New Class1
Set obEvents.SaveBtn = ctl
colSaveButtons.Add obEvents
End If
Next
End If
Next ctl
End Sub
Add a "CurrentRow" field to your class module:
Public WithEvents SaveBtn As MSForms.CommandButton
Public CurrentRow As Long '<< add this
Private Sub SaveBtn_Click()
SendMessage CurrentRow
End Sub
In your loop:
...
If ctl.Name = "btnSavePage" & i Then
Set obEvents = New Class1
obEvents.CurrentRow = 10 'or whatever...
Set obEvents.SaveBtn = ctl
colSaveButtons.Add obEvents
End If
...
And your SendMessage method:
Public Sub SendMessage(CurrentRow As Long)
MsgBox "This works"
End Sub
You can use two differents ways to achieve that.
1. Public Property
To simple access your variable's value you need a Get property and to set its value you need a Let property.
In your Module:
'Your module private variable
Dim nameOfYourModuleVariable As String
...
'Set property to assign a value to your variable
Public Property Let nameOfYourProperty(value As String)
nameOfYourModuleVariable = value
End Property
'Get property to return the value of your variable
Public Property Get nameOfYourProperty() As String
nameOfYourProperty = nameOfYourModuleVariable
End Property
You can then use it like this:
'Set the value
MyModule.nameOfYourProperty = "foo"
'Get the value
MyModule.nameOfYourProperty
I highly recommend to use properties to do such things however you can also simply set your variable as public as shown in point 2.
2. Public Variable
Define your variable to be public so you can access it from everywhere.
In your Module:
Public nameOfYourVariable As String
Get or set the value from another module:
'Set the value
MyModule.nameOfYourVariable = "foo"
'Get the value
MyModule.nameOfYourVariable

Excel VBA: Is there a way to reference an instance of a class stored in a dictionary?

I currently have instances of classes stored using the data structure presented in the image below. Each -List item is a dictionary and each -Info item is an instance of a class.
I read elsewhere that if you Set an instance variable equal to another instance, it just references the original instance. Is this correct?
I have been able to create a reference for fileInfo(1) (in the image) using the following code.
Dim prflInfo As File_Info
Set prflInfo = New File_Info
Set prflInfo = fileList.Items(0)
I have attempted to reference the branchInfo instance using the following code, but I get a Run-time error 13: Type mismatch when I attempt to do so.
Dim prbrInfo As Branch_Info
With prflInfo
Set prbrInfo = New Branch_Info
brKey = .getbrKey(0)
Set prbrInfo = .getbrItem(brKey)
End With
Edit: Included below is the code for the File_Info class. All other classes follow this basic model.
'Class Module: File_Info
'Initialise class variables
Private pfileID As Integer
Private pfilePath As String
Private pfileName As String
Private pbranchList As Scripting.Dictionary
'Declare variantcopy subroutine
Private Declare Sub VariantCopy Lib "OleAut32" (pvarDest As Any, pvargSrc As Any)
Private Sub Class_Initialize()
Set pbranchList = New Scripting.Dictionary
End Sub
Public Property Let fileID(pfileIDi As Variant)
pfileID = pfileIDi
End Property
Public Property Get fileID() As Variant
fileID = pfileID
End Property
Public Property Let filePath(pfilePathi As Variant)
pfilePath = pfilePathi
End Property
Public Property Get filePath() As Variant
filePath = pfilePath
End Property
Public Property Let fileName(pfileNamei As Variant)
pfileName = pfileNamei
End Property
Public Property Get fileName() As Variant
fileName = pfileName
End Property
Public Sub addbrConn(branch As Branch_Info)
pbranchList.Add branch.branchID, branch.brConn
Debug.Print "addbrConn ID: " & branch.branchID
End Sub
Public Sub addBranch(branch As Branch_Info)
pbranchList.Add branch.branchID, branch
Debug.Print pbranchList.Count
End Sub
Public Function countbrList()
countbrList = pbranchList.Count
End Function
Public Function getbrKey(Key As Variant)
getbrKey = pbranchList.Keys(Key)
End Function
Public Function getbrItem(Key As Variant)
getbrItem = GetByRefVariant(pbranchList.Items(Key))
End Function
Public Sub dpbrList()
With pbranchList
Debug.Print pbranchList.Count
For k = 1 To pbranchList.Count
Debug.Print .Keys(k - 1), .Items(k - 1)
Next k
End With
End Sub
Public Sub updbrList(branch As Branch_Info)
Dim branchID As String
branchID = branch.branchID
If pbranchList.exists(branchID) Then
pbranchList.Remove (branchID)
pbranchList.Add branchID, branch
Debug.Print "Complete: " & branchID & " added."
Else
Debug.Print "Error: " & branchID & "does not exist."
End If
End Sub
Private Function GetByRefVariant(ByRef var As Variant) As Variant
VariantCopy GetByRefVariant, var
End Function
Is there a way to reference the branchInfo class, to make it easier to extract the data within it?
Thanks!
Eeshwar
I do things differently in that I iterate through the keys list using a For ... each loop rather than referring to an item number. Here is a snippet that works using two levels.
You can ignore the lines where the property values are written to an array, but they were part of the original code.
cf.Dependents is a dictionary of cDependents within the cFamily object
'Declarations in Main Module
Dim dF As Dictionary, cF As cFamily, cD As cDependents
Dim I As Long, J As Long
Dim V As Variant, W As Variant
...
For Each V In dF
I = I + 1
Set cF = dF(V)
With cF
vRes(I, 1) = .FirstName
vRes(I, 2) = .LastName
vRes(I, 3) = .Birthdate
J = 2
For Each W In .Dependents
J = J + 2
Set cD = .Dependents(W)
With cD
vRes(I, J) = .Relation
vRes(I, J + 1) = .DepName
End With
Next W
End With
Next V
Note that in the sequence, as you show in your question:
set Obj = new Obj
set Obj = myClass(0)
the first line is unnecessary.
IMO it is possible to use simple VBA.Collection, here example for the FileList and BranchList. In this example List classes have Items and Info classes have reference to List where List is wrapper for a VBA.Collection. HTH
For more reading have a look e.g. here.
FileList Class
Option Explicit
Private m_fileInfoCollection As FileInfoCollection
Private Sub Class_Initialize()
Set m_fileInfoCollection = New FileInfoCollection
End Sub
Public Property Get Items() As FileInfoCollection
Set Items = m_fileInfoCollection
End Property
FileInfo Class
Option Explicit
Private m_branchList As BranchList
Private m_fileID As Integer
Private Sub Class_Initialize()
Set m_branchList = New BranchList
End Sub
Public Property Get FileID() As Integer
FileID = m_fileID
End Property
Public Property Let FileID(ByVal vNewValue As Integer)
m_fileID = vNewValue
End Property
Public Property Get BranchList() As BranchList
Set BranchList = m_branchList
End Property
FileInfoCollection Class
Option Explicit
Private m_collection As VBA.Collection
Private Sub Class_Initialize()
Set m_collection = New VBA.Collection
End Sub
Public Sub Add(ByVal newItem As FileInfo)
m_collection.Add newItem, CStr(newItem.FileID)
End Sub
Public Function ItemByKey(ByVal key As String) As FileInfo
Set ItemByKey = m_collection(key)
End Function
Public Function ItemByIndex(ByVal index As Long) As FileInfo
Set ItemByIndex = m_collection(index)
End Function
Public Function Count() As Long
Count = m_collection.Count
End Function
BranchList Class
Option Explicit
Private m_branchInfoCollection As BranchInfoCollection
Private Sub Class_Initialize()
Set m_branchInfoCollection = New BranchInfoCollection
End Sub
Public Property Get Items() As BranchInfoCollection
Set Items = m_branchInfoCollection
End Property
BranchInfo Class
Option Explicit
Private m_branchID As Integer
Public Property Get branchID() As Integer
branchID = m_branchID
End Property
Public Property Let branchID(ByVal vNewValue As Integer)
m_branchID = vNewValue
End Property
BranchInfoCollection Class
Option Explicit
Private m_collection As VBA.Collection
Private Sub Class_Initialize()
Set m_collection = New VBA.Collection
End Sub
Public Sub Add(ByVal newItem As BranchInfo)
m_collection.Add newItem, CStr(newItem.branchID)
End Sub
Public Function ItemByKey(ByVal key As String) As BranchInfo
Set ItemByKey = m_collection(key)
End Function
Public Function ItemByIndex(ByVal index As Long) As BranchInfo
Set ItemByIndex = m_collection(index)
End Function
Public Function Count() As Long
Count = m_collection.Count
End Function
Standard Module
Option Explicit
Sub Demo()
' Fill
Dim bi As BranchInfo
Set bi = New BranchInfo
bi.branchID = 111
Dim fi As FileInfo
Set fi = New FileInfo
fi.FileID = 222
fi.BranchList.Items.Add bi
Dim fl As FileList
Set fl = New FileList
fl.Items.Add fi
' Get
Dim fi1 As FileInfo
Set fi1 = fl.Items.ItemByIndex(1)
Dim bi1 As BranchInfo
Set bi1 = fi1.BranchList.Items(1)
End Sub

VBA Class() object as property of another class

I'm trying to create a class to hold a variable number of items (which are themselves another class object).
So, I have Class 2:
' Class 2 contain each individual quote elements (OTC and MRC)
Private pOTC As String
Private pMRC As String
Public Property Get OTC() As String
OTC = pOTC
End Property
Public Property Let OTC(Value As String)
pOTC = Value
End Property
Public Property Get MRC() As String
MRC = pMRC
End Property
Public Property Let MRC(Value As String)
pMRC = Value
End Property
Then Class 1 contains an array of Class 2:
Private pCurr As String
Private pQuote(20) As Class2
Public Property Get Curr() As String
Curr = pCurr
End Property
Public Property Let Curr(Value As String)
pCurr = Value
End Property
Public Property Set Quote(Index As Integer, cQuote As Class2)
Set pQuote(Index) = cQuote
End Property
Public Property Get Quote(Index As Integer) As Class2
Quote = pQuote(Index)
End Property
And what I would like to do is something like:
Dim myQuotes As Class1
Set myQuotes = New Class1
myQuotes.Curr = "GBP"
myQuotes.Quote(3).OTC = "1200"
The first line setting myQuotes.Curr is no problem, however when I try to set a value inside the array the next line errors with Run-time 91 Object variable or With block variable not set
Any pointers as to what I'm doing wrong and how can I set the values for the elements within the class array?
Thanks in advance!
When you myQuotes.Quote(3) you call Property Get Quote which has an issue.
Your internal array of Class2 is not instantiated so pQuote(Index) refers to an array element of Nothing, when you then myQuotes.Quote(3).OTC = you try to assign to Nothing which fails.
You need to make sure pQuote(Index) is instanced; you can do this on demand:
Public Property Get Quote(Index As Integer) As Class2
If (pQuote(Index) Is Nothing) Then Set pQuote(Index) = New Class2
Set Quote = pQuote(Index)
End Property
(Note the required Set)
Or by adding an intitialisation routine to Class1:
Private Sub Class_Initialize()
Dim Index As Long
For Index = 0 To UBound(pQuote)
Set pQuote(Index) = New Class2
Next
End Sub
You need to set them as New Class2 in Class1:
For intI = LBOUND(pQuote) to UBOUND(pQuote)
Set pQuote(intI) = New Class2
Next IntI
Just as you do with Class1 in your final script.
Maybe it should be
Public Property Let Quote(Index As Integer, cQuote As Class2)
Set pQuote(Index) = cQuote
End Property

Class Object Not Recognized

I'm having an issue with a class object not being recognized (error 91, object reference not set). Here's the code:
---Main (Standard Module)
Option Explicit
Public wbCode As Workbook
Public Sub MainSub()
Dim str As String
Dim tables As New CTables
Call SetExcelObjects
str = tables.shExclusions.Cells(20, 1) ---this produces error 91
End Sub
Public Sub SetExcelObjects()
Dim tables As New CTables
Dim str As String
Set wbCode = ThisWorkbook
Set tables.shExclusions = wbCode.Worksheets("Exclusions")
str = tables.shExclusions.Cells(20, 1) ---this line executes okay
End Sub
---CTables (Class Module)
Option Explicit
Public shExclusions As Worksheet
Try this:
---Main (Standard Module)
Option Explicit
Public wbCode As Workbook
Public Sub MainSub()
Dim str As String
Dim tables As New CTables
Call SetExcelObjects(tables)
str = tables.shExclusions.Cells(20, 1) ---this produces error 91
End Sub
Public Sub SetExcelObjects(tables as CTables)
Dim str As String
Set wbCode = ThisWorkbook
Set tables.shExclusions = wbCode.Worksheets("Exclusions")
str = tables.shExclusions.Cells(20, 1) ---this line executes okay
End Sub
---CTables (Class Module)
Option Explicit
Public shExclusions As Worksheet

Resources