John W Colby
jwcolby at gmail.com
Thu Mar 12 11:48:20 CDT 2009
A.D.
>1.1 - It would be interesting to examine the grounds for concluding that assigning "[Event Procedure]" to a given event in control's property sheet becomes the cause for that event to fire, which would not have happened otherwise (if this value were left blank).
This one is easy to test.
1) Build an event stub in the form or class.
2) Place a message box or debug.print statement in the stub.
3) Make sure the [Event Procedure] test is NOT in the property.
4) Cause the event to fire. Click in the control to cause the click
event etc.
5) Observe that your code in the event stub does not execute.
6) Place the [Event Procedure] text in the property.
7) Cause the event to fire. Click in the control to cause the click
event etc.
8) Notice that your code does in fact execute.
>(a) Various events inherent to a control keep firing as and when merited, irrespective of the value assigned in property sheet.
There is simply no way to determine this since the mechanism is buried back in the Access code. I suppose if you had sufficiently sophisticated debug tools and could step through code at the machine code (PCode) level you might be able to watch this, but you and I cannot do so.
>(b) If any such event is required to be trapped and acted upon in VBA code, it needs two components, namely (i) An event stub in VBA module and (ii) Explicit hooking via assignment of "[Event Procedure]" in the property sheet. Omission of either component will render the event trap ineffective.
This is absolutely the case, two parts are in fact required, at least to make anything useful occur.
(c) If any such event is required to be trapped and acted upon in a function or macro, the trap is confined to a single component, i.e. assignment of function or macro in the property sheet using equal sign.
This too is the case. Again I have no idea how the system does this
"under the covers". Notice that even if an event stub exists, if an
=MyFunction() is placed in the event property the function is called and
not the event stub.
I suspect that there is an "in memory" table built of jump points for
the code when an event is raised. In the case of the =MyFunction(), the
only entry in the table is the entry point to MyFunction(). In the case
of [Event Procedure] in the property, the code searches for every loaded
object (classes in forms and our classes) which dimension WithEvents the
object raising the event, and then searches in the class for event
stubs. Any such stubs are loaded into the table as a jump point. In
fact my guess is that these tables are built as class instances are
loaded, then "hooked up" as an object loads that has events that need to
jump to these jump points.
Look at what happens.
1) A class loads. It has an object dimensioned WithEvents, in other
words a variable ready but not filled. The jump points are NOT HOOKED
YET. VB has been notified that this class instance MAY SINK EVENTS for
that object, it has not yet declared that the class WILL SINK EVENTS for
that object. Even if you have event stubs there is nothing yet that
says the class instance WILL sink events.
2) At some point in time that dimensioned variable is SET = an object
passed in. At this point the jump points MAY OR MAY NOT be hooked. If
the object already has the event property set to [Event Procedure] then
the jump table hooks up the object's event to the event stub in the
class. If the object event property is not yet set to [Event Property]
then the jump table is not yet hooked to the event stub.
3) At some point in time the object event property is set to [Event
Property]. AT THIS POINT the jump table is "hooked" to the event stub.
Any events raised by this property of this object now has code that can
execute out in the class we are discussing. NOTICE, that there may be
event stubs hooked in this one or dozens of instances of this class. In
fact there may be other entirely different classes also hooking this
same event for this same object, and they all get hooked up at the
instance that [Event Property] is set, and unhooked at the instant that
[Event Property] is cleared (set to empty string).
4) At some point in time the object's event property is set to an empty
string, deleting the [Event Property]. At that point the jump table is
unhooked, and the event stub in ANY AND EVERY OBJECT with an event stub
for this object's property loses the ability to sink the event.
Remember that the event can be sunk in one or dozens of class instances.
While I write of a jump table, this is just a not particularly educated
guess as to how it actually works. But something like this has to be
going on under the covers. The event property itself is almost
certainly a function in the object that when you write the [Event
Procedure] string into the property begins to execute code that does the
hooking / unhooking.
Notice that even more than this simple explanation is going on because
events can pass parameters, so something similar to a stack has to be
set up for the parameters (if any) passed in.
1.3 - In support of 1.2 above, it could be said that it requires an event to fire so as even to become aware of the entry in property sheet (whether "[Event Procedure]" or "=MyFunction()"). For example, if a non-existent function were assigned to an event property, access will respond with a message (not able to find the function).
In a manner of speaking yes. Basically something has to go check the
jump table to see if any jump points have been set up.
> 2 - Need for setting up pointers to labels attached to controls:
Access offers a ready made collection of labels belonging to a control, and the count of this collection is usually 1 or Nil. Using a custom function, the available pointer to the control itself could be used to get the attached label via this tiny collection, whenever needed.
AFAICT each control has a controls collection, the label is in the
controls collection. The "usually" is the key here. I have not done
extensive testing of position but I am gun shy of assuming that
collection object 1 is the label, i.e. assuming a fixed position in the
collection. I just iterate the collection looking for an object of type
Label. If the label is in fact the first object, my code gets the
pointer and immediately exits the iteration loop.
So yes, I could just run my function every time but why? Run it once
and provide a pointer to the label. In fact once I have a label
variable I can also allow a control (clsCtlXXX) to be handed a pointer
to a label that is not even in it's controls collection. In the case of
a continuous form the control's label is often stripped off the control
and placed up in the form's header section. By providing a property to
the label in my class, I can (and do) intentionally "program" a control
in this scenario to "own" the label up in the form's header section. It
could in fact point to ANY label, but this is the specific scenario that
I needed to handle and is the one I use most often when the label is not
in the control's control collection.
Classes (and wrappers in particular) give us a slew of abilities that we
wouldn't otherwise have. Once you start using wrapper classes, light
bulbs start going off. Oh wow..., now I can add this behavior to my
combo or text box etc.
A.D.Tejpal wrote:
> John,
>
> Thanks for kindly clarifying various points so thoroughly. It is really nice to have the benefit of your experience and deep understanding. Your views are requested on certain supplementary points placed below:
>
> 1 - Firing of control events:
> 1.1 - It would be interesting to examine the grounds for concluding that assigning "[Event Procedure]" to a given event in control's property sheet becomes the cause for that event to fire, which would not have happened otherwise (if this value were left blank).
> 1.2 - An alternative explanation could be:
> (a) Various events inherent to a control keep firing as and when merited, irrespective of the value assigned in property sheet.
> (b) If any such event is required to be trapped and acted upon in VBA code, it needs two components, namely (i) An event stub in VBA module and (ii) Explicit hooking via assignment of "[Event Procedure]" in the property sheet. Omission of either component will render the event trap ineffective.
> (c) If any such event is required to be trapped and acted upon in a function or macro, the trap is confined to a single component, i.e. assignment of function or macro in the property sheet using equal sign.
> 1.3 - In support of 1.2 above, it could be said that it requires an event to fire so as even to become aware of the entry in property sheet (whether "[Event Procedure]" or "=MyFunction()"). For example, if a non-existent function were assigned to an event property, access will respond with a message (not able to find the function).
>
> 2 - Need for setting up pointers to labels attached to controls:
> Access offers a ready made collection of labels belonging to a control, and the count of this collection is usually 1 or Nil. Using a custom function, the available pointer to the control itself could be used to get the attached label via this tiny collection, whenever needed.
>
> Best wishes,
> A.D. Tejpal
> ------------
>
--
John W. Colby
www.ColbyConsulting.com