Excel VBA Userform - Execute Sub when something changes

前端 未结 7 461
攒了一身酷
攒了一身酷 2020-12-01 15:09

I have a userform containing lots of text boxes. When ever the values of these text boxes changes, I need to recalculate my end result values based on the textbox values by

相关标签:
7条回答
  • 2020-12-01 15:40

    This can be achieved by using a class module. In the example that follows I will assume that you already have a userform with some textboxes on it.

    Firstly, create a class module in your VBA project (let call it clsTextBox -- be sure to change the 'Name' property of the class module!)

    Private WithEvents MyTextBox As MSForms.TextBox
    
    Public Property Set Control(tb As MSForms.TextBox)
        Set MyTextBox = tb
    End Property
    
    Private Sub MyTextBox_Change()
        AutoCalc() //call your AutoCalc sub / function whenever textbox changes
    End Sub
    

    Now, in the userform, add the folowing code:

    Dim tbCollection As Collection
    
    Private Sub UserForm_Initialize()
        Dim ctrl As MSForms.Control
        Dim obj As clsTextBox
    
        Set tbCollection = New Collection
            For Each ctrl In Me.Controls
                If TypeOf ctrl Is MSForms.TextBox Then
                    Set obj = New clsTextBox
                    Set obj.Control = ctrl
                    tbCollection.Add obj
                End If
            Next ctrl
        Set obj = Nothing
    
    End Sub
    
    0 讨论(0)
  • 2020-12-01 15:47

    Take a look at this for how to create a class that responds to a change in any textbox. The example is for buttons, but can be modified. However, be aware that Textbox controls don't have an Exit event (that event is actually part of the userform) so you really will have to use the Change event.

    0 讨论(0)
  • 2020-12-01 15:51

    I had a similar issue where I want to validate approximately 48 different textboxes using a common routine and the class module approach looked interesting (a lot fewer duplicated lines of code). But I didn't want to validate on every character entered, I only wanted to check after the update. And if the data entered was invalid I wanted to clear the textbox and stay in the same textbox which requires the use of Cancel = True in the Exit routine. After several hours of trying this and not having my AfterUpdate and Exit event handlers never trigger I discovered why.

    If you create a class like the following:

    Private WithEvents MyTextBox As MSForms.TextBox
    
    Public Property Set** Control(tb As MSForms.TextBox)
    
        Set MyTextBox = tb
    
    End Property
    

    and then you go into the VBE object browser and select MyTextBox, you will see the enumerated events supported do not include AfterUpdate or Exit. These events are available if you go into the UserForm and use the VBE object browser and look at an instance of a TextBox, but they appear to be inherited from the Controls that the TextBox is a part of. Defining a new class using MSForms.TextBox does not include those events. If you attempt to define those event handlers manually, they will compile and it appears they would work (but they don't). Instead of becoming event handlers of the class object, they will just be private sub routines that show up in (General) under the VBE object browser and never get executed. It appears the only way to create a valid event handler is to select the class object in the VBE object browser and then select the desired event from the enumerated events list.

    After many hours of searching I've been unable to find any references to show how a similar inheritance model can be constructed within a private class so AfterUpdate and Exit would show up as available events for the created classs. So the recommendation (above) of having a separate event handler for each TextBox on a UserForm, may be the only approach that will work if you want to use AfterUpdate and/or Exit.

    0 讨论(0)
  • 2020-12-01 15:53

    Two Class Module Method

    I've created a very easy way to add event listeners to a userform. Additionally, it adds events such as MouseOver and MouseOut. (Cool for doing hover effects)

    The two class modules that need to be imported in order to work can be found on my Github page VBA Userform Event Listeners


    How to use the code in a Userform

    It's easy to get started, once you add my class modules, simple add the sample code below to a Userform.

    Private WithEvents Emitter As EventListnerEmitter
        
    Private Sub UserForm_Activate()
       Set Emitter = New EventListnerEmitter
       Emitter.AddEventListnerAll Me
    End Sub
    

    That's it! Now you can start listening for different events.


    The Main Event Listener

    There is the main event EmittedEvent. This passes in the control that the event on, and the event name. So all events go through this event handler.

    Private Sub Emitter_EmittedEvent(Control As Object, ByVal EventName As String, EventValue As Variant)
        
        If TypeName(Control) = "Textbox" And EventName = "Change" Then
            'DO WHATEVER
        End If
      
    End Sub
    

    Individual Event Listeners

    You can also just listen for the specific events. o in this case the change event.

    Private Sub Emitter_Change(Control As Object)
    
        If TypeName(Control) = "Textbox" Then
              'DO WHATEVER
        End If
        
    End Sub
    

    Please feel free to check out my Github page and make a pull request as not all the events are being captured yet.

    0 讨论(0)
  • 2020-12-01 15:58

    However, be aware that Textbox controls don't have an Exit event (that event is actually part of the userform) so you really will have to use the Change event.

    I'm confused. Perhaps this was added in 2007, or perhaps I don't understand the nuances. I use the Exit event on TextBox controls. When I Tab out of the control, or click the mouse on another control, it triggers the Exit event.

    0 讨论(0)
  • 2020-12-01 16:04

    So the first 9 lines where given to me in a forum I can't remember where. But I built on that and now I would like to use a command button to re-calculate if the use changes a variable listed in this sub.

    Private Sub txtWorked_Exit(ByVal Cancel As MSForms.ReturnBoolean)
        11 Dim OTRate       As Double
           OTRate = Me.txtHourlyRate * 1.5
        If Me.txtWorked > 40 Then
           Me.txtBasePay.Value = Format(Me.txtHourlyRate.Value * 40, "$#,##0.00")
           Me.txtOvertime = Format((Me.txtWorked - 40) * OTRate, "$#,##0.00")
        Else
           Me.txtOvertime.Value = "0"
           Me.txtBasePay.Value = Format(Me.txtHourlyRate.Value * Me.txtWorked.Value, "$#,##0.00")
        End If
        Dim Gross, W2, MASSTax, FICA, Medi, Total, Depends, Feds As Double
           Gross = CDbl(txtBonus.Value) + CDbl(txtBasePay.Value) +    CDbl(txtOvertime.Value)
           W2 = txtClaim * 19
           Me.txtGrossPay.Value = Format(Gross, "$#,##0.00")
           FICA = Gross * 0.062
           Me.txtFICA.Value = Format(FICA, "$#,##0.00")
           Medi = Gross * 0.0145
           Me.txtMedicare.Value = Format(Medi, "$#,##0.00")
           MASSTax = (Gross - (FICA + Medi) - (W2 + 66)) * 0.0545
        If chkMassTax = True Then
           Me.txtMATax.Value = Format(MASSTax, "$#,##0.00")
        Else: Me.txtMATax.Value = "0.00"
        End If
        If Me.txtClaim.Value = 1 Then
           Depends = 76.8
    
        ElseIf Me.txtClaim.Value = 2 Then
           Depends = 153.8
    
        ElseIf Me.txtClaim.Value = 3 Then
           Depends = 230.7
        Else
           Depends = 0
        End If
           If (Gross - Depends) < 765 Then
           Feds = ((((Gross - Depends) - 222) * 0.15) + 17.8)
           Me.txtFedIncome.Value = Format(Feds, "$#,##.00")
        ElseIf (Gross - Depends) > 764 Then
           Feds = ((((Gross - Depends) - 764) * 0.25) + 99.1)
           Me.txtFedIncome.Value = Format(Feds, "$#,##.00")
        Else:
           Feds = 0
        End If
           Total = (txtMATax) + (FICA) + (Medi) + (txtAdditional) + (Feds)
           Me.txtTotal.Value = Format(Total, "$#,##0.00")
           Me.txtNetPay.Value = Format(Gross - Total, "$#,##0.00")
    
    End Sub
    
    Private Sub cmdReCalculate_Click()
    
    End Sub
    
    0 讨论(0)
提交回复
热议问题