Microsoft Access VBA Tutorials for self-paced learning, Class Modules, SQL Techniques, and AI Integration Guides.

Date2Text and Text2Date Functions

Date2Text and Text2Date Functions.

We already have some frequently used Report Footer formatting simple functions, like Report Page Number formatting function =PageNo([page],[pages]),  output: Page: 1/20 –> Page: 20/20, Report Period Function =Period([StartDate], [EndDate]), output: Period: 15/09/2007 To 30/09/2007 and =Dated()  Function, output: Dated: 15/09/2007 on Report Footer.  Even though they are simple Report Header/Footer formatting functions, they save time on report design.  Check the following Links, if you have not yet come across those Functions earlier:

Useful Report Functions.

Continued on Page 2/- an indicator Label on the report page footer, on multi-page reports.

Our new Function formats the Date Value in the following sample Text form:

  Sunday, 27th October 2019.

The Date2Text() Function Code.

Public Function Date2Text(ByVal dt As Date) As String
Dim txt As String, num As Integer

num = Day(dt)

   Select Case num
       Case 1, 21, 31
          txt = "st "
       Case 2, 22
          txt = "nd "
       Case 3, 23
          txt = "rd "
       Case 4 To 20, 24 To 30
          txt = "th "
   End Select
   
   Date2Text = WeekdayName(Weekday(dt)) & "," & Day(dt) & txt & MonthName(Month(dt)) & " " & Year(dt)
   
End Function

Copy and paste the Code above into the Standard VBA Module, save the code, and compile it.

Let us try out the Code directly from the Debug Window. Press Ctrl+G to display the Debug Window if it is not already visible on the VBA editing Window.

Sample Test Runs.

D = #27-10-2019#

? Date2Text(D)
Result: Sunday, 27th October 2019

D=Cdate("22/10/2019")

? Date2Text(D)
Result: Tuesday, 22nd October 2019

D=DateValue("11/10/2019")

? Date2Text(D)
Result: Friday, 11th October 2019

? Date2Text(Date)
Result: Thursday, 31st October 2019

Weekday 1 to 7 is Sunday to Saturday. This depends on your Computer's Regional Settings. If it is not correct in your case, then change it in the Regional Settings on your Computer.

Scope of this Function.

The Date2Text() Function can be placed in a TextBox on the Report Header, used on a Date-Field Query Column, or on the Main Form (Main Switchboard or Control Screen, etc.) as general info.

The Text2Date() Function.

The Date2Text() Function’s complementary function, Text2Date() VBA Code is given below.

Public Function Text2Date(ByVal txtDate As String) As Date
Dim S, dt As String
    
    S = Split(txtDate, " ")
    dt = Str(Val(S(1))) & "-" & S(2) & "-" & S(3)
    Text2Date = DateValue(dt)

End Function

The Date converted to Text form can be changed back to a valid date format with the Text2Date() Function. There is no validation check performed, and it expects the parameter value to be in the correct input format, similar to the Date2Text() Function.

Example:

? Text2Date("Thursday, 31st October 2019")

Result: 31-10-2019

Caution: If the parameter value is entered manually, then there should not be more than one space between each segment of the date text.

  1. Days in Month Function
  2. Custom Calculator and Eval Function
  3. Rounding Function MRound() of Excel
  4. Proper Function of Excel
Share:

Call Function From MouseMove Event Property

Calling a Function from the MouseMove Event Property.

This is about running a User-Defined Function (say =myFunction (Parameter)) on the TextBox MouseMove event property.  The difficult part is that when the MouseMove event occurs, the TextBox Name that runs the event must be passed as a parameter to the function.

This question was asked by a member in the Access Users Forum (www.accessforums.net), in the Forms Category of Posts, seeking suggestions for a solution.  A sample demo database was posted there twice on page 5, but the last one is the final version. 

I thought this would be useful to our readers and have presented it here, with details of how this difficult task was solved using an Object-Oriented Programming approach.

Manual Option.

This is easy to set up if it is manually entered =myFunction("Text1")  on each TextBox's Mouse Move Event Property

But the requirement is to pass the TextBox Name as a parameter dynamically to the Function.  It means we should somehow get the TextBox Name from the Mouse Move Event and pass it as a parameter to the function, placed on the same Mouse Move Event Property.

The real situation that demands this method is the following requirement of an Access Application:

The complexity of Requirements.

Assume that you are developing a database for a movie ticket booking Application and need around 350 or more text boxes on the Form, for a graphical seating arrangement design.  Each TextBox represents a single seat in the cinema hall, in several rows & Columns (i.e., each row has several seat positions). Each seat has a unique identity number (that is, the TextBox name), indicating its position in the auditorium; for example: Row-A, Seat No.5 (A5) or B1, etc. The TextBox will display its current status either Booked or Vacant.

The idea is that when the mouse moves over the TextBox (Seat), it should display the Seat Number (A5, or B1, etc.) on a dedicated Label in the Header or Footer Section of the Form, to help the customer look for their choice of Seat Numbers and book the Seat(s).

A simple Form with several TextBoxes and labels for sample TextBox arrangements, and try out this method to solve the problem.

PS: The technical details presented above may have lapses or may form suggestions in the reader's mind.  That is not important; the core point is how we could get the TextBox Name on the Mouse Move Event and pass the name as a string parameter to the =myFunction() Function, placed on the Mouse Move Event Property of the same TextBox.

Why Manual Method unacceptable.

So, writing =myFunction("A5") or =myFunction("B1") etc., in each one of the 350 Text Boxes' Event Property demands a lot of manual work.  Besides that, if any change to the seating arrangement or Seat Numbering scheme becomes necessary, then all the TextBox Properties have to undergo manual changes. 

Another option available is to set the Control Tip Text Property with the TextBox Name. When the mouse pointer rests on the TextBox, a brief delay occurs (not acceptable), then it displays the Seat Number from the Control Tip Text property.  Modifying the Control Tip Text Property is easy and can be done dynamically in the Form_Load() Event Procedure. 

But the database designer insists on passing the TextBox Name as a parameter to the Function.  Besides displaying the TextBox Name on the designated Label Caption, the Function may have other issues in the program to take care of.  

The Difficult Question.

Even though it sounds like a simple issue, the difficult question is: how do we get the TextBox name, say Text1, from the Name property of the running Form when the mouse moves over that TextBox, and pass the name as a parameter to the Calling Program?   Remember, the Mouse Move Event fires repeatedly, at every mouse-point coordinate on the text box (or on any other control it moves)  and this Event has some default parameters: Button, Shift, X, and Y coordinates of the Mouse Pointer on the Control.  But not the Control Name among them.

The Programming Roadblocks.

There are times when we face roadblocks in solving issues, when conventional programming solutions don't work.  But such issues can be easily handled by a few lines of code through Object-Oriented Programming.  This is a classic example, easy to understand, and does the job with a few lines of code.

Access Class Module Objects.

We have already covered earlier the fundamentals of Access Class Modules and Objects-based programming.  If you are not familiar with stand-alone Access Class Modules and Objects, then the Page links at the bottom of this page to start learning the basics.

The Easy Solution.

To solve the above-narrated issue, we have used a few lines of code in the Access Class Module Objects (both Form and stand-alone Class Modules) and used a Collection Object to organize several instances of the Class Module Objects, rather than using Arrays.

The General-purpose TextBox Object Class Module: ClsTxt Code:

Option Compare Database Option Explicit Private WithEvents txt As Access.TextBox Private frm As Access.Form Public Property Get pFrm() As Access.Form Set pFrm = frm End Property Public Property Set pFrm(ByRef vNewValue As Access.Form) Set frm = vNewValue End Property Public Property Get pTxt() As Access.TextBox Set pTxt = txt End Property Public Property Set pTxt(ByRef vNewValue As Access.TextBox) Set txt = vNewValue End Property Private Sub txt_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) '------------------------------------------------ 'The first MouseMove Event, of each TextBox, 'comes into this sub-routine. 'The MouseMove Event Property is set with the Function: '"=RunMouseOver('Textbox_Name','Form_Name')" 'with the TextBox & Form Names as Parameters. 'Subsequent MouseMove Events Calls the Function 'directly from Standard VBA Module1, 'control will not come into this sub-routine, any more. '------------------------------------------------

txt.OnMouseMove = "=RunMouseOver('" & txt.Name & "')" End Sub

Two Class-Module Properties, the Access.TextBox and 'Access.Form' Objects are declared in txt and frm object variables, respectively, with Private scope.  The txt Property is declared with the WithEvents keyword to capture Events originating from TextBoxes on the form. The next twelve lines of code assign and retrieve objects in TextBox and Form Properties with Set/Get  Property Procedures.  This will prevent direct access to the Class Module Properties txt and frm from outside.  Up to this point, it is the TextBox Object's common feature of assigning and retrieving values to and from the Object Variables.  The frm Property is not used here.

The sub-routine part is what we are interested in.  Any number of Text-Box-based Event Procedure subroutines can be written here rather than directly on the Form's Class Module.

The txt_MouseMove() Event.

The Text Box's first Mouse Move Event transfers control into the txt_MouseMove() Subroutine.  There is only one executable statement in the subroutine that overwrites the Text Box's Mouse Move Event Property Value. 

txt.OnMouseMove = "=RunMouseOver('" & txt.Name & "')"

We can get the TextBox name from the txt Property.  The TextBox Mouse Move Event Property (initially set as "[Event Procedure]" in the Form_Load() Event Procedure) is replaced with the Function "=RunMouseOver('" & 'txt.name' & "')", and the TextBox name as a string Parameter.  The subsequent Mouse Move Events will call the RunMouseOver() Function in the Standard Module from the Mouse Move Event Property, and never come back to the above sub-routine txt_MouseMove() anymore. 

So, the first Mouse Move Event on any Text Box will do the trick, and other TextBoxes will wait for their turn for a Mouse Move Event to take place.

The simple RunMouseOver() Function Code will be presented later on this page.

Form3 Class Module Code.

The Form's (Form3) Class Module VBA Code is given below:

Option Compare Database
Option Explicit

'Declare Class ClsTxt as Object F
Private F As ClsTxt
'Declare Collection Object as C
Private C As Collection

Private Sub Form_Load()
Dim ctl As Control

Set C = New Collection 'instantiate Collection Object

For Each ctl In Me.Controls 'scan through the controls
   If TypeName(ctl) = "TextBox" Then ' Take only Text Boxes
        Set F = New ClsTxt 'instantiate ClsTxt Class Object
        
            Set F.pFrm = Me 'Assign Form to pFrm Property
            Set F.pTxt = ctl 'Assign TextBox to pTxt property
            'enable mouse move event
            F.pTxt.OnMouseMove = "[Event Procedure]"
            
        C.Add F 'add ClsTxt Object to Collection
        
        Set F = Nothing 'remove the ClsTxt object instance from memory
    End If
Next

End Sub

Private Sub Form_Unload(Cancel As Integer)
    'Erase the Collection Object when Form3 is closed.
    Set C = Nothing
End Sub

In the global declaration area, Class-Module ClsTxt is declared as Object F and a Collection Object as C.

In the Form_Load() Event Procedure, we scan through Form3 controls and take only TextBox controls.  The Form Object and TextBox controls are assigned to the F.pFrm and F.pTxt Properties of the ClsTxt Object.

The F.pTxt Object's OnMouseMove() Event Procedure is enabled so that, when it happens, the control goes to the txt_MouseMove() sub-routine of the Class Module instance of ClsTxt for the first time. In the next step, the ClsTxt Object instance F is added to the Collection Object, as its Item.  In the next step, the ClsTxt Object instance F is cleared from memory.  A new F object instance is created for the next Text Box.  This is necessary to identify each instance of the TextBox Object with a different internal reference of each TextBox added to the Collection Object as its Item.

This process repeats for all the TextBoxes on Form3.

When the Form is closed, the Form_Unload() Event executes, and the Collection Object is cleared from Memory.

When Form3 is open, these initialization steps are performed, and all the TextBox Controls are enabled with the Mouse Move Event, added to the Collection Object, and stay in memory till Form3 is closed.  Each Text Box's Mouse Move Event is handled by its respective ClsTxt Object instance added to the Collection Object.

The RunMouseOver() Function Call.

When the user moves the mouse over a Text Box (say Textbox name A1) for the first time, the Mouse Move Event executes and calls the txt_MouseMove() Event Procedure in the ClsTxt Object instance for that Text Box, in the Collection Object item.  In this procedure, the TextBox's MouseMove Event Property is modified and set to the =RunMouseOver("A1")  Function, with the Text Box name A1 as a parameter.

From the second MouseMove event onwards, the event calls the RunMouseOver() Function from the Standard Module1.  The VBA Code of this Function is given below.

Option Explicit

Public Function RunMouseOver(strN As String)
    Screen.ActiveForm.Controls("Label0").Caption = strN

End Function

The RunMouseOver() Function receives the textbox name as a parameter.  The statement addresses the Label0 control directly through the Screen Object ActiveForm route and changes the Label's Caption with the Mouse Moved Textbox Name.

The RunMouseOver() Function can be modified to pass the Form's Name as a second parameter and can be used to address the Label0 control as Forms(strForm).Controls("Label0").Caption = strN.  This is avoided to keep the parameter expression simple.

When the Mouse is moved over other TextBoxes, the same procedure is repeated for that TextBox object instance in the Collection Object.

When Form3 is closed, the Collection Object instance C, containing all TextBoxes' ClsTxt Class Object instances, is cleared from memory.

The Function RunMouseOver() assigned to the TextBox's Mouse Move Event Properties is cleared (as they are assigned dynamically), and the Property will remain empty.

Next time Form3 is opened, everything falls into place again and is ready for action.  So everything is controlled by Object-oriented Programming and happens dynamically.  This sample database is uploaded as a solution to the Access User's Forum Page 5, where several alternative options are suggested by other members.  

The Demo Database is attached and may be downloaded; try it out yourself.



Class Module Tutorials.

  1. MS-Access Class Module and VBA
  2. MS-Access VBA Class Object Arrays
  3. MS-Access Base Class and Derived Objects
  4. VBA Base Class and Derived Objects-2
  5. Base Class and Derived Object Variants
  6. MS Access Recordset and Class Module
  7. Access Class Module and Wrapper Classes
  8. Wrapper Class Functionality Transformation
  9. MS Access and Collection Object Basics
  10. MS Access Class Module and Collection Object
  11. Table Records in Collection Object and Form
  12. Dictionary Object Basics
  13. Dictionary Object Basics-2
  14. Sorting Dictionary Object Keys and Items
  15. Display Records from Dictionary to Form
  16. Add Class Objects as Dictionary Items
  17. Update Class Object Dictionary Item on Form
Share:

ROUNDDOWN Function of Excel

'RoundDown' Excel Function.

We introduced the Excel ROUNDUP(Number, num_digits) Function in Microsoft Access earlier.  It works like the ROUND() Function except that the number is always rounded up. 

Exmple-1: Greater than >0  (>0): ROUNDUP(3.14159, 3) = 3.142

Example-2: Equal to 0 (=0): ROUNDUP(3.2,0) = 4

Example-3: Less than <0 (<0):  ROUNDUP(31415, -2) = 31500

Visit the following link for more details on the ROUNDUP () Function:

The ROUNDUP() Function of Excel in Access.

The ROUNDDOWN() Function of Excel does the opposite of the ROUNDUP() Function.  When the number of digits specified is greater than 0 (zero), then the number is rounded down to the specified number of Decimal Places.  If the number of digits specified is 0 (zero), then the number is rounded down to the nearest integer.

Syntax:

ROUNDDOWN(Number, num_digits)

Number: RequiredAny Real Number.

num_digits: Required, Number of Digits the Number to Round Down to.

Positive Number (>0): ROUNDDOWN(3.149, 2) =  3.14

Zero (0): ROUNDDOWN(8.9, 0) = 8

Negative Number (< 0): ROUNDDOWN(258, -1) = 250


Public Function ROUNDDOWN(ByVal Num As Double, ByVal num_digits As Integer) As Double
'-------------------------------------------------
'ROUNDDOWN() Function of Excel Redefined in MS-Access
'Author: apr pillai
'Date  : Sept 2019
'Rights: All Rights Reserved by www.msaccesstips.com
'-------------------------------------------------

Dim S1 As Integer, S2 As Integer

On Error GoTo ROUNDDOWN_Err
S1 = Sgn(Num)
S2 = Sgn(num_digits)

Select Case S1
    Case 0
        ROUNDDOWN = 0
        Exit Function
    Case 1
    Select Case S2
        Case 0
            ROUNDDOWN = Int(Num) * S1
        Case 1
            ROUNDDOWN = (Int(Num * (10 ^ num_digits)) / 10 ^ num_digits) * S1
        Case -1
            num_digits = Abs(num_digits)
            ROUNDDOWN = Int(Num / (10 ^ num_digits)) * 10 ^ (num_digits) * S1
    End Select
    Case -1
    Select Case S2
        Case 0
            ROUNDDOWN = Int(Abs(Num)) * S1
        Case 1
            ROUNDDOWN = (Int(Abs(Num) * (10 ^ num_digits)) / 10 ^ num_digits) * S1
        Case -1
            num_digits = Abs(num_digits)
            ROUNDDOWN = (Int(Abs(Num) / (10 ^ num_digits)) * 10 ^ num_digits) * S2
    End Select
End Select

ROUNDDOWN_Exit:
Exit Function

ROUNDDOWN_Err:
MsgBox Err & " : " & Err.Description, , "ROUNDDOWN()"
Resume ROUNDDOWN_Exit
End Function


The ROUNDDOWN() Function is not field-tested for accuracy; use it at your own risk.

The Function is developed based on the sample output given in the Microsoft Help Document. The Microsoft Excel Help Document extract is reproduced below for your information.

FormulaDescriptionResult
=ROUNDDOWN(3.2, 0)Rounds 3.2 down to zero decimal places.3
=ROUNDDOWN(76.9,0)Rounds 76.9 down to zero decimal places.76
=ROUNDDOWN(3.14159, 3)Rounds 3.14159 down to three decimal places.3.141
=ROUNDDOWN(-3.14159, 1)Rounds -3.14159 down to one decimal place.3.1
=ROUNDDOWN(31415.92654, -2)Rounds 31415.92654 down to 2 decimal places to the left of the decimal point.31400
  1. ROUNDUP Function of Excel in MS Access
  2. Proper Function of Excel in Microsoft Access
  3. Appending Data from Excel to Access
  4. Writing Excel Data Directly into Access
  5. Printing MS-Access Report from Excel
  6. Copy and Paste Data From Excel to Access2007
  7. Microsoft Excel Power in MS-Access
  8. Rounding Function MROUND of Excel
  9. MS-Access Live Data in Excel
  10. Access Live Data in Excel-2
  11. Opening Excel Database Directly
  12. Create Excel Word File from Access
Share:

WithEvents and All Form Control Types

'WithEvents' and All Form Controls.

So far, we have primarily worked with a single type of control — TextBoxes on a form — enabling specific events to be raised and captured in a Class Module using various methods. We have performed several demo runs to capture events, such as AfterUpdate and LostFocus raised from multiple TextBoxes, and handled them in the Class Module.

For each TextBox on the form, a separate Class Object instance is created, enabled with the required event, and stored either in an Object Array, a Collection Object, or as items in a Dictionary Object.

For example, suppose the form has five TextBoxes, but requires only three event-handling subroutines to perform tasks such as validating the entered data. In that case, only three instances of the ClsText Class Module are created—one for each of the three active TextBoxes—while the remaining two are ignored.

When an enabled event fires, the corresponding Class Object instance captures the event and executes the associated event procedure code.

In our previous session, we extended this concept by adding Command Button controls and their own Class Module alongside the TextBoxes for our demo runs.

New Demo Form: frmControls_All

In this demo Run, we will include most of the frequently used controls on the Form.  The image of the demo Form is given below:

In addition to TextBoxes and CommandButtons, forms can also include other controls such as Tab Controls, Combo Boxes, List Boxes, and Option Groups.

To handle events from these controls, we need to create a separate Class Module for each new control type on the demo form. We already have dedicated Class Modules set up for TextBoxes and Command Buttons.

The TextBox and CommandButton Class Modules.

The TextBox and CommandButton Class Module code, along with their explanations, were presented in an earlier article. To keep this post concise, those details are not repeated here.

You can revisit those sections by using the bookmark links provided below:

  • Class Module: ClsText

  • Class Module: ClsCmdButton

  • Download: You may download the demo database from that post, run the sample form, and experiment with the controls to explore the implemented methods and VBA code.

What we are doing here is simply an extension of that work, now incorporating more control types on the form.

Tab-Control Class Module: ClsTabCtrl

The Tab-Control Class Module: ClsTabCtrl VBA Code is given below:

Option Compare Database
Option Explicit

Private frm As Access.Form
Private WithEvents Tb As Access.TabControl

Public Property Get p_frm() As Access.Form
  Set p_frm = frm
End Property

Public Property Set p_frm(ByRef frmValue As Access.Form)
  Set frm = frmValue
End Property

Public Property Get p_Tb() As Access.TabControl
   Set p_Tb = Tb
End Property

Public Property Set p_Tb(ByRef tbValue As Access.TabControl)
  Set Tb = tbValue
End Property

Public Sub Tb_Change()
Dim msg As String, title As String

Select Case Tb
    Case 0
        frm.Controls("Computer").Value = Environ("ComputerName")
        title = Tb.Pages(0).Name
        msg = "Tab Page Index = 0"
    Case 1
        frm.Controls("UserName").Value = Environ("UserName")
        title = Tb.Pages(1).Name
        msg = "Tab Page Index = 1"
End Select

MsgBox msg, , title

End Sub

The ClsTabCtrl Class Module declares two properties: a Form object (frm) and a Tab Control object (Tb). The Tb property is declared using the WithEvents keyword, enabling it to capture events occurring on the Tab Control.

The Set property procedure p_frm assigns the active form object to the frm property, which is declared with Private scope. The corresponding Get procedure allows access to the form reference, enabling you to address other controls on the form.

For example:

frm.p_frm.Controls("UserName").Value = Environ("UserName")

Similarly, the Set property procedure assigns the Tab Control object to the Tb property, and the Get property procedure returns this Tab Control reference to the calling program, as explained above.

Finally, the Tab Page Change event is captured in the Tb_Change() subroutine, which executes the desired code when the user selects a different tab page. Note that clicking a tab page does not trigger a Click event—instead, it triggers the Change event. This is why we handle it using the Tb_Change() procedure.

Two string variables, msg and title, are initialized with text that will be displayed in a message box. When a Tab Page is clicked, the Tab Page Change event is triggered and captured in the Tb_Change() subroutine. When the event is captured in the Class Module and the Event Procedure is executed successfully, it displays a Message.

The tab control contains two pages, with page index numbers 0 and 1, and each page has a TextBox control.

When the TabPage_Change event fires, the code checks the selected tab page index.

If the first page (index 0) is selected, the TextBox on that page (named Computer) is updated with the computer’s name, using the frm object reference to access the control.

If the second page (index 1) is selected, the TextBox on that page is updated with the current Windows user name (Environ("UserName")).

Note: The exact procedure you write inside the Tb_Change() subroutine will depend on your project’s specific requirements for handling this event.

The Combo Box Class Module: ClsCombo

The Class Module Code for the Combo Box control is given below:

Option Compare Database
Option Explicit

Private WithEvents cbx As Access.ComboBox

Public Property Get p_cbx() As Access.ComboBox
 Set p_cbx = cbx
End Property

Public Property Set p_cbx(ByRef cbNewValue As Access.ComboBox)
 Set cbx = cbNewValue
End Property

Private Sub cbx_Click()
Dim vVal As Variant
Dim cboName As String

vVal = cbx.Value
cboName = cbx.Name

Select Case cbx
    Case "Combo10"
        'Code Goes here
    Case "Combo12"
        'Code Goes here
End Select

MsgBox "Clicked: " & vVal, , cboName
End Sub 

The ComboBox Class Module declares a single property, cbx, with Private scope and the WithEvents keyword to capture any events fired from the ComboBox.

The Public Property Set procedure p_cbx() receives the ComboBox control reference from the Form and assigns it to the cbx property.

The Property Get procedure returns the ComboBox control reference to any calling procedure outside this class module.

The cbx_Click() subroutine captures the ComboBox's Click event on the Form. Within this procedure, a Select Case structure determines which ComboBox triggered the event and then executes the appropriate block of code based on the control’s name.

If you want to capture other events—such as OnGotFocus, OnLostFocus, AfterUpdate, OnChange, and so on—you can create corresponding subroutines in the same class module (for example, cbx_GotFocus()) and write the required VBA code within them.

However, to make these events fire when they occur on the form, you must also enable them in the Derived Class Module.

Currently, the Click event subroutine simply displays a message showing the item value selected from the Combo Box.

The List Box Class Module: ClsListBox

The List Box Class Module Code structure is similar to the ComboBox Code and programmed to capture only the Click Event.

Option Compare Database
Option Explicit

Private WithEvents LstBox As Access.ListBox

Public Property Get p_LstBox() As Access.ListBox
 Set p_LstBox = LstBox
End Property

Public Property Set p_LstBox(ByRef pNewValue As Access.ListBox)
 Set LstBox = pNewValue
End Property

Private Sub LstBox_Click()
Dim vVal As Variant
Dim lst As String

vVal = LstBox.Value
lst = LstBox.Name

Select Case lst
    Case "List16"
        'Code
    Case "List18"
        'Code
End Select

  MsgBox "Clicked: " & vVal, , lst

End Sub

It can be modified with additional subroutines to capture any other Event Raised on the List Box.  The existing Code works on similar lines to the ComboBox and displays the selected item value in the message box.

The Class Module for Options Group: ClsOption

The Options Group has three different styles: Option Buttons, Check Boxes, and Toggle Buttons. In our Demo Form, we have used only two of these styles — Option Buttons and CheckBoxes. All three, however, work in the same way; the only difference is in their display style. Their control names start with the word Frame followed by a number, just like other controls on the form (e.g., Frame18, Text2, Combo10, List12).

The ClsOption Class Module Code is given below:

Option Compare Database
Option Explicit

Private WithEvents Opts As Access.OptionGroup

Public Property Get p_Opts() As Access.OptionGroup
  Set Opts = Opts
End Property

Public Property Set p_Opts(ByRef pNewValue As Access.OptionGroup)
  Set Opts = pNewValue
End Property

Private Sub Opts_Click()
Dim txtName As String, intVal As Integer
Dim msg As String, strVal As String

intVal = Opts.Value
strVal = Opts.Name

Select Case strVal
    Case "Frame25"
        Select Case intVal
            Case 1
                'code
            Case 2
                'Code
            Case 3
                'Code
        End Select
    Case "Frame34"
        Select Case intVal
            Case 1
                'Code
            Case 2
                'Code
            Case 3
                'Code
        End Select
End Select

msg = msg & " Click :" & intVal

MsgBox msg, , Opts.Name

End Sub

Option Group items have labels that indicate their actual purpose and meaning, and each item is assigned an index number starting from 1. In the Click event procedure, we check the selected item's predefined index and perform the Task, such as opening a Form, displaying a Report, or running a Macro.

Whenever an item is clicked, the frame’s events are triggered, and we use the selected index number to run the corresponding action.

Now, the Class Modules for all the controls on the Demo Form are ready.

Note: Some controls on the form may not be associated with any events and therefore will never fire an event. In such cases, we do not create Class Module instances for them. However, we can still read from or update the values of these controls from within the event procedures of other controls that do have Class Module instances.

Example: On the Tab Control pages, we have two Text Boxes that display the computer name and the Windows user name. These values are updated from the Tab Control’s Class Module, even though the Text Boxes themselves do not raise any events.

At this stage, we now have sample Class Modules (we can refer to them as Class Module Templates) for all types of controls on the form. Depending on the needs of a specific form in your project, you can create copies of the required Class Module Templates and customize their existing subroutines—or add new ones—to capture the necessary events and execute the related code.

The Derived Class Module: ClsControls_All

We now need an intermediary Class Module to act as a bridge between the stand-alone Class Modules for each type of control and the Form’s own Class Module. This intermediary will help organize all the control-specific Class Modules used on the form and also enable the required events for each control.

To achieve this, we will create a Wrapper Class Module that contains properties for all the different control-type Class Modules. It will scan the controls on the form, match them by their control names, and then enable the corresponding events by linking each control to its appropriate Class Module instance.

The Wrapper Class Module ClsControls_All Code is given below:

Option Compare Database
Option Explicit

Private tx As ClsText
Private cmd As ClsCmdButton
Private cbo As Clscombo
Private lst As ClsListBox
Private opt As ClsOption
Private tbc As ClsTabCtrl
Private Coll As Collection
Private fom As Access.Form

Public Property Get p_fom() As Access.Form
    Set p_fom = fom
End Property

Public Property Set p_fom(ByRef objFrm As Access.Form)
    Set fom = objFrm
    Class_init
End Property

Private Sub Class_init()
Dim ctl As Control
Const Evented = "[Event Procedure]"

Set Coll = New Collection

For Each ctl In fom.Controls 'check through Form controls

    Select Case TypeName(ctl) 'pick only the required control type
           Case "TextBox"
                Select Case ctl.Name
                    Case "Text2", "Text4", "Text6"
                        Set tx = New ClsText 'create new instance
                        Set tx.p_frm = fom 'assign Form Object to property
                        Set tx.p_txt = ctl 'assign control to p_txt Property
                        
                        tx.p_txt.AfterUpdate = Evented 'enable AfterUpdate Event
                        tx.p_txt.OnLostFocus = Evented 'enable LostFocus Event
                        
                'Add ClsText Object instance tx to Collection
                        Coll.Add tx
                'Release ClsText Object tx from memory
                        Set tx = Nothing
                End Select
                
            Case "TabControl"
                Set tbc = New ClsTabCtrl 'create instance of ClsTabCtrl
                Set tbc.p_frm = fom 'pass Form Object to p_frm Property
                Set tbc.p_Tb = ctl 'pass Tab Control to p_Tb Property
                
                tbc.p_Tb.OnChange = Evented 'enable OnChange Event
                
            'Add ClsTabCtrl instance tbc to Collection Object
                    Coll.Add tbc
            'Release tbc instance from memory
                Set tbc = Nothing
            
            Case "CommandButton"
                Select Case ctl.Name
                    Case "Command8", "Command9"
                    
                       Set cmd = New ClsCmdButton 'create new instance of ClsCmdButton
                       Set cmd.p_Btn = ctl ' pass Command Button control to p_Btn Property
                       
                       cmd.p_Btn.OnClick = Evented 'enable OnClick Event
                       
            'Add ClsCmdButton instance cmd to Collection Object
                      Coll.Add cmd
            'Release cmd instance from memory
                       Set cmd = Nothing
                End Select
                
            Case "ComboBox"
                Select Case ctl.Name
                    Case "Combo10", "Combo12"
                        Set cbo = New Clscombo ' create new instance of ClsCombo Class
                        Set cbo.p_cbx = ctl ' pass control (Combo10 or Combo12) to CB Property
                        
                        cbo.p_cbx.OnClick = Evented ' enable OnClick Event
                        
            'Add ClsCombo instance cbo to Collection Object
                       Coll.Add cbo
            'Release cbo instance from memory
                      Set cbo = Nothing
                End Select
                
            Case "ListBox"
                Select Case ctl.Name
                    Case "List14", "List16"
                      Set lst = New ClsListBox ' create new instance of ClsListBox
                        Set lst.p_LstBox = ctl ' pass the control to lst.LB Property of instance.
                        lst.p_LstBox.OnClick = Evented ' enable OnClick Event
                        
            'Add lst instance to Collection Object
                       Coll.Add lst
            'Release lst instance from memory
                      Set lst = Nothing
                End Select
                
            Case "OptionGroup"
                Select Case ctl.Name
                    Case "Frame25", "Frame34"
                      Set opt = New ClsOption ' create new instance
                        Set opt.p_Opts = ctl  ' pass control to opt.OB Property
                        
                        opt.p_Opts.OnClick = Evented ' enable OnClick Event
                        
            'Add opt instance to Collection Object
                       Coll.Add opt
            'Release lst instance from memory
                      Set opt = Nothing
                End Select
    End Select
    
Next

End Sub

In the global declaration area of the ClsControls_All Class Module, we have declared the Class Module objects of all the controls on the form with Private scope. We have declared a Collection object and a Form object as properties of this class.

From our earlier trial runs, we learned that using a Collection object is a simpler and more efficient approach than using arrays of Class Module instances. The array method requires maintaining separate index counters for each control type, incrementing them for each new object instance, and repeatedly resizing the arrays to add new elements.

By contrast, adding each instance of a control-specific Class Module directly to a single Collection object is much easier and eliminates all extra steps.

The Set property procedure assigns the Form object reference, passed from the active form, to the Fom property in this class.

The Class_Init() subroutine is called from the Set property procedure to enable the events for each required control on the form. This ensures that when an event occurs, it is captured by the corresponding subroutines in their respective Class Modules.

The Get property procedure handles external requests for the Form object reference stored in this class.

Inside Class_Init(), a Control object (ctl) is declared to iterate through the form’s controls, and a string constant named Evented is set to the value "[Event Procedure]", which is used when assigning event procedures to control properties.

The Collection object (Coll) is then instantiated to store the Class Module instances of the controls.

Each control on the form—such as Text Boxes, Tab Controls, and others—is scanned for, and if it requires event handling (like AfterUpdate, LostFocus, or Click), a new instance of its corresponding Class Module is created, events are enabled, and the instance is added to the Coll collection object.

Remember, each Form control’s Object Property—such as a TextBox—was declared with the WithEvents keyword in its corresponding Class Module. This allows the Class Module instance to capture and handle events that occur on the control at runtime. When an event occurs, it is caught by the Class Module object instance stored in the Collection, and the relevant subroutine code is executed.

In the Class_Init() subroutine, a For Each ... Next loop iterates through all controls on the form. Each control is tested for its type—such as TextBox, TabControl, ComboBox, and so on. Controls that are not event-driven, like Labels, Images, or ActiveX objects, are skipped.

Within each control type, an additional check is performed on the control’s Name property to identify which controls should be wired to events.

For example, when a control of type TextBox is found, its Name is checked against "Text2", "Text4", and "Text6".

  • If the control matches one of these names, a new ClsText Class Module instance (e.g., tx) is created.

  • The active form reference (fom) is assigned to the tx.p_frm property, and the TextBox control reference (ctl) is assigned to the tx.p_txt property.

  • The specific events for that control—such as AfterUpdate and LostFocus—are then enabled.

Finally, this initialized ClsText object (tx) is added as an item to the Collection object, so that when any of these events fire, they are captured and handled through their Class Module instance.

The same process is repeated for Text4 and Text6. If each TextBox requires different events to be enabled, then they must be handled in separate Case statements. Each control is configured with its specific events, and a new instance of the corresponding Class Object is created and added to the Collection.

However, since all three TextBoxes (Text2, Text4, and Text6) are enabled with the same AfterUpdate and LostFocus events, their names are grouped within a single Case statement for convenience.

Note: There are two additional TextBoxes, one on each Tab Page. Although they are part of the form’s controls, they are not enabled with any events. Instead, they are used to display values dynamically during the execution of the Tab Control’s Change() event procedure.

The Tab Control does not raise a Click event when its pages are selected; instead, it triggers a Change event. By default, TabCtl18.Pages(0) is the current page.

When the user switches to 'TabCtl18.Pages(1)', the TextBox named UserName is updated with the Windows user name.

When switching back to the first page, the TextBox named Computer is updated with the computer’s name, using the statement:

frm.Controls("Computer").Value = Environ("ComputerName")

To enable this, the ClsTabCtrl Class Module includes a Form object property (frm) that allows the class to directly reference and update these TextBox controls on the form.

All other controls on the form—CommandButtons, Combo Boxes, List Boxes, and Option Groups—are currently enabled only for the click event. Their respective Class Modules contain only Click event procedures to capture the event and display a message for demonstration purposes.

If you want to capture any additional events from these controls, simply add the corresponding event procedures (e.g., GotFocus, AfterUpdate, Change) in their respective Class Modules, and then enable those events in the derived Class Module ClsControls_All.

The Form: frmControls_All's Class Module Code

Option Compare Database
Option Explicit

Private A As New ClsControls_All

Private Sub Form_Load()
    Set A.p_fom = Me
End Sub

The derived class object ClsControls_All is declared and instantiated as the object A.

The current form object is then passed as a parameter to the A.p_fom property procedure.

This is the only code required in the form’s class module to initialize and connect all the individual control class modules through the ClsControls_All class.

The Demo Run

Download the Demo database from the download link given at the end of this Article.

When you open the Demo Database, the Form frmControls_All opens in Normal View by default.

Testing Text Box – AfterUpdate, LostFocus Events.

Press the Tab key while the insertion point is in the first TextBox to trigger its LostFocus event. When this event fires, the TextBox is automatically filled with the text “msaccesstips.com”. This approach is useful for inserting default text when a field should not be left empty.

Next, modify the text by adding or removing some characters, and then press Tab again. This time, the AfterUpdate event will fire, and a message box will appear displaying the updated text.

The other two TextBoxes on the form respond to these events in the same way.

Note: The default text “msaccesstips.com” is inserted only if the TextBox is left empty when it loses focus. If you type any value into the TextBox and then press Tab, both the AfterUpdate and LostFocus events will fire sequentially.

Testing Tab Control Page Click Event.

By default, the first Tab Control Page will be the active page.  Click on the second Tab Control Page.  The Change Event fires, and the TextBox on the page is updated with the Windows User Name.

Click on the first Tab Page.  The text box on the first tab page is updated with the Computer Name.

Command  Button Click Event.

Click on the top CommandButton.  This will open Form1, displaying some text with hyperlinks to this Website.

The second CommandButton Click displays a message from the ClsText Class Module instance Item from the Collection Object.

Click Events of ComboBox, ListBox, Option Group.

All the controls on the frmControls_All form are enabled with the Click event through the Derived Class Module (ClsControls_All). Clicking any of these controls will display the selected item in a message box.

The event-enabled subroutines in these class modules are provided purely to demonstrate the programming approach.

We have developed a systematic and customizable Class Module template that makes VBA coding much easier. When starting a new project, you can simply copy this template and customize it to fit your project’s requirements. This approach not only simplifies debugging but also helps you quickly locate and fix any issues during field testing or while resolving logical errors in your project.

The Functional Diagram.

But before moving on, if you haven’t fully understood how all the pieces of this puzzle fit together and how they interact in their respective roles, take a moment to closely examine the diagram below.

I suggest revisiting the opening pages of this series; the links are provided at the bottom of this page.

If you have a ready-to-use Access database, make a copy of it and try restructuring the code based on what you’ve learned here. You’ll notice the difference when your project becomes better organized and easier to manage.

The Demo Database.

You may download the demo database from the link given below and try out the Form as explained above.


Links to WithEvents Tutorials.

  1. WithEvents MS-Access Class Module Tutorial
  2. WithEvents and Defining Your Own Events
  3. withevents Button Combo List TextBox Tab
  4. Access Form Control Arrays and Event Capturing
  5. Access Form Control Arrays and Event-2
  6. Access Form Control Arrays and Event-3
  7. WithEvents in Class Module for Sub-Form TextBox Events
  8. WithEvents in Class Module and Data Entry
  9. WithEvents and Access Report Event Sink
  10. WithEvents and Report Lines Hiding
  11. WithEvents and Report Lines Highlighting
  12. Withevents TextBox and Command Button Arrays
  13. Withevents TextBox CommandButton Dictionary
  14. Withevents and all Form Control Types
Share:

PRESENTATION: ACCESS USER GROUPS (EUROPE)

Translate

PageRank

Post Feed


Search

Popular Posts

Blog Archive

Powered by Blogger.

Labels

Forms Functions How Tos MS-Access Security Reports msaccess forms Animations msaccess animation Utilities msaccess controls Access and Internet MS-Access Scurity MS-Access and Internet External Links Queries Array Class Module msaccess reports Accesstips msaccess tips WithEvents Downloads Objects Menus and Toolbars MsaccessLinks Process Controls Art Work Collection Object Property msaccess How Tos Combo Boxes ListView Control Query VBA msaccessQuery Calculation Dictionary Object Event Graph Charts ImageList Control List Boxes TreeView Control Command Buttons Controls Data Emails and Alerts Form Custom Functions Custom Wizards DOS Commands Data Type Key Object Reference ms-access functions msaccess functions msaccess graphs msaccess reporttricks Command Button Report msaccess menus msaccessprocess security advanced Access Security Add Auto-Number Field Type Form Instances ImageList Item Macros Menus Nodes Recordset Top Values Variables msaccess email progressmeter Access2007 Copy Excel Expression Fields Join Methods Microsoft Numbering System RaiseEvent Records Security Split SubForm Table Tables Time Difference Utility WScript Workgroup Wrapper Classes database function msaccess wizards tutorial Access Emails and Alerts Access Fields Access How Tos Access Mail Merge Access2003 Accounting Year Action Animation Attachment Binary Numbers Bookmarks Budgeting ChDir Color Palette Common Controls Conditional Formatting Data Filtering Database Records Defining Pages Desktop Shortcuts Diagram Disk Dynamic Lookup Error Handler Export External Filter Formatting Groups Hexadecimal Numbers Import Labels List Logo Macro Mail Merge Main Form Memo Message Box Monitoring Octal Numbers Operating System Paste Primary-Key Product Rank Reading Remove Rich Text Sequence SetFocus Summary Tab-Page Union Query User Users Water-Mark Word automatically commands hyperlinks iSeries Date iif ms-access msaccess msaccess alerts pdf files reference restore switch text toolbar updating upload vba code