Code Samples, Tips, Tricks and
All Content Copyright © 2000
New Vision Software
All rights reserved
Raising Events From Objects In A Collection
Circular Reference Problems
by Bryan Stafford - New Vision Software
How to go about raising
events from the child objects in a collection? This is a question that has been
asked many times in the various on-line forums ever since creating
collection classes became possible in Visual Basic. This feat is actually relatively
easy to accomplish, all you have to do is pass a variable, that references
the parent collection object, to the child.
The child then calls the parent through this reference whenever it
needs to raise an event back to the collection client. Unfortunately, this creates a
circular reference problem. Circular
references are a problem because, until all references to an object are
destroyed, the referenced object cannot be destroyed. When a child of a collection holds a reference to its parent,
there must be a method to release this reference and it is up to you, the
developer, to call the method or the parent object will never be
unloaded. If you forget even
once, you’ve blown it! This
is the stuff that memory leaks are made of. (See Object
References Explained for more info about how object references work)
So, we need a method to
allow the child of a collection to hold a reference to its parent without
creating circular references. Surprisingly,
this is not as difficult as it sounds.
The method I will describe uses un-referenced pointers obtained using
the ObjPtr() function. The
ObjPtr() function resides in a normally hidden part of the VB language but
it can be viewed if you open the object browser and select the “Show
Hidden Members” option from the right click context menu. It returns an un-referenced “dumb”
pointer to any VB object that you pass to it.
Once a child object has a pointer to its parent, it is a fairly
simple process to turn the dumb pointer into a fully referenced VB object. You simply copy the dumb pointer
into the memory location of a variable dimensioned as the desired object
type. (keeping VB from crashing
is slightly more complicated but you can see the details in the ResolvePointer function in the
frmMDIChild FRM module in the sample project)
Now we know how to get
our pointer but since it is un-referenced, there is no guarantee that the
desired object will still be there when we try to resolve the pointer. We handle this situation by creating
a means by which the collection object can register itself for the duration
of its existence (think of this as referencing the object with no strings
attached). In the initialize
event of the collection class, we register its existence and in the
terminate event, we clear the registration.
This allows any child object that wishes to access the parent
collection to first check to make sure the parent still exists before trying
to resolve a pointer. See the functions in the MValidatePointer BAS module for all of the gritty
details of the object registration routines.
We now have all the
background information we need to proceed with a step-by-step explanation of the event
Declare the collection object “WithEvents” in the client module
where it will be used.
In the Initialize event of the collection class, register the object
using the registration routine in the MValidatePointer BAS module.
As each child is created and added to the collection, a dumb pointer
to the collection object is passed to the child.
When a child object of the collection needs to raise an event, it
calls a method on the parent through the dumb pointer which it resolves
before making the call.
In the event sub on the parent, the desired event is raised in the
collection client. The
collection Key of the object as well as the index can be passed to the
client in the event.
That's all there is to it! You now have the tools you need to create your own control and object arrays with full event support. Download the fully commented Collection WithEvents sample project to see this method in action!