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

Table Query Records in Collection Object

Class Module and Collection Object.

  1. Steps to Create a Data View Form

    1. Create the Form

      • Use the built-in Access Form Wizard to create a Data View Form with the required fields from your table or query.

    2. Add a ComboBox in the Form Header

      • Insert a ComboBox in the Form Header Section.

      • Set its Row Source to a field with unique values (for example, [Last Name]) from the form’s record source table/query.

      • The ComboBox will serve as a record key, allowing you to randomly retrieve the selected record from the Collection Object and display its values in unbound TextBoxes.

    3. Insert a Hidden TextBox for the Key Field

      • Add an unbound TextBox in the Header of the form and name it KeyField.

      • Set its Visible property to False.

      • In its Control Source, enter the expression:

        ="[Last Name]"
      • If [Last Name] alone does not provide unique values, create a query that concatenates First Name and Last Name into a single expression (e.g., FullName: [FirstName] & " " & [LastName]).

      • Use this query as the form’s record source and populate the ComboBox with this new field instead.

    4. Add a Close Button in the Footer

      • Insert a Command Button in the Form Footer.

      • Name it cmdClose.

      • Set its Caption property to Close.

    5. Copy VBA Code

      • Open the Form1 Module from the demo database.

      • Copy its VBA code and paste it into the module of your newly created Employees Form.

    6. Save and Close

      • Save the form.

      • Close the form to complete the setup.

The Data View Form runs on the ready-made VBA code in the DATA_View Class Module. This Class Module is fully reusable—any form created using the same method with any Table or Query as the source data can use it without modification.

Once the form is opened in Normal View, simply select an item from the ComboBox. The record with the matching key value is instantly retrieved from the Collection Object and displayed in the unbound TextBoxes. Since this form is designed strictly for data viewing, all TextBoxes are locked to prevent editing.

Creating this form is extremely quick and straightforward:

  • You don’t need to write any code in the Form Module.

  • The Form Wizard automatically places and arranges the TextBoxes properly.

  • With just the simple steps outlined earlier, the entire setup can be completed in about five minutes—and the form is ready to run with the DATA_View Class Module.

The Ready-made Reusable Form Module Code:

Option Compare Database
Option Explicit

Private Cls As New DATA_View

Private Sub Form_Load()
Set Cls.o_frm = Me
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Set Cls = Nothing
End Sub

The DATA_View Class Object is instantiated in the Employees Form Module, and the current Form Object is assigned to the o_Frm() Property of the DATA_View Object.

The ready-made, reusable DATA_View Class Module Code.

Option Compare Database
Option Explicit

Private WithEvents cbo As ComboBox
Private WithEvents cmd As CommandButton
Private oFrm As Form
Private frmSec As Section

Private Coll As New Collection
Private txtBox() As String
Private strTable As String

'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'With Reusable Code
'------------------------------------------------------
'Quick Data View Screen
'Saving Table/Query Records in Collection Object
'Author:  a.p.r. pillai
'Date  :  26/04/2024
'Remarks: Keep Recordset in Collectuon Object
'       : and Retrieve specific record using Key
'Rights:  All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------

Public Property Get o_frm() As Form
    Set o_frm = oFrm
End Property

Public Property Set o_frm(ByRef vfrm As Form)
    Set oFrm = vfrm
    
    Set frmSec = oFrm.Section(acDetail)
    Call Class_Init
End Property

Private Sub Class_Init()
Dim db As Database
Dim rst As Recordset
Dim flds As Integer
Dim ctl As Control
Dim k As Integer
Dim Rec() As Variant, strKey As String
Dim vKeyName As String

strTable = oFrm.RecordSource

Set cmd = oFrm.cmdClose
    cmd.OnClick = "[Event Procedure]"
    
Set cbo = oFrm.cboName
cbo.OnClick = "[Event Procedure]"

'Make the Data Field TextBoxes Unbound
'Save the Field Names on the Form into the txtBox() Array
flds = 0
For Each ctl In frmSec.Controls
    Select Case TypeName(ctl)
        Case "TextBox"
           ctl.ControlSource = ""
           flds = flds + 1
           ReDim Preserve txtBox(1 To flds) As String
           
'Get the selected Field Names from the
'TextBoxes on the Form's Detail Section
           txtBox(flds) = ctl.Name
           ctl.Locked = True
    End Select
Next
'Set ComboBox Default Value
'Change Form Properties
oFrm.cboName.DefaultValue = "=[cboName].[column](0,0)"
oFrm.RecordSelectors = False
oFrm.NavigationButtons = False
oFrm.ScrollBars = 0

'Load the Table/Query Records into Collection Object
ReDim Rec(1 To flds) As Variant

Set db = CurrentDb
Set rst = db.OpenRecordset(strTable, dbOpenSnapshot)

'------------------------------
vKeyName = oFrm!KeyField 'Collection Key Field Value
'------------------------------

Do While Not rst.EOF
    For k = 1 To flds
       Rec(k) = rst.Fields(txtBox(k)).Value
    Next

'Key Field Name in the Form Fields
'=========================================
   strKey = rst.Fields(vKeyName).Value
'=========================================
    Coll.Add Rec, strKey 'Save Rec() Array
    rst.MoveNext
Loop

Set rst = Nothing
Set db = Nothing

End Sub

Private Sub cbo_Click()
Dim strKy As String, Record As Variant
Dim j As Long, L As Long, H As Long

'Get Selected Collection Key from ComboBox
strKy = cbo.Value
 
'Retrieve the record using Key from Collection
'and load into Variant Array Record
  
  Record = Coll(strKy)
  
  L = LBound(Record)
  H = UBound(Record)
  
'Add Field Values into corresponding Text Boxes
  For j = L To H
    oFrm(txtBox(j)) = Record(j) 'Display in Unbound TextBox
  Next
  oFrm.Requery
  
End Sub

Private Sub cmd_Click()
    DoCmd.Close acForm, oFrm.Name
End Sub

Private Sub Class_Terminate()
Do While Coll.Count > 0
    Coll.Remove 1
Loop

End Sub

Data_View VBA Code Segment-wise Review.

Note: You can instantiate the single DATA_View Class Module across multiple data display forms within the same project. If needed, you can even keep several of these forms open simultaneously and work with them independently. There’s no need to duplicate the Class Module or its VBA code—one module efficiently serves them all. 

The Global Declarations.

The ComboBox and Command Button controls are declared with the WithEvents keyword to capture their events when triggered on the form. Following this, a Form object (oFrm) and a Form Section object (frmSec) are declared.

A Collection object (Coll) is then instantiated, along with the txtBox() array (a string array with an unspecified number of elements) and the string variable strTable, which stores the name of the form’s record source (table or query).

The next section defines the Form Get and Set property procedures used to capture the active form object passed from the Form_Load() Event procedure. In the Set property procedure, after assigning the vFrm parameter (the received form object) to the oFrm property, the Employees form’s Detail section reference is assigned to the frmSec object, and after that the Class_Init() subroutine is called.

At the beginning of the Class_Init() Subroutine the statement:

strTable = oFrm.RecordSource

reads the Form's Record Source Property value and retains it in the strTable Variable.

The Command Button and Combobox Object References from the Form are assigned to the cmd and cbo Objects, respectively, and are enabled with the click Events.

'Make the Data Field TextBoxes Unbound
'Save the Field Names on the Form into the txtBox() Array
flds = 0
For Each ctl In frmSec.Controls
    Select Case TypeName(ctl)
        Case "TextBox"
           ctl.ControlSource = ""
           flds = flds + 1
           ReDim Preserve txtBox(1 To flds) As String
           
'Get the selected Field Names from the
'TextBoxes on the Form's Detail Section
           txtBox(flds) = ctl.Name
           ctl.Locked = True
    End Select
Next
'Set ComboBox Default Value
'Change Form Properties
oFrm.cboName.DefaultValue = "=[cboName].[column](0,0)"
oFrm.RecordSelectors = False
oFrm.NavigationButtons = False
oFrm.ScrollBars = 0
 

The For…Next loop scans through the Detail section of the form, retrieves the names of all TextBox controls (which correspond to the source data field names), and loads them into the txtBox() array. At the same time, it calculates the total number of TextBox controls and stores this value in the Flds variable.

Each TextBox is then made unbound by setting its ControlSource property to an empty string (ctl.ControlSource = ""). The txtBox() array is dynamically redimensioned at each iteration, increasing its size by one element while preserving the data already stored. The final count of fields is captured in the Flds variable.

This approach ensures that the code automatically adapts to any changes—fields added or removed from the form by the user—without requiring manual adjustments. The ComboBox's default Value is set with the Statement: =[cboName].[column](0,0).

The next four statements change the Form Properties.

vKeyName = oFrm!KeyField

The expression value, such as '="[Last Name]"', is read and the KeyField name (Last Name) is assigned to the variable vKeyName. The field used as the Collection Object key must contain unique values, and the same field data must also be present in the ComboBox cboName. This ensures that selecting an item from the ComboBox can be used to randomly retrieve the corresponding record from the Collection Object.ReDim Rec(1 To flds) As Variant

Set db = CurrentDb
Set rst = db.OpenRecordset(strTable, dbOpenSnapshot)

'----------------------------
vKeyName = oFrm!KeyField
'----------------------------

Do While Not rst.EOF
    For k = 1 To flds
       Rec(k) = rst.Fields(txtBox(k)).Value
    Next

'Key Value Field Name in the Form Fields
'=========================================
   strKey = rst.Fields(vKeyName).Value
'=========================================
    Coll.Add Rec, strKey
    rst.MoveNext
Loop

Rec()  The array is redimensioned to match the number of data fields on the form. The field values are then read from the source table or query, one record at a time, and stored in the corresponding elements of the Rec() array. Each completed Rec() array is added as a single item in the Collection object, with the Last Name field used as the collection item key (the second parameter of the Collection object’s Add() method). Through this process, source data records are loaded efficiently into the Collection Object.

Note: The source table or query may contain more fields than those placed on the form by the Form Wizard. However, the program only processes the fields whose names appear on the form; any additional fields in the source are ignored. You may freely add or remove fields from the form or rearrange their positions as needed. Just ensure that the Name property of each field remains unchanged and matches a corresponding field in the record source table or query. No changes to the VBA code are required.

The cbo_Click() Event Subroutine.

Private Sub cbo_Click()
Dim strKy As String, Record As Variant
Dim j As Long, L As Long, H As Long

'Get Selected Collection Key from ComboBox
strKy = cbo.Value
 
'Retrieve the record using Key from Collection
'and load into Variant Array R
  
  Record = Coll(strKy)
  
  L = LBound(Record)
  H = UBound(Record)
  
'Add Field Values into corresponding Text Boxes
  For j = L To H
    oFrm(txtBox(j)) = Record(j)
  Next
  oFrm.Requery
  
End Sub

When a user selects an item from the ComboBox, the selected value is used as the Collection Object Item Key to retrieve the corresponding record and load it into the Record() array.

The array element values are read in the same order they were stored in memory, and the unbound TextBoxes are populated accordingly, based on the order in which their names were read from the form. The TextBoxes are locked and cannot be edited.

The Data_View Class Module and accompanying Form Module code can be reused for any form created using this procedure, without modification. Ensure that the ComboBox is named cboName, and the Command Button is named cmdClose.

Data field names are taken directly from the Name property of the TextBoxes created by the Form Wizard in the Detail section of the form.

This approach allows you to create a fully functional Data View Form in a few minutes, complete with ready-to-use code.

Demo Database Download


Streamlining Form Module Code in Standalone Class Module.

  1. Reusing Form Module VBA Coding for New Projects.
  2. Defining Custom Events in Microsoft Access Part Two
  3. Objects and Their Built-in Events Part 3.
  4. Standalone Class Module and Events - Part Four
  5. Several TextBoxes and Event Capturing Part Five
  6.  Class Objects and Wrapper Classes - Part Six
  7. Form Module vs. Reusable Class Module Coding Demo - Part Seven
  8. Form VBA Structured Coding ACCESS USERS-GROUP.ORG Europe Presentation-7A
  9. Collection Object Replaces Class Object Array - Part Eight
  10. Reusability of Streamlined VBA Code - Part Nine
  11. Organizing Wrapper Classes for Different Forms - Part Ten
  12. ComboBox and Option-Group Wrapper Classes - Part Eleven
  13. Report Module Code in Class Module - Part Twelve
  14. Hiding Report Lines Conditionally - Part 13
  15. Form Report Detail Sections Event Handling - Part 14
  16. The Event Firing Mechanism in Access Objects-22
  17. One TextBox and Three Wrapper Class Instances-23
  18. Class for All Data Entry Editing Forms-30
  19. Wrapper Class Module Creation Wizard-v1
  20. Wrapper-Class-Template-Wizard-v2 - Final
  21. Existing Demo Databases Converted to the new Coding

  22. New Custom-Made Form Wizard VBA - Part 15
  23. New Custom-Made Report Wizard - Part 16
  24. Streamlining VBA External Files List in Hyperlinks-17
  25. Streamlining Event Procedures 3D-Text Wizard-18
  26. Streamlining Form Module VBA RGBColor Wizard-19
  27. Form VBA Structured Coding Numbers to Words Converter-20
  28. Streamlining Code Synchronized Floating Popup Form-24
  29. Streamlining Code Compacting/Repair Database-25
  30. Streamlining Code Remainder Popup Form-26
  31. Streamlining Code Editing Data in Zoom-in Control-27
  32. Streamlining Code Filter By Character and Sort-28
  33. Table Query Records in Collection Object-29
Share:

Streamline Filter By Character Sort

Data Filter by Character and Sort on Form.

Version 1.0 of this Article was originally published in April 2009 and upgraded with a significant change in Demo Application Version 2.0. In this version, the Event Subroutines are executed from the standalone class module, rather than from the form module.

After realizing the advantages of Event Procedures Coding in Standalone Class Modules, I found it difficult to revert to the traditional, less organized, and time-consuming Form/Report Module VBA Coding procedures.

In traditional coding, modifying the code of a specific event subroutine in a Form Module—especially when dealing with multiple types of controls—usually involves several manual steps.

  1. Open the Form in Design View.
  2. Select the required Control.

  3. Display its Property Sheet.
  4. Select the specific Event Property.

  5. Click on the Build Button to open the Event Procedure.
  6. Write/Modify the Code.

  7. Save the Form with the Code.
  8. Open the Form in Normal View to test the change.

Typically, it takes about eight steps to reach a specific event procedure in the Form Module to write, edit, and save changes. While it is true that multiple event procedures can be modified once the Form is open, steps 2 through 6 must still be repeated for each individual event procedure. This repetitive process is both tedious and time-consuming, especially when performed numerous times during development. At the same time, designing the user interface also consumes significant effort, as both activities often occur in parallel. Furthermore, any code written directly in the Form Module remains locked there and cannot be reused elsewhere, except for public functions written in a Standard Module.

If you’re a beginner VBA programmer, learning the programming language alongside user interface design is best accomplished through the traditional method of coding. If you’re an experienced developer, I encourage you to experiment with streamlined VBA Event procedure coding in Standalone Class Modules and see firsthand how it compares to the traditional coding style. 

This approach can offer valuable insights and significantly boost coding efficiency, saving substantial time spent in project development. Moreover, the reusable VBA code in Standalone Class Modules can be easily exported and integrated into other projects, further enhancing productivity.

Streamlined event procedure coding involves more than just moving the code from the form module to the standalone class module. It's about organizing the event procedure code in a structured, concise manner that promotes reusability without duplicating code for multiple objects of the same type in the form module. This new coding style enhances maintainability and reduces redundancy, resulting in a more efficient codebase.

Direct access to Structured Event Subroutines in the Standalone Class Module eliminates the struggle with the Form Design view to reach a particular event subroutine. This direct access streamlines the project development process, making it easier to locate and modify event procedures without the hassle of navigating the Form's design view.

Example of Structured Event Subroutine Coding:

The BeforeUpdate Event Procedure Code of several TextBoxes can be written within a single BeforeUpdate() Event Subroutine.

Private Sub txt_BeforeUpdate(Cancel As Integer)

'When the BeforeUpdate Event is captured the txt object will have
'the Name of the Object fired the Event
Select Case txt.Name

Case "Quantity"
    'Code
  
  Case "UnitPrice"
    'Code
  
  Case "SaleTax"
    ' Code
  
  Case . . .
  
End Select
End Sub

The seventh episode in this series of articles is a prime example of event subroutine code Reuse and illustrates an organized, structured approach to event procedure coding. By writing just one set of GotFocus and LostFocus Event Subroutines, you can efficiently manage the behavior of 25 or more text boxes on the form when they gain or lose focus. This example offers a straightforward demonstration of how to implement streamlined event procedure coding in a standalone class module, emphasizing code reusability and reduced redundancy.

Microsoft Access controls, such as TextBoxes, Command Buttons, and others, rely on event-defining, event-firing, and event-capturing mechanisms, which form the foundation of streamlined event subroutine coding. I explored these concepts in detail during my presentation to the Access User Groups (Europe) on January 3, 2024. The presentation is available as a YouTube video on the Access User Groups (Europe) channel, titled "Streamlined Event Procedure Coding in Standalone Class Modules," which offers valuable insights into this coding approach.

In our current project 'Filter by Character and Sort', the Customers Form’s record source is derived from the CustomersQ query, which contains multiple records. To enhance user experience and efficiency, we’ll implement a technique that quickly filters records by allowing users to type the first one or more characters of the selected customer Data Field. Utilizing the form’s filter settings, matching records will be quickly identified based on the characters typed into a text box control. This feature will streamline locating specific customer records, improving overall usability.

The Customers Form Image-1 Normal Data View.

Customers Form Image-2 with Filtered Data.

The yellow-highlighted TextBox serves as the filter input control. Above it, a ComboBox lets users select the field to search—in this case, the Last Name field. As text is entered into the filter box, the system matches the beginning of the selected field’s value and filters the records accordingly. This setup enables efficient, quick record searches based on starting characters, making it easier to locate specific names that meet the criteria.

In this example, three records are initially filtered, each with a Last Name beginning with the letter G. When you type ‘r’ after ‘G’ in the yellow-highlighted filter box, the first record (which contains ‘Go…’) no longer matches and is removed from the results. This dynamic filtering approach provides precise,  efficient record retrieval, updating in real time as users type, and making it easier to quickly locate records that meet the search criteria.

When the Backspace key is pressed to remove the last character from the filter TextBox, the data instantly updates to reflect the new filtering criteria based on the remaining characters. If no characters remain, the filter is cleared, and the full dataset is displayed in the form’s detail section. This ensures a seamless, real-time filtering experience that intuitively responds to user input.

The Cls_ObjInit Class Module VBA Code.

Option Compare Database
Option Explicit

Private WithEvents frm As Access.Form
Private WithEvents txt As Access.TextBox
Private WithEvents cmd As Access.CommandButton
Private WithEvents cbo As Access.ComboBox

Dim txt2Filter

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

Public Property Set m_Frm(ByRef vFrm As Access.Form)
    Set frm = vFrm
    
    Call Class_Init
End Property

Private Sub Class_Init()
Const EP = "[Event Procedure]"

Set txt = frm.FilterText
Set cmd = frm.cmdClose
Set cbo = frm.cboFields

With frm
    .OnLoad = EP
    .OnUnload = EP
End With

With txt
    .OnKeyUp = EP
End With

With cmd
    .OnClick = EP
End With

With cbo
    .OnClick = EP
End With

End Sub

Private Sub cbo_Click()
    frm.FilterText = ""
    txt2Filter = ""
    frm.Filter = ""
    frm.FilterText.SetFocus
    frm.FilterOn = False
End Sub

Private Sub txt_KeyUp(KeyCode As Integer, Shift As Integer)
Dim C As Integer, sort As String
Dim L As String

On Error GoTo txt_KeyUp_Err
C = KeyCode

With frm
Select Case C
    Case 8 'backspace key
        txt2Filter = Nz(![FilterText], "")
        If Len(txt2Filter) = 1 Or Len(txt2Filter) = 0 Then
            txt2Filter = ""
            .FilterOn = False ' remove filter
            frm.Recalc
            
        Else
            txt2Filter = Left(txt2Filter, Len(txt2Filter) - 1) 'delete the last character
            If Len(txt2Filter) = 0 Then
                .FilterOn = False ' remove filter
                
            Else 'set filter and enable
                .Filter = "[" & ![cboFields] & "]" & " like '" & txt2Filter & "*'"
                ![FilterText] = txt2Filter
                
                'position cursor position at the end of the text
                If Len(!FilterText) > 0 Then
                    .Section(acFooter).SetTabOrder
                    ![FilterText].SelLength = Len(![FilterText])
                    SendKeys "{END}" 'position cursor at right end of text
                End If
                
                .FilterOn = True
            End If
        End If
       
    Case 37 'right arrow key, prevent text highlighting
        SendKeys "{END}" 'position cursor at right end of text
    
    Case 32, 48 To 57, 65 To 90, 97 To 122 'space, 0 to 9, A to Z, a to z keys
        txt2Filter = txt2Filter & Chr$(C)
        
        'First letter of words to uppercase
        ![FilterText] = StrConv(txt2Filter, vbProperCase)
        SendKeys "{END}"
        GoSub SetFilter
End Select
End With

txt_KeyUp_Exit:
Exit Sub

SetFilter:
With frm
  .Refresh
  If Len(txt2Filter) = 0 Then
        .FilterOn = False ' remove filter
  Else 'set filter and enable
        .Filter = "[" & ![cboFields] & "]" & " like '" & txt2Filter & "*'"
        .FilterOn = True
  
  ' Set sort order
        sort = IIf(!Frame10 = 1, "ASC", "DESC")
        .OrderBy = "[" & !cboFields & "] " & sort
        .OrderByOn = True
  
        .Section(acFooter).SetTabOrder 'Form Footer Section Active
  'position cursor at end of text
        ![FilterText].SelLength = Len(![FilterText])
        SendKeys "{END}"
  End If
End With
Return

txt_KeyUp_Err:
MsgBox Err.Description, , "txt_KeyUp()"
Resume txt_KeyUp_Exit
End Sub

Private Sub cmd_Click()
    DoCmd.Close acForm, frm.Name
End Sub


The three controls in the footer of the form, each triggering simple events that run corresponding code—except for the TextBox, which handles a more complex KeyUp() event.

In this scenario, creating separate Wrapper Class Objects for the TextBox, ComboBox, and Command Button is not necessary, since only one instance of each exists on the form.

Within the Cls_ObjInit class module, the main object instances are declared in the global section using the WithEvents keyword, enabling Event capture and execution of their respective subroutines. Additionally, a Variant-type variable, txt2Filter, is declared globally, followed by the Form Property procedures.

The Class_Init() subroutine is then called from the Set m_Frm() property procedure after receiving the Form object from the Form_Load() event procedure in the Form module.

Next, the txt, cmd, and cbo objects are assigned references to their respective controls on the Form and enabled with the required events.

  • The ComboBox Click event selects a field name to serve as the Filter target. This action resets any previously applied filter.

  • The CommandButton Click event closes the form.

  • The TextBox KeyUp event captures valid keystrokes, builds the input string character by character, and applies it dynamically as a filter to the field selected in the ComboBox.

The txt_KeyUp() Event Subroutine Code.

Private Sub txt_KeyUp(KeyCode As Integer, Shift As Integer)
Dim C As Integer, sort As String
Dim L As String

On Error GoTo txt_KeyUp_Err
C = KeyCode

With frm
Select Case C
    Case 8 'backspace key
        txt2Filter = Nz(![FilterText], "")
        If Len(txt2Filter) = 1 Or Len(txt2Filter) = 0 Then
            txt2Filter = ""
            .FilterOn = False ' remove filter
            frm.Recalc
            
        Else
            txt2Filter = Left(txt2Filter, Len(txt2Filter) - 1) 'delete the last character
            If Len(txt2Filter) = 0 Then
                .FilterOn = False ' remove filter
                
            Else 'set filter and enable
                .Filter = "[" & ![cboFields] & "]" & " like '" & txt2Filter & "*'"
                ![FilterText] = txt2Filter
                
                'position cursor position at the end of the text
                If Len(!FilterText) > 0 Then
                    .Section(acFooter).SetTabOrder
                    ![FilterText].SelLength = Len(![FilterText])
                    SendKeys "{END}" 'position cursor at right end of text
                End If
                
                .FilterOn = True
            End If
        End If
       
    Case 37 'right arrow key, prevent text highlighting
        SendKeys "{END}" 'position cursor at right end of text
    
    Case 32, 48 To 57, 65 To 90, 97 To 122 'space, 0 to 9, A to Z, a to z keys
        txt2Filter = txt2Filter & Chr$(C)
        
        'First letter of words to uppercase
        ![FilterText] = StrConv(txt2Filter, vbProperCase)
        SendKeys "{END}"
        GoSub SetFilter
End Select
End With

txt_KeyUp_Exit:
Exit Sub

SetFilter:
With frm
  .Refresh
  If Len(txt2Filter) = 0 Then
        .FilterOn = False ' remove filter
  Else 'set filter and enable
        .Filter = "[" & ![cboFields] & "]" & " like '" & txt2Filter & "*'"
        .FilterOn = True
  
  ' Set sort order
        sort = IIf(!Frame10 = 1, "ASC", "DESC")
        .OrderBy = "[" & !cboFields & "] " & sort
        .OrderByOn = True
  
        .Section(acFooter).SetTabOrder 'Form Footer Section Active
  'position cursor at end of text
        ![FilterText].SelLength = Len(![FilterText])
        SendKeys "{END}"
  End If
End With
Return

txt_KeyUp_Err:
MsgBox Err.Description, , "txt_KeyUp()"
Resume txt_KeyUp_Exit
End Sub
The Sub KeyUp() Event Subroutine takes only the key code from Keys 0-9, A-Z, and a-z. The Backspace key removes the last character entered into the Filter Text input TextBox. The right-arrow character Code is also valid, which moves the I-bar to the End of the Filter Text and prevents selecting the full text when the Input TextBox is refreshed.

The Backspace keypress will truncate the rightmost character from the Filter input Text, and the Filter action is refreshed. When the Filter input control is empty, the data filter is reset, and full data is displayed on the Form.

The Form Module Code.

Option Compare Database
Option Explicit

'Global declaration
Private obj As New Cls_ObjInit

Private Sub Form_load()
    Set obj.m_Frm = Me
    Application.SetOption "Behavior Entering Field", 2
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Application.SetOption "Behavior Entering Field", 0
    Set obj = Nothing
End Sub

Filter By Character in ComboBox Items.

The screenshot above shows the second form, Customers_Combo, which demonstrates the Filter by Character feature applied to ComboBox items. The KeyUp() event subroutine is almost identical to the filter method used in the first form we reviewed earlier.

For this Form, a new class module named Cbo_ObjInit has been introduced. The VBA code for the Cbo_ObjInit class module is given below:

Option Compare Database
Option Explicit

Private WithEvents frm As Access.Form
Private WithEvents txt As Access.TextBox
Private WithEvents cmd As Access.CommandButton
Private cbo As Access.ComboBox

Dim txt2Filter

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

Public Property Set m_Frm(ByRef vFrm As Access.Form)
    Set frm = vFrm
    
    Call Class_Init
End Property

Private Sub Class_Init()
Const EP = "[Event Procedure]"

Set txt = frm.FilterText
Set cmd = frm.cmdExit
Set cbo = frm.cboCust

With frm
    .OnLoad = EP
    .OnUnload = EP
End With

With txt
    .OnKeyUp = EP
End With

With cmd
    .OnClick = EP
End With


End Sub

Private Sub txt_KeyUp(KeyCode As Integer, Shift As Integer)
Dim i As Integer
Dim SQL As String
Dim SQL1 As String
Dim SQL2 As String


On Error GoTo txtKeyUp_Err
SQL = "SELECT CustomersQ.* FROM CustomersQ ORDER BY CustomersQ.[Last Name];"

SQL1 = "SELECT CustomersQ.* FROM CustomersQ "
SQL2 = "WHERE (((CustomersQ.[Last Name]) Like '" '"Gr*"));

i = KeyCode

Select Case i
    Case 8 'backspace key
        frm.Refresh
        If Len(txt2Filter) = 1 Or Len(txt2Filter) = 0 Then
            txt2Filter = ""
        Else
            txt2Filter = Left(txt2Filter, Len(txt2Filter) - 1) 'delete the last character
        End If
        GoSub SetFilter
    Case 37 'right arrow keys
        SendKeys "{END}"
    Case 32, 48 To 57, 65 To 90, 97 To 122 'space, 0 to 9, A to Z, a to z keys
        txt2Filter = txt2Filter & Chr$(i)
        frm![FilterText] = StrConv(txt2Filter, vbProperCase)
        GoSub SetFilter
End Select

txtKeyUp_Exit:
Exit Sub

SetFilter:
  If Len(Nz(txt2Filter, "")) = 0 Then
    With frm
        .cboCust.RowSource = SQL
        .cboCust.Requery
        .cboCust.SetFocus
        .cboCust.Dropdown
    End With
  Else 'set filter and enable
        SQL = SQL1 & SQL2 & txt2Filter & "*'));"
    With frm
        .cboCust.RowSource = SQL
        .cboCust.Requery
        .cboCust.SetFocus
        .cboCust.Dropdown
    End With
  End If
Return

txtKeyUp_Err:
MsgBox Err.Description, , "txtKeyUp()"
Resume txtKeyUp_Exit
End Sub

Private Sub cmd_Click()
    DoCmd.Close acForm, frm.Name
End Sub

In the earlier approach, we applied the Form Filter method, where the characters entered into a TextBox served as filter criteria to restrict the records displayed in the form’s record source.

In contrast, the ComboBox method dynamically builds an SQL statement from the input filter text entered into a TextBox. This SQL is then assigned to the ComboBox Row Source, refreshing its contents in real time based on the updated criteria.

Download Demo Database


Streamlining Form Module Code in Standalone Class Module.

  1. Reusing Form Module VBA Coding for New Projects.
  2. Defining Custom Events in Microsoft Access Part Two
  3. Objects and Their Built-in Events Part 3.
  4. Standalone Class Module and Events - Part Four
  5. Several TextBoxes and Event Capturing Part Five
  6.  Class Objects and Wrapper Classes - Part Six
  7. Form Module vs. Reusable Class Module Coding Demo - Part Seven
  8. Form VBA Structured Coding ACCESS USERS-GROUP.ORG Europe Presentation-7A
  9. Collection Object Replaces Class Object Array - Part Eight
  10. Reusability of Streamlined VBA Code - Part Nine
  11. Organizing Wrapper Classes for Different Forms - Part Ten
  12. ComboBox and Option-Group Wrapper Classes - Part Eleven
  13. Report Module Code in Class Module - Part Twelve
  14. Hiding Report Lines Conditionally - Part 13
  15. Form Report Detail Sections Event Handling - Part 14
  16. The Event Firing Mechanism in Access Objects-22
  17. One TextBox and Three Wrapper Class Instances-23
  18. Class for All Data Entry Editing Forms-30
  19. Wrapper Class Module Creation Wizard-v1
  20. Wrapper-Class-Template-Wizard-v2 - Final
  21. Existing Demo Databases Converted to the new Coding

  22. New Custom-Made Form Wizard VBA - Part 15
  23. New Custom-Made Report Wizard - Part 16
  24. Streamlining VBA External Files List in Hyperlinks-17
  25. Streamlining Event Procedures 3D-Text Wizard-18
  26. Streamlining Form Module VBA RGBColor Wizard-19
  27. Form VBA Structured Coding Numbers to Words Converter-20
  28. Streamlining Code Synchronized Floating Popup Form-24
  29. Streamlining Code Compacting/Repair Database-25
  30. Streamlining Code Remainder Popup Form-26
  31. Streamlining Code Editing Data in Zoom-in Control-27
  32. Streamlining Code Filter By Character and Sort-28
  33. Table Query Records in Collection Object-29
Share:

Streamline Zoom-in Control Data Editing

 Editing Text Box Contents in Zoom-in Control.

This topic was originally published in August 2007 under the title Edit Data in Zoom-in Control.’ In that example, a custom shortcut menu was created and linked to the form to activate the zoom-in control, allowing users to edit the textbox contents containing multiple lines of data, similar to the Notes field in the Employees table.

The earlier version of the customized Shortcut Menu for the Employees Form is shown below for reference.

The built-in Shortcut Menu was customised by designing a new button image, highlighted in the Form image with a red oval. This button, resembling a CRT screen, is programmed to execute when clicked. However, designing a new button image within the existing shortcut menu is very difficult.

 Opening the Zoom Control is a two-step process: first, right-click the TextBox to display the Shortcut Menu, then select the Zoom option to open the Zoom-in Form with the active TextBox Contents. 

Now, we have a more efficient method: simply right-click on a TextBox to open the Zoom-in Form prefilled with its data. After editing the contents, click the [Save] command button on the Zoom Form to update the original TextBox with the revised text.

The New Version of the Zoom-in Form Image.

The New Employees Form with the Zoom-in Control with the Notes Field Data is given below:

In addition to copying the data from the Employees Notes TextBox into the larger TextBox on the Zoom Form, the formatting attributes from the original TextBox on the Employees Form are also applied, ensuring consistency in appearance.

The Main Public Functions.

The ZoomOpen() Function.

Public Function ZoomOpen()
'------------------------------------------------------
'Function : Edit Data in Zoom-in Control.
'Author   : a.p.r.pillai
'Date     : 29/07/2007, 26/03/2024
'Rights(c): www.msaccesstips.com
'------------------------------------------------------
Dim varVal, ctl As Control, intFontWeight As Integer
Dim strFont As String, intFontSize As Integer
Dim boolFontstyle As Boolean
Dim lngfontColor As Long, boolFontUnderline As Boolean
Dim bkgColor As Long

On Error GoTo ZoomOpen_Err

Set ctl = Screen.ActiveControl
With ctl
   strFont = .FontName
   intFontSize = .FontSize
   intFontWeight = .FontWeight
   boolFontstyle = .FontItalic
   boolFontUnderline = .FontUnderline
   lngfontColor = .ForeColor
   'bkgColor = .BackColor
End With

   varVal = Screen.ActiveControl.Value
   DoCmd.OpenForm "Zoom", acNormal

With Screen.ActiveForm.Controls("TxtZoom")
   .Value = varVal
   .FontName = strFont
   .FontSize = intFontSize
   .FontWeight = intFontWeight
   .FontItalic = boolFontstyle
   .FontUnderline = boolFontUnderline
   .ForeColor = lngfontColor
   '.BackColor = bkgColor
End With

ZoomOpen_Exit:
Exit Function

ZoomOpen_Err:
Resume ZoomOpen_Exit
End Function
 

When you right-click a TextBox, the ZoomOpen() function is executed. It copies the TextBox contents into a Variant variable, opens the Zoom-in Form, and transfers the data into the larger TextBox. The original formatting attributes from the source TextBox are also applied to ensure the text appears the same in the Zoom-in TextBox.

After editing the text in the Zoom-in control, click the [Save] Command Button to save the changes into the original Employees Form TextBox, and close the Zoom-in Form.

You can drag and move the Zoom-in Form to a convenient position in the Application Window.  The Zoom Form will open in Popup and Modal Mode, and you must close it to access other controls or Forms. 

The SaveZoomData() Function.

The [Save] Command Button Click Runs the SaveZoomData() Function.  The VBA Code is given below.

Public Function SaveZoomData()
'------------------------------------------------------
'Function : Save Edited Data in the Control
'Author   : a.p.r.pillai
'Date     : 29/07/2007, 26/03/2024
'Rights(c): www.msaccesstips.com
'------------------------------------------------------
Dim vartxtZoom, strControl As String

On Error GoTo SaveZoomData_Err

 vartxtZoom = Forms("Zoom").Controls("txtZoom").Value

 DoCmd.Close acForm, "zoom"
 
 If Screen.ActiveControl.Locked = True Then
   strControl = Screen.ActiveControl.Name
   MsgBox strControl & " is Read-Only, Changes discarded!"
   Exit Function
 Else
    If IsNull(vartxtZoom) = False And Len(vartxtZoom) > 0 Then
        Screen.ActiveControl.Value = vartxtZoom
    End If
 End If
 
SaveZoomData_Exit:
Exit Function

SaveZoomData_Err:
Resume SaveZoomData_Exit
End Function

The SaveZoomData() Function saves the edited data into its Source TextBox. If the TextBox is locked, the edited data cannot be saved.

In both of the above Functions, we used the Screen Object to address the active Form or active TextBox control without using their object names directly, e.g., Screen.ActiveForm, Screen.ActiveControl that has the Focus.

As I stated earlier, all you need to do is right-click on the TextBox to open the Zoom-in control and present the TextBox contents in the Zoom Window for editing. 

For those who prefer a shortcut menu to open the Zoom Control, I created a small macro-based menu that can be assigned to the Form Shortcut Menu Bar property or to the same property of any individual control on the Form. When applied at the Form level, the shortcut menu will appear whenever you right-click anywhere on the Form, not just on a specific control such as a TextBox. The Macro Shortcut Menu Options.

The Shortcut Menu Macro displays two options. 

  1. Open Zoom
  2. Cancel

The macro Commands for the Shortcut Menu Bar are listed in the McrZoom Macro Image shown below:

The macro shown above provides two options. The first option runs the ZoomOpen() function, which opens the Zoom Form and loads the text from the active TextBox into the Zoom Control for editing. The second option simply cancels the right-click event.

2. Create the Menu Macro.

A Menu Macro is required, with the Menu Options Macro McrZoom.

 The Menu Macro Image is given below:

The Menu Macro name is McrControlShortcut.

The 'Shortcut Menu Bar' Property of the Form and Controls.

The Menu Macro must be inserted into the Form's Shortcut Menu Bar Property or into the Control's Shortcut Menu Bar Property on the Form.

When added to the Form Property, the Menu appears wherever you right-click on the Form. When added to a specific Control's Shortcut Menu Bar Property, the Menu appears for that Control.

Most controls on the form have the 'Shortcut Menu Bar' property, allowing you to insert a menu macro name to display the shortcut menu. When inserted into the TextBox's property, you can even right-click on the child label of the TextBox to bring up the shortcut menu.

Normally, in the OnClick Event Property of a Command Button or a TextBox, we can insert a Macro or a Public Function Name that executes when it receives a Mouse-Button Click.

Despite the 'Shortcut Menu Bar' property expecting a menu bar, it directly executes the macro or function name inserted into this property when the control receives a Right-click Event. Additionally, it briefly displays a small empty menu bar.

Examples:

  1. Text0.Shortcut Menu Bar = "Macro2"
  2. Text0.Shortcut Menu Bar = "=ZoomOpen()"

We will experiment with both Macro Menu methods above for easy implementation.

In this round of streamlining VBA code, we do not employ any object-level wrapper classes. Instead, we use only the interface class Cls_ObjInit, which we can use to experiment with both approaches for utilizing the Zoom control.

The Zoom Form with the txtZoom TextBox.

The Zoom Form Image is given below for Reference.

The Zoom Form has two Command Buttons. The first one saves the edited data into its Source Textbox, and the other one cancels the operation. Both CommandButton Click Subroutines are written in the Form Module.

Option Compare Database
Option Explicit

Private Sub cmdSave_Click()
  Call SaveZoomData
End Sub

Private Sub cmdCancel_Click()
  DoCmd.Close acForm, "Zoom"
End Sub

The Interface Class Module Cls_ObjInit

Option Compare Database
Option Explicit

Private frm As Access.Form

Public Property Get m_Frm() As Form
  Set m_Frm = frm
End Property

Public Property Set m_Frm(ByRef vForm As Form)
  Set frm = vForm
  
  Call Class_Init
End Property

Private Sub Class_Init()
Dim opt As String

opt = "McrControlShortcut"

'opt = "=ZoomOpen()" 'Call Function directly on Right-Click

frm.ShortcutMenu = True 'True by default
Call SetupControls(opt)
       
End Sub

Private Sub SetupControls(ByVal strOpt As String)
Dim ctl As Control

For Each ctl In frm.Controls
  Select Case ctl.ControlType
Case acTextBox
    'ctl.ShortcutMenuBar = StrOpt 'For all TextBoxes
            
      Select Case ctl.Name 'Only selected TextBoxes
          Case "Title", "Address", "Notes"
          
            ctl.ShortcutMenuBar = strOpt
            
      End Select

  End Select
Next
End Sub

Private Sub Class_Terminate()
Dim opt As String

opt = ""
Call SetupControls(opt)

End Sub

The Cls_ObjInit interface class declares a Form object in the global area, followed by the property procedures for handling the Form.

After receiving the active Form object through the frm property, the Class_Init() subroutine is executed. Both Class_Init() and Class_Terminate() call the common SetUpControls() subroutine. This subroutine assigns the macro-based shortcut menu to the Employees Form Shortcut Menu Bar property or invokes the ZoomOpen() function.

The Class_Terminate() subroutine resets the Shortcut Menu Bar property, ensuring that changes made by the standalone class module Cls_ObjInit are applied dynamically.

With a small adjustment to the Class_Init() subroutine, you can configure the Shortcut Menu Bar property either for all TextBoxes (or any other control) on the Form or a specific one.

The Trial Runs.

In the Class_Init() subroutine, we plan to call the SetUpControls() subroutine to dynamically configure the Shortcut Menu Bar property of the TextBox controls. To better understand its behavior, we will experiment with two alternate settings, applying them one at a time. This trial run allows us to observe how each configuration affects the functionality of the Zoom Control feature.

opt = "McrControlShortcut"

'opt = "=ZoomOpen()" 'Call Function directly on Right-Click

frm.ShortcutMenu = True 'True by default
 

By default, the second line of code is kept disabled. Remember that the setting frm.ShortcutMenu = True is the default for the form. If the shortcut menu does not appear as expected, check this property in the Form and correct it.

Before invoking the SetUpControls() subroutine, the parameter variable Opt is initialized with the macro menu name "McrControlShortcut". The subroutine then assigns this macro name to the TextBox control’s Property Shortcut Menu Bar. When this option is active, right-clicking the TextBox displays the custom shortcut menu. Choosing “Open Zoom” from the menu runs the public function: ZoomOpen() while the “Cancel” option simply cancels the right-click action.

When the second option in the Class_Init() subroutine is enabled, the right-click event bypasses the macro and executes the ZoomOpen() function.

Similarly, the SetUpControl() subroutine has two options. The default method assigns the Shortcut Menu Bar property only to selected TextBox controls where data is likely to exceed the visible boundary. In this example, the fields include Title, Address, and Notes. Include more fields as needed, depending on the requirements.

If we plan to implement it on all the TextBoxes on the Form, then the 'ctl.ShortcutMenuBar = StrOpt can be enabled, and the following lines of VBA Code can be removed.

      Select Case ctl.Name 'Only selected TextBoxes
          Case "Title", "Address", "Notes"
          
            ctl.ShortcutMenuBar = strOpt
            
      End Select

The Employees Form Module VBA Code.

Option Compare Database
Option Explicit

Dim Cl As New Cls_ObjInit

Private Sub Form_Load()
Set Cl.m_Frm = Me
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Set Cl = Nothing
End Sub

The Interface Class Cls_ObjInit is declared and instantiated in the global declaration area of the Employees Form. In the Form_Load() Event Procedure, the Current Form Object is passed to the Form Property Procedure, and from there the Class_Init() Subroutine is called to set the Shortcut Menu Bar Property of the TextBoxes in the Employees Form.

This trick also works on the Tabular and Datasheet Forms. Two Demo Forms, Tabular and Datasheet Employee Forms, are also provided in the Demo Database.

Download the Demo Database.



Streamlining Form Module Code in Standalone Class Module.

  1. Reusing Form Module VBA Coding for New Projects.
  2. Defining Custom Events in Microsoft Access Part Two
  3. Objects and Their Built-in Events Part 3.
  4. Standalone Class Module and Events - Part Four
  5. Several TextBoxes and Event Capturing Part Five
  6.  Class Objects and Wrapper Classes - Part Six
  7. Form Module vs. Reusable Class Module Coding Demo - Part Seven
  8. Form VBA Structured Coding ACCESS USERS-GROUP.ORG Europe Presentation-7A
  9. Collection Object Replaces Class Object Array - Part Eight
  10. Reusability of Streamlined VBA Code - Part Nine
  11. Organizing Wrapper Classes for Different Forms - Part Ten
  12. ComboBox and Option-Group Wrapper Classes - Part Eleven
  13. Report Module Code in Class Module - Part Twelve
  14. Hiding Report Lines Conditionally - Part 13
  15. Form Report Detail Sections Event Handling - Part 14
  16. The Event Firing Mechanism in Access Objects-22
  17. One TextBox and Three Wrapper Class Instances-23
  18. Class for All Data Entry Editing Forms-30
  19. Wrapper Class Module Creation Wizard-v1
  20. Wrapper-Class-Template-Wizard-v2 - Final
  21. Existing Demo Databases Converted to the new Coding

  22. New Custom-Made Form Wizard VBA - Part 15
  23. New Custom-Made Report Wizard - Part 16
  24. Streamlining VBA External Files List in Hyperlinks-17
  25. Streamlining Event Procedures 3D-Text Wizard-18
  26. Streamlining Form Module VBA RGBColor Wizard-19
  27. Form VBA Structured Coding Numbers to Words Converter-20
  28. Streamlining Code Synchronized Floating Popup Form-24
  29. Streamlining Code Compacting/Repair Database-25
  30. Streamlining Code Remainder Popup Form-26
  31. Streamlining Code Editing Data in Zoom-in Control-27
  32. Streamlining Code Filter By Character and Sort-28
  33. Table Query Records in Collection Object-29
Share:

Streamlining Code Reminder Popup Form

Reminder Popup Form.

Understanding the importance of reminders is essential. For important occasions, such as a family member’s or a friend’s birthday, having sufficient time to prepare is crucial. Being notified at least two days in advance helps ensure that these occasions are not overlooked amid our busy schedules and other pressing commitments.

When considering business-related activities, let's examine the Inventory System's advance notification of the Pharmacy's re-order status as an example. It's imperative to print a list of medicines that fall below the minimum stock level or reach reorder levels on the 25th of each month to facilitate stock replenishment by placing orders in advance.

Seasonal demand requires higher stock levels of certain medicines during winter. By reviewing usage patterns from previous years, we can identify high-demand medicines and place advance orders with suppliers to ensure adequate availability before the season begins.

Tasks that require advanced alerts or scheduled notifications can be programmed to trigger a pop-up form or report with the relevant information, ensuring timely attention and prompt action. The Birthday Experiment.

Here, we will demonstrate this feature using the Employees table, which has been extended with two additional fields: BirthDate and BFlag. The BirthDate field contains the assumed date of Birth of each employee. For calculation purposes, the day and month of birth are combined with the current year (e.g., 14-March-1961 becomes 14-March-2024) to determine the birthdate. The logical field BFlag is updated to True once the birthday greeting has been printed from the alert pop-up form.

The Alerts are programmed to run in three stages:

  1. A pop-up form appears 48 hours before the individual's birthday and recurs again on the eve of the celebration.
  2. The pop-up form will display on the birthday when the database is accessed. A straightforward birthday greeting card in PDF format is generated directly from the pop-up. Upon the card Printing process, the Employees Table field BFlag is marked as True, ensuring that the pop-up won't reappear during subsequent database openings.

  3. If the Birthday Greetings are not printed and the BFlag Field is not set to True, then the alert pop-up will appear for the next two days after the due date, indicating overdue case(s). 

Initially, we establish an input Query named Birthday_RemData that includes a new column to record each employee's birthdate for the Current Year, sourced from the actual BirthDate field in the Employees1 table. The Birthday_RemData Query is the foundational dataset for categorizing data into the above three categories for pop-up forms.

The Employees1 Table Image.

The Employees1 Table image is shown below, with the required Fields for ready reference.

Reminder: Data Filtering Queries.

The SQL of the Input Query, with the current-year date of Birth, is calculated for each Employee, from the actual Date of Birth in the Table given below:

Query Name: BirthDay_RemData (Birthday Reminder Input Data).

SELECT Employees1.EmployeeID, 
[FirstName] & " " & [LastName] AS Name, 
Employees1.BirthDate, 
Employees1.BFlag, 
DateDiff("yyyy",[birthdate],Date()) AS Age, 
DateValue(Format([BirthDate],"dd/mm") & "-" & CStr(Year(Date()))) AS DueDate
FROM Employees1;

1. Query: RemindQ1_OnDate - to filter data for the Popup on the actual BirthDay:

SELECT BirthDay_RemData.*
FROM BirthDay_RemData
WHERE (((BirthDay_RemData.DueDate)=Date()) AND ((BirthDay_RemData.BFlag)=False));

2. Query: RemindQ2_Advance - to filter data for the Popup that appears two days before the BirthDay:

SELECT BirthDay_RemData.*
FROM BirthDay_RemData
WHERE (((BirthDay_RemData.BFlag)=False) AND (([DueDate]-1)=Date()))
OR (((BirthDay_RemData.BFlag)=False) AND (([DueDate]-2)=Date()));

3. Query: RemindQ3_OverDue - to filter data for the Popup that appears two days after the BirthDay, if the birthday card is not printed on the Birthday.:

SELECT BirthDay_RemData.*
FROM BirthDay_RemData
WHERE (((BirthDay_RemData.BFlag)=False) AND (([DueDate]+1)=Date())) 
OR (((BirthDay_RemData.BFlag)=False) AND (([DueDate]+2)=Date()));

The RemindQ1_OnDate Query is the source data for the Reminder1 Popup Form. The Reminder2 and Reminder3 Forms are linked to the RemindQ2_Advance and RemindQ3_Overdue Queries, respectively. All three are Tabular Forms.

Reminder: POPUP Forms.

1. The Reminder1 Popup Form Image is given below for reference.

The employee records with birthdays matching today’s date will be displayed on the Form, showing their actual date of birth and the birthday for the current year in separate columns. You can print the greeting card by clicking the “Print PDF Greeting” Command Button. The default path of the greeting’s target location can be temporarily altered directly in the text box. You can make the change permanent in the Default Value Property of the TextBox in design view.

The Advanced and Overdue Reminder Popups may show differently in Day/Month in both columns. The Printing option is not available in the Advanced and Overdue Popup Forms Footer Section.

The Greetings PDF file will be saved to the path indicated in the TextBox in the Form Footer Section. The path displayed in the TextBox is set in the TextBox Default Value Property, which you can modify to save it to your preferred path.

The Sample Greetings Card.

The sample Greetings Card image is given below for reference.

The other two Popup Form Images are given below for information.

2. Alert about upcoming Birthdays:

3. Alerts about the missed Birthday Celebrations:

How do the Popup Form(s) open automatically if the Employee's Date of Birth meets any of the criteria specified above?

It is easy; during the database opening phase, it checks for records in the three Queries (i.e., RemindQ1_OnDate, RemindQ2_Advance, and RemindQ3_OverDue) and opens the Alert Form linked to the Queries. For this purpose, a small Function is created in the Standard Module.

The VBA Code of the Checkpopup() Function.

Public Function Checkpopup()
'========================================================
'Project: Reminder Popup Form
'Author : a.p.r. pillai
'Date   : March, 2024
'Rights : All Rights(c) Reserved by www.msaccesstips.com
'Remarks: Opens up preset Reminder Popup Form(s)
'       : on pre-scheuled Date/Month
'Example: Employees' Date-of-Birth Reminder.
'========================================================
Dim i, j, k As Integer
i = DCount("*", "RemindQ1_OnDate")
If i > 0 Then
    DoCmd.OpenForm "Reminder1", acNormal
End If

j = DCount("*", "RemindQ2_Advance")
If j > 0 Then
    DoCmd.OpenForm "Reminder2", acNormal
End If

k = DCount("*", "RemindQ3_OverDue")
If k > 0 Then
    DoCmd.OpenForm "Reminder3", acNormal
End If

End Function

Running the Popup Forms.

Now, all that we need to do is call this Function immediately after opening the Database. The first choice is to create a Macro named AutoExec (the AutoExec Macro) and call the Function using the RunCode Command. The Macro name must be AutoExec to run immediately during the Database opening phase. Another option is to drag this Macro and place it on the Desktop as a Shortcut. Double-click on it to open the Database and run the Macro.

Another option is to run the Function from the Form_Load() Event Subroutine of the first Form that opens when the database is open.

If there is no urgency, call the Function when the Employees Form is open; the PopUp Form(s) will appear if any of the three data-filtering Queries, or all of them, have data after a brief 3-second delay.

This is good for experimenting with this trick and learning to devise a better method for Advanced Alerts, on-the-day, or Overdue Alerts in your other Projects.

The Employee Form-based Popup.

There are three employee Forms for each Reminder category. All of them can be opened from a small Main Form. The Image of the Main Form is given below.

The Employees Form Image of the First Option:

When you open this Form, there is a delay of about 3 seconds; the Reminder1_OnDate pop-up form will open with the Employees' records who have a birthday today. If there are no records, the [Open Reminder] Command Button is disabled. If the pop-up doesn't appear, change the BirthDate of one or two employees to match the Day and Month (don't change the year) to match the current Date.

If you close the Reminder Form by mistake, use the CommandButton that opens the Reminder Form.  

There are two other Forms for experimenting with Reminders of forthcoming or overdue Reminder setups.

Streamlining VBA Event Subroutine Codes.

Now, coming to the Streamlined VBA Coding Part, there are three Employee Forms with two CommandButtons. The Employee information is for display purposes only. If you plan to edit the employee's Birthdate through this Form, you are welcome, but the validation check is not performed, and full responsibility is yours. All three Forms are linked to the same Employees1 Table. 

Since all three forms have two command buttons each, we need only one CommandButton Wrapper Class to handle the CommandButton Clicks. But we will use three different Class_Init() Interface Classes (intermediary classes) to create separate instances for all three Employee Forms, so that their identity references will remain separate in memory. The 3-second TimerInterval subroutine is also run from this Class Module before opening the Popup Form.

The Command Button Wrapper Class: Rm1_cmdButton - Employee Forms.

 
Option Compare Database
Option Explicit

Private WithEvents cmd As Access.CommandButton
Private mfrm As Form
Dim t As Integer
'========================================================
'Project: Reminder Popup Form
'Author : a.p.r. pillai
'Date   : March, 2024
'Rights : All Rights(c) Reserved by www.msaccesstips.com
'Remarks: Opens up preset Reminder Popup Form(s)
'       : on pre-scheuled Date/Month
'Example: Employees' Date-of-Birth Reminder.
'========================================================

Public Property Get m_Frm() As Access.Form
  Set m_Frm = mfrm
End Property

Public Property Set m_Frm(ByRef vfrm As Access.Form)
  Set mfrm = vfrm
End Property

Public Property Get m_cmd() As Access.CommandButton
  Set m_cmd = cmd
End Property

Public Property Set m_cmd(ByRef vcmd As Access.CommandButton)
  Set cmd = vcmd
End Property

Private Sub cmd_Click()
  Select Case cmd.Name
    Case "cmdReminder1"
        DoCmd.OpenForm "Reminder1", acNormal
    Case "cmdReminder2"
        DoCmd.OpenForm "Reminder2", acNormal
    Case "cmdReminder3"
        DoCmd.OpenForm "Reminder3", acNormal
        
    Case "cmdExit1"
        DoCmd.Close acForm, "Employees1"
    Case "cmdExit2"
        DoCmd.Close acForm, "Employees2"
    Case "cmdExit3"
        DoCmd.Close acForm, "Employees3"
  End Select
End Sub

We need only one Click-Event Subroutine in the Class Module to handle the Command Button Clicks from all three Forms. The Code is not messy and remains clean and directly accessible, rather than through the Form Design View. The Event Procedure Code is self-explanatory.

Three different Interface Class Modules for different Employee Forms. We don't create Instances of the Interface Class. Hence, we need to create three different Interface Classes. Moreover, it runs the TimerInterval Subroutine for three different Forms.

Interface Class of Employees1 Form: Rm1_Init Class VBA Code.

 Option Compare Database
Option Explicit

Private cmd As Rm1_CmdButton
Private WithEvents frm As Form

Private Coll As New Collection
Dim t As Integer

'========================================================
'Project: Reminder Popup Form
'Author : a.p.r. pillai
'Date   : March, 2024
'Rights : All Rights(c) Reserved by www.msaccesstips.com
'Remarks: Opens up preset Reminder Popup Form(s)
'       : on pre-scheuled Date/Month
'Example: Employees' Date-of-Birth Reminder.
'========================================================

Public Property Get m_Frm() As Form
  Set m_Frm = frm
  
End Property

Public Property Set m_Frm(ByRef vfrm As Form)
  Set frm = vfrm
  
  Call Class_Init
  
End Property

Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"

frm.OnTimer = EP

For Each ctl In frm.Controls
    Select Case TypeName(ctl)
        Case "CommandButton"
          Select Case ctl.Name
            Case "cmdReminder1", "cmdExit1"
            Set cmd = New Rm1_CmdButton
            Set cmd.m_Frm = frm
            Set cmd.m_cmd = ctl
                cmd.m_cmd.OnClick = EP
                Coll.Add cmd
            Set cmd = Nothing
          End Select
    End Select
Next

t = 0
frm.TimerInterval = 1000
End Sub

Private Sub frm_Timer()
Dim icount As Long
On Error GoTo frmTimer_Err

t = t + 1
If t = 3 Then
    frm.TimerInterval = 0
icount = DCount("*", "RemindQ1_OnDate")
    If icount > 0 Then
        t = 0
        frm.cmdReminder1.Enabled = True
        Call PopupOpen("Reminder1")
    Else
        frm.cmdReminder1.Enabled = False
        frm.Requery
    End If
End If

frmTimer_Exit:
Exit Sub

frmTimer_Err:
MsgBox Err.Description, , "frmTimer()"
Resume frmTimer_Exit
End Sub

Private Sub Class_Terminate()
Do While Coll.Count > 0
    Coll.Remove 1
Loop

End Sub
 

This class module is declared within the Employee1 Form Module, where a reference to the Employee1 form is passed to the `frm` object. The `Class_Initialize` procedure is then invoked. At the beginning of this procedure, the Employee1 Form's Timer event is enabled, followed by the creation of Command Button instances and the initialization of their event-handling procedures.

The Timer Interval is set to 1000 milliseconds (1 second), and the Timer event runs for three consecutive intervals (3 seconds). After this 3-second delay, the record count of the `ReminderQ1_OnDate` query is retrieved. If the record count is greater than 0, the `Reminder1_OnDate` popup form is opened to display the corresponding reminder records.

Interface Class of Employees2 Form: Rm2_Init Class.

Option Compare Database
Option Explicit

Private cmd As Rm1_CmdButton
Private WithEvents frm As Form

Private Coll As New Collection
Dim t As Integer
'========================================================
'Project: Reminder Popup Form
'Author : a.p.r. pillai
'Date   : March, 2024
'Rights : All Rights(c) Reserved by www.msaccesstips.com
'Remarks: Opens up preset Reminder Popup Form(s)
'       : on pre-scheuled Date/Month
'Example: Employees' Date-of-Birth Reminder.
'========================================================

Public Property Get m_Frm() As Form
  Set m_Frm = frm
  
End Property

Public Property Set m_Frm(ByRef vfrm As Form)
  Set frm = vfrm
  
  Call Class_Init
  
End Property

Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"

frm.OnTimer = EP

For Each ctl In frm.Controls
    Select Case TypeName(ctl)
        Case "CommandButton"
          Select Case ctl.Name
            Case "cmdReminder2", "cmdExit2"
            Set cmd = New Rm1_CmdButton
            Set cmd.m_Frm = frm
            Set cmd.m_cmd = ctl
                cmd.m_cmd.OnClick = EP
                Coll.Add cmd
            Set cmd = Nothing
          End Select
    End Select
Next

t = 0
frm.TimerInterval = 1000
End Sub

Private Sub frm_Timer()
Dim icount As Long
On Error GoTo frmTimer_Err

t = t + 1
If t = 3 Then
    frm.TimerInterval = 0
icount = DCount("*", "RemindQ2_Advance")
    If icount > 0 Then
        t = 0
        frm.cmdReminder2.Enabled = True
        frm.Requery
  
        Call PopupOpen("Reminder2")
    Else
        frm.cmdReminder2.Enabled = False
        frm.Requery
    End If
End If

frmTimer_Exit:
Exit Sub

frmTimer_Err:
MsgBox Err.Description, , "frmTimer()"
Resume frmTimer_Exit
End Sub

Private Sub Class_Terminate()
Do While Coll.Count > 0
    Coll.Remove 1
Loop

End Sub

The only difference in this Module is the query name and CommandButton Names. We use the same Rm1_CmdButton Wrapper Class.

Rm3_Init Interface Class also has the same VBA Code with different Query and Command Button Names.

Option Compare Database
Option Explicit

Private cmd As Rm1_CmdButton
Private WithEvents frm As Form

Private Coll As New Collection
Dim t As Integer
'========================================================
'Project: Reminder Popup Form
'Author : a.p.r. pillai
'Date   : March, 2024
'Rights : All Rights(c) Reserved by www.msaccesstips.com
'Remarks: Opens up preset Reminder Popup Form(s)
'       : on pre-scheuled Date/Month
'Example: Employees' Date-of-Birth Reminder.
'========================================================

Public Property Get m_Frm() As Form
  Set m_Frm = frm
  
End Property

Public Property Set m_Frm(ByRef vfrm As Form)
  Set frm = vfrm
  
  Call Class_Init
  
End Property

Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"

'frm.OnTimer = EP

For Each ctl In frm.Controls
    Select Case TypeName(ctl)
        Case "CommandButton"
          Select Case ctl.Name
            Case "cmdReminder3", "cmdExit3"
                Set cmd = New Rm1_CmdButton
                Set cmd.m_Frm = frm
                    cmd.m_Frm.OnTimer = EP
                Set cmd.m_cmd = ctl
                    cmd.m_cmd.OnClick = EP
                    Coll.Add cmd
            Set cmd = Nothing
        End Select
    End Select
Next

t = 0
frm.TimerInterval = 1000

End Sub

Private Sub frm_Timer()
Dim icount As Long
'On Error GoTo frmTimer_Err

t = t + 1
If t = 3 Then
    frm.TimerInterval = 0
  icount = DCount("*", "RemindQ3_OverDue")
    If icount > 0 Then
        t = 0
        frm.cmdReminder3.Enabled = True
        frm.Requery
  
        Call PopupOpen("Reminder3")
    Else
        frm.cmdReminder3.Enabled = False
        frm.Requery
    End If
End If

frmTimer_Exit:
Exit Sub

frmTimer_Err:
MsgBox Err.Description, , "frmTimer()"
Resume frmTimer_Exit
End Sub

Private Sub Class_Terminate()
Do While Coll.Count > 0
    Coll.Remove 1
Loop

End Sub

The Popup Forms' Wrapper Class and Interface Class Module VBA Code.

Only one Wrapper Class Module and one Interface Class are required for all three Popup Forms to handle the Event Procedures of Command Buttons. All three Forms have only the CommandButton click events to handle in the Wrapper Class Sub_CmdButton and Interface Class Modules.

The Sub_CmdButton Wrapper Class Module VBA Code.

Option Compare Database
Option Explicit

Private WithEvents cmd As Access.CommandButton
Private frm As Form
'========================================================
'Project: Reminder Popup Form
'Author : a.p.r. pillai
'Date   : March, 2024
'Rights : All Rights(c) Reserved by www.msaccesstips.com
'Remarks: Opens up preset Reminder Popup Form(s)
'       : on pre-scheuled Date/Month
'Example: Employees' Date-of-Birth Reminder.
'========================================================

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

Public Property Set m_Frm(ByRef vfrm As Access.Form)
  Set frm = vfrm
End Property

Public Property Get m_cmd() As Access.CommandButton
  Set m_cmd = cmd
End Property

Public Property Set m_cmd(ByRef vcmd As Access.CommandButton)
  Set cmd = vcmd
End Property

Private Sub cmd_Click()
  Select Case cmd.Name
   Case "cmdPrint1"
        Call GreetingsPrint
        
    Case "cmdCancel1"
        DoCmd.Close acForm, "Reminder1"
    Case "cmdCancel2"
        DoCmd.Close acForm, "Reminder2"
    Case "cmdCancel3"
        DoCmd.Close acForm, "Reminder3"
  End Select
End Sub

Private Sub GreetingsPrint()
Dim strSQL As String
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim qryDef As DAO.QueryDef
Dim EID As Integer
Dim OutFile As String

'On Error GoTo GreetingsPrint_Err

Set db = CurrentDb
Set rst = db.OpenRecordset("RemindQ1_OnDate", dbOpenDynaset)

rst.MoveLast
rst.MoveFirst

Do While Not rst.EOF And Not rst.BOF
    EID = rst![EmployeeID]
    strSQL = "SELECT RemindQ1_OnDate.* FROM RemindQ1_OnDate "
    strSQL = strSQL & "WHERE (((RemindQ1_OnDate.EmployeeID)= "
    strSQL = strSQL & EID & "));"

Set qryDef = db.QueryDefs("BirthDayQ1OnDate_PDF")
    qryDef.SQL = strSQL
    db.QueryDefs.Refresh

DoCmd.OpenReport "Greetings1_PDF", acViewPreview
If MsgBox("Birthday Greetings Print Initiated, Proceed?", vbYesNo, "Greetings Print()") = vbNo Then
DoCmd.Close acReport, "Greetings1_PDF"
   Exit Sub
End If
DoCmd.Close acReport, "Greetings1_PDF"

OutFile = frm!Path & DLookup("Name", "BirthdayQ1OnDate_PDF", "EmployeeID = " & EID) & ".pdf"
DoCmd.OutputTo acOutputReport, "Greetings1_PDF", "PDFFormat(*.pdf)", OutFile, False, "", 0, acExportQualityPrint

rst.MoveNext
Loop

  rst.Close
  Set rst = Nothing
  Set db = Nothing


DoCmd.SetWarnings False
'Delete earlier saved records
DoCmd.OpenQuery "BirthDayReminder1_Del", acViewNormal

'Add latest records
DoCmd.OpenQuery "BDay_SavedQ1", acViewNormal

'Flag the Employee Record as Greetings Printed
'Reset the floags on January 1st, Next Year
DoCmd.OpenQuery "BirthDayQ_UpdateFlag1", acViewNormal
DoCmd.SetWarnings True

MsgBox "Greetings PDFs are saved in Path: " & frm!Path

GreetingsPrint_Exit:
Exit Sub

GreetingsPrint_Err:
MsgBox Err & ": " & Err.Description, , "GreetingsPrint_Click()"
Resume GreetingsPrint_Exit
End Sub

In the cmd_Click() Event Subroutine, the cmdPrint1 Command Button Click on the Reminder1 Form calls the GreetingsPrint() Subroutine and prints the Birthday Greetings in PDF format. It updates the Employees1 Table, marking the BFlag logical Field as True to prevent it from appearing in the Employee record again in the Popup Form. The Greetings will be printed separately for each Employee Record.

After printing the Popup Form, the records will be saved into a separate temporary Table: Birthday_Reminder1.

The Sub_Init Interface Class Module VBA Code.

Option Compare Database
Option Explicit

Private cmd As Sub_CmdButton
Private frm As Form
Private Coll As New Collection

Public Property Get m_Frm() As Form
  Set m_Frm = frm
  
End Property

Public Property Set m_Frm(ByRef vfrm As Form)
  Set frm = vfrm
  
  Call Class_Init
  
End Property

Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"

'Set frm2 = frm.BReminderSub1.Form
For Each ctl In frm.Controls
    Select Case TypeName(ctl)
        Case "CommandButton"
          Select Case ctl.Name
            Case "cmdPrint1", "cmdCancel1", _
            "cmdCancel2", "cmdCancel3"
            Set cmd = New Sub_CmdButton
            Set cmd.m_Frm = frm
            Set cmd.m_cmd = ctl
                cmd.m_cmd.OnClick = EP
                Coll.Add cmd
            Set cmd = Nothing
        End Select
    End Select
Next
End Sub

Private Sub Class_Terminate()
Do While Coll.Count > 0
    Coll.Remove 1
Loop

End Sub

All three Popup Forms: Reminder1, Reminder2, and Reminder3 have Command Buttons with unique Names to close the Forms, and all of them are included in the Class_Init() Subroutine. Because of their unique names, we could handle their Event Procedures in a Single Wrapper Class Module.

Even though the Code requirement is simple, the Standalone Class Module VBA Coding gives you much flexibility for maintaining Code in a centralized location. It needs only one Click Event Subroutine for several CommandButton Click Events.

Demo Database Download


Streamlining Form Module Code in Standalone Class Module.

  1. Reusing Form Module VBA Coding for New Projects.
  2. Defining Custom Events in Microsoft Access Part Two
  3. Objects and Their Built-in Events Part 3.
  4. Standalone Class Module and Events - Part Four
  5. Several TextBoxes and Event Capturing Part Five
  6.  Class Objects and Wrapper Classes - Part Six
  7. Form Module vs. Reusable Class Module Coding Demo - Part Seven
  8. Form VBA Structured Coding ACCESS USERS-GROUP.ORG Europe Presentation-7A
  9. Collection Object Replaces Class Object Array - Part Eight
  10. Reusability of Streamlined VBA Code - Part Nine
  11. Organizing Wrapper Classes for Different Forms - Part Ten
  12. ComboBox and Option-Group Wrapper Classes - Part Eleven
  13. Report Module Code in Class Module - Part Twelve
  14. Hiding Report Lines Conditionally - Part 13
  15. Form Report Detail Sections Event Handling - Part 14
  16. The Event Firing Mechanism in Access Objects-22
  17. One TextBox and Three Wrapper Class Instances-23
  18. Class for All Data Entry Editing Forms-30
  19. Wrapper Class Module Creation Wizard-v1
  20. Wrapper-Class-Template-Wizard-v2 - Final
  21. Existing Demo Databases Converted to the new Coding

  22. New Custom-Made Form Wizard VBA - Part 15
  23. New Custom-Made Report Wizard - Part 16
  24. Streamlining VBA External Files List in Hyperlinks-17
  25. Streamlining Event Procedures 3D-Text Wizard-18
  26. Streamlining Form Module VBA RGBColor Wizard-19
  27. Form VBA Structured Coding Numbers to Words Converter-20
  28. Streamlining Code Synchronized Floating Popup Form-24
  29. Streamlining Code Compacting/Repair Database-25
  30. Streamlining Code Remainder Popup Form-26
  31. Streamlining Code Editing Data in Zoom-in Control-27
  32. Streamlining Code Filter By Character and Sort-28
  33. Table Query Records in Collection Object-29
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