MS-Access / Getting Started

Exploiting Access class module events

You can add custom events to Access forms and to raise those events from code within the form. Custom events are declared with exactly the same syntax as declaring events within any class module and are raised with the RaiseEvent statement. The only tricky part is sinking custom events raised by a form in another form's module.

Custom events can be exploited as a way to convey messages and data between forms. Recently, responded to a reader's question about dialog boxes with a relatively lengthy explanation of modally opening the dialog box, hiding the dialog box when the user was ready to return to the main form, and then reading a custom property from the hidden dialog box. Although this technique works well, it requires quite a bit of planning and preparation.

The dialog box operation can be more simply implemented by adding a custom event to the dialog form that is raised by the dialog form and sunk by the main form. Information entered by the user on the dialog form is passed to the main form as an event argument. The event is raised when the user closes the dialog form and the information passed as the event argument is captured by the main form. There is no need for the main form to close or otherwise manage the dialog form.

The user types something into the text box and clicks either OK or Cancel. The OK button passes the text box's contents to the main form, while the Cancel button passes a "No data" message, indicating that the user dismissed the dialog box without entering any data.

Here's all the code behind this simple dialog box:

Public Event FormClosing(Message As String)
Private Sub cmdOK_Click()
  DoCmd.Close acForm, Me.Name
End Sub
Private Sub cmdCancel_Click()
  txtSomeData.Value = Null
  DoCmd.Close acForm, Me.Name
End Sub
Private Sub Form_Close()
  If Not IsNull(txtSomeData.Value) Then
    RaiseEvent FormClosing(txtSomeData.Value)
    RaiseEvent FormClosing("No data")
  End If
End Sub

A public event named FormClosing is declared at the top of the dialog form's module. This event returns a single argument named Message. The cmdOK_Click event procedure closes the form, while the cmdCancel_Click event clears the contents of the text box named txtSome- Data before closing the form.

The FormClosing event is raised by the dialog form's Close event procedure, ensuring that the event is raised whenever the form is closed. If the txtSomeData is not Null, the value of the text box is passed by the FormClosing event, while a default message is passed if the text box's value is Null.

No other code is needed by the dialog form, and the form is allowed to close normally because the FormClosing event fires just before the form disappears from the screen.

The code behind the main form is also quite simple. Notice the WithEvents keyword applied to the form object's declaration:

Private WithEvents frm As Form_frmDialogForm
Private Sub cmdOpenDialogForm_Click()
  Set frm = New Form_frmDialogForm
  frm.Visible = True
End Sub
Private Sub frm_FormClosing(Message As String)
  txtDialogMessage.Value = Message
End Sub

The dialog form must be declared as a module-level variable behind the main form. The WithEvents keyword notifies the VBA engine that you want the main form to capture (or sink) events raised by the frm object.

Also notice that the form's class name is Form_frmDialogForm. This is the name of the class module behind frmDialogForm, and it's the entity that actually raises the event. From the perspective of the VBA project driving the application, the form's surface is just a graphic interface and has nothing to do with the class module that supplies the logic driving the form.

The WithEvents keyword is almost magical. Once you've qualified an object declaration with WithEvents, the name of the object appears in the drop-down list at the top of the class module, and the object's events appear in the right drop-down list.

All Access developers are familiar with how the object drop-down list shows all the controls placed on the surface of an Access form, as well as an entry for the form itself. In this case, the object drop-down list shows the form object declared with the WithEvents keyword in addition to controls on the form's surface.

In this case, the form object named frm is declared and instantiated, and it's completely controlled by the main form. The main form captures the dialog form's events and uses the data passed through the FormClosing event. The main form could just as easily reference other properties of he dialog form.

Access forms are objects
It's important to understand that every Access form is actually an object created from a class and is not a physical entity stored within the .accdb file. Most people think of forms as UI objects that are maintained somewhere within the .accdb file and used as needed. In reality, each form is stored as a class, and Access instantiates a form object and displays the form on the screen whenever you work with the form's class. In Design view, Access presents you with an editable interface to the form's class, and you work with the form's properties.
Interestingly enough, the code behind an Access form is nothing more than a property of the form's class. The code behind an Access form is, itself, a class. There is nothing in the object-oriented paradigm supported by Access that prohibits a class from containing another class.
[Previous] [Contents]