Single Master Event Handler for Multiple Click Events that target same sub

mwtinec

New Member
Joined
Aug 21, 2013
Messages
18
Apologies in advance for the long post. I think this is a relatively easy question for you more experienced VBA folks out there. I've been teaching myself VBA over the last few weeks and have learned a quite a bit from this forum and others. Thank You!!


I need some help taking the next step. I created a financial planning userform with so many controls (1200 textboxes and comboboxes across 10 multi tabs) that I eventually got the "out of memory" error. So I've now broken my giant userform into 10 smaller userforms with the corresponding subs moved into the appropriate forms. These subs calculate income vs savings vs expenses vs investing etc.


All my control events point to a specific sub based on what category it belongs to, e.g. income controls point to incomecalc(), etc. There's a LOT of duplication and it creates lots of clutter (which I hate scrolling through). With so many controls I started looking for a solution that allowed all the control events that point to a unique sub to be handled by a single master event handler, e.g. 1200 events collapsed into 10 "master" event handlers each pointing to one sub.

After much searching and reading I found a solution, i.e. creating a class (thanks J. Walkenbach whoever you are!). His simple version works. I've now started modifying it and now I'm stuck. Here's his simple solution (Handling Multiple UserForm Controls with One Event Handler - Excel 2007 Power Programming with VBA (Mr. Spreadsheets Bookshelf)).


This simple solution basically just calls the class which executes a msgbox line of code located in the class module. My need is a bit more complicated. I need my solution to a) call the class anytime a textbox or combo box is changed on my userform and b) execute a calculation sub that updates values on the userform that initiated the handling call. It is part b that has me stumped. After researching I've think the issue is that the calculation sub might need to be in a standard module (effectively creating a three modul solution of userform module + class module + standard module. I've made a few attempts at creating the "userform-->class module-->standard module-->userform" roundtrip to no avail. Help!



  1. First, how should the solution be architected?
  2. Second, how to I combine the texbox and combobox variants into a single class solution?
  3. Third, how do I call the various objects to work in harmony?


Here's my code.
Userform Module
Code:
Public EnableEvents As Boolean
Dim TB() As New tbClass

Private Sub UserForm_Initialize()
'load Paycheck helper form with gross pay from formIncome Adult 1
'Does not work
   
    Dim tbCount As Integer
    Dim ctl As Control
    
    ' Create the Textbutton objects
    tbCount = 0
    For Each ctl In formIncome.Controls
    If TypeName(ctl) = "TextBox" Then
    ReDim Preserve TB(1 To tbCount)
    Set TB(tbCount).tbGroup = ctl
    End If
    
    Next ctl

me.EnableEvents = True
End Sub

Private Sub IncomeCalc()
'Sums up all income streams for both adults. 
'Works


Me.EnableEvents = False


    Dim MonthlyTakeHomePay As Double
    Dim AnnualTakeHomePay As Double
    Dim AnnualTakeHomeBonus As Double
    Dim AnnualOtherIncome As Double
    Dim AnnualIncomeTaxIncome As Double
    Dim AnnualAdult1Income As Double

    On Error Resume Next
    For a = 1 To 2 'adult
    For j = 1 To 4 'job
    MonthlyTakeHomePay = Me.Controls("tbIncomeJob" & j & "Adult" & a & "MTakeHome_0").Value
    AnnualTakeHomePay = MonthlyTakeHomePay * 12
    AnnualTakeHomeBonus = Me.Controls("tbIncomeJob" & j & "Adult" & a & "Bonus_0").Value
    AnnualOtherIncome = Me.Controls("tbIncomeJob" & j & "Adult" & a & "Other_0").Value
    AnnualIncomeTaxIncome = Me.Controls("tbIncomeJob" & j & "Adult" & a & "Tax_0").Value
    
    AnnualAdultIncome = AnnualTakeHomePay + AnnualTakeHomeBonus + AnnualOtherIncome + AnnualIncomeTaxIncome
    
    Me.Controls("tbIncomeJob" & j & "Adult" & a & "Annual_0").Value = AnnualAdultIncome
    


    Next j
    Next a


Me.EnableEvents = True
End Sub

Here's the class module code
Code:
Public WithEvents tbGroup As MsForms.TextBox
'This class should excute code on formIncome or subIncomeCalc, whichever is determined as the go forward solution


Private Sub tbGroup_Click()
Run subIncomeCalc
End Sub
 

Excel Facts

Copy PDF to Excel
Select data in PDF. Paste to Microsoft Word. Copy from Word and paste to Excel.
So, after much deliberation and fiddling I have a workable solution. It's not perfect but I'll post it here so I can have that warm fuzzy of FINALLY giving back to this community and not just taking!:)

So the aha moment came when I figured out that I needed to put the userform code I wanted run when a change event happened INTO THE CLASS MODULE that managed the change events. Is this the best solution? Who knows but it has reduced my code by a lot (# of change events x # of controls).

Userform Module
Code:
Dim Events1() As New classIncomeEvents 'name of the class module
Dim Events2() As New classIncomeEvents
Dim Events3() As New classIncomeEvents


Private Sub UserForm_Initialize()
Dim EventsCount1 As Integer
Dim ctla As Control


' Create the TEXTBOX objects
EventsCount1 = 0
For Each ctla In formPaycheckAdult1.Controls[INDENT]If TypeName(ctla) = "TextBox" Then[/INDENT]
[INDENT=2]EventsCount1 = EventsCount1 + 1[/INDENT]
[INDENT=2]ReDim Preserve Events1(1 To EventsCount1)
Set Events1(EventsCount1).IncomeTBGroup = ctla[/INDENT]
[INDENT]'End If[/INDENT]
Next ctla


Dim EventsCount2 As Integer
Dim ctlb As Control

' Create the COMBOBOX objects
EventsCount2 = 0
For Each ctlb In formPaycheckAdult1.Controls[INDENT]If TypeName(ctlb) = "ComboBox" Then
[/INDENT]
[INDENT=2]EventsCount2 = EventsCount2 + 1
ReDim Preserve Events2(1 To EventsCount2)
Set Events2(EventsCount2).IncomeCBGroup = ctlb[/INDENT]
[INDENT]End If[/INDENT]
[INDENT]Next ctlb[/INDENT]


Dim EventsCount3 As Integer
Dim ctlc As Control

' Create the CHECKBOX objects
EventsCount3 = 0
For Each ctlc In formPaycheckAdult1.Controls[INDENT]If TypeName(ctlc) = "CheckBox" Then
[/INDENT]
[INDENT=2]EventsCount3 = EventsCount3 + 1
ReDim Preserve Events3(1 To EventsCount3)
Set Events3(EventsCount3).IncomeCKBGroup = ctlc[/INDENT]
[INDENT]End If[/INDENT]
Next ctlc

'a combo box set of pay options
cbPayFrequencyAdult1_txt.AddItem "Once"
cbPayFrequencyAdult1_txt.AddItem "twice"
cbPayFrequencyAdult1_txt.AddItem "weekly"


End Sub

Here's the Class Module Code
Code:
Public WithEvents IncomeCBGroup As MsForms.ComboBox
Public WithEvents IncomeTBGroup As MsForms.TextBox
Public WithEvents IncomeCKBGroup As MsForms.CheckBox


Private Sub IncomeTBGroup_change()[INDENT]PaycheckCalc[/INDENT]
End Sub

Private Sub IncomeCBGroup_change()[INDENT]PaycheckCalc[/INDENT]
End Sub

Private Sub IncomeCkBGroup_change()[INDENT]PaycheckCalc[/INDENT]
End Sub


Private Sub PaycheckCalc()
On Error Resume Next

'PRE-TAX
Dim Medical As Integer
Dim Dental As Integer
Dim Vision As Integer
Dim k401 As Integer
Dim AccDeath As Integer
Dim DepCare As Integer
Dim hearing As Integer
Dim HSA As Integer
Dim OtherPreTax As Integer
Dim PreTaxSub As Integer


    'Check to see if item values should be used
    If formPaycheckAdult1.Controls("ChkUsePreTaxItems").Value = True Then
        Medical = formPaycheckAdult1.Controls("txtMedical1_0").Value
        Dental = formPaycheckAdult1.Controls("txtDental1_0").Value
        Vision = formPaycheckAdult1.Controls("txtVision1_0").Value
        k401 = formPaycheckAdult1.Controls("txt401kContribution1_0").Value
        AccDeath = formPaycheckAdult1.Controls("txtAccidentalDeath1_0").Value
        DepCare = formPaycheckAdult1.Controls("txtDependentCare1_0").Value
        hearing = formPaycheckAdult1.Controls("txtHearing1_0").Value
        HSA = formPaycheckAdult1.Controls("txtHSA1_0").Value
        OtherPreTax = formPaycheckAdult1.Controls("txtOtherPreTax1_0").Value
        PreTaxSub = Medical + Dental + Vision + k401 + AccDeath + DepCare + hearing + HSA + OtherPreTax
        formPaycheckAdult1.Controls("tbPreTaxSubTotalAdult1_0").Value = PreTaxSub
    End If
'this sub goes on for a while but you get the picture

I couldn't figure out how to collapse the textbox, combobox and checkbox code in the userform module. If someone wants to show that I'd appreciate it. Also, there doesn't appear to be an afterupdate even option with class modules.

Hope this helps.
 
Last edited:
Upvote 0
Hi, mwtinec

> Also, there doesn't appear to be an afterupdate even option with class modules.
It is possible to receive Enter / Exit / BeforeUpdate / AfterUpdate events in the class module,
although it involves difficult processing. Please refer to the explanation below.

[ Implementation of the event handling by API : ConnectToConnectionPoint ]
http://addinbox.sakura.ne.jp/Breakthrough_P-Ctrl_Arrays_Eng_ref.htm#C2CP

As a reusable tool including Click and Change events etc, I have released a class module named clsBpca.
With this class module, the processing method "control array" can also be used in VBA.
http://addinbox.sakura.ne.jp/Breakthrough_P-Ctrl_Arrays_Eng_ref.htm

Sample macros of some usage examples are introduced below.
http://addinbox.sakura.ne.jp/Breakthrough_P-Ctrl_Arrays_Eng.htm#Chap5-1
Since I posted it on this forum several times, you can find it by searching for "clsBpca".


From your explanation, I did not understand what layout your UserForm is.

Is it like the following?
Image4.gif



Code:
=== UserForm module ===

Private WithEvents Medical As clsBpca   ' Do not use "New" keyword.
Private WithEvents Dental As clsBpca
Private WithEvents Vision As clsBpca
  :
Private WithEvents Other As clsBpca
'------------------------------------------------------------
Private Sub UserForm_Initialize()
Dim j As Integer

    Set Medical = New clsBpca    ' Create Instance
    Set Dental = New clsBpca
    Set Vision = New clsBpca
     :
    Set Other = New clsBpca

    For [COLOR=#ff0000]j = 1 To ???[/COLOR]
      Medical.Add  Me.Controls("txtMedical" & [COLOR=#ff0000]j[/COLOR] & "_0")
      Dental.Add   Me.Controls("txtDental" & [COLOR=#ff0000]j[/COLOR] & "_0")
      Vision.Add   Me.Controls("txtVision" & [COLOR=#ff0000]j[/COLOR] & "_0")
        :
      Other.Add    Me.Controls("txtOtherPreTax" & [COLOR=#ff0000]j[/COLOR] & "_0")
    Next j

    Medical.Rgst  BPCA_EnterExit
    Dental.Rgst  BPCA_EnterExit
    Vision.Rgst  BPCA_EnterExit
       :
    Other.Rgst  BPCA_EnterExit
End Sub
'------------------------------------------------------------
Private Sub UserForm_Terminate()
    Medical.Clear
    Dental.Clear
    Vision.Clear
      :
    Other.Clear

    Set Medical = Nothing
    Set Dental = Nothing
    Set Vision = Nothing
      :
    Set Other = Nothing
End Sub
'------------------------------------------------------------
Private Sub Medical_OnEnter(ByVal Index As Integer)
    Medical.ItmTxt(Index).BackColor = &HFFFFE0    'LightCyan
End Sub
'------------------------------------------------------------
Private Sub Medical_OnExit(ByVal Index As Integer, _
                      ByVal Cancel As MSForms.ReturnBoolean)
    If (Medical.ItmTxt(Index).Value = "") Then
        'Empty is OK
    ElseIf IsNumeric(Medical.ItmTxt(Index).Value) Then
        'Numeric is OK
    Else
        'Not Numeric is Error
        Medical.ItmTxt(Index).BackColor = &HCCCCFF    'Light Red
        Beep
        Cancel = True
        Exit Sub
    End If

    Medical.ItmTxt(Index).BackColor = vbWindowBackground

    [B]Call CalcSubTotal(Index)[/B]
End Sub
'------------------------------------------------------------
Private Sub Dental_OnEnter(ByVal Index As Integer)
    Dental.ItmTxt(Index).BackColor = &HFFFFE0    'LightCyan
End Sub
'------------------------------------------------------------
Private Sub Dental_OnExit(ByVal Index As Integer, _
                      ByVal Cancel As MSForms.ReturnBoolean)
    If (Dental.ItmTxt(Index).Value = "") Then
        'Empty is OK
    ElseIf IsNumeric(Dental.ItmTxt(Index).Value) Then
        'Numeric is OK
    Else
        'Not Numeric is Error
        Dental.ItmTxt(Index).BackColor = &HCCCCFF    'Light Red
        Beep
        Cancel = True
        Exit Sub
    End If

    Dental.ItmTxt(Index).BackColor = vbWindowBackground

    Call CalcSubTotal(Index)
End Sub
'------------------------------------------------------------

Below, the same, so skip (Vision, ... ,Other)

'------------------------------------------------------------
Private Sub [B]CalcSubTotal[/B](ByVal Index As Integer)
Dim dblMedical As Double
Dim dblDental As Double
Dim dblVision As Double
  :
Dim dblOther As Double

  'In the case of empty characters, 
  'zero of the initial value remains due to error skipping.
  On Error Resume Next
  dblMedical = CDbl(Medical.ItmTxt(Index).Value)
  dblDental = CDbl(Medical.ItmTxt(Index).Value)
  dblVision = CDbl(Medical.ItmTxt(Index).Value)
   :
  dblOther = CDbl(Medical.ItmTxt(Index).Value)
  On Error GoTo 0

  Me.Controls("tbPreTaxSubTotalAdult" & [COLOR=#ff0000]Index[/COLOR] & "_0").Value = _
          dblMedical + dblDental + dblVision + ... + dblOther
End Sub

There is no need to change the imported class module.
 
Upvote 0
Thanks Tsunoda for the reply. Your skills are obviously way above mine (I'm not a programmer and have been teaching myself VBA for only a few weeks). I'll play around with the clsBpca and see if I can get it working. I like the error handling especially. Regarding the userform. The controls on my userform are unique in the since that there are no repeating control names, e.g. only one instance of medical, dental, etc. If I knew how to post a screenshot I could do that. If you'll walk me through the steps I'll post it here shortly thereafter. Thanks again.
 
Upvote 0

Forum statistics

Threads
1,215,321
Messages
6,124,239
Members
449,149
Latest member
mwdbActuary

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top