What You Need to Know About Access Events

MajP

You've got your good things, and you've got mine.
Local time
Today, 12:38
Joined
May 21, 2018
Messages
8,904
There is often a lot of confusion about Access Events and often answering questions posed here is difficult because of inaccurate terminology. People often say "Event" when it is unclear if they mean the actual event that takes place, the event procedure, or the event property. This thread will try to clarify the correct terminology and discuss the basic and advanced Event concepts. Please feel free to critique or recommend additional topics. I would like to clean this up eventually and make a sticky. I have included a db that demonstrates these concepts. This thread includes
- What is an Event and raising / handling events
- What are Event Properties
- What are Event Procedures / Event Handlers
- How a single function can handle multiple Events
- Handling events in external Classes (handling another form’s events)
- Handling other object’s events in custom classes
- Raising and handling Custom Events

A. Events raising and handling:
The best way to think of an event is an announcement (Raised) that something has happened and your code can then listen to this announcement and react to it (Handle). Events are Raised (announced) and code can Handle the announcement. Often people will say "I call this event" which is not correct when they mean they handle an event.

B. Event Properties:
In form or report design you see what are called Event Properties (examples afterupdate, onclick, onEnter). As you can see all event properties start with a preposition (On, before, after,...) which tell when the event takes place. These event properties do two things if filled in. One they tell the event to announce that it has occured. If left blank the event does not announce that it has occured. Two it describes what is listening for the event in order to handle it. There are three things that can handle an event: an event procedure, a macro, or a function. So the following can be put in an event property:

[Event Procedure] 'Informs that the event will be handled by an event procedure
[Embedded Macro] 'Informs that a macro will handle the event
=SomeFunctionName() 'Informs that a function will handle the event

C. Event Procedures / Event Handlers:
Event procedures are the VBA code that listens for an event to occur (Raise) and then reacts (Handles) to it. A more descriptive name is "event handler", because the event procedure code is not an "event" but a procedure that handles the event.

Code:
Private Sub cmbo1_Click()
   'This is an event procedure that handles the combobox click event
 End Sub

In something like vb.net the code construct makes this clearer. An event procedure will be followed by the names of the event/s it handles. So it would look something like this (pseudo code)
Code:
 Private Sub cmbo1_Click() handles cmbo1.onclick
   'This is an event procedure that handles the combobox click event
 End Sub

What is nice in .net and which you cannot do in vba is tell a procedure to handle multiple events. It would look something like this (pseudo code)

Code:
 Private Sub SomeProcedure() handles cmbo1.onclick, text1.afterupdate,
text2.afterupdate
   'This is an event procedure handles the combobox click event and textbox 1,2 after update
 End Sub

D. One Function to Handle Multiple Events:
As mentioned there is not a way to write a single event procedure to handle multiple events in vba, but you can do this using a function. You can make a function (it has to be a function not a sub even though it returns nothing). Then in each controls event properties, you do not put in "[Event Procedure]" you put in the name of the function like =SomeFunctionName(). This function now can handle multiple events.

Code:
Private Function SomeFunctionName()
  'Often you want to do something based on which control's event is handled
   dim ctrl as access.control
   set ctrl = me.activecontrol
   msgbox ctrl.name
end function

E. Handling other Object Events in a different Class / With Events
This is probably one of the most underused and valuabe techniques. You can handle in one class the events of another object. For example form 1 opens form 2 as a pop up. Suppose when you do something like change a value in form 2 you want to do something in form 1. Often people will hard code form 2 to update form 1. This is bad design because you are tightly coupling form 1 and form 2. If you now want form 3 to call form 2 and do the same thing then the code in form 2 gets complicated. Instead you can have form 1 and form 3 handle events that occur in form 2 (an external object). Assume form 1 opens form 2 and form 2 has a combobox. You want to handle in form 1 the afterupdate of the combobox that takes place in form 2. To do this:
1. In form 1 define a variable using WithEvents
2. Set the variable to the external combobox in form 2
3. Make sure the combobox in form 2 raises the event by setting the eventProperty to "[Event Procedure]" You can do it manually in form2, but I usually do this by code so I do not forget. Remember if you leave the Event property blank it will not announce the event. In form 2 the combobox afterupdate needs "[Event Procedure]" either by doing it manually or by code.
4. Handle the after update event for your variable. Notice you are not handling CmboProducts event, you are handling your variable "Cmbo" that is set to cmboProducts. What is nice, is after you define your Cmbo using withevents it will appear in intellisense and you can choose it like other form 1 objects.

Code:
Private WithEvents Cmbo As ComboBox

Private Sub cmdOpen_Click()
  Set Cmbo = Nothing
  DoCmd.OpenForm "form2"
  'Set your variable to the combobox in form 2
  Set Cmbo = Forms!form2.cmboProducts
  'To ensure that the cmbo raises the afterupdate event need to add "[Event
Procedure]" in the event property in form 2
  Cmbo.AfterUpdate = "[Event Procedure]"
End Sub

Private Sub cmbo_AfterUpdate()
  'Now can trap an event from a control on another form
  Me.txtOutPut = Nz(Cmbo, "Nothing Selected")
End Sub

F. Handling other Object events in Custom Class
This is no different than what was described in E because a form or report's class is a class just like any custom class. So the technique is the same. To me this is the most powerful use of vba in Access. You can now build custom classes that extends the capability of a control or group of controls. You can handle multiple events from multiple controls and make them act basically as a user defined control. Here is the technique. This class only demonstrates the handling of events, this class has no real utility.

Class Code
Code:
Private WithEvents mCmbo As ComboBox
Public Property Get Combo() As ComboBox
  Set Combo = mCmbo
End Property

Public Property Set Combo(ByVal TheCombo As ComboBox)
  Set mCmbo = TheCombo
End Property

Public Sub Initialize(TheCombo As ComboBox)
  Set Me.Combo = TheCombo
  'Need to ensure you raise the events you want to capture
  Me.Combo.OnEnter = "[Event Procedure]"
  Me.Combo.BeforeUpdate = "[Event Procedure]"
  Me.Combo.AfterUpdate = "[Event Procedure]"
End Sub
'------------------------------------------------------ Trap Events --------------------------------------
Private Sub mCmbo_BeforeUpdate(Cancel As Integer)
  MsgBox "Before update Trapped in custom class " & Me.Combo.Value
End Sub

Private Sub mCmbo_Enter()
    MsgBox "Enter event Trapped in custom class " & Me.Combo.Value
End Sub
Private Sub mCmbo_AfterUpdate()
  MsgBox "After update Trapped in custom class. This demonstrates two different event handlers trapping the same event. " & Me.Combo.Value
End Sub

In the form you would initialize the custom class like this

Code:
Public CC As CustomClassHandler

Private Sub Form_Load()
  Set CC = New CustomClassHandler
  CC.Initialize Me.cmboProducts
End Sub

Private Sub cmboProducts_AfterUpdate()
  MsgBox "After update event handled in the form class " & Me.cmboProducts
End Sub

Even though the form does not have any beforeupdate or enter event handler the class will handle the events. Note there is an afterupdate event handler in the form's class and also the custom class. This actually demonstrates two different event handlers listening to the same event. This is important to prove the point that you do not call event handlers, they listen for the event.

G. Raising custom Events
In a class module you can raise your own custom events and then handle them. Since a form's or report's module is a class module you can do this here as well as in a custom class.
Public Event EventName(ReturnValue as datatype, ReturnValue2 as
datatype....)
So not only can you raise an event you can pass a value back. You have seen this before in the beforeupdate event of a form

Code:
  Private Sub SomeControl_BeforeUpdate(Cancel as as integer)
     ‘cancel is passed when the event is raised by reference.
 End sub

Here is the Code in Form 2 to raise an event. If the final product selected has the letter "A" in the name it raises the event HasA and passes to the event handler the product name.

Code:
Public Event HasA(Product As String) 'Raise event if selection has an 'a' in the name

Private Sub Form_Close()
  If InStr(Me.cmboProducts, "A") > 0 Then
    RaiseEvent HasA(Me.cmboProducts)
  End If
End Sub

The code in Form 1 to trap this custom event is

Code:
Private WithEvents cmbo As ComboBox
[B]Private WithEvents Frm As Form_form2[/B]

Private Sub cmdOpen_Click()
  Set cmbo = Nothing
  DoCmd.OpenForm "form2"
  Set cmbo = Forms!form2.cmboProducts
  Set Frm = Forms!form2
  'To ensure that the cmbo raises the afterupdate event need to add "[Event Procedure]" in the event property
  cmbo.AfterUpdate = "[Event Procedure]"
End Sub
Private Sub cmbo_AfterUpdate()
  'Now can trap an event from a control on another form
  Me.txtOutPut = Nz(cmbo, "Nothing Selected")
End Sub

[B]Private Sub Frm_HasA(Product As String)
  MsgBox "Final Selection Has An A in the product name"
End Sub[/B]

Any questions or suggestions please provide.
 
Last edited:
MajP:

After reading your truly excellent set of explanations and examples, there are a couple of points I would add, and I hope you don't object.

First, although it is theoretically possible for you to call one event's handler from another event manually (i.e. Call Button1_Click), events don't normally interrupt each other or run as subroutines of each other. You can make that manual call because event routines are declared with Private Sub Control_EventType() declarations. I.e. they are subroutines. Since the default for event routines is a "Private Sub" declaration, you can only make that call from within the same class module as the targeted event handler.

That non-interruptible concept means that if you have an event X that causes a condition that would trigger (raise) another event Y, the handler for Y normally cannot start until the handler for X executes an Exit Sub or reaches the End Sub of the handler code. One exception is when you call "DoEvents" which can allow events in other contexts to occur.

Second, there is yet another "exception" to event handlers not interrupting other event handlers. An EXCEPTION HANDLER is a handler for a specific class of event but it typically is treated differently than ordinary events (even though both use the "Handler" nomenclature.)

An exception (also commonly called a trap) is an event that was detected and signaled in a different way than most other events, sometimes by hardware and sometimes by non-Access facilities (such as Windows or an Application Object).

For instance, if you are using SINGLE or DOUBLE numbers and encounter a floating-point overflow trap, that is something that can be managed by a trap-handler. Division by zero is another typical math-oriented trap. System power alerts can cause traps as well.

Just as an event cannot interrupt another event, so a trap normally cannot interrupt another trap. (Which makes a trappable error in a trap handler the worst and most insidious loop you have ever seen.) Like events which have an event queue, traps have a trap queue. They also linearize.

You can tell the difference between an event handler and a trap handler in another way when you are looking at them... Event handlers EXIT but trap handlers RESUME. Don't ever get them confused because if you do, that is a program crash waiting to happen, probably with an illegal memory reference.

There is another fine point that makes trap handlers and event handlers different. With the single exception of a Form_Error event, most other events do not activate the Err object - because they aren't errors.

I bring this all up because of the confusion often experienced by new Access users with the common "handler" nomenclature. The truth is, that confusion isn't exclusive to Windows. UNIX also has the concept. OpenVMS (a mainframe-class O/S) also had the concept. It is a computing concept of considerable importance.

Another important point has to do with event handlers and their events. MajP's point "A" is incredibly important to understand. Access is the main program at all times in your App and is driving the process that your App has specified. AS A COURTESY, when certain things happen in Access, if you declared a handler then Access will call your handler. But unless the event in question has the ability to be canceled, the named event will occur REGARDLESS of whether you had a handler for it.

For example, even if you have no Form_Load event, Access will load a form that was successfully opened - because there is no Cancel option for Form_Load. Once opened, a form WILL load regardless of the event handler. Typically, the event handler is called when the event has already occurred. The exceptions are events with the word "Before" in their name. BeforeUpdate, BeforeInsert, and BeforeDelete come to mind.
 
I do not object and appreciate feedback. However, I have to admit I had a hard time making heads or tails of your response. Maybe just me, but I found it confusing and kind of all over the map. Not sure of the salient points, but I will see what I can address

1. Calling Event Procedures from current form/report
You said that it is "Theoretical" to call an Event Procedure / Event Handler. I would say the word "theoretical" is a little misleading, it sounds like something that can be done but has not been achieved or something extremely complicated to do. Calling an Event Procedure from other code is a simple to do, but not IMO the best code design. However it is worthwhile to understand that this can be done because it shows that these Event Procedures are like other procedures, and it can be called like any other pocedure. Assume I have an on click Event Procedure.

Code:
Private Sub cmdOne_Click()
  msgbox "Hello World"
End Sub

This is where bad terminology often comes in. People will ask something like "Can I make the click event occur from another event". As written the answer is no and a meaningless question. What they usually mean to say is, "Can I call the on click Event Procedure from another procedure" So the answer is yes to the latter because an Event Procedure is just a procedure like other procedures with the unique ability to respond when an event is raised.

so to call the CmdOne_Click Event Procedure from another Event Procedure:

Code:
Private Sub Form_Close()
  cmdOne_Click
End Sub

This is easy to do but IMO not the cleanest design. To allow multiple events to call the same procedure the cleaner design is simply to make a common procedure and have the Event Procedures call the common event.

Common Event that can be called from other procedures:
Code:
Private Sub HelloWorld
  Msgbox "Hello World"
end Sub

It can then be called by multiple events

Code:
Private Sub cmdOne_Click()
  HelloWorld
End Sub

Private Sub Form_Close()
  HelloWorld
End Sub

2. Calling Form/Report Event Procedures from External
Calling an Event Procedure from an external form, report, or module is again very possible and easy to do. Here again not the best design IMO. Like any class procedure that gets called from outside the class you have to make the Event Procedure "Public". This is done by simply changing the procedure access identifier from "private" to "public". Assume this code is in Form1.

Code:
Public Sub cmdOne_Click()
  msgbox "Hello World"
End Sub

You could call form1 CmdOne_Click event procedure from form2 like

Code:
dim frm as Form_Form1
'Instantiate an instance of form1
docmd.openform "Form1"
set frm as forms!form1
'call the public procedure
frm.cmdOne_click

If you really wanted to do this, again the cleaner design would be to simply have the common procedure in form1. Again it would have to be Public.

Code:
Public Sub HelloWorld
  Msgbox "Hello World"
end Sub

When I say calling an event procedure is a bad design it is not like writing bad inefficient or error prone code. In truth it is not fundamentally or functionally different than calling any other procedure because as stated Event Procedures are procedures like any other procedure. I just think it makes following the code less clear and not as well encapsulated.

3. Event Handlers and Error Handlers
You mentioned that a lot of novice Access developers confuse event handlers and error handlers. I find that extremely suprising and have never seen a single example of this on my 20 years of active participation on Access forums. Few vba users even use the term Event Handler. However, the remaining discussions on exceptions I did not feel was relevant to this discussion since the focus was on events.
 
Last edited:
As it was not my intention to confuse anyone, I guess I missed my mark since I confused you. Sorry 'bout that.

Clarification #1: We agree (but view through the filter of our different backgrounds) that you CAN call an event routine since it is just a subroutine. However, it can never occur that event X's routine will be interrupted by event Y's routine - because events are said to "linearize" and fall into a sequence. Event Y's routine can be CALLED from event X and thus be a part of program flow. But if event Y actually occurs and event X wouldn't call it, event Y's routine MUST wait until event X's routine is complete. I.e. there is no "natural" sequence in which event X's routine would wait for event Y's routine IF event X occurred first.

Clarification #2, regarding nomenclature of handlers: Some will refer to "event routines" or "event procedures" and others will refer to "event handlers." However, if you come from certain specific backgrounds, you will hear "handler" more often - e.g. in writing device drivers or real-time interrupt-driven code, which is MY background.

We agree that if you had something in event Y's handler that you wanted event X to be able to use, you would do far better to make that something be its own sub in a general module. Then X and Y event handlers would be able to use it easily. Directly calling a handler routine from another handler routine is generally better managed by making the common code become a separate procedure.

My point about scoping is actually corroborated by what you said. In order to activate a specific event procedure declared in another form, you have to override the Access default that makes all event procedures Private. You certainly CAN declare the routine to be Public, but to my way of thinking (and apparently yours as well), doing that is inferior to splitting out the code into its own public routine in a general module. It leads to confusing code and is to be shunned as a form of "spaghetti code."

You and I don't really disagree, MajP - we just have radically different backgrounds that lead to different ways of seeing things. And I strongly support your efforts here!
 
Any questions or suggestions please provide.

Thank you very much Mr. MajP,


You know you revived hope in us after I
upset from must of answers I found in this forum, because they just give a very short answer for the one who ask a question, but they don't show him the write steps of using it, and kept him ask again and trial many ways to found the write steps.


I don't know their reasons for that, if they consider everyone knows the steps like them or just they want them to work hard to know the steps, then what is the reason of visiting/joining this forum if I do not found a complete answer for my question?

If they give a complete answer, then the learner can learn more and upgrade them self from that point, without needs to spend that time for reaching the write answer they are looking for, and the others KNOW THE CORRECT ANSWER AND STEPS FOR IT.

You are the one from a very view I found in this forum trying to give a clear explanation with all steps and samples for the learners.


Hopping all trying to help others as much as they can.

Thanks again Mr. MajP
 
MajP:

After reading your truly excellent set of explanations and examples, there are a couple of points I would add, and I hope you don't object.

Sorry Mr. The_Doc_Man, (in my opinion) I assume your entries as negative energy and confusing instead of adding a positive result to the helpers and learners.

 
@IbrBadri,
Thanks for the kind words, but I have to disagree with some of it. You will find very many people on this forum who will go out of their way to try to help and explain in detail. I think the more you look you will see that to be true. People have different styles. I am a visual learner and learn by seeing, so I tend to show a lot of code and provide a lot of demonstration applications and examples. I think you will also see the @The_Doc_Man provides a lot of very positive posts throughout the forum.
 
@IbrBadri,
Thanks for the kind words, but I have to disagree with some of it. You will find very many people on this forum who will go out of their way to try to help and explain in detail. I think the more you look you will see that to be true. People have different styles. I am a visual learner and learn by seeing, so I tend to show a lot of code and provide a lot of demonstration applications and examples. I think you will also see the @The_Doc_Man provides a lot of very positive posts throughout the forum.

I hope so, and as I said, most of my search for answers, I found it very short and not complete. I shall keep searching.

Thanks and apologize for The_Doc_Man
 
Updated Example database. I could not post to the original thread due to length of the thread.

I also added a Pseudo Control Array class. This is needed in special cases where you want to trap multiple events, but those events return arguments. Such as keydown, keypress, keyup, mousedown, mousemove, mouseup
 

Attachments

Last edited:
I agree with Doc, event procedures should not be called from other events. Not for the technical reasons he explained because Access VBA is single threaded and calling an event procedure doesn't trigger the event, it just runs the code in the event. But because, it is unexpected and someone down the line could put code in the event procedure you are calling that will cause a problem for the procedure that is attempting to reuse the event code. Therefore, when I want to reuse event code, I ALWAYS move it to a standard module if it is code I will use from multiple forms. Then I can pass in the form to the procedure so it can work with the form objects. This also makes it obvious that the code is reused and so changing it will impact multiple calling procedures.
Code:
Call SomeProcedure(Me)
Then the called procedure will be
Code:
Public Sub SomeProcedure(frm as Form)
    frm.SomeControl = Somevalue
End Sub

This also allows your event code to have different sections and common sections.
 
I think this simple strategy, if implemented by Access developers, would save a lot of grief.

"...when I want to reuse event code, I ALWAYS move it to a standard module if it is code I will use from multiple forms..."
 
You can actually take the word "event" from the quoted phrase and it still makes sense as long as "want to reuse code" is still there.
 
I specified event code because I wanted to show how to make the code reusable by multiple forms without having to create a class. But, you are correct. If I use it more than once, it goes to a standard module unless the reuse will be form specific.

Sometimes I know when I create a procedure that I will want to use it elsewhere but not always. Once I determine I want to use the code again, I copy it from where it was, generalize it if I need to, and then call it from multiple places. Just because you're ONLY going to use it TWICE, doesn't mean you should copy it:( Use the 1-m rule. If something occurs more than once, it occurs many times and therefore belongs in a separate table or in the case of code, a separate procedure.

Don't ever be unwilling to clean up code as you find it sloppy or need to reuse it. It is sooooo much easier than having to go back and touch multiple copies later. That is when you are most likely to break things or be inconsistent and cause bugs.
 
Don't ever be unwilling to clean up code as you find it sloppy or need to reuse it. It is sooooo much easier than having to go back and touch multiple copies later. That is when you are most likely to break things or be inconsistent and cause bugs.
I fully agree!
Just one small addition: If you find sloppy code in your application, clean it up! - Even if you don't want to reuse it (now).
 
I fully agree!
Just one small addition: If you find sloppy code in your application, clean it up! - Even if you don't want to reuse it (now).
A lesson I am painfully learning and living each day. Some of it my own, most of it somebody else's...
 
I've been doing this since before a lot of you were born. It was really hard to redo bad code when you were working with a card deck and you had to retype every line or even if you could move it around, you needed to send the deck to be copied so the old cards could be renumbered. One of my programmers couldn't spell to save himself and everyone complained if they had to work on code he wrote due to the spelling errors that always made them make typos. We did make him fix spelling errors on transactions (forms) and reports but I had to let the code go. Spelling aside, he was a competent programmer otherwise I would have found a way to fire him. The second year, I had a brilliant idea, I told him he couldn't type anything. All programs and updates had to be written on forms and given to the keypunch operators. Them I instructed to fix spelling errors if they saw them:)
 
I've been doing this since before a lot of you were born. It was really hard to redo bad code when you were working with a card deck and you had to retype every line or even if you could move it around, you needed to send the deck to be copied so the old cards could be renumbered. One of my programmers couldn't spell to save himself and everyone complained if they had to work on code he wrote due to the spelling errors that always made them make typos. We did make him fix spelling errors on transactions (forms) and reports but I had to let the code go. Spelling aside, he was a competent programmer otherwise I would have found a way to fire him. The second year, I had a brilliant idea, I told him he couldn't type anything. All programs and updates had to be written on forms and given to the keypunch operators. Them I instructed to fix spelling errors if they saw them:)
I'm here a couple of years later, looking for stuff I've forgotten or never knew because I was too busy meeting deadlines. Editing code on 80 or 96 col cards is a pain, but try doing it on paper tape using a Friden Flexowriter. I also had fun with 80 col cards. I worked for Burroughs in the early 1970s. Our software development centre in the UK shared facilities with the field engineer training school. We shared a B3500. They had it in the mornings and we had it in the afternoons. Their instructors would set faults for the students to fix and then let us do our stuff on the machine. Sometimes the faults weren't completely fixed. I can remember an 80 col card reader that went mad and shot the cards around the room. The worst problem was when it shuffled the cards. We were coding in assembler. That was fun. Grace Hopper had it right when she described debugging as the process of replacing known bugs with unknown bugs.
 
Ah, yes, a reference to RADM Mary Grace Hopper, who had the distinction of being the oldest actively serving uniformed female Navy officer ever. She was part of the Navy's public relations group and toured the country giving lectures and handing out nanoseconds.

"What?" you say... handing out nanoseconds? Yes, she had some wires cut to the exact length it would take for an electric current pulse to travel in one nanosecond (at the speed of light in copper). Souvenirs, so to speak. Rounding off, the speed of light is 300,000 Km/s, or 300,000,000 m/s. In a nanosecond, light travels 0.300 meters. So wire segments 30 cm (close to 1 foot) were available. She also carried a coiled wire that was her analog of 1 microsecond. She apologized for not being able to carry the millisecond as it was too heavy. She was one very cool lady.
 
She was one very cool lady.
She was. I met her when she came to my company to speak. We had a meet and greet after the lecture. There were 100 people in my department but only 6 of us were women so she made sure she talked to all of us.
 

Users who are viewing this thread

Back
Top Bottom