Subject: event triggered when any control changed
Posted By: hoen Post Date: 1/21/2006 12:25:53 PM
I have an Access Form with many text boxes on the form: named "Text1", "Text2"....."Text80", etc.  Is it possible to have a single event triggered when any of the text boxes is changed?  I am trying to avoiding writing 80+ event handlers for each of the boxes.  In other words, could a single event sense when anyone of the 80 boxes has been changed, and then pass the appropriate information as to which control has changed?
Thanks


Reply By: Bob Bedell Reply Date: 1/21/2006 3:53:44 PM
Hi hoen,

The concept you are describing is known as a control array. Technically speaking, VB is the only MS language that provides intrinsic support for them. If you place 3 TextBox controls on a VB form and give them all the same name, VB initializes them in an array of TextBoxes as:

Textbox(0)
Textbox(1)
Textbox(2)

You can then do stuff like:

Private Sub TextBox_Change(Index As Integer)
    ‘ The textbox that fired the event gets its text property changed.
    TextBox (Index).Text = "something"
End Sub


Or:

For i = 1 To 6
     TextBox(i).Text = "TextBox"
Next


VB.Net abandoned this type of control array, but replaces it with the ability to create event handlers that handle multiple control events with syntax like:

Private Sub AllControlButtins_Click( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
      Handles Button1.Click, _
                  Button2.Click, _
                 Button3.Click


The "sender" argument identifies the control that fired the event.

VBA doesn't support control arrays either. To emulate this behavior, however, you can to design a class to store a reference to each TextBox control on your form in a module level object variable declared WithEvents. You declare and event handler in the class, like:

Private Sub m_TextBox_Change()

Then give the event handler the ability to notify a specific textbox’s parent form which textbox fired the event by calling a method on the form.

This is as close as you can get in VBA to having a single event sink for multiple controls:




=====================================================
Module For frmParent:

frmParent  has two TextBox controls on it:  txtTextBox1 and txtTextbox2
=====================================================

Option Compare Database

' Collection to store instances of clsTextBox
Private colTextBoxes As New Collection
Private ctlTextBox As clsTextBox

Public Sub WhichTextBox(ctl As TextBox)
    ' Notify user which TextBox fired the change event.
    MsgBox "ChangeEvent fired by: " & ctl.Name
End Sub

Private Sub Form_Load()
    
    ' Instantiate new clsTextBox objects.
    ' Each object holds a reference to one
    ' of the textbox controls on the form
    ' and a reference to the controls parent
    ' form. The newly instantiatied object is
    ' then added to the colTextBoxes collection.
    
    ' Create a class to reference txtTextBox1
    Set ctlTextBox = New clsTextBox
    ctlTextBox.Initialize txtTextBox1, Me
    colTextBoxes.Add ctlTextBox
    
    ' Create a class to reference txtTextBox2
    Set ctlTextBox = New clsTextBox
    ctlTextBox.Initialize txtTextBox2, Me
    colTextBoxes.Add ctlTextBox
    
End Sub

=====================================================
Class Module for clsTextBox:

Sinks Change events for all clsTextBox objects
=====================================================

Option Compare Database
Option Explicit

' Holds a reference to one TextBox control on
' frmParent. It is declared WithEvents so it
' can sink events for the TextBox control it
' refers to.
Private WithEvents m_TextBox As TextBox
Private Const mstrEventProcedure = "[Event Procedure]"

' Holds a reference to the parent form that hosts
' the TextBox control.
Private m_Form As Form_frmParent

' Initialize a new clsTextBox object, passing it
' references to a TextBox control and its parent
' form
Public Sub Initialize(ctl As TextBox, frm As Form_frmParent)
    Set m_TextBox = ctl
    
    'Set the OnChange property of the control to [Event Procedure]
     m_TextBox.OnChange = mstrEventProcedure
    Set m_Form = frm
End Sub

' This event handler will handle the Change event for each
' TextBox control referenced by a clsTextBox object in
' colTextBoxes.
Private Sub m_TextBox_Change()
    
    ' Call the WhichTextBox method of the parent form, passing
    ' it the instance of the TextBox that fired the Change event.
    m_Form.WhichTextBox m_TextBox
    
End Sub

' Garbage collection.
Private Sub Class_Terminate()
    Set m_TextBox = Nothing
    Set m_Form = Nothing
End Sub


NOTE:

You can also use a controls Tag Property to emulate some control array type behavior:

Private Sub cmdButton1_Click()
    Dim ctl As Control
    Dim i As Integer

    For Each ctl In Me.Controls
        If Not ctl.Tag = "" Then
            For i = 1 To 3 ' Effects controls whose tag property is in the range 1 to 3
                If ctl.Tag = i Then
                    ctl.Enabled = True
                End If
            Next
        End If
    Next

End Sub


HTH,

Bob


Reply By: Bob Bedell Reply Date: 1/21/2006 4:33:20 PM
Hi hoen,

Now change your Form_Load event to:

Private Sub Form_Load()
    
    ' Instantiate new clsTextBox objects.
    ' Each object holds a reference to one
    ' of the textbox controls on the form
    ' and a reference to the controls parent
    ' form. The newly instantiatied object is
    ' then added to the colTextBoxes collection.

    Dim ctl As Control
    Dim i As Integer

    For Each ctl In Me.Controls
        With ctl
            If .ControlType = acTextBox Then
                Set ctlTextBox = New clsTextBox
                ctlTextBox.Initialize ctl, Me
                colTextBoxes.Add ctlTextBox
            End If
        End With
    Next ctl
    
End Sub


And you won't have to cut and paste the following 80 times:


   ' Create a class to reference txtTextBox1
    Set ctlTextBox = New clsTextBox
    ctlTextBox.Initialize txtTextBox1, Me
    colTextBoxes.Add ctlTextBox


The loop will wire each TextBox control to the class event sink.

HTH,

Bob

Reply By: Bob Bedell Reply Date: 1/21/2006 4:59:59 PM
There are a lot of variations on this theme, of course. For example you can also create a form class with a ClassFactory method and have it generate all your TextBox classes, or you can just have the TextBox class itself modify a property of the TextBox without having the event sink call back to a method on the parent form, etc....

What do you want your generic Change event to do?

Bob

Reply By: SerranoG Reply Date: 1/24/2006 7:58:30 AM
You don't need to use each textbox's After Update event nor access an array.  Simply put code on the FORM's After Update Event.

Greg Serrano
Michigan Dept. of Environmental Quality, Air Quality Division
Reply By: kindler Reply Date: 1/24/2006 10:26:43 AM
If the After Update event doesn't work, On Dirty might.

Reply By: Bob Bedell Reply Date: 1/24/2006 11:57:30 AM
Unfortunately, the form's After_Update event won't fire until the text box value is updated (i.e., the record is saved). It won't fire if you simply tab to a textbox and type a new value in the textbox. Only the textboxes Change event will repond to typing the new value.

The Dirty event won't help either. It will fire the first time you enter a new value in a textbox, but only for the first textbox. It won't fire for chnges made to subsequent textboxes. The dirty event juts sets a booleann value the first time a form is dirtied.

There isn't anyway to make Form events emulate a single Change event that all TextBoxes on a form are wired to. You need to use the Class approach.

Bob

Reply By: Bob Bedell Reply Date: 1/24/2006 12:44:20 PM
Of course, I'm assuming that since hoen's post includes the phrases:

"...when any control changed."
"...when any of the text boxes is changed?"
"...when any one of the 80 boxes has changed..."
"...as to which control has changed?"

that what he wants is a single textbox Change event and not a textbox AfterUpdate event.

Bob




Go to topic 39121

Return to index page 387
Return to index page 386
Return to index page 385
Return to index page 384
Return to index page 383
Return to index page 382
Return to index page 381
Return to index page 380
Return to index page 379
Return to index page 378