[AccessD] Classes and events - a quick introduction

jwcolby jwcolby at colbyconsulting.com
Thu Mar 5 22:49:38 CST 2009


Guys,

I have majorly expanded the original quick introduction in preparation for eventually publishing it, 
probably in an eBook.

I would appreciate comments and critiques on the new text.  There is a lot of stuff in here, and I 
need your technical expertise to tell me if I am putting out bad information, or if there is 
anything that was left out which would make the introduction more informative or understandable.

I have this in a word document.  If anyone would like the word document for the purpose of marking 
it up and returning it to me let me know and I will send off line.  Because of formatting, the Word 
document is much easier to read.

In the meantime I am inserting the text below.

A QUICK introduction to classes and events.

CLASSES

•	A class is a module, but a module is not a class.
•	A class has properties and behaviors that a module does not.
•	A class is actually instantiated (loaded into memory) when a set statement is executed.  In other 
words, an instance of the class is loaded into memory, and stays in memory until it is specifically 
unloaded (destroyed).  A module is loaded into memory the first time any function in that module is 
called.
•	Like a module, a class can contain data (variables) and code.  However the variables in a module 
can only contain one value at a time.  A class can contain one value per class instance.
•	A class can be loaded into memory as many times as you want (limited only by the size of your 
memory) and each instance of a class can contain its own data values in its variables.
•	All instances of a particular class share code, but do not share variables.  In other words, the 
code is only loaded into memory one time, but the variables are loaded once per class instance loaded.
•	Variables in a class instance header are assigned memory from the Heap.  That memory remains 
assigned to that class instance until that class instance is destroyed (unloads).  When a class 
instance unloads, the variable memory storage for that instance is returned to the Heap.
•	The code for a class loads when the first class instance loads.
•	A class instance (and in fact any object instance, including forms and controls) unloads from 
memory when the last variable holding a pointer to the object is set to nothing.  When a class 
instance unloads, the memory storage for the data for that instance is returned to the system.  When 
the last instance of a particular class unloads, then the memory storage for the code for that class 
is returned to the system.
•	A class has two built-in Events that fire, one as a class instance loads (Class_Initialize), and 
the other as the class instance unloads (Class_Terminate).
•	A class can define an event that it will Raise using the keyword Event followed by the name of the 
event it intends to raise, followed by any parameters it will pass.  The class can then use the 
keyword RaiseEvent to raise the event.  Events can pass parameters to the event sink.  Modules 
cannot define or raise events.
•	The Code Behind Forms module in a form is a class module, but it is a special class in some ways 
as we will see later.

Think of a class as a place to store information and code about some thing in the real world that we 
are modeling.  Perhaps you have a clsPerson.  That class has a bunch of variables called FirstName, 
LastName, SSN, ColorHair, ColorEyes, Gender, Birthdate etc.  Load an INSTANCE of that class and fill 
in the data about John Smith, load another instance and fill in the data about Mary Smith etc.  You 
might then have a piece of code that takes the birthdate and calculates the current age from that. 
The data and the code are all stored together in the class module.

EVENTS

Events can be thought of kind of like a radio transmission.  The radio station transmits a signal, 
but they have no idea whether anyone is listening.  In the case of events, this is called “Raising 
(or sourcing) an event”.   The process of executing code for an event is called “sinking an event”.

•	Events can only be raised in classes.  Modules cannot raise events.
•	Events can only be sunk in classes.  Modules cannot sink events.
•	Events to be raised are defined in the header of a class using the following syntax:

Event MyEventName([MyParam1 as type], [MyParam2 as type], …)

•	Events can only be raised in the body of subs and functions in classes using the syntax:

RaiseEvent [MyParam1], [MyParam2], …

•	Parameters passed by RaiseEvent can be of any datatype understood by the VB interpreter including 
objects such as controls, forms, recordsets or even other class instances.
•	When an event is raised, program control passes to the event sinks in the order that their 
containing class is set.  As an example, if a ClassA raises an event, and Class1, Class5 and Class10 
sink that event, the order that the classes 1,5, and 10 execute their event sinks depend on the 
order that those three classes are instantiated (loaded using the Set keyword), from first to last 
instantiated.  Unfortunately, the order was backwards in Access97, in other words, the last instance 
Set was the first instance to receive control.
•	If the Code Behind Form class of a form sinks an event of a control or other object, that event 
sink will always receive control first


One thing not commonly understood is that an event is not even raised if the property of the event 
does not contain the text “[Event Procedure]”.  Simply placing this text in a given event property, 
perhaps the OnClick or AfterUpdate event, will cause that event to start transmitting.  As you will 
see, we use this fact to “hook” an event by programmatically placing this text string into an event 
property that we want to execute code for.  For example if we want to sink an AfterUpdate event in 
our class for a combo named cboState, we simply execute the following code in the class’ 
initialization code:

cboState.AfterUpdate = “[Event Procedure]”.

Always remember though that just because an event is being raised doesn’t mean any code is being 
executed in response to the event.  In the case of events, receiving the signal is called “sinking” 
the event.  The code that receives control when an event is raised is called an event stub.  The 
event stub looks like the following:

Sub MyCbo_Click()

End sub

The format of the event stub is the keyword Sub followed by the name of the object raising the 
event, followed by an underscore _ followed by the name of the event.  As we will soon see, the 
visual basic editor can automatically create the event stub for us using two combo boxes in the vb 
editor.  If an event stub like the above is left empty, the next time that a compile is performed, 
the event stub will be deleted from the code.  If you need to keep the stub around for the future, 
you need to place at least a comment in the stub to prevent it from being deleted.

If no event stub exists for a given event for a given object, then the visual basic program control 
is never transferred and that event does nothing.  If you have nothing but a comment in the event 
stub, then control will be transferred and the sub and end sub lines will execute.  You can verify 
this by placing a breakpoint on the sub and end sub lines and causing the event to fire (be raised).

Continuing with the radio metaphor, if someone is listening to that radio signal, then the person 
listening can do whatever they want with the signal they are receiving.  They can do nothing at all, 
they can use it as a signal to launch an attack on an enemy, they can enjoy music, they can…  The 
important thing to understand here is that what the listener does is up to the listener.

Notice that the person broadcasting the signal (raising the event) doesn’t know or care whether 
anyone is listening (sinking the event), nor do they know or care what the listener (if they even 
exist) does with the signal (event sink).

When you open a form, the form is constantly raising events.  It raises OnOpen, OnClose, OnCurrent, 
BeforeUpdate, AfterUpdate, MouseMove etc etc.  If those events are hooked, the events are raised 
whether or not anyone is listening.  The form neither knows nor cares whether anyone is listening to 
(sinking) those events, it is simply raising these events so that if anyone is listening to 
(sinking) the events, they can do whatever they want when the events fire.

When you place a control on the form, the control raises events under certain circumstances.  When 
the control gets the focus it raises an OnFocus event, when it loses the focus it raises a LostFocus 
event, it raises a BeforeUpdate, AfterUpdate etc.  Of course these events depend on what the user 
does, in other words they don’t happen unless the user manipulates the control in the correct 
manner, enters data for example.  But notice that while the control always raises the event, it 
neither knows nor cares whether anyone is listening, nor does it know or care what the listener does 
with the event if anyone is listening (sinking the event).

This is a critical thing to understand, that the object raising an event does not know nor care 
about the listener, nor what the listener does.   The reason that this is critical is because it 
allows you to design an interface between objects which is totally asynchronous or disconnected. 
Have you ever built a subform and referenced a control on the parent form?  Have you ever tried to 
open that subform by itself?  It complains that it cannot find the control on the parent.  The 
subform is has a “connected” interface to the parent, without the parent it cannot do its job 
correctly.  The event “Raise/Sink” interface eliminates that dependence.  The object raising the 
event does not depend on having a receiver of the event in order to function correctly.  The 
receiver of events does not depend on the broadcaster existing in order to function, although of 
course it cannot do whatever it would do with the events if they are not being broadcast.  But each 
side can be loaded and code can execute without the other side being loaded, without compile errors etc.

The last thing to know is that regular modules cannot sink events, but a class can.  A regular 
module cannot RAISE an event, but a class can.  Classes are modules, but modules are not classes.

Sinking events in classes

Sinking events in classes is quite common.  In order to sink an event in a class, you must dimension 
a variable of type SomeObject, and that object must source events.  Furthermore you must use the 
keyword WithEvents.

Dim WithEvents ctlCboState as ComboBox

Any object which can raise events is known as an “automation object” which simply means that it is 
capable of raising events.  The WithEvents keyword informs the VBA interpreter that this class may 
sink events for ctlCboState.

In the VB Editor there are two combo boxes just above the code area.  The combo on the left contains 
every object that can source events.  Once you select an object in the left combo, the combo on the 
right contains a list of all of the events for the object in the left combo.  Selecting an event in 
the right combo will cause an event stub to be inserted into the code area of the class, and the 
cursor to be placed into that event stub.

If you dimension another object variable without the WithEvents keyword:

Dim ctlTxtSSN as TextBox

That object will not appear in the left combo of the editor and you will not be able to sink events 
for that object.

Interestingly, I tried dimensioning a line control WithEvents.

Dim WithEvents ctlLine as Line

A line is an object, but it is not an automation object, i.e. it cannot raise events.  The compiler 
did not throw an error when I compiled.  ctlLine could be seen in the left combo box of the editor 
but it could not be selected, which makes sense as it has no events.  In other words, I could not 
use the editor to select the line and create an event stub for ctlLine, but it did in fact appear in 
the left combo, and there was no compile error.

On the other hand, trying to dimension WithEvents a normal variable of type long, string etc simply 
does not work at all, you get an immediate error as soon as you try to leave the line of code – 
Compile Error: Expected: Identifier.

Sourcing events in classes

Sourcing (raising) events in a class is much less common but can be quite useful as well.  Sourcing 
an event simply means that the class becomes an automation object, can be dimensioned WithEvents in 
other classes, and the events raised by the class can be sunk in other classes.

Sourcing events starts with telling VBA that the class is able to raise events.  This is done with 
the keyword Event in the class header.  The syntax is as follows:

Event MyEventName([MyParam1 as AnyValidType], MyParam2 as AnyValidType]..)

Placing a statement like that anywhere in the header of a class immediately makes the class an 
automation object.  You can declare as many events as you wish.  The parameters cannot be 
ParamArrays, nor can they be optional.  Events cannot be declared to return values, however the 
parameters can be ByReference which can then be modified inside of the event sink, which is a form 
of return value.
Summary
In this lecture we have learned important terminology for working with Classes and events.  We have 
what classes are, how they differ from modules, how they are instantiated, and how they are 
destroyed.  We have discovered that classes can process events raised by other objects, and what 
events are, how to cause events to be raised, and how to sink events.

-- 
John W. Colby
www.ColbyConsulting.com



More information about the AccessD mailing list