How can I use a collection inside a class?

D

deltaquattro

Hi,

I would like to add a collection in my class: naturally I would like
to keep all the automagic methods of the Collection object (Add,
Count, etc.). For now I did it like this:

'Class Module
Public ErrorString As Collection

'Class Initialization Method
Private Sub Class_Initialize()
Set ErrorString = New Collection
End Sub

but I know that using Public properties is Not A Good Thing. How could
I do this with a Private collection? Can you show me how to rewrite
the Add, Count, etc. methods? I tried to do that myself but it didn't
work. For example, if I write

Public Function Add(Error As String)
ErrorString.Add String
End Sub

I cannot see .ErrorString.Add among the properties of my class! What
am I doing wrong? Thanks,

Best Regards

deltaquattro
 
B

Bob Phillips

I was going to suggest yesterday that you went this way :).

First, you have two classes, one for the object, one for the collection.
This is example code for these two classes for a User object.

'User class =================================================
Option Explicit

Private mcName As String
Private mcGender As String
Private mcRole As String

Private Sub Class_Initialize()
End Sub

Public Property Get Name() As String
Name = mcName
End Property
Public Property Let Name(Value As String)
mcName = Value
End Property

Public Property Get Gender() As String
Select Case mcGender
Case "M": Gender = "Male"
Case "M": Gender = "Female"
End Select
End Property
Public Property Let Gender(Value As String)
mcGender = Value
End Property

Public Property Get Role() As String
Role = mcRole
End Property
Public Property Let Role(Value As String)
mcRole = Value
End Property


'Users class ================================================
Option Explicit

Private mcUsers As Collection
Private mcTemplate As String

Function NewEnum() As IUnknown
Set NewEnum = mcUsers.[_NewEnum]
End Function

Public Function Add(ByRef mUser As User)
mcUsers.Add mUser, mUser.Name
End Function

Public Property Get Count() As Long
Count = mcUsers.Count
End Property

Public Property Get Items() As Collection
Set Items = mcUsers
End Property

Public Property Get Item(Index As Variant) As User
Set Item = mcUsers(Index)
End Property

Public Sub Remove(Index As Variant)
mcUsers.Remove Index
End Sub


Private Sub Class_Initialize()
Set mcUsers = New Collection
End Sub

Private Sub Class_Terminate()
Set mcUsers = Nothing
End Sub


Not that I also provide you wa way to loop through the collection, just like
in VBA. This is code that demonstrates how to use it

Sub MyUsers()
Dim AllUsers As Users
Dim ThisUser As User
Dim usr As User

Set AllUsers = New Users

Set ThisUser = New User
ThisUser.Name = "Bob"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

Set ThisUser = New User
ThisUser.Name = "delquattro"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

For Each usr In AllUsers

Debug.Print usr.Name & " is a " & usr.Gender & " " & usr.Role
Next usr

Set ThisUser = Nothing
Set AllUsers = Nothing

End Sub
 
D

deltaquattro

Ciao, Bob,

eh, eh, we thought of the same thing to solve my issue of yesterday.
Thanks for the reply! You're answering all of my questions lately.
I'll have to make you a present for Easter :) Uhmmm, your code works
(as always), and running it gave me a funny surprise :) However, I
really don't understand this part:

Private mcTemplate As String

Function NewEnum() As IUnknown
Set NewEnum = mcUsers.[_NewEnum]
End Function

I guess I'll have to study some reference: can you point me to a good
book/tutorial on this aspects of OOP in VBA?

Best Regards

deltaquattro

I was going to suggest yesterday that you went this way :).

First, you have two classes, one for the object, one for the collection.
This is example code for these two classes for a User object.

'User class =================================================
Option Explicit

Private mcName As String
Private mcGender As String
Private mcRole As String

Private Sub Class_Initialize()
End Sub

Public Property Get Name() As String
    Name = mcName
End Property
Public Property Let Name(Value As String)
    mcName = Value
End Property

Public Property Get Gender() As String
    Select Case mcGender
        Case "M": Gender = "Male"
        Case "M": Gender = "Female"
    End Select
End Property
Public Property Let Gender(Value As String)
    mcGender = Value
End Property

Public Property Get Role() As String
    Role = mcRole
End Property
Public Property Let Role(Value As String)
    mcRole = Value
End Property

'Users class ================================================
Option Explicit

Private mcUsers As Collection
Private mcTemplate As String

Function NewEnum() As IUnknown
    Set NewEnum = mcUsers.[_NewEnum]
End Function

Public Function Add(ByRef mUser As User)
    mcUsers.Add mUser, mUser.Name
End Function

Public Property Get Count() As Long
    Count = mcUsers.Count
End Property

Public Property Get Items() As Collection
    Set Items = mcUsers
End Property

Public Property Get Item(Index As Variant) As User
    Set Item = mcUsers(Index)
End Property

Public Sub Remove(Index As Variant)
    mcUsers.Remove Index
End Sub

Private Sub Class_Initialize()
    Set mcUsers = New Collection
End Sub

Private Sub Class_Terminate()
    Set mcUsers = Nothing
End Sub

Not that I also provide you wa way to loop through the collection, just like
in VBA. This is code that demonstrates how to use it

Sub MyUsers()
Dim AllUsers As Users
Dim ThisUser As User
Dim usr As User

    Set AllUsers = New Users

    Set ThisUser = New User
    ThisUser.Name = "Bob"
    ThisUser.Gender = "M"
    ThisUser.Role = "Helper"
    AllUsers.Add ThisUser

    Set ThisUser = New User
    ThisUser.Name = "delquattro"
    ThisUser.Gender = "M"
    ThisUser.Role = "Helper"
    AllUsers.Add ThisUser

    For Each usr In AllUsers

        Debug.Print usr.Name & " is a " & usr.Gender & " " & usr.Role
    Next usr

    Set ThisUser = Nothing
    Set AllUsers = Nothing

End Sub

--

HTH

Bob


I would like to add a collection in my class: naturally I would like
to keep all the automagic methods of the Collection object (Add,
Count, etc.). For now I did it like this:
'Class Module
Public ErrorString As Collection
'Class Initialization Method
Private Sub Class_Initialize()
   Set ErrorString = New Collection
End Sub
but I know that using Public properties is Not A Good Thing. How could
I do this with a Private collection? Can you show me how to rewrite
the Add, Count, etc. methods? I tried to do that myself but it didn't
work. For example,  if I write
Public Function Add(Error As String)
   ErrorString.Add String
End Sub
I cannot see .ErrorString.Add among the properties of my class! What
am I doing wrong? Thanks,
Best Regards
deltaquattro
 
H

Hakyab

Dear Bob:

Could you please explain some of the things in your code, or give a website
with more info?
I did a custom collection, largely by trial and error, and I compared my
code with your sample.

My questions:
1. What does initialize and terminate do? Mine seems to work without these.
2. Whit your code, will AllUsers(i) work? In mine, it does not, I have to
writeout AllUsers.Item(i).
3. What makes "for each xx in " work?
4. What is the role of newenum function?

I know I am asking a lot, but thanks for your post anyway.

Best,

Bob Phillips said:
I was going to suggest yesterday that you went this way :).

First, you have two classes, one for the object, one for the collection.
This is example code for these two classes for a User object.

'User class =================================================
Option Explicit

Private mcName As String
Private mcGender As String
Private mcRole As String

Private Sub Class_Initialize()
End Sub

Public Property Get Name() As String
Name = mcName
End Property
Public Property Let Name(Value As String)
mcName = Value
End Property

Public Property Get Gender() As String
Select Case mcGender
Case "M": Gender = "Male"
Case "M": Gender = "Female"
End Select
End Property
Public Property Let Gender(Value As String)
mcGender = Value
End Property

Public Property Get Role() As String
Role = mcRole
End Property
Public Property Let Role(Value As String)
mcRole = Value
End Property


'Users class ================================================
Option Explicit

Private mcUsers As Collection
Private mcTemplate As String

Function NewEnum() As IUnknown
Set NewEnum = mcUsers.[_NewEnum]
End Function

Public Function Add(ByRef mUser As User)
mcUsers.Add mUser, mUser.Name
End Function

Public Property Get Count() As Long
Count = mcUsers.Count
End Property

Public Property Get Items() As Collection
Set Items = mcUsers
End Property

Public Property Get Item(Index As Variant) As User
Set Item = mcUsers(Index)
End Property

Public Sub Remove(Index As Variant)
mcUsers.Remove Index
End Sub


Private Sub Class_Initialize()
Set mcUsers = New Collection
End Sub

Private Sub Class_Terminate()
Set mcUsers = Nothing
End Sub


Not that I also provide you wa way to loop through the collection, just like
in VBA. This is code that demonstrates how to use it

Sub MyUsers()
Dim AllUsers As Users
Dim ThisUser As User
Dim usr As User

Set AllUsers = New Users

Set ThisUser = New User
ThisUser.Name = "Bob"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

Set ThisUser = New User
ThisUser.Name = "delquattro"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

For Each usr In AllUsers

Debug.Print usr.Name & " is a " & usr.Gender & " " & usr.Role
Next usr

Set ThisUser = Nothing
Set AllUsers = Nothing

End Sub


--

HTH

Bob

deltaquattro said:
Hi,

I would like to add a collection in my class: naturally I would like
to keep all the automagic methods of the Collection object (Add,
Count, etc.). For now I did it like this:

'Class Module
Public ErrorString As Collection

'Class Initialization Method
Private Sub Class_Initialize()
Set ErrorString = New Collection
End Sub

but I know that using Public properties is Not A Good Thing. How could
I do this with a Private collection? Can you show me how to rewrite
the Add, Count, etc. methods? I tried to do that myself but it didn't
work. For example, if I write

Public Function Add(Error As String)
ErrorString.Add String
End Sub

I cannot see .ErrorString.Add among the properties of my class! What
am I doing wrong? Thanks,

Best Regards

deltaquattro


.
 
B

Bob Phillips

deltaquatro,

Well, I like chocolate!

The Private variable mcTemplate is superfluous, I took from this one of my
apps and forgot that bit.

The NewEnum() As IUnknown is the way that we can enumerate our collection
class, just as you can a standard collection.It is a bit of a frig that I
got from VB6 many years ago.

Hakyab,

that should also answer your questions 3 & 4.

On the first, does it really work? I can see that you can get away without
the terminate, garbage collection will take care of that, but the
initialize? How does the collection get setup without that?

On the second, I never thought of that, as I always add the Item anyways.
But it wouldn't work, because if you look at the code for the Users class,
you will see an Item property. That is how we emulate the collection
methods, but you have to call it here.

--

HTH

Bob

Ciao, Bob,

eh, eh, we thought of the same thing to solve my issue of yesterday.
Thanks for the reply! You're answering all of my questions lately.
I'll have to make you a present for Easter :) Uhmmm, your code works
(as always), and running it gave me a funny surprise :) However, I
really don't understand this part:

Private mcTemplate As String

Function NewEnum() As IUnknown
Set NewEnum = mcUsers.[_NewEnum]
End Function

I guess I'll have to study some reference: can you point me to a good
book/tutorial on this aspects of OOP in VBA?

Best Regards

deltaquattro

I was going to suggest yesterday that you went this way :).

First, you have two classes, one for the object, one for the collection.
This is example code for these two classes for a User object.

'User class =================================================
Option Explicit

Private mcName As String
Private mcGender As String
Private mcRole As String

Private Sub Class_Initialize()
End Sub

Public Property Get Name() As String
Name = mcName
End Property
Public Property Let Name(Value As String)
mcName = Value
End Property

Public Property Get Gender() As String
Select Case mcGender
Case "M": Gender = "Male"
Case "M": Gender = "Female"
End Select
End Property
Public Property Let Gender(Value As String)
mcGender = Value
End Property

Public Property Get Role() As String
Role = mcRole
End Property
Public Property Let Role(Value As String)
mcRole = Value
End Property

'Users class ================================================
Option Explicit

Private mcUsers As Collection
Private mcTemplate As String

Function NewEnum() As IUnknown
Set NewEnum = mcUsers.[_NewEnum]
End Function

Public Function Add(ByRef mUser As User)
mcUsers.Add mUser, mUser.Name
End Function

Public Property Get Count() As Long
Count = mcUsers.Count
End Property

Public Property Get Items() As Collection
Set Items = mcUsers
End Property

Public Property Get Item(Index As Variant) As User
Set Item = mcUsers(Index)
End Property

Public Sub Remove(Index As Variant)
mcUsers.Remove Index
End Sub

Private Sub Class_Initialize()
Set mcUsers = New Collection
End Sub

Private Sub Class_Terminate()
Set mcUsers = Nothing
End Sub

Not that I also provide you wa way to loop through the collection, just
like
in VBA. This is code that demonstrates how to use it

Sub MyUsers()
Dim AllUsers As Users
Dim ThisUser As User
Dim usr As User

Set AllUsers = New Users

Set ThisUser = New User
ThisUser.Name = "Bob"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

Set ThisUser = New User
ThisUser.Name = "delquattro"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

For Each usr In AllUsers

Debug.Print usr.Name & " is a " & usr.Gender & " " & usr.Role
Next usr

Set ThisUser = Nothing
Set AllUsers = Nothing

End Sub

--

HTH

Bob


I would like to add a collection in my class: naturally I would like
to keep all the automagic methods of the Collection object (Add,
Count, etc.). For now I did it like this:
'Class Module
Public ErrorString As Collection
'Class Initialization Method
Private Sub Class_Initialize()
Set ErrorString = New Collection
End Sub
but I know that using Public properties is Not A Good Thing. How could
I do this with a Private collection? Can you show me how to rewrite
the Add, Count, etc. methods? I tried to do that myself but it didn't
work. For example, if I write
Public Function Add(Error As String)
ErrorString.Add String
End Sub
I cannot see .ErrorString.Add among the properties of my class! What
am I doing wrong? Thanks,
Best Regards
deltaquattro
 
D

deltaquattro

deltaquatro,

Well, I like chocolate!

Hi,Bob,

ok, my email is working, send me your address and if I find a way to
send you the chocolate which is not overly expensive, you're gonna be
treated to some good Italian chocolate ;)
The Private variable mcTemplate is superfluous, I took from this one of my
apps and forgot that bit.

The NewEnum() As IUnknown is the way that we can enumerate our collection
class, just as you can a standard collection.It is a bit of a frig that I
got from VB6 many years ago.

Ehm, pardon the ignorance, but what "enumerate" means? Surely I'm
missing something here, but looking at your code I got the impression
that the other properties/methods already give your custom Collection
Class all the functions a standard Collection has. You can Set a new
instance of the Collection class, you can Add a member (you also
provide a Key for the item, very smart :) You can Count the members,
you can export the full Collection, you can retrieve a member at a
time, you can Remove members, and you can Set the instance to Nothing.
I don't understand which other functionality is provided by NewEnum().
Hakyab,

that should also answer your questions 3 & 4.

On the first, does it really work? I can see that you can get away without
the terminate, garbage collection will take care of that, but the
initialize? How does the collection get setup without that?

On the second, I never thought of that, as I always add the Item anyways.
But it wouldn't work, because if you look at the code for the Users class,
you will see an Item property. That is how we emulate the collection
methods, but you have to call it here.

Hakyab, Bob,

maybe this time I can of help this time :) The Visual Basic Editor of
Excel doesn't support natively the definition of a Default Property
for a class, i.e. the property which is called automatically when you
don't follow the object name with a dot and the Property name. But you
can add one, using an external text editor and following this useful
link:

http://www.cpearson.com/excel/DefaultMember.aspx

Best Regards,

deltaquatttro
 
B

Bob Phillips

I was only joking, I don't really expect chocolate (but I do like it). I
will be in Italy in May, I am going to Lucca for a week, really looking
forward to it, I love Italian ice-cream and Italian espresso.

Back to the subject matter. Literally, enumerate means to count off one by
one, so in a collection we mean that we will retrieve each item in turn and
process it, in a For Each ... Next loop. This is a standard feature of
built-in collections, and I felt our collection class should have the same
functionality. You can see how it is used in my code example where I display
details of each User (via debug.print).
 
H

Hakyab

Thanks Bob, more things to try out for me.

About initialize, I guess I did it in a round about way. I needed my
collection to start out with a set of objects, so added a load method that
actually does initializing:

If Not (cact Is Nothing) Then
ErrMsg = "Already loaded"
GoTo LoadError
End If
Set cact = New Collection

Best,


Bob Phillips said:
deltaquatro,

Well, I like chocolate!

The Private variable mcTemplate is superfluous, I took from this one of my
apps and forgot that bit.

The NewEnum() As IUnknown is the way that we can enumerate our collection
class, just as you can a standard collection.It is a bit of a frig that I
got from VB6 many years ago.

Hakyab,

that should also answer your questions 3 & 4.

On the first, does it really work? I can see that you can get away without
the terminate, garbage collection will take care of that, but the
initialize? How does the collection get setup without that?

On the second, I never thought of that, as I always add the Item anyways.
But it wouldn't work, because if you look at the code for the Users class,
you will see an Item property. That is how we emulate the collection
methods, but you have to call it here.

--

HTH

Bob

Ciao, Bob,

eh, eh, we thought of the same thing to solve my issue of yesterday.
Thanks for the reply! You're answering all of my questions lately.
I'll have to make you a present for Easter :) Uhmmm, your code works
(as always), and running it gave me a funny surprise :) However, I
really don't understand this part:

Private mcTemplate As String

Function NewEnum() As IUnknown
Set NewEnum = mcUsers.[_NewEnum]
End Function

I guess I'll have to study some reference: can you point me to a good
book/tutorial on this aspects of OOP in VBA?

Best Regards

deltaquattro

I was going to suggest yesterday that you went this way :).

First, you have two classes, one for the object, one for the collection.
This is example code for these two classes for a User object.

'User class =================================================
Option Explicit

Private mcName As String
Private mcGender As String
Private mcRole As String

Private Sub Class_Initialize()
End Sub

Public Property Get Name() As String
Name = mcName
End Property
Public Property Let Name(Value As String)
mcName = Value
End Property

Public Property Get Gender() As String
Select Case mcGender
Case "M": Gender = "Male"
Case "M": Gender = "Female"
End Select
End Property
Public Property Let Gender(Value As String)
mcGender = Value
End Property

Public Property Get Role() As String
Role = mcRole
End Property
Public Property Let Role(Value As String)
mcRole = Value
End Property

'Users class ================================================
Option Explicit

Private mcUsers As Collection
Private mcTemplate As String

Function NewEnum() As IUnknown
Set NewEnum = mcUsers.[_NewEnum]
End Function

Public Function Add(ByRef mUser As User)
mcUsers.Add mUser, mUser.Name
End Function

Public Property Get Count() As Long
Count = mcUsers.Count
End Property

Public Property Get Items() As Collection
Set Items = mcUsers
End Property

Public Property Get Item(Index As Variant) As User
Set Item = mcUsers(Index)
End Property

Public Sub Remove(Index As Variant)
mcUsers.Remove Index
End Sub

Private Sub Class_Initialize()
Set mcUsers = New Collection
End Sub

Private Sub Class_Terminate()
Set mcUsers = Nothing
End Sub

Not that I also provide you wa way to loop through the collection, just
like
in VBA. This is code that demonstrates how to use it

Sub MyUsers()
Dim AllUsers As Users
Dim ThisUser As User
Dim usr As User

Set AllUsers = New Users

Set ThisUser = New User
ThisUser.Name = "Bob"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

Set ThisUser = New User
ThisUser.Name = "delquattro"
ThisUser.Gender = "M"
ThisUser.Role = "Helper"
AllUsers.Add ThisUser

For Each usr In AllUsers

Debug.Print usr.Name & " is a " & usr.Gender & " " & usr.Role
Next usr

Set ThisUser = Nothing
Set AllUsers = Nothing

End Sub

--

HTH

Bob


I would like to add a collection in my class: naturally I would like
to keep all the automagic methods of the Collection object (Add,
Count, etc.). For now I did it like this:
'Class Module
Public ErrorString As Collection
'Class Initialization Method
Private Sub Class_Initialize()
Set ErrorString = New Collection
End Sub
but I know that using Public properties is Not A Good Thing. How could
I do this with a Private collection? Can you show me how to rewrite
the Add, Count, etc. methods? I tried to do that myself but it didn't
work. For example, if I write
Public Function Add(Error As String)
ErrorString.Add String
End Sub
I cannot see .ErrorString.Add among the properties of my class! What
am I doing wrong? Thanks,
Best Regards
deltaquattro


.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top