VBA - Accessing object properties via another object (property set) - vba

New to building class modules in VBA and struggling with populating one object which is defined within another object.
For instance, I have two class modules, Class1 and Class2
Class1
Dim mobj As Class2
Public Property Set SetObj(obj As Class2)
Set mobj = obj
End Property
Class2
Public FirstName As String
These are accessed from a standard module as follows:
Sub test()
Dim X As Class1
Set X = New Class1
Set X.SetObj = New Class2
X.SetObj.FirstName = "Bruce"
End Sub
This however fails in X.SetObj.FirstName = "Bruce" when i get an "Invalid use of property message". Any assistance would be greatly appreciated.

You are close.
Class1:
Private mobj As Class2
Public Property Set Obj(Obj As Class2)
Set mobj = Obj
End Property
Public Property Get Obj()
Set Obj = mobj
End Property
Sub (the X.Obj.FirstName line calls the Get property, not the Set property):
Sub test()
Dim X As Class1
Set X = New Class1
Set X.Obj = New Class2
X.Obj.FirstName = "Bruce"
End Sub

Related

User Defined Class using Implements: how to put common implemented code in one place [duplicate]

This question already has an answer here:
VBA inheritance pattern
(1 answer)
Closed 4 months ago.
I’m creating a set of User Defined Classes using Implements. Some (but not all) of the Properties and Methods of the implemented classes use exactly the same code in each implemented class. What I would like to do is move that code to one place, to avoid repeating myself.
An minimal example to demonstrate the requirement:
Class cMasterClass
Option Explicit
Private pMyClasses As Collection
Public Property Get Item(idx As Long)
Set Item = pMyClasses.Item(idx)
End Property
Public Property Get SomeProperty() As String
SomeProperty = "Master Class"
End Property
Public Sub AddClass(Name As String, Instance As Long)
Dim NewClass As cTemplateClass
Select Case Instance
Case 1
Set NewClass = New cMyClass1
Case 2
Set NewClass = New cMyClass2
End Select
NewClass.Init Me, Name
pMyClasses.Add NewClass, Name
End Sub
Private Sub Class_Initialize()
Set pMyClasses = New Collection
End Sub
Class cTemplateClass
Option Explicit
Public Property Get Name() As String: End Property
Public Property Get Parent() As cMasterClass: End Property
Public Property Get SomeProperty() As String: End Property
Public Sub Init(Parent As cMasterClass, Name As String): End Sub
Class cMyClass1
Option Explicit
Implements cTemplateClass
Private pParent As cMasterClass
Private pName As String
Public Property Get cTemplateClass_Name() As String: cTemplateClass_Name = pName: End Property
Public Property Get cTemplateClass_Parent() As cMasterClass: Set cTemplateClass_Parent = pParent: End Property
Public Property Get cTemplateClass_SomeProperty() As String
cTemplateClass_SomeProperty = "Some String from MyClass 1"
End Property
Public Sub cTemplateClass_Init(Parent As cMasterClass, Name As String)
Set pParent = Parent
pName = Name
End Sub
Class cMyClass2
Option Explicit
Implements cTemplateClass
Private pParent As cMasterClass
Private pName As String
Public Property Get cTemplateClass_Name() As String: cTemplateClass_Name = pName: End Property
Public Property Get cTemplateClass_Parent() As cMasterClass: Set cTemplateClass_Parent = pParent: End Property
Public Property Get cTemplateClass_SomeProperty() As String
cTemplateClass_SomeProperty = "Some String from MyClass 2"
End Property
Public Sub cTemplateClass_Init(Parent As cMasterClass, Name As String)
Set pParent = Parent
pName = Name
End Sub
Standard Module
Sub Demo()
Dim MyMasterClass As cMasterClass
Set MyMasterClass = New cMasterClass
MyMasterClass.AddClass "Example class 1", 1
MyMasterClass.AddClass "Example class 2", 2
Dim SomeInstance As cTemplateClass
Set SomeInstance = MyMasterClass.Item(1)
Debug.Print "Instance 1", SomeInstance.Name, "Parent", SomeInstance.Parent.SomeProperty
Set SomeInstance = MyMasterClass.Item(2)
Debug.Print "Instance 2", SomeInstance.Name, "Parent", SomeInstance.Parent.SomeProperty
End Sub
Notice that in cMyClass1 and cMyClass2 the code for Init, Name and Parent are identical (but SomeProperty is not)
How could I move the common code from the individual classes into one place (I know the Template class cannot contain the common code)?
How could I move the common code from the individual classes into one place (I know the Template class cannot contain the common code)?
Hi Chris, actually the Template class can contain that common code and be used with Implements to ensure that interface implementation. I am late with this answer and I am not sure if this can help you but anyway ... :). What you could consider would be to actually move the parent and name to your template class and use init to fill them and at the same time with using implements create instance of the template so your classes ensure that interface and they can redirect the code to the template at the same time. If you wish have a look here for another example. HTH, Dan
Change the child classes a bit:
Option Explicit
Implements cTemplateClass
'Move this to your template class:
'Private pParent As cMasterClass
'Private pName As String
'and declare the varaible of template class here instead:
Private pTemplate As cTemplateClass
Public Property Get cTemplateClass_Name() As String
'Redirect the property to template class instance
'cTemplateClass_Name = pName
cTemplateClass_Name = pTemplate.Name
End Property
Public Property Get cTemplateClass_Parent() As cMasterClass
'Redirect the property to template class instance
'Set cTemplateClass_Parent = pParent
Set cTemplateClass_Parent = pTemplate.Parent
End Property
Public Sub cTemplateClass_Init(Parent As cMasterClass, Name As String)
' Instantiate here the template class and redirect the Init
' Set pParent = Parent
' pName = Name
Set pTemplate = New cTemplateClass
pTemplate.Init Parent, Name
End Sub
Public Property Get cTemplateClass_SomeProperty() As String
cTemplateClass_SomeProperty = "Some String from MyClass 2"
End Property
And anhance the template class a bit too:
Option Explicit
'Move this here from your 'derived' classes:
Private pParent As cMasterClass
Private pName As String
Public Property Get Name() As String
Name = pName
End Property
Public Property Get Parent() As cMasterClass
Set Parent = pParent
End Property
Public Sub Init(Parent As cMasterClass, Name As String)
Set pParent = Parent
pName = Name
End Sub
Public Property Get SomeProperty() As String
End Property

VB.Net Changing Value of a property in one instance of a class, changes it in another instance

I have the following (greatly simplified) VB.Net code:
Public Class Class1
Public Property X Integer
Public Overrides Function Equals(ByVal obj As [Object]) As Boolean
If obj Is Nothing OrElse Not [GetType]().Equals(obj.GetType()) Then
Return False
End If
Dim c As Class1 = CType(obj, Class1)
Return X.Equals(c.X)
End Function
End Class
Public Class Class2
Public Property Y As Class1
Get
Return propY
End Get
Set(value As Class1)
If value.X <> propY.X Then
End If
End Set
End Property
Private propY as New Class1
End Class
Sub Main()
Dim C1 As New Class1
C1.X = 1
Dim C2 As New Class2
C2.Y = C1
Dim Cx as New Class1
Cx.X = 2
C2.Y = Cx
When the C2.Y = Cx statement executes, the test for inequality in the C2.Y property fails because propY.X is already set to 2 before the test executes. Nothing is declared as "Shared".
How do I fix this?

Access a class modules variables within a second class

I was wondering if there's a way in which I can share variables between instances of separate class modules?
I have two classes:
Class 1
Class 2
Inside class 1, I have multiple global variables which I would like Class 2 to have access to once instantiated.
I could use get and set properties for each of the variables but I have about 40/50 so it just seems a bit tedious.
So, instead, I'm trying to pass the current instance of Class 1 to Class 2 using set property.
I've created a minimal example to illustrate my current efforts:
Class 1:
Public test As String
Private Sub Class_Initialize()
Call setTest
Dim b As Class2
Set b = New Class2
End Sub
Public Property Set Classed(ByRef vClass As Class1)
Set vClass = Me
End Property
Public Sub setTest(t As String)
test = "Sam"
End Sub
Class 2:
Private Sub Class_Initialize()
Dim newClass As Class1
newClass.Classed = newClass
' Want to be able to access the test String from class 1
End Sub
Obviously what I am doing at the moment is incorrect, so am wondering if someone could point out where I'm going wrong and show me how to achieve this class sharing?
Just to add: when running the code, I receive a compile error at line: newClass.Classed = newClass. Error: Invalid use of property
Not too sure but I sense a bit of a Circular Reference in your example?
What Are Circular References?
A circular reference occurs when two objects hold references to each other.
You could try an alternative by exposing a Dictionary object through your class, where the Key will be your "variable name", and the Value will hold the actual value.
An example could be:
Class1
Option Explicit
Private mList As Object
Public Property Get List() As Object
Set List = mList
End Property
Private Sub Class_Initialize()
Set mList = CreateObject("Scripting.Dictionary")
End Sub
Private Sub Class_Terminate()
Set mList = Nothing
End Sub
Implementation:
Sub ClassTest()
Dim a As Class1
Set a = New Class1
Dim b As Class1
Set b = New Class1
a.List("VarName") = "Sam" 'Set
b.List("VarName") = a.List("VarName") 'Get / Set
Debug.Print b.List("VarName") 'Get
Set a = Nothing
Set b = Nothing
End Sub
'Output
'Sam

Array in object vba

I have 2 classes
Class1
Private pClass2Arr(10) as Class2
Public Property Get class2Arr() as Class2()
class2Arr = pClass2Arr
End Property
Public Property Let class2Arr(mClass2Arr() as Class2)
pClass2Arr = mClass2Arr
End Property
Class2
Private pStr1 as String
Private pStr2 as String
Public Property Get str1() as String
str1 = pStr1
End Property
Public Property Let str1(mStr1 as String)
pStr1 = mStr1
End Property
Public Property Get str2() as String
str2 = pStr2
End Property
Public Property Let str2(mStr2 as String)
pStr2 = mStr2
End Property
And Id' like to do
Dim a as Class1
Set a = New Class1
a.class2Arr(0).str1 = "test"
Debug.Print a.class2Arr(0).str1
And I have an error because get property on class2Arr have no arguments
You just declared array, but not initialized it Private pClass2Arr(10) as Class2. I've added Private Sub Class_Initialize() event in Class1:
Private pClass2Arr(10) As Class2
Public Property Get class2Arr() As Class2()
class2Arr = pClass2Arr
End Property
Public Property Let class2Arr(mClass2Arr() As Class2)
pClass2Arr = mClass2Arr
End Property
Private Sub Class_Initialize()
Dim i As Byte ' change Byte to Integer if your array contains more than 255 elements
For i = LBound(pClass2Arr) To UBound(pClass2Arr)
Set pClass2Arr(i) = New Class2
Next
End Sub
and then you can use it like this:
Sub test()
Dim a As Class1
Set a = New Class1
a.class2Arr()(0).str1 = "test"
Debug.Print a.class2Arr()(0).str1
End Sub

Issue when implementing getter and setter from an interface

I have a class which implements another object. I set a property function for each property of the implemented object but keep getting an 'Invalid use of property' error. Here's my code:
Test Sub:
Sub tst()
Dim a As Derived
Set a = New Derived
a.Base_name = "ALGO" 'Error happens when this executes
End Sub
Derived class module:
Option Explicit
Implements Base
Private sec As Base
Private Sub Class_Initialize()
Set sec = New Base
End Sub
Public Property Get Base_name() As String
Call sec.name
End Property
Public Property Let Base_name(value As String)
Call sec.name(value) 'Error happens here
End Property
Base Class module:
Private pname As String
Public Property Get name() As String
name = pname
End Property
Public Property Let name(value As String)
pname = value
End Property
Is this what you want?
Module1
Sub tst()
Dim a As Derived
Set a = New Derived
Debug.Print a.Base_name
a.Base_name = "ALGO"
Debug.Print a.Base_name
End Sub
Base Class Module
Private pname As String
Public Property Get name() As String
name = pname
End Property
Public Property Let name(value As String)
pname = value
End Property
Derived Class Module
Option Explicit
Implements Base
Private sec As Base
Private Sub Class_Initialize()
Set sec = New Base
End Sub
Public Property Get Base_name() As String
Base_name = sec.name
End Property
Public Property Let Base_name(value As String)
sec.name = value
End Property