Learn Microsoft Access Advanced Programming Techniques, Tips and Tricks.

Streamlining Form Module Code - Part Five

 Continued from the last Episode - Part Four.

Introduction.

Writing Form Module VBA Code in Standalone Class Module.

Earlier Episode Links:

  1. Re-using Form Module VBA Coding for New Projects.
  2. Streamlining Form Module Coding Part-Two.
  3. Streamlining Form Module Coding Part-Three.
  4. Streamlining Form Module Coding Part-Four.

Readers still need to go through the earlier Posts on this topic may please read them before continuing.

So far we worked with only one TextBox object on the Form and found how it can be programmed in several ways by writing VBA Code in a standalone Class Module rather than on the Form Module. We are familiar with the rules that govern defining Events, RaiseEvent to announce the Event, and declaring a TextBox object instance with the keyword WithEvents, for capturing the Events in the Class Module, raised on the TextBox control on the Form. 

Let us see what it takes to deal with Events for more than one TextBox on the Form.  This time we will introduce three TextBoxes on the Form for our experiments of Event handling in the standalone Class Module.  Normally, we can expect 10 to 20 or more TextBoxes on a Form, but very unlikely that all of them need to be programmed with Event Procedures. But, if all of them are required we can do that too. There are several inbuilt Events in a TextBox but all of them may not be used, but several of them may be used depending on the requirements.  We must be prepared to take that challenge too.

Let's start with three TextBoxes and see how we can handle Coding for a few Event Procedures. We will start with a crude approach first that works okay in the beginning, even though it is not an elegant solution, but is easy to understand.

Demo Form Design.

Create a new Form with the following design in your database:
  1. Create a new Form and insert 3 unbound TextBoxes, one below the other, on the Detail Section of the Form.  

  2. Insert a Label control above the Textboxes and write the description as shown in the Form Image given above, in its Caption Property.

  3. Select the first TextBox and display the Property Sheet. Change the Name Property Value to Quantity and change its Child-label caption to Quantity(1-10).

  4. Select the second TextBox change its Name Property Value to UnitPrice and change the child-label Caption to Unit Price.

  5. Change the Name Property value of the third TextBox as TotalPrice and enter Total Price in the child-label Caption.

  6. Display the VBA Code Module of the Form.

  7. Copy and Paste the following VBA Code into the Form Module, overwriting existing lines of Code, if any:

    Option Compare Database
    Option Explicit
    
    Private C As New Class3_1 'create an instance of Class3_1 Class Object
    
    Private Sub Form_Load()
      Set C.m_Frm = Me 'Pass current Form Object to C.m_Frm Property Procedure
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
        Set C = Nothing 'Release the Class Object instance from memory
    End Sub
    
  8. Save the Form with the name Form1_RaiseEvent_4

We made some changes, in the earlier Global Declaration of Class Object C to create an Instance of the Class Module Class3_1 object. This is the short form (Private C As New Class3_1) of the following two-line statements we used in earlier cases:

Private C As Class3_1 'create an instance of Class3_1 Class Object

Private Sub Form_Load()
  Set C = New Class3_1
End Sub

The New keyword is used in the class object declaration itself to create an instance of the Class Object in memory. The current Form Object Me is passed to the SET Property Procedure C.m_Frm() as a Parameter in the Form_Load() Event Procedure. You will see the m_Frm() Property Procedure when we create the Class3_1 Class Module Code. The Class Object declaration in the Global area alone will not load the Class Object into the Memory.  We instantiated the class object with the keyword New to load an instance of the class object into memory. When we open a Form its Class Module is also loaded into the memory, when the Form's Has Module Property is set to Yes.

The Form_Unload() Event Procedure removes the Class Object Instance C from memory to release the memory for other uses, when the Form is closed.

The New Class Module: Class3_1.

We will find a better way to name our Class Modules so that by looking at them, we can easily guess where it belongs, or what category of object it is.  But, we are on a learning curve now, and understanding the new concept is more important than such cosmetic changes.  

Create a new Class Module, display its Property Sheet (Select Properties Window from the View Menu), and change its Name Property Value to Class3_1.

Copy and Paste the following VBA Code into the Class Module and Save the Module:

Option Compare Database
Option Explicit

Private frm As Form

Private WithEvents Qty As TextBox 'TextBox object Instance - 1
Private WithEvents UPrice As TextBox  'TextBox object Instance - 2
Private WithEvents Total As TextBox 'TextBox object Instance - 3

'---------------------------------------------------------------------

'Form Object's Property GET/SET Procedures
Public Property Get m_Frm() As Form
    Set m_Frm = frm
End Property

Public Property Set m_Frm(ByRef mfrm As Form)

    Set frm = mfrm

'After receiving Form Object, scan for TextBoxes on Form
'and Enable them with required Events.
'
   Call class_Init

End Property
'---------------------------------------------------------------------

'Scan for TextBox Controls on Form
'and Enable the required Events
'
Private Sub class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"

For Each ctl In frm.Controls
    Select Case ctl.Name
    
    Case "Quantity"
 'Assign Quantity TextBox on Form to 'Qty'
 'Private Property of Class_3_1 Class Module
 
          Set Qty = ctl
        
    'Enable RaiseEvent
        Qty.OnExit = EP
        Qty.OnGotFocus = EP
        Qty.OnLostFocus = EP
'------------------------------        
        Case "UnitPrice"
 'Assign UnitPrice TextBox on Form to 'UPrice'
 'Private Property of Class_3_1 Class Module

    Set UPrice = ctl
    
    'Enable RaiseEvent
        UPrice.OnExit = EP
        UPrice.OnGotFocus = EP
        UPrice.OnLostFocus = EP
'------------------------------    

        Case "TotalPrice"
 'Assign TotalPrice TextBox on Form to 'Total'
 'Private Property of Class_3_1 Class Module
        
            Set Total = ctl
    
    'Enable RaiseEvent
            Total.OnGotFocus = EP
            Total.OnLostFocus = EP
    End Select
Next

End Sub

'---------------------------------------------------------------------

'Qty.Exit() Qty.GotFocus() & Qty.LostFocus() Event Procedures

Private Sub Qty_Exit(Cancel As Integer)
Dim i As Integer, Msg As String
Dim info As Integer

i = Nz(Qty.Value, 0)
If i < 1 Or i > 10 Then
    Msg = "Valid Value Range 1 - 10 Only."
    info = vbCritical
    Cancel = True 'I-bar to stay in the TextBox
Else
    Msg = "Quantity: " & i & " Valid."
    info = vbInformation
End If

MsgBox Msg, vbOK + info, "Quatity_Exit()"

End Sub

Private Sub Qty_GotFocus()
    With Qty
        .BackColor = &H20FFFF
        .ForeColor = 0
    End With
End Sub

Private Sub Qty_LostFocus()
On Error Resume Next
    With Qty
        .BackColor = &HFFFFFF
        .ForeColor = 0
    End With
End Sub

'---------------------------------------------------------------------
'UPrice.Exit() Qty.GotFocus() & UPrice.LostFocus() Event Procedures

Private Sub UPrice_Exit(Cancel As Integer)
Dim i As Single, Msg As String
Dim info As Integer

i = Nz(UPrice.Value, 0)
Msg = ""
If i <= 0 Then
    Msg = "Enter a Value greater than Zero!"
    info = vbCritical
    Cancel = True 'I-bar to stay in the TextBox
End If

If Len(Msg) > 0 Then
    MsgBox Msg, vbOK + info, "UnitPrice_Exit()"
Else
    MsgBox "Unit Price: " & i, vbOK + vbInformation, "UPrice_Exit()"
    
End If

End Sub

Private Sub UPrice_GotFocus()
    With UPrice
        .BackColor = &H20FFFF
        .ForeColor = 0
    End With
End Sub

Private Sub UPrice_LostFocus()
    With UPrice
        .BackColor = &HFFFFFF
        .ForeColor = 0
    End With
End Sub

'---------------------------------------------------------------------
'Total.GotFocus() & Total.LostFocus() Event Procedures

Private Sub Total_GotFocus()
    
    With Total
        .BackColor = &H20FFFF
        .ForeColor = 0
    End With
    
    frm!TotalPrice = Qty * UPrice
    frm.TotalPrice.Locked = True
        
End Sub

Private Sub Total_LostFocus()
    With Total
        .BackColor = &HFFFFFF
        .ForeColor = 0
    End With
End Sub

Private Sub Class_Terminate()
    Set Qty = Nothing
    Set UPrice = Nothing
    Set Total = Nothing
End Sub

'---------------------------------------------------------------------

You can ignore/delete the dashed lines I put in between segments for better readability on this Page.

Segment-wise Review of VBA Code.

Declaration of Objects and Get/Set Property Procedures of Form Object.

Let us review the Class Module Code segment-wise to understand what they do. This  VBA Code structure will undergo some major changes altogether later to make them easy to manage.

Option Compare Database
Option Explicit

Private frm As Form

Private WithEvents Qty As TextBox     'TextBox object Instance - 1
Private WithEvents UPrice As TextBox  'TextBox object Instance - 2
Private WithEvents Total As TextBox   'TextBox object Instance - 3

'---------------------------------------------------------------------

'Form Object's Property GET/SET Procedures
Public Property Get m_Frm() As Form
    Set m_Frm = frm
End Property

Public Property Set m_Frm(ByRef mfrm As Form)

    Set frm = mfrm

'After receiving Form Object, scan for TextBoxes on Form
'and Enable them with required Event Procedures.
'
   Call class_Init

End Property
'---------------------------------------------------------------------

In the Global declaration area, a Form object Frm is declared with Private Scope.  That means this Form object cannot be accessed directly from outside the Class Module except through Property Procedures declared with Public Scope.  We can pass the current Form Object Me to the SET m_Frm() Property Procedure through the Form_Load() Event Procedure. When we get the current Form instance in the Frm object in the Class3_1 Class Module we get access to all Controls or get their values from the Form. We can capture the Events fired (RaiseEvent) from the Controls on the Form, update values in the Textbox or access other properties of the Controls on the Form. 

We will create a Form Object in every standalone Class Module we create in the demo of the 'Streamlining the Form Module VBA Code' procedure. You can track how the current active Form Object is passed from the Form_Load() Event Procedure to the form object Instance in the standalone Class Module, through the Set Property Procedure parameter. You may keep an eye on its usage in the standalone Class Module (Class3_1) and in the Event Procedure Subroutines.

The next three TextBox Object declarations: Qty, UPrice, and Total will be assigned with the three TextBox object References on the Form Quantity, UnitPrice, and TotalPrice respectively. 

Next, the Set Property Procedure will receive the Form Object into the Frm Property, through the Form_Load() Event Procedure through the m_frm Property Procedure. The Get Property Procedure will stand by for servicing the external request for the Form object when received.

The Call Class_Init() statement calls the Subroutine Class_Init().

The Class_Init() subroutine scans the Form for TextBox Objects, and when found their References are assigned to the three TextBox Control Instances with the names, Qty, UPrice, and Total we created in the Global declaration area.

Scan the Form to find the Quantity, UnitPrice, and TotalPrice TextBoxes.

'Scan for TextBox Controls on Form
'and Enable the required Event Procedures
'
Private Sub class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"

For Each ctl In frm.Controls
    Select Case ctl.Name
    
    Case "Quantity"
 'Assign Quantity TextBox on Form to 'Qty'
 'Private Property of Class_3_1 Class Module
 
          Set Qty = ctl
        
    'Enable RaiseEvent
        Qty.OnExit = EP
        Qty.OnGotFocus = EP
        Qty.OnLostFocus = EP
'------------------------------        
        Case "UnitPrice"
 'Assign UnitPrice TextBox on Form to 'UPrice'
 'Private Property of Class_3_1 Class Module

    Set UPrice = ctl
    
    'Enable RaiseEvent
        UPrice.OnExit = EP
        UPrice.OnGotFocus = EP
        UPrice.OnLostFocus = EP
'------------------------------    

        Case "TotalPrice"
 'Assign TotalPrice TextBox on Form to 'Total'
 'Private Property of Class_3_1 Class Module
        
            Set Total = ctl
    
    'Enable RaiseEvent
            Total.OnGotFocus = EP
            Total.OnLostFocus = EP
    End Select
Next

End Sub

'--------------------------------------------------------------------- 

Let us check the Code Segmentwise. We declared one general purpose Control Object Ctl and a Constant EP with the text "[Event Procedure]". In the next step the For Each Ctl In Frm.Controls . . . Next check through the controls on the Form one by one and read in the Ctl Control Variable.  We are only interested in three TextBoxes with the name Quantity, UnitPrice, and TotalPrice on the Form. Other TextBoxes, if any, or any other controls on the Form are ignored. So, we need the Select Case Ctl.Name ... Case ... End Select Structure to test the name the Ctl Control represents in each cycle of the For . . . Next Loop to find the required one.

When the Case "Quantity" is found the Ctl-object reference is assigned to the Qty TextBox Instance (Set Qty = Ctl), declared in the global declaration area. The next three statements are enabled, by setting the constant EP ([Event Procedure]) text in all the three Event Procedures (OnExit, OnGotFocus, and OnLostFocus) of the Quantity Textbox on the Form.

This time we took the OnExit Event instead of AfterUpdate. The OnExit Event has some added advantages, if the value entered into the TextBox is not within the allowed range we can keep the I-bar in the same TextBox, after displaying an Error message, by executing the line Cancel = True, till the user enters a valid entry.

The UnitPrice Textbox object is assigned to the UPrice TextBox object instance and enables the same set of Event Procedures.

The TotalPrice TextBox Reference is assigned to the Total TextBox object instance and enables the OnGotFocus and OnLostFocus Events only.

The Quantity TextBox Events

The Qty_Exit, Qty_Gotfocus, and Qty_LostFocus Event Procedures.

'---------------------------------------------------------------------

Private Sub Qty_Exit(Cancel As Integer)
Dim i As Integer, Msg As String
Dim info As Integer

i = Nz(Qty.Value, 0)
If i < 1 Or i > 10 Then
    Msg = "Valid Value Range 1 - 10 Only."
    info = vbCritical
    Cancel = True 'I-bar to stay in the TextBox
Else
    Msg = "Quantity: " & i & " Valid."
    info = vbInformation
End If

MsgBox Msg, vbOK + info, "Quatity_Exit()"

End Sub

Private Sub Qty_GotFocus()
    With Qty
        .BackColor = &H20FFFF
        .ForeColor = 0
    End With
End Sub

Private Sub Qty_LostFocus()
On Error Resume Next
    With Qty
        .BackColor = &HFFFFFF
        .ForeColor = 0
    End With
End Sub

'---------------------------------------------------------------------

In the Qty_Exit() Subroutine three Variables are declared. The i = Nz(Qty.Value,0) statement reads the Quantity value from the Qty TextBox object instance. In the next step, it checks whether the received value is within the valid range of 1 to 10, if not the Msg and Info String Variables are assigned with Message Text for the MsgBox. The Cancel = True ensures that the I-Bar stays in the Quantity TextBox till a valid value is entered.

Next, the GotFocus Event Procedure changes the background Color and LostFocus resets the TextBox Color to its default value.

The UnitPrice TextBox goes through the same process.

 '---------------------------------------------------------------------

Private Sub UPrice_Exit(Cancel As Integer)
Dim i As Single, Msg As String
Dim info As Integer

i = Nz(UPrice.Value, 0)
Msg = ""
If i <= 0 Then
    Msg = "Enter a Value greater than Zero!"
    info = vbCritical
    Cancel = True 'I-bar to stay in the TextBox
End If

If Len(Msg) > 0 Then
    MsgBox Msg, vbOK + info, "UnitPrice_Exit()"
Else
    MsgBox "Unit Price: " & i, vbOK + vbInformation, "UPrice_Exit()"
    
End If
End Sub

Private Sub UPrice_GotFocus()
    With UPrice
        .BackColor = &H20FFFF
        .ForeColor = 0
    End With
End Sub

Private Sub UPrice_LostFocus()
    With UPrice
        .BackColor = &HFFFFFF
        .ForeColor = 0
    End With
End Sub

'---------------------------------------------------------------------
 

The TotalPrice TextBox Event Procedures.

'---------------------------------------------------------------------

Private Sub Total_GotFocus()
    
    With Total
        .BackColor = &H20FFFF
        .ForeColor = 0
    End With
    
    frm!TotalPrice = Qty * UPrice
    frm.TotalPrice.Locked = True
        
End Sub

Private Sub Total_LostFocus()
    With Total
        .BackColor = &HFFFFFF
        .ForeColor = 0
    End With
End Sub

The TotalPrice TextBox is enabled with the GotFocus and LostFocus Events only. The OnGotFocus Event Procedure will change the background Color. Next, the calculated value Qty * UPrice is updated in the TotalPrice TextBox. Immediately after updating the value the TextBox is locked by executing the statement frm.TotalPrice.Locked = True. The OnLostFocus Event resets to its earlier background Color.

We could write the full Event Procedure Code in the standalone Class Module, except the Form_Load() Event Procedure, which works the way we planned.  This is the first time we are implementing Event Procedure Code for more than one Textbox in the standalone Class Module and it works the way we wanted it now. With only three TextBoxes and three Events for each TextBox.

Private Sub Class_Terminate()
    Set Qty = Nothing
    Set UPrice = Nothing
    Set Total = Nothing
End Sub

The Sub Class_Terminate () is a very special Subroutine that executes automatically when the Class Module Class3_1 is in the closing phase and is used for clearing the memory occupied by the Objects.

There is another Subroutine with the name Sub Class_Initialize(), if present in the Class Module, that also executes automatically, when the Class Module is loaded into memory, to instantiate some other Class Object or any other object like Collection, Dictionary in memory. 

But, in totality it is crowded with Code in the Class3_1 Class Module, defining TextBox instances, Enabling Event Procedures, writing Event Procedure codes, and all these actions stuffed into a single Standalone Class Module.  We need to be better organized for handling more than three Instances of the TextBoxes with ease. 

For the Quantity TextBox, there were three Events OnExit, OnGotFocus, and OnLostFocus. All three Event Procedures are written in the Class3_1 Module. The UnitPrice TextBox has 3 Events, and TotalPrice 2 Events. Total 8 separate Event Procedures as we normally write in the Form Class Module. Okay, all of them are taken out from the Form Module and put in the Standalone class module.  But, if we have 15 Textboxes on the Form having 3 Event Procedures each then what we will do?  We will write all 45 Event Procedures on the Form Module, or we will transfer them to the standalone Class Module.

We must find a better way to organize the VBA Code of several other Control types on the Form. We are now considering only the TextBox objects. What about other Controls, like Command Buttons, combo boxes, and others? 

But First, we will concentrate on refining our current Coding approach for our TextBox Objects. 

We will make a detailed study of this aspect next week. When we are familiar with that procedure then we can handle other controls on the Form very easily.

Demo Database Download Link:

Streamlining Form Module Code in Standalone Class Module.

  1. Reusing Form Module VBA Code for New Projects.
  2. Streamlining Form Module Code - Part Two.
  3. Streamlining Form Module Code - Part Three
  4. Streamlining Form Module Code - Part Four
  5. Streamlining Form Module Code - Part Five
  6. Streamlining Form Module Code - Part Six
  7. Streamlining Form Module Code - Part Seven
  8. Streamlining Form Module Code - Part Eight
  9. Streamlining Form Module Code - Part Nine
  10. Streamlining Form Module Code - Part Ten
  11. Streamlining Form Module Code - Part Elevan
  12. Streamlining Report Module Code in Class Module
  13. Streamlining Module Code Report Line Hiding-13.
  14. Streamlining Form Module Code Part-14.
  15. Streamlining Custom Made Form Wizard-15.
  16. Streamlining VBA Custom Made Report Wizard-16.
  17. Streamlining VBA External Files List in Hyperlinks-17
  18. Streamlining Events VBA 3D Text Wizard-18
  19. Streamlining Events VBA RGB Color Wizard-19
  20. Streamlining Events Numbers to Words-20
  21. Access Users Group(Europe) Presentation-21
  22. The Event Firing Mechanism of MS Access-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:

Streamlining Form Module Code Part Four

 Continued from the earlier episodes on Streamlining Form Module VBA Code Part Three.

Links to the earlier Episodes of the above topic are given below for Reference:

  1. Re-using Form Module VBA Coding for New Projects.
  2. Streamlining Form Module Coding Part-Two.
  3. Streamlining Form Module Coding Part-Three.

A Quick Look at Last Week's Session.

A quick look at how the Quantity TextBox object Events were enabled (RaiseEvent) by selecting the "[Event Procedure]" Option from the drop-down list in the Event Property of TextBox. When the Events are fired in the TextBox object instance, they are captured in the Form Module itself and the Event Procedure is executed, as depicted in the graphical image given below. 

When the Quantity TextBox Object's Events are fired on the Form those Events can be captured in the Class1 standalone Class Module also. For doing that we must define an Instance of the TextBox Object and qualify it with the keyword WithEvents in the Class Module. Then the Quantity TextBox object Reference from the Form is assigned to the new object instance.  In this way when the Events of the TextBox are fired in the Form Module they can be captured in the Class1 Class Module also. The same Event Procedures, which we normally write in the Form Module, where the Quantity TextBox Instance resides, can be written in the Class1 Class Module. The Graphic image of these actions is shown below for reference. 

We want the AfterUpdate Event and others to fire from the Quantity Textbox in the Form to check the validity of the value entered into the TextBox to ensure that the criteria range of values between 1 and 10, both included is met.  The GotFocus Event changes the background color of the active TextBox. The LostFocus Event ensures that the background color is reset to its earlier color. The "[Event Procedure]" option selected in all three Event Properties of the Textbox will ensure that these Events will fire (RaiseEvent) at the appropriate Time.

When the Events are fired the Event Procedure Code we write within the empty program stub is executed.  But, in our case, we would like to redirect these Events to a stand-alone Class Module-based Event Procedure. Why, because we want the Form for User Interface designing only and take away the Coding task into separate Class Module(s) for ease of Coding and Maintenance.

To achieve that we must devise a new procedure for the Coding task, which is what we plan to do. VBA beginners may find it difficult to understand the concept fully. The full story of this concept cannot be explained in a few pages, which is why this task has been taken in the form of a Tutoring Session so that each level of concept and practical implementation can be explained in detail.

The new RaiseEvent Procedure.

We can set, the "[Event Procedure]" option in real-time, rather than manually setting, in the AfterUpdate, OnGotFocus, and OnLostFocus, and others in the Event Property for invoking the RaiseEvent action to happen in the Object. To try out that, we have created a new Form (with the name: Form1_RaiseEvent_2), by copying the earlier Form1 and renaming it Form1_RaiseEvent_2, to implement the new method in its Class Module.

Make a copy of Form1 as explained above. Open the new Form in Design View and change the Detail Section background color then display its VBA Module. Copy the following Codes and Paste them over the existing Code to replace them.

Option Compare Database
Option Explicit

'Decare an Object Variable C as Type Class1
Private C As Class1

Private Sub Form_Load()
'Instantiate Class1 Class Object
  Set C = New Class1

'Assign Quantity TextBox Object to C.Txt Property
  Set C.Txt = Me.Quantity
  
'Enable Event Procedures of Quantity TextBox
'This method replaces the empty
'Event Procedure stub used earlier.

    Me.Quantity.AfterUpdate = "[Event Procedure]"
    
    Me.Quantity.OnGotFocus = "[Event Procedure]"
    
    Me.Quantity.OnLostFocus = "[Event Procedure]"
    
End Sub

Private Sub Form_Unload(Cancel As Integer)
'Release Class1 Object instance C from memory
    Set C = Nothing
End Sub

VBA Code Change Review.

As you can see at the end of the Form_Load() Event Procedure we have added three lines of Code to set the "[Event Procedure]" option, in real-time, in their respective Event Property, when the Form is loaded into memory, and will keep it there till the Form is Closed. This approach will eliminate the need for keeping an empty Event Procedure stub on the Form Module to trigger the RaiseEvent Action. 

Save and Close the Form. Open it in Normal View. Try entering a value greater than 10 to test whether the AfterUpdate Event fires and an invalid Quantity message is displayed or not.

Now, try entering a valid value within the range of 1 to 10. All of these tests will work normally as before.

But, again we have made all of these changes on the Form Module itself to capture the action in the Class1 Class Module-based TextBox object instance.  Our main targeted plan is to remove the VBA Coding from the Form Module and capture them into the stand-alone Class Module and do all the coding there, leaving the Form for User Interface design only, except for a few lines of essential Code is unavoidable. 

The real-time RaiseEvent is enabled in the Class1 Module.

Now, we understand the need for the real-time Event enabling Property option setting on the Form and have successfully done that with VBA Code. Let us see how we can implement this method in the stand-alone Class Module rather than Coding in the Form Module.

Make a copy of the Class1 Module and rename it as Class2. We will preserve the Code in the Class1 Class Module to refer them to see what changes we made in the new Copy.  Make a copy of the earlier Form1_RaiseEvent_2 Form and rename it as Form1_RaiseEvent_3.

New Form Module Code Change.

Open the new Form and open its VBA Module. Copy the following Codes and Paste them over the existing Form Module Code.

Option Compare Database
Option Explicit
Private C As Class2

Private Sub Form_Load()
  Set C = New Class2
  Set C.m_Frm = Me 'What is m_Frm member in Class2 Module?
End Sub

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

Save the Form with the new Code. 

Now that we have removed the Me.Quantity.AfterUpdate = "[Event Procedure]" Event enabling lines from the Form Module and these will be run from the new Class2 Class Module. This will retain the earlier Class1 Class Module Code and you can compare what kind of transformation took place in the Class2 Class Module.

The second line in the Form_Load() Event Procedure is new and needs some explaining. As you can see on the left side of the = symbol the Class2 Class Module object C has a Property Procedure with the name m_Frm that demands a Form Object to be passed to it as a parameter. 

On the right side of the = symbol, the Form Object Me is passed to the C.m_Frm property Procedure of the Class2 Class Module. It simply means that we are passing our Form Object to a Property of the same type in the Class2 Class Module. In earlier examples we pass the current Form Object directly to the Form Object Property declared in the Class1 Class Module with Public scope. In the new Class2 Class Module, we declared the Frm object with Private Scope. Form Object assignment to the Frm Property we routed it through the Public Property Procedure so that external Programs cannot change the Frm Property value and will remain safe. It will be clear when we see the new VBA Code of the Class2 Class Module.

Class2 Class Module Code Change.

Open Class2 Class Module (normally the Class Modules will be given some meaningful name, instead of Class1, Class2). Copy the following VBA Code and Paste them overwriting the existing Code in the Module:

Option Compare Database
Option Explicit

Private Frm As Form
Private WithEvents Txt As TextBox

Public Property Get m_Frm() As Form 'GET Property Procedure
    Set m_Frm = Frm
End Property

Public Property Set m_Frm(ByVal mFrm As Form) 'SET Property Procedure

If Left(TypeName(mFrm), 4) = "Form" Then
    Set Frm = mFrm
Else
    MsgBox "Invalid Object " & mFrm & " passed as Form!"
    Exit Property
End If
    
    Call Class_Init 'Run Class_Init() Subroutine
End Property


Private Sub Class_Init()
    Set Txt = frm.Quantity
    
    Txt.AfterUpdate = "[Event Procedure]"
    Txt.OnGotFocus = "[Event Procedure]"
    Txt.OnLostFocus = "[Event Procedure]"
    
End Sub

Private Sub txt_AfterUpdate()
Dim i As Integer, msg As String
Dim info As Integer

i = Nz(Txt.Value, 0)
If i < 1 Or i > 10 Then
    msg = "Valid Value Range 1 - 10 Only."
    info = vbCritical
Else
    msg = "Quantity: " & i & " Valid."
    info = vbInformation
End If

MsgBox msg, vbOK + info, "txt_AfterUpdate()"

End Sub

Private Sub txt_GotFocus()
    With Txt
        .backcolor = &H20FFFF 
        .forecolor = 0
    End With
End Sub

Private Sub txt_LostFocus()
    With Txt
        .backcolor = &HFFFFFF
        .forecolor = 0
    End With
End Sub

Let us check the Code segment-wise to understand them. We are already familiar with the AfterUpdate and other Event Procedures.

Private Frm As Form
Private WithEvents Txt As TextBox

We declared two object Properties in the global declaration area of the Class Module: Frm the Form object Property and the earlier Property Txt the TextBox Object. Both are declared with Private scope here rather than with the Public scope, which we preferred in earlier examples. As I stated earlier the Public scope declaration is not advisable for Class Object Properties because their values can be changed by external programs.

In this case, we should route the Form Object reference through the Property Procedure, as shown in the Code segment given below and we can check the validity of the data passed to the Property Procedure, before assigning it to the Property.

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

Public Property Set m_Frm(ByRef mFrm As Form)

If Left(TypeName(mFrm), 4) = "Form" Then
    Set Frm = mFrm
Else
    MsgBox "Invalid Object " & mFrm & " passed to Form"
    Exit Property
End If

    Call Class_Init 'Run Class_Init() Subroutine
End Property

We are receiving a Form Object in the mFrm Parameter in the Public Property Set m_Frm() Property Procedure. We check the received object Type by using the TypeName() Function and extracting the first four characters of the object name Form_Form1_RaiseEvent_3.  If the first Four Characters match the text 'Form' then it is the correct object for the Frm Property declared in the Global area. The Set Frm = mFrm assigns the Form object to the Property Frm.  

When we want the Form reference in the Frm Property we call for the Public Property Get m_frm() Property Procedure, like Debug.Print m_Frm.Name But, from within the Class2 Class Module, we can refer to the Frm Property directly like Debug.Print Frm.Name

The SET and GET Property Procedures.

Both the GET and SET Property Procedures must have the same name m_Frm, or any suitable name you prefer, and its Data Type is shown as Form Object. If you remember our Form Module entry: Set C.m_Frm = Me passes the Form Object Me as a parameter to the Set Property Procedure in Class2 Class Module. In this Property Set Procedure, we perform a validation check to ensure that the parameter value is a Form Object and then assign it to the Frm Property. The Set Key word is used for Objects only, and the LET keyword is used for other Property Types, like String, Integer, and others.

Note: To know more about Property Procedures and how they work in the Class Module visit an earlier Page: MS Access Class Module And VBA

Once we get the Form Object reference in our Class Module we can easily address the controls on the Form and get their references in the Class Module to work with them. We need the reference of the TextBox with the name Quantity to enable its Events and validate the value entered into the Textbox, by running the Event Procedures in the Class Module. For these actions, we have created a separate Subroutine called Class_Init()

The Class_Init() Subroutine is called from the Property Set m_Frm() Procedure, immediately after assigning the Form (Form_Form1_RaiseEvent_3) reference to the Global Property Frm. 

Private Sub Class_Init()
    Set Txt = Frm.Quantity
    
    Txt.AfterUpdate = "[Event Procedure]"
    Txt.OnGotFocus = "[Event Procedure]"
    Txt.OnLostFocus = "[Event Procedure]"
    
End Sub

In this procedure, we first assigned the Quantity TextBox reference to the Txt TextBox Object Instance declared in the global declaration area of the Class3 Class Module. Check how we address the Quantity Textbox on the Form from Class3 Class Module (Frm.Quantity like Me.Quantity). If you would like to assign some value to the Quantity TextBox on the Form you can do that with the statement Frm!Quantity = 25, from the Class Module.

The next three statements enable the AfterUpdate, OnGotFocus, and OnLostFocus Event Procedures by setting the "[Event Procedure]" Option and setting them dynamically when the Form is Open. Earlier this procedure executed on the Form Module itself is now transferred to the Class Module.

Did you notice the change in the Event Procedure Names? In the Form Module, the AfterUpdate Event Procedure name is written as Private Sub Quantity_AfterUpdate(), the Textbox name as a name prefix follows the Event name, and both these elements are joined with an underscore character. 

In the Class Module, the TextBox object instance declaration name Txt becomes the Event Procedure name prefix: Private Sub Txt_AfterUpdate()

Instead of repeating the text "[Event Procedure]" we can define this as a Constant and use a simple name instead, like:

Private Sub Class_Init()
Const EP = "[Event Procedure]"
    Set Txt = frm.Quantity
    
    Txt.AfterUpdate = EP
    Txt.OnGotFocus = EP
    Txt.OnLostFocus = EP
    
End Sub

Loading Standalone Class Module in Memory.

Note: One important point you should know about is that the standalone Class Module Object cannot load itself into memory like the Form or Report-based Class Modules. 

We have to instantiate the Class Module Object, in any other Module that will be active in memory, like Form Module, or in another stand-alone Class Module that loads into memory first, to use its Properties, Methods, and Events. We already did this in the Form_Load() Event Procedure of the Form, with the following declarations:

Option Compare Database
Option Explicit
Private C As Class2

Private Sub Form_Load()

  Set C = New Class2
  
  Set C.m_Frm = Me 'What is m_Frm member in Class2 Module?
  
End Sub

Coming back to the remaining Code in the Class2 Class Module we are familiar with the Event Procedure Code given below used to validate the Quantity entered into its TextBox on Form.

Private Sub txt_AfterUpdate()
Dim i As Integer, msg As String
Dim info As Integer

i = Nz(Txt.Value, 0)
If i < 1 Or i > 10 Then
    msg = "Valid Value Range 1 - 10 Only."
    info = vbCritical
Else
    msg = "Quantity: " & i & " Valid."
    info = vbInformation
End If

MsgBox msg, vbOK + info, "txt_AfterUpdate()"

End Sub

Private Sub txt_GotFocus()
    With Txt
        .backcolor = &H20FFFF
        .forecolor = 0
    End With
End Sub

Private Sub txt_LostFocus()
    With Txt
        .backcolor = &HFFFFFF
        .forecolor = 0
    End With
End Sub

There is no change in the above Event Procedure Code for the time being. So far we have played around with a single TextBox object on the Form. If you could follow the concept so far then that would be very helpful to go further deep into this concept and to learn more tricks.

Open Form1_RaiseEvent_3 in normal view and try it out by entering a value greater than 10 first, then retry with a new value from the range 1 to 10 to ensure that all three Event Procedures work as planned. 

Event Procedure of all Controls on Form in Standalone Class Module.

If you are getting impatient, bored with the details and explaining, and wondering what this is all about, I will give a sample of the Final Product. Open the Link: WithEvents and all Form Control Types for a sample demo of the final product we plan to implement.

At the end of the Page, you will find a Demo Database Download Link. Download the Demo Database and open it. Open the frmControls_All Form in Design View and then display its Form Class Module. You will find only three or four lines of Code. Open the Form in Normal View, enter some Data in TextBoxes, and select options from ComboBox, ListBox, and other controls on the Form. You will find responses either by displaying the data you entered in the Message Box or on the colored Help text display Label control at the bottom of the Form, indicating that the Events in the Form are tracked and responded to by the Class Module objects in memory. This is only a sample database demonstrating how the Events of the Form-based controls are captured in the Class Module and display messages.

Demo Database Download

Streamlining Form Module Code in Standalone Class Module.

  1. Reusing Form Module VBA Code for New Projects.
  2. Streamlining Form Module Code - Part Two.
  3. Streamlining Form Module Code - Part Three
  4. Streamlining Form Module Code - Part Four
  5. Streamlining Form Module Code - Part Five
  6. Streamlining Form Module Code - Part Six
  7. Streamlining Form Module Code - Part Seven
  8. Streamlining Form Module Code - Part Eight
  9. Streamlining Form Module Code - Part Nine
  10. Streamlining Form Module Code - Part Ten
  11. Streamlining Form Module Code - Part Elevan
  12. Streamlining Report Module Code in Class Module
  13. Streamlining Module Code Report Line Hiding-13.
  14. Streamlining Form Module Code Part-14.
  15. Streamlining Custom Made Form Wizard-15.
  16. Streamlining VBA Custom Made Report Wizard-16.
  17. Streamlining VBA External Files List in Hyperlinks-17
  18. Streamlining Events VBA 3D Text Wizard-18
  19. Streamlining Events VBA RGB Color Wizard-19
  20. Streamlining Events Numbers to Words-20
  21. Access Users Group(Europe) Presentation-21
  22. The Event Firing Mechanism of MS Access-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:

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 Class Module External Links Queries Array msaccess reports Accesstips WithEvents msaccess tips Downloads Objects Menus and Toolbars Collection Object MsaccessLinks Process Controls Art Work Property msaccess How Tos Combo Boxes Dictionary Object ListView Control Query VBA msaccessQuery Calculation 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 RaiseEvent Recordset Top Values Variables Wrapper Classes msaccess email progressmeter Access2007 Copy Excel Export Expression Fields Join Methods Microsoft Numbering System Records Security Split SubForm Table Tables Time Difference Utility WScript Workgroup 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 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