Enumeration through a collection in a custom class (1 Viewer)

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
I have been making use of custom collection classes based on this article:

Using Custom Collections in Microsoft Access
http://www.databaseadvisors.com/new...ng custom collections in microsoft access.asp

I need to add enumeration through the custom collection. Since this custom class is implementing the code VBA Collection, I suppose I need to only wrap methods of the VBA Collection and expose them in my custom class interface. Am I suspecting correctly? Does anyone happen to have sample code? I am unsure of what sort of return codes VBA expects in order to comply with supporting proper individual object enumeration within a For Each...Next Statement.

Solution:

In post #49 the light bulb came on that a VBA.Collection supports retrieval of Objects stored in the VBA.Collection via two ways:
1) String key name assigned when the Object was Added to the VBA.Collection
2) A Long Integer number which VBA assigns when the Object is Added to the VBA.Collection

Therefore I suggest using the Variant variable type to support both the String / Long data types possible to use to retrieve a particular Object out of the VBA.Collection.
 
Last edited:

Galaxiom

Super Moderator
Staff member
Local time
Today, 13:04
Joined
Jan 20, 2009
Messages
12,852
I am pretty sure you have a good idea what is required, Michael. However I am not clear on exactly what you are wanting the method to return.

BTW, you might want to consider the Dictionary object in the Scripting Library as an alternative to a Collection.

The index in a Dictionary can be searched for a match. The only way to do this in a Collection is to loop through the whole index and test each item. It is probably what the Dictionary does behind the scenes too but it does save a lot of coding.
 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
However I am not clear on exactly what you are wanting the method to return.

I want to arrive at my custom collection class being able to be used within the context of a For Each...Next Statement.

So I will need to supply in my class what ever the "For Each...Next Statement" experts a valid collection to supply.

Obviously the Next / Prev methods usually return my custom object type. But what to return when the end of the line has been reached? The Nothing object in that case, or or or...???

All Intillisense prompts that a VBA Collection supports are: Add / Count / Item / Remove methods. I recall Collection objects themselves support more methods... such capabilities which enable "For Each...Next Statement" support.

I would like to see a working example of a customized collection which has been enabled for use in "For Each...Next Statement" context.
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 04:04
Joined
Sep 12, 2006
Messages
15,656
Michael

I expect for each .. next expects an object of some sort. ie a pointer to a data structure

so i expect you can examine the type of object that was passed to determine how to continue

I just checked, so here's an example. Typename was the first function I found, but I expect there is a constant of some sort as well.

just checked help on TypeName, and you can use IsDate, IsObject etc to determine the type as well.

Code:
Sub showtype(x)
     MsgBox (TypeName(x))
 
    select case typename(x)
    case "collection":
    case else:
    end select
    'etc
End Sub
 
Sub test() 
Dim coll As Collection
     Set coll = New Collection
     showtype coll
End Sub
 
Last edited:

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 04:04
Joined
Sep 12, 2006
Messages
15,656
Further example - works fine with an empty collection, as well. just comment out the coll.add lines


Code:
Sub showobject(obj)
 
Dim item
Dim s As String
Dim items As Long
 
'ignore now.
'MsgBox (TypeName(x) & " ")
 
    s = ""
    items = 0
    For Each item In obj
        If s <> "" Then s = s & ", "
        s = s & item
        items = items + 1
    Next
    MsgBox ("Results: " & items & " items " & vbCrLf & s)
End Sub
 
Sub test()
Dim coll As Collection
 
    Set coll = New Collection
 
    coll.Add 12
    coll.Add 14
    coll.Add 6
 
    showobject coll
 
 
    set coll = nothing
    Set coll = New Collection
 
    showobject coll
End Sub
 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
I expect for each .. next expects an object of some sort. ie a pointer to a data structure

My situation is a bit more elaborate than that. Per the article I linked to, I created a custom collection class. The article stopped short of enabling "For Each...Next Statement" support for the custom collection class. Now I need to enable it.

Since the custom collection class makes use of the native VBA Collection class, I foresee being able to wrap methods of the native VBA Collection class and expose them in my custom class. Just I do not know what methods exist needing to be wrapped in order for my custom class to be "For Each...Next Statement" enabled, and what I should return for my wrapper of the Next method once the end of the Collection has been reached.

The caller should have no knowledge that my class actually has a native VBA Collection class inside my class. My class should behave such that it can be interfaced to with the VBA Collection class protocol.

Better?
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 04:04
Joined
Sep 12, 2006
Messages
15,656
i just looked at the article - just as they have a

oVBCollection.Add( Item, [Key], [Before], [After])

then i presume you just need (say) a

oVBCollection.Iterate

to do a for... next of the items within the collection.


 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
i just looked at the article - just as they have a

oVBCollection.Add( Item, [Key], [Before], [After])

then i presume you just need (say) a

oVBCollection.Iterate

to do a for... next of the items within the collection.

Yes yes, that should be the worker code inside yet to be known methods in my class.

I can envision how the events should work.

What I am not sure of is what methods I need to create, and what the correct thing is to return to signal that the end of the collection has been reached. Like I said before, Next / Prev (or what ever the correct name of those methods should be) usually should return an item from the collection. However, what to return to signal the end of the collection so that the "For Each...Next Statement" would end normally?

I have already populated the Add / Remove / Item / Get / Exists methods but those methods are not complete enough to have VBA support the class for use in a "For Each...Next Statement".
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 04:04
Joined
Sep 12, 2006
Messages
15,656
I don't follow

the for .. next loop just ends when all the itesm have been examined

it's like iterating a recordset

Code:
while not rst.eof
    dostuff
    rst.movenext
wend
in this case

Code:
for each item in collection 
     do stuff
next

i imagine there is a looping structure for a collection similar to the first example.

Code:
while not coll.eof
     do stuff
     coll.movenext
wend


-----
the whole irritation about collections, to my mind - is that the whole thing would be so much easier if VBA gave us a pointer type.

stacks, queues, trees b-trees, doubly linked lists. all hard to implement with collections, and easy to do with poiinters.

as it is, i think I would probably prefer to manage a temporary table to hold the collection - and get the full power of SQL queries, instead.
 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
I don't follow

the for .. next loop just ends when all the itesm have been examined

it's like iterating a recordset

So, a recordset object, a VBA Collection object... what methods do those support to be able to be traversed in a "For Each...Next Statement"?

I have encapsulated a VBA Collection object within a custom class I developed. As-is, an instance of my class does not work in a "For Each...Next Statement" block.

Therefor I need to wrap methods in my class around the VBA Collection object my class contains to have it begin acting / responding like a VBA Collection object.

I am seeking a skeleton / prototype interface of what methods my custom class must have in order to be able to be used as the collection object in a "For Each...Next Statement".
 

stopher

AWF VIP
Local time
Today, 04:04
Joined
Feb 1, 2006
Messages
2,395
The cCollections class is not a collection or an object that can be enumerated. Note that “For Each “ requires a collection to iterate over so the class needs to expose a collection of some sort.

The brutish simple way is to just make the collection public. Then you can iterate (using your example:

Code:
    For Each contact In m_oContacts.m_PrivateCollection
        Debug.Print contact.FullName
    Next contact

Although as I think you already elude, that approach is a bit amateurish as private variables should not really be exposed that way (the principle of encapsulation). You should really use Get/Let to expose only what you want to. But do this you will have to clone the collection for both Get & Let I think. Note that you can't simply write aColl=bColl. You'll need to write a routine to clone it (I think).
 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
The cCollections class is not a collection or an object that can be enumerated. Note that “For Each “ requires a collection to iterate over so the class needs to expose a collection of some sort.

I was hoping that having the VBA Collection defined at class level as private, that I could execute some "get next" method and return each object contained until there was no more, Further, I was hoping that since the VBA Collection was kept in scope by virtue of my custom class object being global to the entire application (created in a separate VBA module) that it would naturally retain knowledge of which entry in the collection it last handed out, thus the next call to "get next" would actually return the object following the prior one returned.

Have I been hoping in vain?
 

MarkK

bit cruncher
Local time
Yesterday, 20:04
Joined
Mar 17, 2004
Messages
8,181
Check out these posts, one where your custom class can expose a default property, and another where your custom class can be indistinguishable from an actual VBA.collection type.

You need to export the module, edit text outside of the VBA IDE, and then reimport the module.

And I just posted a little database here that contains a few classes that snap forms together if you drop them close enough together, but a couple of the objects are collections. One, the cSnapHost, has a collection of cSnapForm objects, which are wrappers around the forms. cSnapForm handles a few of the form's events and contains a Sides property, which is a custom collection of cSnapSide objects, called, yeah, cSnapSides.

It's not robust enough to use in an application, but it's a pretty good demo of the custom collection idea, default property thing, and OOP programming principles in general. Like, what amazed me when it was done was how little code it actually took to implement.

Cheers,
 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
Eureka! Perhaps the silver lining to the cloud I have been in just appeared. I will dig into your links / example shortly. Thank you!
 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
You need to export the module, edit text outside of the VBA IDE, and then reimport the module.

1) I exported all modules from your example DB, and I am not noticing differences in the file header attributes. (When comparing to an exported Class file of my own) Which module(s) were you indicating needed adjusting?

2) I have not exported / imported in order to tinker with the extended attributes before. What is that exact process? Will VBA handle updating the existing module? Or must the module be deleted, then re-imported from the exported/edited file?
 

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631
1) I exported all modules from your example DB, and I am not noticing differences in the file header attributes. (When comparing to an exported Class file of my own) Which module(s) were you indicating needed adjusting?

I found the part about export / import in the web page link. So I need to export / import in order to place this bit in the body of the source code?
Code:
Property Get Item(index) As cSnapSide
[B][COLOR=Blue]Attribute Item.VB_UserMemId = 0[/COLOR][/B]
    Set Item = Items(index)
End Property

Property Get NewEnum() As IUnknown
[B][COLOR=Blue]Attribute NewEnum.VB_UserMemId = -4[/COLOR][/B]
    Set NewEnum = Items.[_NewEnum]
End Property
I was thinking you were referring to attributes above the top line of code visible in the VBA editor.

Aaaahhh... I think I found what you were talking about... am I correct?
 
Last edited:

mdlueck

Sr. Application Developer
Local time
Yesterday, 23:04
Joined
Jun 23, 2011
Messages
2,631

ChrisO

Registered User.
Local time
Today, 13:04
Joined
Apr 30, 2003
Messages
3,202
There’s a demo here:-

http://www.access-programmers.co.uk/forums/showthread.php?t=214369


In the Form_Load of frmPictureBox the Collection is passed through OpenArgs so GetObjectFromPointer(Me.OpenArgs) is the Collection.

The Collection is simply iterated like an array, that is, by its numerical index:-

Code:
For lngObjectIndex = 1 To GetObjectFromPointer(Me.OpenArgs).Count

    ' Get the passed Object.
    With GetObjectFromPointer(Me.OpenArgs)(lngObjectIndex)

        ' Draw the passed Object.
        .Draw pba
    End With

Next lngObjectIndex

If you wanted to do a next/previous step between 1 and .Count of a collection in a Class then you should be able to hold lngObjectIndex as the current index as a private member of that Class.

I find the wording of this thread very confusing and would need some working code to see exactly what is required.

Chris.
 

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 04:04
Joined
Sep 12, 2006
Messages
15,656
The cCollections class is not a collection or an object that can be enumerated. Note that “For Each “ requires a collection to iterate over so the class needs to expose a collection of some sort.

The brutish simple way is to just make the collection public. Then you can iterate (using your example:

Code:
    For Each contact In m_oContacts.m_PrivateCollection
        Debug.Print contact.FullName
    Next contact

Although as I think you already elude, that approach is a bit amateurish as private variables should not really be exposed that way (the principle of encapsulation). You should really use Get/Let to expose only what you want to. But do this you will have to clone the collection for both Get & Let I think. Note that you can't simply write aColl=bColl. You'll need to write a routine to clone it (I think).


thanks for this.

clearly I am missing the nuances of what Michael is investigating.
 

MarkK

bit cruncher
Local time
Yesterday, 20:04
Joined
Mar 17, 2004
Messages
8,181
I found the part about export / import in the web page link. So I need to export / import in order to place this bit in the body of the source code?
Code:
Code:
Property Get Item(index) As cSnapSide
[COLOR="Blue"]Attribute Item.VB_UserMemId = 0[/COLOR]
    Set Item = Items(index)
End Property

Property Get NewEnum() As IUnknown
[COLOR="Blue"]Attribute NewEnum.VB_UserMemId = -4[/COLOR]
    Set NewEnum = Items.[_NewEnum]
End Property
I was thinking you were referring to attributes above the top line of code visible in the VBA editor.

Aaaahhh... I think I found what you were talking about... am I correct?
Yeah, that's exactly right, and now your custom collection is indistinguishable from an actual collection, and can function as the target in a For...Each...Next block, and your item property is the default property and it can return a strongly typed class instance.
This is exactly the same kind of behavior as a Form's Controls collection, or a Recordset's Fields collection. :)
Cheers,
 

Users who are viewing this thread

Top Bottom