I was given an answer on how to make a general class module: Class "let" stuck in infinite loop
I'm trying to apply this to dictionaries inside my classes.
My class module:
Option Explicit
Private Type categories
Temp As scripting.Dictionary
Humid As scripting.Dictionary
Wind As scripting.Dictionary
End Type
Private this As categories
Public Sub Initialize()
Set this.Temp = New scripting.Dictionary
Set this.Humid = New scripting.Dictionary
Set this.Wind = New scripting.Dictionary
End Sub
Public Property Get Temp(ByVal HourIndex As Long) As Double
Temp = this.Temp(HourIndex)
End Property
Public Property Let Temp(ByVal HourIndex As Long, ByVal Value As Double)
this.Temp(HourIndex) = Value
End Property
Public Property Get Humid(ByVal HourIndex As Long) As Double
Humid = this.Humid(HourIndex)
End Property
Public Property Let Humid(ByVal HourIndex As Long, ByVal Value As Double)
this.Humid(HourIndex) = Value
End Property
Public Property Get Wind(ByVal HourIndex As Long) As Double
Wind = this.Wind(HourIndex)
End Property
Public Property Let Wind(ByVal HourIndex As Long, ByVal Value As Double)
this.Wind(HourIndex) = Value
End Property
I tried to test this in the immediate window with set tester = new WeatherData (the name of the module) and Initialize. That did not work.
I then modified Initialize:
Public Sub Initialize(ByVal variable As categories)
Set variable.Temp = New scripting.Dictionary
Set variable.Humid = New scripting.Dictionary
Set variable.Wind = New scripting.Dictionary
End Sub
and entered Initialize tester, but this did not work either ("Compile Error: Sub or Function not defined").
How do I put three dictionaries in a class module?
The following doesn't solve the problem, but it did skirt around it to the point that I don't have to acknowledge it:
Option Explicit
Private Type categories
Temp(23) As Double
Humid(23) As Double
wind(23) As Double
End Type
Private this As categories
Public Property Get Temp(ByVal HourIndex As Long) As Double
Temp = this.Temp(HourIndex)
End Property
Public Property Let Temp(ByVal HourIndex As Long, ByVal Value As Double)
this.Temp(HourIndex) = Value
End Property
Public Property Get Humid(ByVal HourIndex As Long) As Double
Humid = this.Humid(HourIndex)
End Property
Public Property Let Humid(ByVal HourIndex As Long, ByVal Value As Double)
this.Humid(HourIndex) = Value
End Property
Public Property Get wind(ByVal HourIndex As Long) As Double
wind = this.WindChill(HourIndex)
End Property
Public Property Let wind(ByVal HourIndex As Long, ByVal Value As Double)
this.wind(HourIndex) = Value
End Property
tl;dr: make arrays instead of dictionaries, and cut out initialize entirely. Your "keys" have no choice but to be numbers, but it works. I would be interested in knowing an actual solution, but the specific issue is solved.
Seems you want to implement an indexed property.
Simplified to a bare minimum:
Option Explicit
Private values As Scripting.Dictionary
Private Sub Class_Initialize()
Set values = New Scripting.Dictionary
End Sub
Public Property Get Something(ByVal key As String) As Double
Something = values(key)
End Property
Public Property Let Something(ByVal key As String, ByVal value As Double)
values(key) = value
End Property
You keep the dictionaries safely encapsulated as an implementation detail of your class (external code cannot set them to Nothing, for example), and expose an indexed Get+Let property for each encapsulated dictionary, that takes the index (/key) as a parameter.
In the case of your WeatherData class, this means you can populate the data like this:
Set data = New WeatherData
With data
.Temp("day 1") = 76
.Temp("day 2") = 78
.Humid("day 1") = 0.55
.Humid("day 2") = 0.61
.Wind("day 1") = 0.92
.Wind("day 2") = 1.27
End With
And then retrieve the temperature of "day 1" with data.Temp("day 1").
As for your initializer method, it needed to be called from an instance of the class - being an instance method.
So instead of Initialize tester you should have done tester.Initialize.
Whether you make the internal encapsulated storage an array, a Collection or a Dictionary makes no difference to the calling code - it's an encapsulated implementation detail: your class could just as well store the data in .csv files or into a database if it wanted.
I've found Mathieu Guindon example very instructive but quite minimalist for beginners.
All credits for Mathieu Guindon, but let me share an extended version of his code, using late binding just to change little details.
Class code module named WeatherData:
'Mathieu Guindon,Feb 6 '17
'https://stackoverflow.com/a/43263480
Option Explicit
Private dTemp As Object
Private dHumid As Object
Private dWind As Object
Private Sub Class_Initialize()
Set dTemp = CreateObject("Scripting.Dictionary")
Set dHumid = CreateObject("Scripting.Dictionary")
Set dWind = CreateObject("Scripting.Dictionary")
End Sub
Public Property Get Temp(ByVal key As String) As Double
Temp = dTemp(key)
End Property
Public Property Let Temp(ByVal key As String, ByVal value As Double)
dTemp(key) = value
End Property
Public Property Get TempItemCount() As Long
TempItemCount = dTemp.Count
End Property
Public Property Get Humid(ByVal key As String) As Double
Humid = dHumid(key)
End Property
Public Property Let Humid(ByVal key As String, ByVal value As Double)
dHumid(key) = value
End Property
Public Property Get HumidItemCount() As Long
HumidItemCount = dHumid.Count
End Property
Public Property Get Wind(ByVal key As String) As Double
Wind = dWind(key)
End Property
Public Property Let Wind(ByVal key As String, ByVal value As Double)
dWind(key) = value
End Property
Public Property Get WindItemCount() As Long
WindItemCount = dWind.Count
End Property
Standar code module:
Sub test()
Set Data = New WeatherData
With Data
.Temp("day 1") = 76
.Temp("day 2") = 78
.Humid("day 1") = 0.55
.Humid("day 2") = 0.61
.Wind("day 1") = 0.92
.Wind("day 2") = 1.27
Debug.Print .Temp("day 2")
Debug.Print .Humid("day 2")
Debug.Print .Wind("day 2")
Debug.Print .Wind("day 2")
Debug.Print .TempItemCount
End With
End Sub
In this case you should use late binding as follows:
Private Type categories
Temp As Object
Humid As Object
Wind As Object
End Type
Private this As categories
Public Sub Initialize()
Set this.Temp = CreateObject("Scripting.Dictionary")
Set this.Humid = CreateObject("Scripting.Dictionary")
Set this.Wind = CreateObject("Scripting.Dictionary")
End Sub
Furthermore you can't use Let with multiple arguments. You should use a function to do that:
Public Function SetTemp(ByVal HourIndex As Long, ByVal Value As Double)
this.Temp(HourIndex) = Value
End Function
To run this I used:
Sub test()
Dim multi As Dictionaries
Set multi = New Dictionaries
multi.Initialize
multi.SetTemp 13, 25.522
Debug.Print multi.Temp(13)
End Sub
Where my class module is named Dictionaries. So basically use late binding and change all your multi argument let functions to simple functions.
Related
While this code works and I can assign and retrieve values across all levels, intellisense only displays the methods or properties 1 level deep. How would I go about coding this so that I can follow my "Path" all the way down using intellisense and not necessarily have to just remember the methods or properties?
for instance if I type Wip. I get
but when I type Wip.Parts("Test"). , the SequenceNumbers member and its Methods/Properties are not displayed
I have the following code
clsSeq:
Option Explicit
Private iSeq As String
Private iQty As Double
Public Property Get Qty() As Double
Qty = iQty
End Property
Public Property Let Qty(lQty As Double)
iQty = lQty
End Property
Public Property Get Sequence() As String
Sequence = iSeq
End Property
Public Property Let Sequence(lSeq As String)
iSeq = lSeq
End Property
clsPart:
Option Explicit
Private iPart As String
Public SequenceNumbers As Collection
Public Property Get PartNumber() As String
PartNumber = iPart
End Property
Public Property Let PartNumber(lPart As String)
iPart = lPart
End Property
Public Sub AddSequence(aSeq As String, aQty As Double)
Dim iSeq As clsSeq
If SeqExists(aSeq) Then
Set iSeq = SequenceNumbers.Item(aSeq)
iSeq.Qty = iSeq.Qty + aQty
Else
Set iSeq = New clsSeq
With iSeq
.Sequence = aSeq
.Qty = aQty
End With
SequenceNumbers.Add iSeq, iSeq.Sequence
End If
Set iSeq = Nothing
End Sub
Private Sub Class_Initialize()
Set SequenceNumbers = New Collection
End Sub
Private Function SeqExists(iSeq As String) As Boolean
Dim v As Variant
On Error Resume Next
v = IsObject(SequenceNumbers.Item(iSeq))
SeqExists = Not IsEmpty(v)
End Function
clsParts:
Option Explicit
Public Parts As Collection
Public Sub AddPart(iPart As String)
Dim iPrt As clsPart
If Not PartExists(iPart) Then
Set iPrt = New clsPart
With iPrt
.PartNumber = iPart
End With
Parts.Add iPrt, iPrt.PartNumber
End If
End Sub
Private Function PartExists(iPT As String) As Boolean
Dim v As Variant
On Error Resume Next
v = IsObject(Parts.Item(iPT))
PartExists = Not IsEmpty(v)
End Function
Private Sub Class_Initialize()
Set Parts = New Collection
End Sub
modTest:
Sub TestWipCls()
Dim Wip As clsParts
Dim Part As clsPart
Set Wip = New clsParts
Wip.AddPart ("Test")
Set Part = Wip.Parts("Test")
Part.AddSequence "Proc7", 1505
Debug.Print Wip.Parts("Test").SequenceNumbers("Proc7").Qty
Part.AddSequence "Proc7", 100
Debug.Print Wip.Parts("Test").SequenceNumbers("Proc7").Qty
End Sub
That is because Parts is a Collection and its Default Member Call (or .Item) will return a value/object depending what was stored. While editing your code VBA does not know what kind of value/object is stored in the collection (as this is only established during run-time, eg. late-bound), so it can not give you any Intellisense-suggestions.
To circumvent this, you need a method (property/function) that returns a defined type of value/object (early-bound).
btw. (myCollection.("Foo") is the same as myCollection.Item("Foo"))
The solution is to create a custom collection that returns a value of a defined type.
The following example also explains how to implement a custom Collection so you can use the default member call instead of using .Item.
How to use the Implements in Excel VBA
While we're at it, please never use public variables in classes, make them accessible via Property Let/Set/Get methods!
More on this here: https://rubberduckvba.wordpress.com/2019/07/08/about-class-modules/
Edit:
Example for a custom Collection for classes that implement ICustomElement (Interfaces are explained in the link above)
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "CustomCollectionTemplate"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'#Folder("Classes")
Option Explicit
Private Type TCustomCollection
CustomCollection as Collection
End Type
Dim this as TCustomCollection
Private Sub Class_Initialize()
Set this.CustomCollection = New Collection
End Sub
Private Sub Class_Terminate()
Set this.CustomCollection = Nothing
End Sub
Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
Attribute NewEnum.VB_MemberFlags = "40"
Set NewEnum = this.CustomCollection.[_NewEnum]
End Property
Public Sub Add(ByVal newCustomElement As ICustomElement)
this.CustomCollection.Add newCustomElement
End Sub
Public Sub Remove(ByVal Index As Long)
this.CustomCollection.Remove Index
End Sub
Public Function Item(ByVal Index As Long) As ICustomElement
Set Item = this.CustomCollection.Item(Index)
End Function
Public Function Count() As Long
Count = this.CustomCollection.Count
End Function
Thanks to M.Doerner & Mathieu Guindeon for the edits/comments
I have two classes of shapes and an interface. After I instantiate objects from each class and set their properties I add them to a collection. That's pretty straightforward. Then I declare a variable of type MyInterface and loop through the collection to add each shape. But there is an additional property for each type of shape that I want to set but which are not part of the interface. Is there a way to do this? I think other languages call this type casting but I am not sure. Does VBA support this? Any help is appreciated. My code is below:
Interface (iShape)
Option Explicit
Public Property Let Top(value As Long)
End Property
Public Property Get Top() As Long
End Property
Public Property Let Left(value As Long)
End Property
Public Property Get Left() As Long
End Property
Public Property Let Width(value As Long)
End Property
Public Property Get Width() As Long
End Property
Public Property Let Height(value As Long)
End Property
Public Property Get Height() As Long
End Property
Public Function Draw(obj As Worksheet) As Excel.Shape
End Function
Class (cDiamond)
Option Explicit
Private pTop As Long
Private pLeft As Long
Private pWidth As Long
Private pHeight As Long
Private pColor As Long
Implements iShape
'====================Properties====================
Public Property Let Top(value As Long)
pTop = value
End Property
Public Property Get Top() As Long
Top = pTop
End Property
Public Property Let Left(value As Long)
pLeft = value
End Property
Public Property Get Left() As Long
Left = pLeft
End Property
Public Property Let Width(value As Long)
pWidth = value
End Property
Public Property Get Width() As Long
Width = pWidth
End Property
Public Property Let Height(value As Long)
pHeight = value
End Property
Public Property Get Height() As Long
Height = pHeight
End Property
Public Property Let Color(value As Long)
pColor = value
End Property
Public Property Get Color() As Long
Color = pColor
End Property
'====================Methods====================
Public Function Draw(obj As Worksheet) As Excel.Shape
Set Draw = obj.Shapes.AddShape(msoShapeFlowchartOffpageConnector, Me.Left, Me.Top, Me.Width, Me.Height)
End Function
'====================Interface====================
Private Property Get iShape_Height() As Long
iShape_Height = Height
End Property
Private Property Let iShape_Height(RHS As Long)
Height = RHS
End Property
Private Property Get iShape_Left() As Long
iShape_Left = Left
End Property
Private Property Let iShape_Left(RHS As Long)
Left = RHS
End Property
Private Property Get iShape_Top() As Long
iShape_Top = Top
End Property
Private Property Let iShape_Top(RHS As Long)
Top = RHS
End Property
Private Property Get iShape_Width() As Long
iShape_Width = Width
End Property
Private Property Let iShape_Width(RHS As Long)
Width = RHS
End Property
Private Function iShape_Draw(obj As Worksheet) As Shape
Set iShape_Draw = Draw(obj)
End Function
Class (cTextbox)
For the sake of brevity, this class is identical to cDiamond except it has a Caption property instead of a Color property.
Module (mTest)
Option Explicit
Private Sub Test()
Dim wks As Excel.Worksheet
Set wks = ActiveSheet
Dim c As Collection
Set c = New Collection
Dim d1 As cDiamond
Set d1 = New cDiamond
d1.Top = 10
d1.Left = 10
d1.Height = 25
d1.Width = 25
d1.Color = RGB(255, 0, 0)
c.Add d1
Dim d2 As cDiamond
Set d2 = New cDiamond
d2.Top = 50
d2.Left = 10
d2.Height = 25
d2.Width = 25
d2.Color = RGB(0, 255, 0)
c.Add d2
Dim t1 As cTextbox
Set t1 = New cTextbox
t1.Top = 90
t1.Left = 10
t1.Height = 25
t1.Width = 25
t1.Caption = "Textbox"
c.Add t1
Dim shp As iShape
For Each shp In c
shp.Draw wks
' I would like to set the color or caption properties depending on the type of shape in the collection.
Next shp
Set c = Nothing
End Sub
If I understand you correctly, (and I don't fully understand interfaces either), you should be able to do what you want by declaring shp as of type variant.
Dim shp
For Each shp in C ...
shp will then take on the type of either cDiamond or cTextbox depending on which is retrieved from the Collection. Then you will be able to retrieve or modify shp.color or shp.caption. You may also want to change Caption to datatype String in cTextBox
Declaring shp as object you will lose intellisense. Another technique would be to have Interface return a reference to the instance of object. Similarly to how OLEObject.Object returns the instance of the object that it is wrapping. In my example I use This to return the instance of the class.
In this way you will have intellisense for all the common properties and methods. We can also access the properties and methods that are unique to the classes that implement your interface using a With statement.
You'll need to first test check the type of the object. Then use a With statement to temporarily instantiate a new Object of that type and write your code inside the With statement .
If TypeOf shp Is cTextbox Then
With New cTextbox
Msgbox .Caption
End With
End If
Finally, you simply replace the New instance with the actual instance of the Object.
If TypeOf shp Is cTextbox Then
With shp.This
Msgbox .Caption
End With
End If
Interface (iShape)
Public Function This() As Object
End Function
Classes cTextbox & cDiamond
Public Function This() As Object
Set This = Me
End Function
Public Function iShape_This() As Object
Set iShape_This = This
End Function
mTest.Test
Dim shp As iShape
For Each shp In c
shp.Draw wks
If TypeOf shp Is cTextbox Then
With shp.This
MsgBox .Caption
End With
End If
' I would like to set the color or caption properties depending on the type of shape in the collection.
Next shp
' CDays
Private pZone As New Collection
Public Property Get Zone() As Collection
Set Zone = pZone
End Property
Public Property Let Zone(Value As Variant)
For Each element In Value
pZone.Add element
Next
End Property
and sub has
' Main Sub
dy.Zone.item("Zone1")(5) = 0
Where dy is an instance
"Zone1" is set as the key for Item1 in below
Structure for dy instance looks as follows
This does not change the value in Item(5) however. Why?
Create class which will wrap the items which will be added to VBA.Collection. Then it is possible to add/change such items using the collection reference. HTH
Class Foo
Option Explicit
Private m_id As Integer
Private m_value As String
Public Property Get ID() As Variant
ID = m_id
End Property
Public Property Let ID(ByVal vNewValue As Variant)
m_id = vNewValue
End Property
Public Property Get Val() As Variant
Val = m_value
End Property
Public Property Let Val(ByVal vNewValue As Variant)
m_value = vNewValue
End Property
Module
Option Explicit
Sub test1()
Dim col As VBA.Collection
Set col = New VBA.Collection
Dim f As Foo
Set f = New Foo
f.ID = 1
f.Val = "AAA"
col.Add f, VBA.CStr(f.ID)
Debug.Print col(f.ID).Val
col(f.ID).Val = "BBB"
Debug.Print col(f.ID).Val
End Sub
Output
AAA
BBB
Recently learned a bit of object oriented in Python, and I'm trying to do the same things in VBA.
I manage to construct a parent object (PC) that contains a dictionary of children objects:hooks. Hooks is also an object with a dictionary of children: rows.
All I want to do it to be able to write:
for each hook in PC
for each row in hook
sheets("X").cells(i,1) = contract.price
next row
next hook
Im looking at this but can't make it work...
Here summary of classes:
Class PC
Option Explicit
Public pPC As Object
Private pName As String
Private pInclude As Boolean
Private Sub Class_Initialize()
Set pPC = CreateObject("Scripting.Dictionary")
End Sub
Private Sub Class_Terminate()
Set pPC = Nothing
End Sub
Public Property Get hook(HookName As String) As CHook:
Set hook = pPC(HookName)
End Property
Public Sub Add(hook As CHook):
If Not pPC.exists(hook.Name) Then pPC.Add hook.Name, hook
End Sub
Public Property Get Include(HookName As String) As Boolean:
pInclude = pPC.exists(HookName)
Include = pInclude
End Property
Public Property Let Name(pcname As String):
pName = pcname
End Property
Public Property Get Name() As String:
Name = pName
End Property
Class Hook
Option Explicit
Public pHook As Object
Private pName As String
Private pLTFlatPrice As Double
Private pLTBasisPrice As Double
Private pLTDate As Date
Private Sub Class_Initialize()
Set pHook = CreateObject("Scripting.Dictionary")
pLTDate = Sheets("Control").Cells(2, 2)
End Sub
Private Sub Class_Terminate()
Set pHook = Nothing
End Sub
Public Sub AddRow(Row As CRow)
If Not pHook.exists(Row.ContractLot) Then pHook.Add Row.ContractLot, Row
If Row.TradeDate < pLTDate Then
pLTDate = Row.TradeDate
If IsNumeric(Row.FlatMV) And Row.FlatMV <> 0 Then pLTFlatPrice = Row.FlatMV
If IsNumeric(Row.BasisMV) Then pLTBasisPrice = Row.BasisMV
End If
End Sub
Public Property Get Row(ContractLot As String) As CRow:
Set Row = pHook.Item(ContractLot)
End Property
Public Property Let Name(HookName As String):
pName = HookName
End Property
Public Property Get Name() As String:
Name = pName
End Property
Public Property Get LTFlatPrice() As Double:
LTFlatPrice = pLTFlatPrice
End Property
Public Property Get LTBasisPrice() As Double:
LTBasisPrice = pLTBasisPrice
End Property
Public Property Get LTDate() As Double:
LTDate = pLTDate
End Property
and here is the peace of code where the error happens (Object doesn't support this property or method):
For i = 2 To UBound(path, 1)
tName = path(i, 1)
Next i
Set PC = SArray.PC(tName)
For Each hook In PC
For Each row In hook
With Sheets("COB")
.Cells(ii, 2) = row.PC
.Cells(ii, 3) = row.hook
.Cells(ii, 4) = row.Period
End With
ii = ii + 1
Next row
Next hook
You can iterate over either the keys or the items of a dictionary:
Sub Tester()
Dim d As New Scripting.Dictionary
Dim k
d.Add "one", 1
d.Add "two", 2
d.Add "three", 3
For Each k In d.Keys
Debug.Print k
Next
For Each k In d.Items
Debug.Print k
Next
End Sub
So, you can expose your dictionary as a property of an object and iterate over that. It does mean you need to specify .Items though (since it will default to keys.
I have a class with the following properties:
Dim pBonds() as string
Private Property Get Bonds() As String
Bonds = pBonds
End Property
Private Property Get Bond(index As Long) As String
Bond = pBonds(index)
End Property
Private Property Let Bond(index As Long, strValue As String)
If index > UBound(pBonds) Then ReDim Preserve pBonds(index)
pBond(index) = strValue
End Property
when I try:
Set o = New CBondBasket
For k = LBound(arr) To UBound(arr)
o.Bond(k) = arr(k)
Next k
I get error Method or data member not found
Any idea where that comes from?
made the changes
marked them as public now and added initialization and byval (got me another error w/o it)
Private Sub Class_Initialize()
ReDim pBonds(0)
End Sub
Public Property Get Bonds() As String()
Bonds = pBonds
End Property
Public Property Get Bond(index As Long) As String
Bond = pBonds(index)
End Property
Public Property Let Bond(ByVal index As Long, ByVal strValue As String)
If index > UBound(pBonds) Then ReDim Preserve pBonds(index)
pBonds(index) = strValue
End Property
error is: 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 can anyone help me with that? thanks
You also need to initialise the pBonds array or you will get an error when calling UBound the first time:
Main module
Option Explicit
Sub testClass()
Dim o As CBondBasket
Dim k As Long
Dim arr As Variant
arr = Array(1, 2, 3, 4, 5)
Set o = New CBondBasket
For k = LBound(arr) To UBound(arr)
o.Bond(k) = arr(k)
Next k
For k = LBound(o.Bonds) To UBound(o.Bonds)
Debug.Print o.Bond(k)
Next k
End Sub
Class CBondBasket
Private pBonds() As String
Private Sub Class_Initialize()
ReDim pBonds(0)
End Sub
Public Property Get Bonds() As String()
Bonds = pBonds
End Property
Public Property Get Bond(index As Long) As String
Bond = pBonds(index)
End Property
Public Property Let Bond(index As Long, strValue As String)
If index > UBound(pBonds) Then ReDim Preserve pBonds(index)
pBonds(index) = strValue
End Property
Your class methods are marked Private if you want to expose them to automation clients make them Public.
(You also need parens to return an array: Public Property Get Bonds() As String())
Option Compare Database
Option Explicit
Public Function test1() As Integer
Dim sdate(2) As Date
Dim edate(2) As Date
Dim serdat As Class_erviceDate
sdate(1) = #1/2/2015#
edate(1) = #10/21/2015#
sdate(2) = #2/5/2015#
edate(2) = #12/25/2015#
Set serdat = New Class_ServiceDate
serdat.serviceStart = sdate
serdat.serviceEnd = edate
Debug.Print serdat.serviceStart(1), serdat.serviceEnd(1)
Debug.Print serdat.serviceStart(2), serdat.serviceEnd(2)
End Function
Option Compare Database
Option Explicit
Private f_datServiceStart As Variant
Private f_datServiceEnd As Variant
Public Property Get serviceStart() As Variant
serviceStart = f_datServiceStart
End Property
Public Property Let serviceStart(Value As Variant)
f_datServiceStart = Value
End Property
Public Property Get serviceEnd() As Variant
serviceEnd = f_datServiceEnd
End Property
Public Property Let serviceEnd(Value As Variant)
f_datServiceEnd = Value
End Property