[AccessD] Building a control class

A.D.Tejpal adtp at airtelmail.in
Thu Mar 12 09:40:25 CDT 2009


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
------------

  ----- Original Message ----- 
  From: jwcolby 
  To: Access Developers discussion and problem solving 
  Sent: Monday, March 09, 2009 21:01
  Subject: Re: [AccessD] Building a control class


  A.D.

  WOW.

  I have never done side by side comparisons of this stuff in different versions of Access.  Good job on that.

   >     1 - Hooking events:
   >     There should be no harm in hooking all events available in a control, by assigning "[Event Procedure]" wherever the value is found blank. As and when particular events are required to be sunk, pertinent stubs can be inserted in the class module, without having to worry about matched hooking.

  In fact having the event "hooked" and not having it hooked cause different actions by the object itself.  If the property is set to [Event Procedure] the object actually raises the event, whereas if the property is blank, then the object does not even raise the event.  Thus by implication there is some small overhead for each event "hooked" because that code has to run back in the object itself.

  Obviously if there is no event stub then nothing actually happens, however the event is still being raised.  The only case where I personally believe this might matter is in the mouse move events. Each object (and the form) raises mouse move events whenever the mouse is over the control or form. Thus events would be raises constantly even when there is no code that you want to run.

  In my form I actually created an event stub for every event and placed a debug.Print in the event bracketed by conditional compilation statements so that I could study the event sequence.  The MouseMove was the only event that I did not hook, simply because it caused hundreds of debug.print statements, obscuring what I wanted to see.

   >     2 - Precautions for ensuring proper termination of all classes:

  My gut feeling is that Access 2000 had a bug having to do with the execution thread not terminating properly in all cases.

  If the clsFrm has code control and the form's unload event fires, AND the code in the Code Behind Form Close event sets the pointer to clsFrm to nothing then bad things happen.

  Remember that even though VBA is single threaded, events can preempt running code.  So code can be running in the clsFrm and the OnClose event in the main form's Code Behind Form can fire.  If CBF then set the clsFrm pointer to nothing, when the OnClose event in  CBF tries to return to the previously running code... that code was in the class that you just set the pointer to nothing.  My guess is that in Access 2000, the garbage collector unloaded the code as SOON AS the pointer was set to nothing and there was no place for the code execution to return to.

  Kind of a sticky situation.

  It appears that they fixed that bug in Access 2002 ("XP" version).

   >     2.2 - Forms with subforms:
   >     2.2.1 - In Access 2000, if the parent form as well as the form serving as its subform are both set to the form class (even though these are different instances of the same class), application freeze / crash can take place on eventual closing of parent form. (No such problem in Access 2003).
   >     2.2.2 - The above problem can be overcome by off-loading the SourceObject of subform controls, BEFORE setting the form object variable (pointer to real form) to nothing.

  As you no doubt know, subform load / unload is backwards from what you would expect.  Subforms load before the main form.  This means that clsFrm in every subform loads before the main form's clsFrm. This can cause issues if you want to reach up into the main form for some reason as the subform loads... it can't be done at that point.

  In general though, you need to unload ALL CLASSES that hold pointers to objects on the form before actually closing the form itself... and I understand you were not discussing that specifically but it is an important point to make.  A form cannot correctly close if a pointer to any object on the form is still set.  The form APPEARS to unload but doesn't.  It becomes invisible, but is in fact 
  still loaded.  Did they fix that in XP?  No se!

  Part of my problem is than one of my major clients still uses 2000 and shows no sign of EVER moving on.  So I try to program to that platform, which means I have to handle scenarios that I would not if I could program to 2002.

   >     2.3.3 - In conclusion, it seems preferable not to set form class variable to nothing in real form's module.

  Correct, IIRC it can cause page faults.  Clean up well inside of your classes, then let the garbage collector actually set the clsFrm pointer to nothing as the form closes.

   >     3.2 - Use of general module as global interface would minimize the extent of interference to existing code in client classes. The interface module could be named in such a manner as to reflect its association with the given set of classes. As an added reminder, name of this module could be mentioned as part of the comments at beginning of each control class.

  Yes, except that the use of a class allows storage of VARIABLES specific to the class, whereas the use of a module ONLY ALLOWS common code.  As an example in my real framework I maintain a pointer to the label for a control so that I can manipulate the label in the class (turn it different colors). 
    This requires a variable to point to the label, as well as code to iterate the control's control collection and find the label (get it into the label pointer).  This is common code, but it is also common data!  By having a clsGlobalInterface for this stuff, I can place the label pointer VARIABLE in that class, whereas if I use a module, then I have to put a pointer variable to the label in each control class.  There are enough things like this that come along where a common variable is required, that it soon becomes apparent that the class wins.

  This is experience talking!  I moved to clsGlobalInterface precisely because I noticed a growing set of common variables in the header of every (or most) of my control classes.  I was adding these kinds of behaviors to my framework and found myself opening every control class and copying and pasting the code and variables into each class.  YUK!

  With a clsGlobalInterface, all you do is set the variables and any code in clsGlobalInterface and every control "inherits" the new capabilities automatically.  OK I use the work "inherits" very loosely but you get my meaning.

  One thing I found useful is to have a parent object and a grandparent object in every class.  As an example the parent of a control might be a BUSINESS CLASS, the parent of that business class might be the clsFrm or even the framework.  In any event it is sometimes useful to be able to reach up into the parent, and occasionally even the grandparent.  Obviously you can walk the chain to get from parent to parent to parent but I just found (for my purposes) that establishing the grandparent is useful enough to just do it.  So in my clsGlobalInterface I have a parent and a grandparent variable.  EVERY class that uses clsGlobalInterface just "inherits" these variables.  I expose them with properties and any class that uses clsGlobalInterface can now get at its parent and grandparent.

   >     3.3 - Use of general module as global interface would also avoid the need for setting up parent/child linkage, leading to improved robustness. Tests show that if a class is used as global interface, additional precaution becomes necessary for proper termination (in the context of Access 2000). In form class module, setting the parent to nothing, for each instance of global interface class, BEFORE setting the parent to nothing for each instance of control class has to be done, which in turn has to be carried out BEFORE setting the form variable (pointer to real form) to nothing in its close event.

  True but once the code is written to do the cleanup it just works.  You will find that no matter WHAT you do, working in 2K is less robust.  I actually built a tool to record / track every single instance of every single class loaded and unloaded.  It is a class that I instantiate in the framework Init() and then everything logs in / out of that class.  This allowed me to watch the tree built, but more importantly to see things unload and NOT UNLOAD.  I can turn off the logging but turn it on every once in awhile to check that everything unloads as objects close.

  John W. Colby
  www.ColbyConsulting.com


More information about the AccessD mailing list