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

The Event Firing Mechanism in Access Objects

 Streamlining  Event Subroutine Code in Standalone Class Module.

How Does the Event Firing Mechanism Work within Access Objects?

This topic was briefly touched on during the Presentation of  Streamlining Form Module Code in the Standalone Class Module for Access User Groups (Europe) Chapter. 

The Event-related keywords: Event, RaiseEvent, and WithEvents.

  1. Event - used to define an Event.
  2. RaiseEvent - to Invoke the Event.
  3. WithEvents - to capture the fired Event and execute the Event Subroutine Code.

In the preceding articles, we gained insights into using Event-related Keywords and crafting Event Subroutines within Standalone Class Modules rather than in the Form Module. Notably, the Event and WithEvents keywords were prominently featured in the Object Browser, as illustrated below:

However, RaiseEvent is an internal event-firing mechanism that supports multiple options via a dedicated event-related property. This Event is invoked from the Class Object functions as a system program. It assesses the specified option in the event property and executes the selected choice, be it a macro, function (user-defined or built-in), or the text [Event Procedure]. This, in turn, triggers the RaiseEvent, like the functionality of the Call Statement in VBA.

The following Link gives the details about the RaiseEvent Statement: https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/raiseevent-statement

The authentic Object Event-firing mechanism is an internal program in the Access System. For instance, the AfterUpdate Event encompasses an AfterUpdateMacro, typically kept hidden from the Object Browser Window. To reveal it, one can simply right-click and select the 'Show Hidden Members' option. Analogously, other Object Events, such as CommandButton Click, feature the OnClick Event property to specify the execution option. Furthermore, there's the OnClickMacro, which evaluates the given value in the OnClick Property, facilitating the execution of the designated option.  

Assumptions Based on Observation.

Upon scrutinizing the execution pattern of the specified option within the Event Property, I found it worthwhile to attempt to create a straightforward subroutine that emulates the methodology employed in the Event mechanism. This endeavor aims to replicate the mechanism by which the event executes the designated option specified in the Property Sheet.

An interesting observation emerges: when a macro name or function name is specified in the Event Property, it triggers the execution of the designated macro or built-in function, as well as user-defined functions in a standard module. Notably, for these two options, there is no requisite attachment of a Class Module to the Form, and the 'Has Module' Property of the Form can be set to False.

We require only one instance of the TextBox control and one instance each of two Command Button controls, each with a different name, in the intermediary class module. All three object instances are declared using the `WithEvents` keyword, enabling their event procedures to be implemented within the `Card_ObjInit` class.

In this scenario, adopting object-level wrapper classes would likely result in a larger VBA codebase for event handling. Following the recommended design guidelines would require wrapper classes, particularly for the Command Button controls. In addition, a `Collection` object would be necessary to maintain wrapper class instances in memory, ensuring that their event procedures remain available and are executed correctly.

However, given the simplicity of the form interface and the limited number of events to be managed, this approach may introduce unnecessary complexity and resource overhead. If the streamlined solution implemented in the intermediary class module is sufficient to handle these events, it provides a more efficient implementation. The choice should therefore be based on adherence to design guidelines with the practical resource requirements of the specific form.

Accordingly, in this case, we chose to manage all event handling directly within the `Card_ObjInit` class module.

The Event Running Form Image is given below:

The above Form is divided into two Parts:

  1. The upper portion of the form, delineated by the thick horizontal black line in the middle, serves as our experimental ground for exploring the event execution method. Here, we endeavor to unravel the intricacies of the event-running mechanism within the Access System.
  2. In the Section below the horizontal line, we will run the same Event Options as we normally do in the AfterUpdate Property of a TextBox.

In the initial section, a ListBox offers a range of options that can be selected by a simple click. Upon selection, the chosen option promptly populates the Text2 TextBox Control above. Analogous to the AfterUpdate Event Property in the TextBox's Property Sheet, the specified option in the TextBox Control executes within our Event Subroutine AfterUpdateMac()

The event-running Subroutine, AfterUpdateMac(), is written in the Standalone Class Module ClsAfterUpdateMacro

The ClsAfterUpdateMacro Class Module VBA Code.

The Class Module with the Subroutine AfterUpdateMac() VBA Code is given below:

Option Compare Database
Option Explicit

'User-Defined Event
Public Event AfterUpdat(ByVal txt As String)
Private After_Update As String
'Options: Macro,Function,"[Event Procedure]"

'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'AfterUpdateMac() Event Processing Subroutine
'Author: a.p.r. pillai
'Date  : 19/01/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------

Public Property Get OnAfterUpdate() As String
 OnAfterUpdate = After_Update
End Property

Public Property Let OnAfterUpdate(ByVal vNewValue As String)
 After_Update = vNewValue
 
 Call AfterUpdateMac
End Property

Private Sub AfterUpdateMac()
'Evaluate the given option
'and Run the Event action
Dim opt As String
Dim vx As Variant

On Error GoTo AfterUpdateMac_Err

opt = Nz(After_Update, "")

If Len(opt) = 0 Then
    Exit Sub
ElseIf UCase(opt) = "[EVENT PROCEDURE]" Then
    'RaiseEvent: Call Event Subroutine
    RaiseEvent AfterUpdat("RaiseEvent MESSAGE TEXT")
ElseIf Left(opt, 1) = "=" Then
    'Expression
    opt = Mid(opt, 2)
    vx = Eval(opt)
Else
    'Run Macro
    DoCmd.RunMacro opt
End If

AfterUpdateMac_Exit:
Exit Sub

AfterUpdateMac_Err:
MsgBox Err & ": " & Err.Description, , "AfterUpdateMac_Err()"
Resume AfterUpdateMac_Exit
End Sub

Review of the Class Module Code.

In the Global declaration area, an Event is defined with the name UpdateAfter(ByVal txt As String). The two words in AfterUpdate are switched to avoid it being mistaken for the AfterUpdate() System Event Procedure. intentionally. Another Property, After_Update As String, is also declared for inserting the Event running option, analogous to the AfterUpdate Property of the TextBox.

Then, the Get and Let Property Procedures are used to get the selected option from the Form and pass it on to the 'AfterUpdateMacro()' to execute the Option. The 'AfterUpdateMac()' is trying to mimic the action of the AfterUpdateMacro hidden Property/Procedure of the TextBox we saw in the Object Browser Image given at the top of this Page.

The options that we can normally insert into an Event Property are given in a ListBox. 

  1. A Macro with the name Macro1
  2. The Function/Expression =DisplayText() to call the Function in the Standard Module 
  3. The String [Event Procedure] to call the declared Event Subroutine in the Form Module.
  4. Other built-in Functions like MsgBox() and InputBox().

By clicking an option, you insert it into the After_Update Property and activate a process within the Class Module that evaluates and executes the chosen option.

The AfterUpdateMac() Subroutine VBA Code.

Let us have a closer look at the AfterUpdateMac() Subroutine Code.

Private Sub AfterUpdateMac()
'Evaluate the given option
'and Run the Event action
Dim opt As String
Dim vx As Variant

On Error GoTo AfterUpdateMac_Err

opt = Nz(After_Update, "")

If Len(opt) = 0 Then
    Exit Sub
ElseIf UCase(opt) = "[EVENT PROCEDURE]" Then
    'RaiseEvent: Call Event Subroutine
    RaiseEvent UpdateAfter("RaiseEvent MESSAGE TEXT")
ElseIf Left(opt, 1) = "=" Then
    'Expression
    opt = Mid(opt, 2)
    vx = Eval(opt)
Else
    'Run Macro
    DoCmd.RunMacro opt
End If

AfterUpdateMac_Exit:
Exit Sub

AfterUpdateMac_Err:
MsgBox Err & ": " & Err.Description, , "AfterUpdateMac_Err()"
Resume AfterUpdateMac_Exit
End Sub

Within the subroutine, two local variables, opt and vx, are declared. The selected option, inserted into the Text2 TextBox Control on the form, is assigned to the After_Update Property declared in the global area of the Class Module. The statement opt = Nz(After_Update, "") checks whether the After_Update Property contains any value. If it does not, the subroutine gracefully exits.

If the received value is the text [Event Procedure], the user-defined UpdateAfter() is called with the sample text parameter. This event is captured in the Form Module and subsequently displays the parameter text in a MessageBox.

In the scenario where the opt variable contains an expression (Note: an expression starts with an '=' symbol), a check is made for the presence of the equal symbol as the first character. If detected, it is presumed to be a function or a valid expression. The expression is then passed to the Eval() function after removing the '=' symbol.

If the value received in the After_Update Property doesn't satisfy any of the aforementioned criteria, it is assumed to be a macro name. Subsequently, the macro is executed using the DoCmd.RunMacro command.

If any Error is encountered, it shows an Error Message and exits from the Program.

The Form Module Code is listed below:

Option Compare Database
Option Explicit

Private WithEvents C1 As ClsAfterUpdateMacro

Private Sub Form_Load()
Set C1 = New ClsAfterUpdateMacro
End Sub

Private Sub cmdClose_Click()
DoCmd.Close
End Sub

Private Sub List0_Click()
    Me.Text2.Value = List0
    C1.OnAfterUpdate = Me![Text2]
End Sub

'UserDefined Event Message
Private Sub C1_UpdateAfter(ByVal otxt As String)
    MsgBox otxt
End Sub

'This is the Normal Procedure
'Executed by Access System.
Private Sub Text27_AfterUpdate()
    MsgBox "AfterUpdate Event Subroutine Fired."
End Sub

The Form Module Code Review.

The ClsAfterUpdateMac Class is declared with the Object name C1 in the Global declaration area.

In the Form_Load() Event Subroutine, the C1 Object is instantiated and loaded into memory. 

In the List0_Click() Event Procedure, the selected ListBox option is assigned to the After_Update Property through the C1.OnAfterUpdate Property Procedure.

The following code segment represents the subsequent subroutine that captures the AfterUpdateMac() Event when triggered from the Class Module using the RaiseEvent action, specifically when the option selected from the ListBox is [Event Procedure].

'UserDefined Event Subroutine
Private Sub C1_UpdateAfter(ByVal otxt As String)
MsgBox otxt End Sub

This is used for the second part of this experiment in the Normal Form Module Coding and Event firing from the Access System.

'This is the Normal Procedure
'Executed by Access System.
Private Sub Text27_AfterUpdate()
    MsgBox "AfterUpdate Event Subroutine Fired."
End Sub

The Second Part of the Form.

In the Second part of the Form, the same set of options is entered into a Label Control, so that when you are in the Form Design View, you can highlight and copy the required option from the Label Control and paste it into the AfterUpdate Event Property of the Text27 TextBox Control. This is easier than typing them correctly in the AfterUpdate Property, without errors.

Then save the Form and open it in Normal View.

Type at least one character in the TextBox, and press the Enter key to fire the AfterUpdate Event for the option inserted into the TextBox Text27 Property.

The AfterUpdate() Event Fires at this point, depending on the option in the Property, and executes the action as we saw it in our own earlier experiment.

Event Properties and their related Macros.

As illustrated in the Object Browser image shown above, the left pane displays the `CommandButton` class selection, while the right pane lists its event-related properties, including the hidden ones. Notably, all event option-setting properties are prefixed with On followed by the event name, such as `OnClick`. Associated with each of these properties is another property or procedure with the same event name, suffixed with Macro, such as `OnClickMacro`. This suggests the presence of an internal routine that evaluates the option specified in the `OnClick` property and then executes the corresponding action.

It is highly likely that these additional procedures associated with the event properties, identified by the Macro suffix, encapsulate logic similar to the code structure we explored in the initial section. The consistent naming convention suggests a standardized internal implementation, reinforcing the plausibility of such an underlying code structure.

The Demo Database is attached for your own experiments and learning.


  1. Re-using 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. Collection Object replaces Class Object Array - Part Eight
  9. Reusability of Streamlined VBA Code - Part Nine
  10. Organizing Wrapper Classes for Different Forms - Part Ten
  11. ComboBox and Option-Group Wrapper Classes - Part Eleven
  12. Report Module Code in Class Module - Part Twelve
  13. Hiding Report Lines Conditionally - Part 13.
  14. Form Report Detail Sections Event Handling - Part 14.
  15. New Custom-Made Form Wizard VBA - Part 15.
  16. New Custom-Made Report Wizard - Part 16.
  17. Streamlining VBA External Files List in Hyperlinks-17
  18. Streamlining Event Procedures 3D-Text Wizard-18
  19. Streamlining Form Module VBA RGBColor Wizard-19
  20. Form VBA Structured Coding Numbers to Words Converter-20
  21. Form VBA Structured Coding Access Users-Group Europe Presentation-21
  22. The Event Firing Mechanism in Access Objects-22
  23. One TextBox and Three Wrapper Class Instances-23
  24. Streamlining Code Synchronized Floating Popup Form-24
  25. Streamlining Code Compacting/Repair Database-25
  26. Streamlining Code Remainder Popup Form-26
  27. Streamlining Code Editing Data in Zoom-in Control-27
  28. Streamlining Code Filter By Character and Sort-28
  29. Table Query Records in Collection Object-29
  30. Class for All Data Entry Editing Forms-30
  31. Wrapper Class Module Creation Wizard-31
  32. wrapper-class-template-wizard-v2
Share:

Form VBA Structured Coding Access Users-Group Europe Presentation

Streamlining Form Module Code in Standalone Class Module.

On January 3, 2024, I presented a concise overview of the intricate topic: "Streamlining Form Module Code in Standalone Class Module" for the Access User Groups (Europe) Chapter.   https://accessusergroups.org/europe/ . 

https://youtu.be/AjvjN3h1ipY

The YouTube subtitles are available in eleven languages: Danish, Dutch, English, French, German, Italian, Spanish, Hindi, Malayalam, Bangla (India), and English (Auto-generated). Choose your preferred language subtitles from the Settings Menu. Experience the video, packed with technical details, all conveniently in one place. YouTube.com 

PS: The Subtitle Translation may not be accurate.

Download Demo Database

Streamlining Form Module Code in Standalone Class Module.

  1. Re-using 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. Collection Object replaces Class Object Array - Part Eight
  9. Reusability of Streamlined VBA Code - Part Nine
  10. Organizing Wrapper Classes for Different Forms - Part Ten
  11. ComboBox and Option-Group Wrapper Classes - Part Eleven
  12. Report Module Code in Class Module - Part Twelve
  13. Hiding Report Lines Conditionally - Part 13.
  14. Form Report Detail Sections Event Handling - Part 14.
  15. New Custom-Made Form Wizard VBA - Part 15.
  16. New Custom-Made Report Wizard - Part 16.
  17. Streamlining VBA External Files List in Hyperlinks-17
  18. Streamlining Event Procedures 3D-Text Wizard-18
  19. Streamlining Form Module VBA RGBColor Wizard-19
  20. Form VBA Structured Coding Numbers to Words Converter-20
  21. Form VBA Structured Coding Access Users-Group Europe Presentation-21
  22. The Event Firing Mechanism in Access Objects-22
  23. One TextBox and Three Wrapper Class Instances-23
  24. Streamlining Code Synchronized Floating Popup Form-24
  25. Streamlining Code Compacting/Repair Database-25
  26. Streamlining Code Remainder Popup Form-26
Share:

Form VBA Structured Coding Numbers to Words Converter

 Numbers to Words Converter.

While Microsoft Word Mail Merge provides a straightforward way to convert numbers into words, this functionality is not directly available in MS Access. To bridge this gap, we developed a versatile function that can be used wherever required—whether in a Form TextBox control, Report Summary, printed invoices, or any other context. Simply call the CardText() function with the desired number, and it will return the corresponding words for display. It’s that simple.

The Main Demo Form Image is given below:

The Demo Form in Design View.

Sample Report Image. The Group-level Subtotal Amount is printed in Words.

The CardText() Function VBA Code Listing.

Option Compare Database
Option Explicit

Public Function CardText(ByVal inNumber As Double, Optional ByVal precision As Integer = 2) As String
'------------------------------------------------------------------------
'Author : a.p.r. pillai
'Date   : December 2008/2023
'URL    : www.msaccesstips.com
'Version: 2.0
'All Rights Reserved by www.msaccesstips.com
'------------------------------------------------------------------------
Dim ctu, ctt, bmth
Dim strNum As String, j As Integer, k As Integer, fmt As String
Dim h As Integer, xten As Integer, yten As Integer
Dim cardseg(1 To 4) As String, txt As String, d As String, txt2 As String
Dim locn As Integer, xfract As String, xhundred As String
Dim xctu As String, xctt As String, xbmth As String

On Error GoTo CardText_Err

strNum = Trim(Str(inNumber))
locn = InStr(1, strNum, ".")
'Check Decimal Places and rounding
If locn > 0 Then
  xfract = Mid(strNum, locn + 1)
 strNum = Left(strNum, locn - 1)
    If precision > 0 Then
        If Len(xfract) < precision Then
            xfract = xfract & String(precision - Len(xfract), "0")
        ElseIf Len(xfract) > precision Then
            xfract = Format(Int(Val(Left(xfract, precision + 1)) / 10 + 0.5), String(precision, "0"))
        End If
        xfract = IIf(Val(xfract) > 0, xfract & "/" & 10 ^ precision, "")
    Else
        strNum = Val(strNum) + Int(Val("." & xfract) + 0.5)
        xfract = ""
    End If
End If

h = Len(strNum)
If h > 12 Then
'if more than 12 digits take only 12 (max. 999 Billion)
'extra value will get truncated from left.
   strNum = Right(strNum, 12)
Else
   strNum = String(12 - h, "0") & strNum
End If

GoSub initSection

txt2 = ""
For j = 1 To 4
    If Val(cardseg(j)) = 0 Then
       GoTo NextStep
    End If
    txt = ""
    For k = 3 To 1 Step -1
      Select Case k
       Case 3
            xten = Val(Mid(cardseg(j), k - 1, 1))
            If xten = 1 Then
                txt = ctu(10 + Val(Mid(cardseg(j), k, 1)))
            Else
                txt = ctt(xten) & ctu(Val(Mid(cardseg(j), k, 1)))
            End If
        Case 1
            yten = Val(Mid(cardseg(j), k, 1))
            xhundred = ctu(yten) & IIf(yten > 0, bmth(1), "") & txt
            Select Case j
                Case 2
                      d = bmth(2)
                Case 3
                    d = bmth(3)
                Case 4
                    d = bmth(4)
            End Select
            txt2 = xhundred & d & txt2
    End Select
   Next
NextStep:
Next

If Len(txt2) = 0 And Len(xfract) > 0 Then
    txt2 = xfract & " only. "
ElseIf Len(txt2) = 0 And Len(xfract) = 0 Then
    txt2 = ""
Else
  txt2 = txt2 & IIf(Len(xfract) > 0, " and " & xfract, "") & " only."
End If

CardText = txt2

CardText_Exit:
Exit Function

initSection:
'Units to 19
xctu = ", One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Eleven, Twelve,"
xctu = xctu & " Thirteen, Fourteen, Fifteen, Sixteen, Seventeen, Eighteen, Nineteen"
ctu = Split(xctu, ",")

'Tens
xctt = ", Ten, Twenty, Thirty, Fourty, Fifty, Sixty, Seventy, Eighty, Ninety"
ctt = Split(xctt, ",")

xbmth = ", Hundred, Thousand, Million, Billion"
bmth = Split(xbmth, ",")
k = 4
For j = 1 To 10 Step 3
    cardseg(k) = Mid(strNum, j, 3)
    k = k - 1
Next
Return

CardText_Err:
CardText = ""
MsgBox Err.Description, , "CardText()"
Resume CardText_Exit
End Function

The CardText() function, first written and published in January 2009, accepts two parameters. The initial parameter should be either a Decimal Number or a valid expression that resolves to a Decimal Number. The second parameter determines the precision of decimal digits. Notably, the second parameter is optional and already seeded with a default value of 2. The precision setting can be modified when invoking the CardText() function to align with specific requirements.

How to Run the Function on the Form.

Upon entering a numeric value in the first TextBox, you can execute the CardText() function by pressing the Enter key or by clicking on the Show Command Button. This operation converts the entered number to words and subsequently presents it in the Label Control situated below.

In addition to entering a straightforward numeric value, you can compose an expression for calculation and get the result. The CardText() function processes this expression, and the resulting value is transformed into words for display. A sample expression is demonstrated below:

((625*25+0.75)*0.80)

There are two Command Buttons. One to run the Function and the other to close the Form. The Label Control displays the entered Number in Words.  A simple interface allows you to enter the required parameters and call the function seamlessly.

To illustrate the straightforward application of the CardText() function, two TextBox Controls have been incorporated beneath the Close Command Button. Specifically named "Calc," the first TextBox is unbound. Users can input a numeric value into this TextBox. The adjacent TextBox, on the right side, contains the expression "=CardText([Calc])." This expression uses the CardText() function to convert the value entered into the "Calc" TextBox, presenting it in words within the same TextBox on the right side. This intuitive setup demonstrates the seamless integration of the CardText() function, which converts the number into its textual representation.

Preparing for the Streamlining VBA Code Procedure.

Only one TextBox has the AfterUpdate Event. When fired, it simply calls the Command Button Click Event, validates the input value in the TextBox, and runs the CardText() Function.  

Given that the form boasts a straightforward interface with minimal events to manage, the streamlining allows these uncomplicated event procedures within the intermediary class module. Consequently, there is no imperative need for wrapper classes. This simplifies the structure and enhances efficiency by consolidating the handling of basic events directly within the intermediary class module, eliminating the necessity for additional layers of abstraction. This approach streamlines the code and enables a more concise and manageable implementation.

We require only a single instance of the TextBox control and two instances of the CommandButton control, each with a different name, in the intermediary class module. All three object instances are declared using the `WithEvents` keyword, enabling their event procedures to be implemented within the `Card_ObjInit` class.

In this particular scenario, adopting object-level wrapper classes would likely result in a larger VBA codebase for event handling. Following the recommended design guidelines would require wrapper classes, especially for the CommandButton controls. In addition, a `Collection` object would be necessary to maintain the wrapper class instances in memory, ensuring that their event procedures remain active and can be executed when the corresponding events occur.

However, given the simplicity of the form interface and the limited number of events, the coding approach would introduce unnecessary complexity and resource overhead. If the streamlined implementation in the intermediary class module is sufficient to manage these events, it provides a more resource-efficient solution. The choice should therefore be based on balancing adherence to established coding practices with the optimized resource usage for the specific Form requirements.

Accordingly, for this implementation, we chose to manage all event handling directly within the `Card_ObjInit` class module.

The Card_ObjInit Wrapper Class.

The Card_ObjInit Class, which is a Wrapper Class, is listed below.

Option Compare Database
Option Explicit

Private WithEvents txt As Access.TextBox
Private WithEvents cmdS As Access.CommandButton
Private WithEvents cmdE As Access.CommandButton
Private frm As Access.Form

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 cmdS = frm.cmdResult
    cmdS.OnClick = EP
Set cmdE = frm.cmdClose
    cmdE.OnClick = EP
Set txt = frm.Amt
    txt.AfterUpdate = EP
End Sub

Private Sub cmdE_Click()
If MsgBox("Close the Form? ", vbYesNo + vbQuestion, "CmdClose_Click()") = vbYes Then
    DoCmd.Close acForm, frm.Name
End If
End Sub

Private Sub txt_AfterUpdate()
 Call cmdS_Click
End Sub

Private Sub cmdS_Click()
Dim tx As Variant
Dim t As Variant
Dim ctxt As String
Dim Rounding As Integer
Dim dblResult As Double
Dim msg As String
Dim fmt As String

On Error GoTo cmdResult_Click_Err
tx = frm!Amt
t = Replace(tx, ",", "")
tx = t
Rounding = frm!RoundTo
fmt = "#,##0." & String(Rounding, "0")
dblResult = Eval(tx)

If dblResult > (10 ^ 12 - 1) Then
  msg = "Value: " & dblResult & " Exceeds permissible limit."
  MsgBox msg, , "cmdResult_Click()"
Else

frm!Amt = Format(dblResult, fmt)

    ctxt = CardText(dblResult, Rounding)
    frm!Result.Caption = ctxt
End If

cmdResult_Click_Exit:
Exit Sub

cmdResult_Click_Err:
MsgBox Err & " : " & Err.Description, , "cmdResult_Click()"
Resume cmdResult_Click_Exit
End Sub

The Intermediary Class Module.

In the global declaration area, the TextBox instance named txt and two Command Button Control instances, cmdS and cmdE, are also declared. Each of these instances is qualified with the 'WithEvents' keyword, empowering them to capture events triggered on the form.

At the onset of the Class_Init() Subroutine, the cmdS Command Button object, labeled Show, is linked to the cmdResult Command Button through the assignment. Simultaneously, the cmdE Command Button object is associated with the cmdClose Command Button. Both of these Command Button objects have their OnClick() events enabled. Additionally, the txt object is connected to the Amt TextBox, and its AfterUpdate event is activated.

The cmdE Click Event Subroutine closes the Main Form.

In the cmdS Click Event Procedure, the entered value is validated, and the CardText() Function is invoked to convert the number to words. The resulting output is then displayed in the Label Control on the Form. If the input is an expression rather than a direct number, it is first evaluated to a number before calling the CardText() Function.

The AfterUpdate () Event of the Amt TextBox calls the cmdS_Click() Event Subroutine to run the validation check, and subsequently calls the CardText() Public Function.

Demo Database Download Link.


Streamlining Form Module Code in Standalone Class Module.

  1. Re-using 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. Collection Object replaces Class Object Array - Part Eight
  9. Reusability of Streamlined VBA Code - Part Nine
  10. Organizing Wrapper Classes for Different Forms - Part Ten
  11. ComboBox and Option-Group Wrapper Classes - Part Eleven
  12. Report Module Code in Class Module - Part Twelve
  13. Hiding Report Lines Conditionally - Part 13.
  14. Form Report Detail Sections Event Handling - Part 14.
  15. New Custom-Made Form Wizard VBA - Part 15.
  16. New Custom-Made Report Wizard - Part 16.
  17. Streamlining VBA External Files List in Hyperlinks-17
  18. Streamlining Event Procedures 3D-Text Wizard-18
  19. Streamlining Form Module VBA RGBColor Wizard-19
  20. Form VBA Structured Coding Numbers to Words Converter-20
  21. Form VBA Structured Coding Access Users-Group Europe Presentation-21
  22. The Event Firing Mechanism in Access Objects-22
  23. One TextBox and Three Wrapper Class Instances-23
  24. Streamlining Code Synchronized Floating Popup Form-24
  25. Streamlining Code Compacting/Repair Database-25
  26. Streamlining Code Remainder Popup Form-26
  27. Streamlining Code Editing Data in Zoom-in Control-27
  28. Streamlining Code Filter By Character and Sort-28
  29. Table Query Records in Collection Object-29
  30. Class for All Data Entry Editing Forms-30
  31. Wrapper Class Module Creation Wizard-31
  32. wrapper-class-template-wizard-v2
Share:

Streamlining Form Module VBA RGBColor Wizard

RGB Color Wizard.

Create your own RGB Color Palette for Form Design.

This special episode focuses on streamlining Form Module code. In our RGB Color Wizard, we use an ActiveX ScrollBar control. It is important to note that ActiveX controls—such as ScrollBars, Sliders, TreeView, and ListView—cannot be instantiated within a standalone class module. As a result, all event procedures for these controls must be written directly in the Form Module.

In this example, three ScrollBar controls represent the Red, Green, and Blue color values, each ranging from 0 to 255. Together, these values generate a full RGB spectrum, thus enabling the creation of up to 16.7 million distinct colors.

The RGB Color Wizard Image is given below.

Let us take a look at the Color Wizard's User Interface.

The Wizard Controls.

To the left of the scrollbars, three TextBox controls are positioned. As you move the scrollbar slider left or right, the corresponding color value (ranging from 0 to 255) is displayed in the adjacent TextBox. You can also manually enter numeric values into these TextBoxes to define a specific RGB color.

On the right side of the scrollbars, three Label controls display the intensity of the individual color selected Red, Green, and Blue values, visually resembling a bar graph. The RGB() function then mixes the three values to generate the final color, which is prominently shown in the large rectangle control below.

When you’re satisfied with the generated color, click the RGB Color rectangle to select it. Next, choose one of the 25 color boxes to save the new color—this action replaces the existing color in that box with your selection. The corresponding RGB value is also displayed in the RGBColor TextBox located beneath the color box grid.

You can store and maintain up to 25 colors at a time. To apply a saved color to a control—such as a TextBox or Label—for properties like ForeColor, BackColor, or BorderColor, simply highlight the value in the RGB Color TextBox and copy-paste it into the desired property.

For convenience, you can also copy the RGB number directly to the Clipboard by clicking the Label control labeled ‘Copy to Clipboard’ beneath the RGB Color TextBox. Once copied, the RGB value can be pasted wherever needed.

The Form Module Code with the ActiveX Control's OnChange() Event Procedures is given below.

Option Compare Database
Option Explicit

Private CWiz As CWiz_ObjInit 'Intermediary Class

Const GraphFactor = (1 / 255) * 1440 'The Color Graph Width is 1 inch
Dim intR As Long, intG As Long, intB As Long
Dim cdb As Database, doc As Document

Private Sub Form_Load()

Set CWiz = New CWiz_ObjInit 'Instantiate CWiz_ObjInit Class
Set CWiz.o_Frm = Me 'Assign the Form object to its Property

End Sub

Private Sub form_Unload(Cancel As Integer)
    Set CWiz = Nothing
End Sub

'ActiveX Control
Private Sub Ctl_B_Change() 'Blue Color ScrollBar Control
Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")

intB = Ctl_B.Value
With Me
    ![BN] = intB
    .B.Width = GraphFactor * intB
    .B.BackColor = RGB(0, 0, intB)
    .Color.BackColor = RGB(intR, intG, intB)
    .RGBColor = .Color.BackColor

'Save RGBColor and BN TextBoxes contents in Form Custom Properties
doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("BN").Value = intB

    .Controls("Color").SpecialEffect = 0
    .CheckBox.Value = False
End With
End Sub

'ActiveX Control
Private Sub Ctl_G_Change()
Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")

intG = Me.Ctl_G.Value

With Me
    intG = .Ctl_G.Value
    ![GN] = intG
    .G.Width = GraphFactor * intG
    .G.BackColor = RGB(0, intG, 0)
    .Color.BackColor = RGB(intR, intG, intB)
    .RGBColor = Color.BackColor

doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("GN").Value = intG

    .Controls("Color").SpecialEffect = 0
    .CheckBox.Value = False
End With

End Sub

'ActiveX Control
Private Sub Ctl_R_Change()
Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")

intR = Me.Ctl_R.Value
With Me
    ![RN] = intR
    .R.Width = GraphFactor * intR
    .R.BackColor = RGB(intR, 0, 0)
    .Color.BackColor = RGB(intR, intG, intB)
    .RGBColor = Color.BackColor

doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("RN").Value = intR

    .Controls("Color").SpecialEffect = 0
    .CheckBox.Value = False
End With
End Sub

The ScrollBar Controls.

The three ScrollBars, named Ctl_R, Ctl_G, and Ctl_B, correspond to the Red, Green, and Blue colors. When the slider control on each is moved to adjust the color numbers within the range of 0 to 255, the Change Event is triggered. This event records these actions and updates other related controls, such as the TextBox content on the left side. Additionally, it increases the width of the label controls and dynamically shows the color variations based on the selected color range.

Additionally, the Change event dynamically updates both the RGB color shown in the large rectangular Label control and the corresponding RGB color value displayed in the TextBox beneath the Colors Grid.

Since we cannot create an instance of the ScrollBar ActiveX Control in the standalone Class Module, we are persuaded to write the Change Event Subroutines in the Form's Class Module.

The ColorWizard and Run-Time Data.

Typically, changes to a control’s ForeColor, BackColor, or BorderColor are made in the Form’s Design View. After updating these property values and saving the Form, the modifications are preserved. The next time the Form is opened, it will display the updated colors applied during design.

With the RGB Color Wizard, however, we modify control properties directly in Form View. Changes made in this mode are temporary—they are not automatically saved and will be lost once the form is closed. A practical solution is to store these settings in a table, so they can be reloaded and applied the next time the form is opened.

Contrary to the conventional approach of using tables for everything, we're opting for a different method. In this case, we'll save the entire data within the form itself. While not a novel concept, this method is rarely employed due to its complexity. Specifically, we'll store the data in the form's custom-made properties, akin to the Tag property of a form or control. Creating these properties with VBA is possible, although the procedure is somewhat uncommon.

 You can see how these Custom Properties are addressed for storing/retrieving data to/from them. For an introduction to this method, visit this Link: Saving Data on Forms, Not in a Table to see a simple, practical usage. 

To preserve data from the ColorGrid, Text Boxes, and other controls during changes or when closing, we implemented custom properties to store this information.

Saving of values to all custom properties occurs when the form is closed. Upon reopening the form, these values are read from the custom properties and displayed on the corresponding controls. These two event procedures are implemented in the CWiz_ObjInit Class Module.

Regarding the ScrollBar Change Event Subroutine, pay attention to the subsequent lines responsible for updating custom properties and the method employed to address and store values into them:

Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")
.
.
.
doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("BN").Value = intB
.

Before saving the values into the Custom Properties, we must create the Properties on the Form.  This is a one-time exercise.

Sample Custom Property Management VBA Code.

Let us see an example of creating a Custom Property to save an Employee's Name in Form1. Sample VBA Code is given below:

'Create a Custom Property in Form1
Private Sub CreateProperty()
Dim db As Database
Dim doc As Document
Dim prp As Property

Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")

Set prp = doc.CreateProperty("EmpName", dbText, "SampleText")

doc.Properties.Append prp

Set prp = Nothing
Set doc = Nothing
Set db = Nothing

End Sub

'Assign a value to Custom Property in Form1
Private Sub AssignPropertyValue()

Dim db As Database
Dim doc As Document

Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")
    doc.Properties("EmpName").Value = "Michael Colins"

Set doc = Nothing
Set db = Nothing

End Sub

'Read value from Custom Property in Form1
Private Sub ReadPropertyValue()

Dim db As Database
Dim doc As Document
Dim strName As String

Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")
  strName = doc.Properties("EmpName").Value
  MsgBox "Name: " & UCase(strName)

Set doc = Nothing
Set db = Nothing

End Sub

'Create a Custom Property in Form1
Private Sub DeleteProperty()

Dim db As Database
Dim doc As Document

Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")
    doc.Properties.Delete "EmpName"
    
Set doc = Nothing
Set db = Nothing
End Sub

All the procedures for creating a custom property to save an employee's name, assigning a name to the property, reading it back, displaying it in a message box, and deleting the custom property from Form1 are outlined in the individual subroutines above.

The CWiz_TextBox Wrapper Class.

The CWiz_TextBox Wrapper Class manages the AfterUpdate() Event, allowing for direct entry of color numbers for Red, Green, and Blue into the TextBoxes named RN, GN, and BN. The subsequent changes are seamlessly reflected in the Scrollbars, the color graphs situated to the right of the Scrollbars, the new color showcased in the large rectangle label background, and the RGB color number displayed in the Textbox.

You may save your new Color in the Color Grid as explained earlier. 

The CWiz_TextBox Wrapper Class Module Code is given below:

Option Compare Database
Option Explicit

Private WithEvents ctxt As Access.TextBox
Private cFrm As Form

Const GraphFactor = (1 / 255) * 1440
Private db As Database
Private doc As Document

Public Property Get c_Frm() As Form
    Set c_Frm = cFrm
End Property

Public Property Set c_Frm(ByRef vcFrm As Form)
    Set cFrm = vcFrm
End Property

Public Property Get c_txt() As Access.TextBox
    Set c_txt = ctxt
End Property

Public Property Set c_txt(ByRef vctxt As Access.TextBox)
    Set ctxt = vctxt
    
    Set db = CurrentDb
    Set doc = db.Containers("Forms").Documents("ColorPalette")
End Property

Private Sub ctxt_AfterUpdate()
 With cFrm
    Select Case ctxt.Name
        Case "RN"
            .Ctl_R.Value = cFrm![RN]
            doc.Properties("RN").Value = cFrm!RN
            .CheckBox.Value = False
        Case "GN"
            .Ctl_G.Value = cFrm![GN]
            doc.Properties("GN").Value = cFrm!GN
            .CheckBox.Value = False
        Case "BN"
            .Ctl_B.Value = cFrm![BN]
            doc.Properties("BN").Value = cFrm!BN
            .CheckBox.Value = False
    End Select
  End With
End Sub

The CWiz_Label Wrapper Class.

The Labels in the ColorGrid, the color graph Labels beside the ScrollBars, and the RGB color display Label are all managed through their Click event subroutines within the CWiz_Label Wrapper Class.

The CWiz_Label Wrapper Class Module Event Procedure Code is given below:

Option Compare Database
Option Explicit

Private WithEvents clbl As Access.Label
Private sFrm As Form

Const GraphFactor = (1 / 255) * 1440
Private db As Database
Private doc As Document

Private selflag As Boolean
Private lngColor As Long

Public Property Get s_Frm() As Form
    Set s_Frm = sFrm
End Property

Public Property Set s_Frm(ByRef vsFrm As Form)
    Set sFrm = vsFrm
End Property

Public Property Get s_clbl() As Access.Label
    Set s_clbl = clbl
End Property

Public Property Set s_clbl(ByRef vclbl As Access.Label)
    Set clbl = vclbl
    
    Set db = CurrentDb
    Set doc = db.Containers("Forms").Documents("ColorPalette")
End Property

Private Sub clbl_Click()
Dim I As Integer
If Val(Mid(clbl.Name, 5)) > 0 Then
    I = Val(Mid(clbl.Name, 5))
End If
Select Case I
    Case 1 To 25
        Call Boxes(I) 'Click on Color Grid
End Select

Select Case clbl.Name
    Case "Color"
        Call ColorClick 'Click on the RGB Color Display Label
    Case "Clip"
       Call ClipClick   'Click on this Labek to Copy RGB Color number to ClipBoard
End Select
End Sub

Private Sub ColorClick()
With sFrm
    lngColor = .Color.BackColor
    !RGBColor = .Controls("Color").BackColor
    .Controls("Color").SpecialEffect = 2
    
    'Copy the created color to the grid
    !CheckBox.Value = True
End With
End Sub

Private Sub ClipClick()
    If Not IsNull(sFrm![RGBColor]) Then
        ' Copy the TextBox contents to the clipboard
        sFrm.RGBColor.SetFocus
        DoCmd.RunCommand acCmdCopy
        MsgBox "RGB Color Number Copied to Clipboard!", vbInformation
    Else
        ' Display a message if the TextBox is empty
        MsgBox "RGBColor is empty!", vbExclamation
    End If

End Sub

Private Sub Boxes(ByVal bx As Integer)
Dim j As Integer
Dim ctl As String
Dim Colr As Long
Dim intR As Integer
Dim intG As Integer
Dim intB As Integer

selflag = sFrm!CheckBox.Value

For j = 1 To 25
If j = bx Then
   If selflag Then
    With sFrm
      ctl = "lblC" & j
        .Controls(ctl).SpecialEffect = 2
        .Controls(ctl).BackColor = .Color.BackColor
        doc.Properties("Selected").Value = .Controls(ctl).BackColor
        !RGBColor = .Controls(ctl).BackColor
        !CheckBox.Value = False
      ctl = "C" & j
      doc.Properties(ctl).Value = .Color.BackColor
      doc.Properties("Selctl").Value = "C" & j
    End With
   Else
    With sFrm
         ctl = "lblC" & j
            !RGBColor = .Controls(ctl).BackColor
            .Controls(ctl).SpecialEffect = 2
         doc.Properties("Selected").Value = .Controls(ctl).BackColor
         doc.Properties("Selctl").Value = "C" & j
   End With
   
 Colr = sFrm!RGBColor
 'Split into R,G,B
 intR = Colr Mod 256
 intG = Colr \ 256 Mod 256
 intB = Colr \ 256 \ 256 Mod 256
 
 With sFrm
    !RN = intR
    .Ctl_R.Value = sFrm!RN
    !GN = intG
    .Ctl_G.Value = sFrm!GN
    !BN = intB
    .Ctl_B.Value = sFrm!BN
 
    .R.Width = GraphFactor * intR
    .G.Width = GraphFactor * intG
    .B.Width = GraphFactor * intB

    .R.BackColor = RGB(intR, 0, 0)
    .G.BackColor = RGB(0, intG, 0)
    .B.BackColor = RGB(0, 0, intB)

    .Color.BackColor = RGB(intR, intG, intB)
  End With
  With doc
    .Properties("RGBColor").Value = sFrm.Color.BackColor
    .Properties("RN").Value = intR
    .Properties("GN").Value = intG
    .Properties("BN").Value = intB
  End With
  End If
   
Else
   ctl = "lblC" & j
   sFrm.Controls(ctl).SpecialEffect = 0
End If

Next

End Sub

The Intermediary Class Module CWiz_ObjInit VBA Code is given below:

Option Compare Database
Option Explicit

Private cw As CWiz_Label
Private txt As CWiz_TextBox
Private WithEvents cmd As CommandButton

Private WithEvents frm As Form
Private coll As New Collection

Const GraphFactor = (1 / 255) * 1440
Const MaxColor = 25
Private cdb As Database, ctr As Container, doc As Document


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

Public Property Set o_Frm(ByRef voFrm As Form)
    Set frm = voFrm
    
    Call Class_Init
End Property

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

Call ColorPalette_Init 'Initialize

Set cmd = frm.cmdClose
    cmd.OnClick = EP
     
For Each ctl In frm.Controls

 I = Val(Mid(ctl.Name, 5))
 
  Select Case TypeName(ctl)
    Case "Label"
      Select Case I
          Case 1 To 25
            Set cw = New CWiz_Label
            Set cw.s_Frm = frm
            Set cw.s_clbl = ctl
                cw.s_clbl.OnClick = EP
            coll.Add cw
            Set cw = Nothing
      End Select
      Select Case ctl.Name
        Case "Color"
            Set cw = New CWiz_Label
            Set cw.s_Frm = frm
            Set cw.s_clbl = ctl
                cw.s_clbl.OnClick = EP
                
            coll.Add cw
            Set cw = Nothing
        Case "Clip"
            Set cw = New CWiz_Label
            Set cw.s_Frm = frm
            Set cw.s_clbl = ctl
                cw.s_clbl.OnClick = EP
            coll.Add cw
            Set cw = Nothing
        End Select
     
      Case "TextBox"
        Select Case ctl.Name
            Case "RN", "GN", "BN"
              Set txt = New CWiz_TextBox
              Set txt.c_Frm = frm
              Set txt.c_txt = ctl
                txt.c_txt.AfterUpdate = EP
                coll.Add txt
              Set txt = Nothing
            Case "RGBColor"
              Set txt = New CWiz_TextBox
              Set txt.c_Frm = frm
              Set txt.c_txt = ctl
                txt.c_txt.OnGotFocus = EP
                coll.Add txt
              Set txt = Nothing
        End Select
    End Select
Next
End Sub

Private Sub ColorPalette_Init()
Dim xRN As Integer
Dim xGN As Integer
Dim xBN As Integer
Dim xRGBColor As Long
Dim j As Integer
Dim cdb As Database
Dim ctr As Container
Dim doc As Document
Dim strctl As String

Set cdb = CurrentDb
Set ctr = cdb.Containers("Forms")
Set doc = ctr.Documents("ColorPalette")

xRN = doc.Properties("RN").Value
xGN = doc.Properties("GN").Value
xBN = doc.Properties("BN").Value
xRGBColor = doc.Properties("RGBColor").Value

With frm 
    ![RN] = xRN
    ![GN] = xGN
    ![BN] = xBN
    .R.Width = xRN * GraphFactor
    .R.BackColor = RGB(xRN, 0, 0)
    
    .G.Width = xGN * GraphFactor
    .G.BackColor = RGB(0, xGN, 0)
    
    .B.Width = xBN * GraphFactor
    .B.BackColor = RGB(0, 0, xBN)
    
.Ctl_R.Value = xRN
.Ctl_G.Value = xGN
.Ctl_B.Value = xBN

.Color.BackColor = RGB(xRN, xGN, xBN)
.RGBColor = .Color.BackColor
End With

For j = 1 To MaxColor
   strctl = "lblC" & j
   frm.Controls(strctl).BackColor = doc.Properties("C" & j).Value
   If ("C" & j) = doc.Properties("Selctl").Value Then
      frm.Controls(strctl).SpecialEffect = 2
   End If

Next j

Form_Load_Exit:
Exit Sub

Form_Load_Err:
MsgBox Err.Description, , "Form_Load"
Resume Form_Load_Exit

End Sub

Private Sub cmd_Click()
Dim msg As String
Dim ctl As String, strC1 As String, j As Integer

msg = "Close the Color Wizard?"
If MsgBox(msg, vbYesNo + vbQuestion, "cmd_Click()") = vbYes Then

    Set cdb = CurrentDb
    Set ctr = cdb.Containers("Forms")
    Set doc = ctr.Documents("ColorPalette")

For j = 1 To MaxColor
  ctl = "lblC" & j
  strC1 = "C" & j
    doc.Properties(strC1).Value = frm.Controls(ctl).BackColor
  If frm.Controls(ctl).SpecialEffect = 2 Then
      doc.Properties("Selected").Value = frm.Controls(ctl).BackColor
      doc.Properties("SelCtl").Value = strC1
  End If
Next
    doc.Properties("RGBColor").Value = Nz(frm.Controls("RGBColor").Value, 0)
    DoCmd.Close acForm, frm.Name
End If
End Sub

Private Sub Class_Terminate()
Do While coll.Count > 1
    coll.Remove 1
Loop
End Sub

When the form is opened, the form object is passed to the intermediary class module, initiating the execution of the Class_Init subroutine. The first subroutine, ColorPalette_Init, is invoked from within the Class_Init subroutine. This procedure retrieves all values from the form's custom properties and assigns them to the labels, scroll bars, and text boxes.

This procedure is normally run in the Form_Load() Event Procedure, and the current Values on the Form Controls are saved when the Form is closed.

There is a single command button to close the form. A single command button object is created in the Intermediary Class Module, and its Click Event is enabled. Consequently, when the cmdClose command button is clicked, the form close event procedure is executed in the CWiz_ObjInit Module. Before closing the form, all the values of the Color Wizard form controls are saved in the form's custom properties.

This topic was originally published in October 2010. The initial version featured a 15-color palette, with all wizard VBA code implemented in the form module. That original wizard form is included in the demo database named ColorPaletteOld. You are encouraged to open and review its code to see how it has been refactored into a form that can now be executed from a standalone class module VBA code associated with the ActiveX ScrollBar control.

Demo Database Download Link.

Streamlining Form Module Code in Standalone Class Module.

  1. Re-using 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. Collection Object replaces Class Object Array - Part Eight
  9. Reusability of Streamlined VBA Code - Part Nine
  10. Organizing Wrapper Classes for Different Forms - Part Ten
  11. ComboBox and Option-Group Wrapper Classes - Part Eleven
  12. Report Module Code in Class Module - Part Twelve
  13. Hiding Report Lines Conditionally - Part 13.
  14. Form Report Detail Sections Event Handling - Part 14.
  15. New Custom-Made Form Wizard VBA - Part 15.
  16. New Custom-Made Report Wizard - Part 16.
  17. Streamlining VBA External Files List in Hyperlinks-17
  18. Streamlining Event Procedures 3D-Text Wizard-18
  19. Streamlining Form Module VBA RGBColor Wizard-19
  20. Form VBA Structured Coding Numbers to Words Converter-20
  21. Form VBA Structured Coding Access Users-Group Europe Presentation-21
  22. The Event Firing Mechanism in Access Objects-22
  23. One TextBox and Three Wrapper Class Instances-23
  24. Streamlining Code Synchronized Floating Popup Form-24
  25. Streamlining Code Compacting/Repair Database-25
  26. Streamlining Code Remainder Popup Form-26
  27. Streamlining Code Editing Data in Zoom-in Control-27
  28. Streamlining Code Filter By Character and Sort-28
  29. Table Query Records in Collection Object-29
  30. Class for All Data Entry Editing Forms-30
  31. Wrapper Class Module Creation Wizard-31
  32. wrapper-class-template-wizard-v2
Share:

Streamlining Event Procedures 3D-Text Wizard

The 3D Text Wizard was first introduced in a series of articles published in September 2006. I originally created this website to share practical tips and techniques, many of which I had applied in real-world projects while working for an automotive company in the Sultanate of Oman. At that time, the wizard demonstrated the QBColor version, providing a simple way to explore color effects.

The updated 3D Text Wizard now supports the full RGB color spectrum, making color selection significantly more flexible. To further enhance customization, a dedicated ColorList table has been added, allowing users to extend the available palette with their own preferred colors.

This feature is particularly useful for creating three-dimensional text effects for form headings or for displaying field values, such as employee names or product names. These enhancements make the displayed text more visually appealing and easier to distinguish, especially when viewed from a distance.

Example-1: Employee Name.

Example-2:Order Details Form View-2

The 3D Text Wizard Image is given below:

The event procedures and functions in the 3D Text Wizard have been streamlined in line with the new Event Procedure Coding Rules implemented in the standalone class module. This structured approach improves code organization and readability while making maintenance and future development more efficient.

3D Text Creation Technique.

The 3D text effect is created by layering multiple Label controls or Text Boxes, each displaying the same text. Alternatively, the effect can be produced manually by placing a label or text box with an attractive foreground color on top and positioning additional layers with a darker foreground color behind it, slightly offset toward one of the four corners—top-left, top-right, bottom-left, or bottom-right. This technique creates a shadow effect that enhances the visual appearance of the text.

Manually creating this effect each time can be both tedious and inefficient. As demonstrated in earlier articles, I created form headings using only two labels to achieve a simple yet effective 3D appearance.

On the left side of the Text Wizard interface is a ListBox that displays a collection of colors. This ListBox is bound to a table named Colors, allowing you to add additional color codes whenever required.

To the right of the Colors List are two Option Group controls. The upper Option Group contains two choices. When the first option button is selected, the color chosen from the ListBox is displayed in the upper rectangle control and is applied to the caption of the topmost Label or to the font color of the Text Box used to create the 3D text.

When the second option button is selected, the color chosen from the ListBox is applied as the border color in the first two Text Wizards—Border 2D and Border 3D. All other Text Style Wizard options use only the ForeColor property.

The 3D Text Shadow Positions.

At the top right, there is a ComboBox with four options (0-3) to specify the light and shadow positions for the 3D Text.

Shadow Positions:

0 - Left Top Corner.

1 - Left Bottom Corner.

2 - Right Top Corner

3 - Right Bottom Corner 

The first text style, 2D, creates a white border around the text and requires no additional configuration. For both the 2D and 3D text styles, the wizard generates the effect by creating five or seven labels with the same text but with different ForeColor values. These labels are positioned beneath the top label and slightly offset to produce the desired visual effect.

For the 2D border style, the underlying labels are positioned toward the four corners of the top label, creating the appearance of a border surrounding the text.

3D Text Control Types.

The ComboBox positioned below the Shadow Style ComboBox offers two options and utilizes two types of controls to generate the 3D Text.

1 - Label 

2 - TextBox

The first option is good for creating Static Headings on Forms or Reports.

The second option creates 3D text using Text Box controls. This style is particularly useful for displaying data from form or report fields by using expressions, as illustrated in the examples shown at the top of this page.

After selecting the desired options, click the Create 3D Text command button to generate the 3D text. The generated text is displayed in the Detail section of a new form. Beneath it, an instructional label provides guidance on modifying the text, font, font size, and font styles, such as Bold, Italic, and Underline, if required. To edit the generated text, carefully select the top Label or Text Box without changing its position, and then modify the label caption or the Text Box contents as needed.

After completing the modifications, select all the Label or Text Box controls by dragging a selection rectangle around them. Then copy and paste the entire group to the desired location.

To display data from form fields in the Text Box controls, assign an expression—such as `=[First Name] & " " & [Last Name]`—to the Control Source property after selecting all the Text Box controls that make up the 3D text.

After generating a text style, you can save it within the 3D Text Wizard for future use. It can then be imported into other projects and modified as required.

With the event procedures consolidated in a standalone intermediary class module, only a single wrapper class is required to handle all the Command Button controls on the form.

The form contains only one ListBox control, and its Click event is handled by a procedure in the intermediary class module.

Similarly, the form contains two Option Group controls. One is used to select the 3D text style, while the other manages the ForeColor and BackColor parameter selection. The BackColor option applies only to the first two text styles—2D and 3D Border. When either of these styles is selected, the BackColor option is enabled; otherwise, it remains disabled.

Because these actions are controlled through the Text Style Option Group, a separate wrapper class for the Option Group controls is unnecessary. The ListBox and Option Group controls are declared as object instances in the intermediary class module using the WithEvents keyword, allowing their events to be handled directly within that class.

The form also contains two ComboBox controls. One is used to select the required 3D text shadow option, while the other is used to choose whether the output should be created as a Label or a Text Box. These ComboBox controls do not require any event procedures.

The Form Module VBA Code.

Both the ListBox and the Option Group Control's Click Events are enabled in the Class_Init() subroutine, and corresponding subroutines are written in this module. First, the Form Module Code is listed below:

Option Compare Database
Option Explicit

Private W As TWiz_Obj_Init

Private Sub Form_Load()
Set W = New TWiz_Obj_Init
Set W.w_frm = Me

End Sub

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

The TWiz_Obj_Init class is declared in the Module's global area with the object name 'W.' In the Form_Load() event procedure, the object is instantiated, and the current form object (Me) is passed to the W.w_frm() Property Procedure. When the form is closed, the class object 'W' is released from memory.

The TWiz_Obj_Init Class Module Code is Listed Below.

Option Compare Database
Option Explicit

Private wcmd As TWiz_CmdButton

Private WithEvents lst As Access.ListBox
Private WithEvents opt As Access.OptionGroup

Private wfrm As Access.Form
Private Coll As New Collection

Public Property Get w_frm() As Form
  Set w_frm = wfrm
End Property

Public Property Set w_frm(ByRef vfrm As Form)
Set wfrm = vfrm
DoCmd.Restore
 
Call Class_Init
End Property

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

Set opt = wfrm.TxtStyle '3D Text Styles
    opt.OnClick = EP

Set lst = wfrm.ColorList 'List of Colors
    lst.OnClick = EP

For Each ctl In wfrm.Controls
    Select Case TypeName(ctl)
        Case "CommandButton"
          Select Case ctl.Name
            Case "cmd3D", "cmdClose"
              Set wcmd = New TWiz_CmdButton
              Set wcmd.c_Frm = wfrm
            Set wcmd.c_cmd = ctl
                wcmd.c_cmd.OnClick = EP
                Coll.Add wcmd
            Set wcmd = Nothing
          End Select
    End Select
Next
End Sub

Private Sub lst_Click()
Dim cl As Long
cl = lst.Value
Select Case lst.Name
    Case "ColorList"
    If wfrm.FBack = 1 Then
        wfrm.Fore.BackColor = cl
        wfrm.CFore = cl
    Else
        wfrm.Back.BackColor = cl
        wfrm.CBack = cl
    End If

End Select
End Sub

Private Sub opt_Click()
Dim opval As Integer

Select Case opt.Name
    Case "TxtStyle"
        opval = opt.Value
        With wfrm.cboStyle
            If opval > 1 Then
                .Enabled = True
            Else
                .Enabled = False
            End If
        End With
        With wfrm.Opt2
            Select Case opval
                Case 1, 2
                    .Enabled = True
                Case Else
                    .Enabled = False
            End Select
        End With
End Select
End Sub

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

The following ListBox and OptionGroup Control declarations are placed in the Global area of the Class Module.

 
Private WithEvents lst As Access.ListBox
Private WithEvents opt As Access.OptionGroup

The following statements in the Class_Init() Subroutine assign the References from these Objects in the Form and enable their Click Events by assigning the "[Event Procedure]" text in their Event Properties:

Set opt = wfrm.TxtStyle '3D Text Styles
    opt.OnClick = EP

Set lst = wfrm.ColorList 'List of Colors
    lst.OnClick = EP

Both these objects, Sub lst_Click() and Sub opt_Click() Event Subroutines, are written below the Sub Class_Int() Procedure.

The Command Button Wrapper Class Module.

There is only one Wrapper Class for both the CommandButtons on the Form. All the Wizard Functions are called from the Command Button with the Caption Create 3D Text Click Event Procedure, depending on the 3D Text Style Option.

The CommandButton Wrapper Class Subroutine that calls the Wizard Functions is listed below for reference.

Option Compare Database
Option Explicit

Private WithEvents cmd As Access.CommandButton
Private cfrm As Access.Form

Public Property Get c_Frm() As Form
   Set c_Frm = cfrm
End Property

Public Property Set c_Frm(ByRef vfrm As Form)
   Set cfrm = vfrm
End Property

Public Property Get c_cmd() As CommandButton
   Set c_cmd = cmd
End Property

Public Property Set c_cmd(ByRef vcmd As CommandButton)
   Set cmd = vcmd
End Property

Private Sub cmd_Click()
Select Case cmd.Name
    Case "cmd3D"
        Call Create3D(cfrm) 'Call the 3D Text Wizard
        
    Case "cmdClose"
If MsgBox("Close the 3DTextWizard? ", vbYesNo + vbQuestion, "cmdClose_Click()") = vbYes Then
    DoCmd.Close acForm, cfrm.Name
End If
End Select

End Sub

The Cmd3D Click Event Subroutine invokes the Create3D(cfrm) Subroutine and passes the Form Object as a Parameter. This Subroutine in the Standard Module gathers the 3D Text Wizard option settings from the Form into related variables and then calls the wizard function based on the selected text style. Each Wizard function, such as Border2D(), calls three different programs to create the 3D Text.

For example, the Border2D Wizard calls the following three Functions to complete the full task of creating the 3D Text:

  1. FormTxtLabels() ' Creates a Form and the Label or Text Controls
  2. Validate_Dup() ' Performs a Validation check.

  3. MsgLabel()  'Creates a Label control with instructions to use the 3D Text.

Listing all the Wizard VBA codes here is not feasible due to their large volume. However, the 3DTextWizard Demo Database is attached with all the code. You can download it from the link provided at the end of this Page. All Wizard VBA codes are available in the TxtWizard Standard Module.

Visit the following Links to Articles published earlier for more details on the Wizard Functions:

  1. Create 3D Headings on Form
  2. Border 2D Heading Text
  3. Border3D Heading Style
  4. Shadow3D Heading Style

Demo Database Download Link.

Streamlining Form Module Code in Standalone Class Module.

  1. Re-using 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. Collection Object replaces Class Object Array - Part Eight
  9. Reusability of Streamlined VBA Code - Part Nine
  10. Organizing Wrapper Classes for Different Forms - Part Ten
  11. ComboBox and Option-Group Wrapper Classes - Part Eleven
  12. Report Module Code in Class Module - Part Twelve
  13. Hiding Report Lines Conditionally - Part 13.
  14. Form Report Detail Sections Event Handling - Part 14.
  15. New Custom-Made Form Wizard VBA - Part 15.
  16. New Custom-Made Report Wizard - Part 16.
  17. Streamlining VBA External Files List in Hyperlinks-17
  18. Streamlining Event Procedures 3D-Text Wizard-18
  19. Streamlining Form Module VBA RGBColor Wizard-19
  20. Form VBA Structured Coding Numbers to Words Converter-20
  21. Form VBA Structured Coding Access Users-Group Europe Presentation-21
  22. The Event Firing Mechanism in Access Objects-22
  23. One TextBox and Three Wrapper Class Instances-23
  24. Streamlining Code Synchronized Floating Popup Form-24
  25. Streamlining Code Compacting/Repair Database-25
  26. Streamlining Code Remainder Popup Form-26
  27. Streamlining Code Editing Data in Zoom-in Control-27
  28. Streamlining Code Filter By Character and Sort-28
  29. Table Query Records in Collection Object-29
  30. Class for All Data Entry Editing Forms-30
  31. Wrapper Class Module Creation Wizard-31
  32. wrapper-class-template-wizard-v2
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