2011-05-09 31 views
6

我有一个包含大量文本框的用户窗体。当这些文本框的值发生变化时,我需要通过调用子程序AutoCalc()根据文本框值重新计算最终结果值。Excel VBA用户窗体 - 当发生某些变化时执行Sub

我有大约25个盒子,我不想为每个调用该子例程的文本框分别添加一个Change()事件。每当某个值发生变化时,调用AutoCalc()的最快和最有效的方法是什么?

回答

14

这可以通过使用类模块来实现。在下面的例子中,我将假设你已经有一个带有一些文本框的用户窗体。

首先,创建您的VBA项目类模块(我们称之为clsTextBox - !一定要更改类模块的“名称”属性)现在

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 

,在用户窗体中添加如下因素代码:

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 
+1

嗨,什么是创建tbCollection的需要?它有什么作用? – Ashok 2011-05-10 02:01:20

+1

集合用于存储和保留创建的文本框对象。 – 2011-05-10 06:30:54

5

看看this了解如何创建一个响应任何文本框中的更改的类。该示例是用于按钮的,但可以修改。但是,请注意Textbox控件没有Exit事件(该事件实际上是用户窗体的一部分),因此您将不得不使用Change事件。

+0

谢谢!它导致我在正确的方向... – Ashok 2011-05-10 02:02:22

6

使用类,如上面的回答表明,这是应对许多控件以简洁和优雅的方式一个很好的策略,但是:

1)我发现用1行创建25个事件没有问题,除非控件的数量是动态的,否则调用普通的用户窗体私有程序。这是一个KISS哲学。

2)一般来说,我认为变化事件非常令人不安,因为他做了每个数字输入的所有重新计算。使用退出事件或事件之前这是更明智和适度做此事件之前更新事件,因为它仅在决定一个值时进行重新计算。举例来说,如果用户没有定义问题,那么我会试图返回响应,消耗资源。

3)出现验证问题。我同意您可以通过更改事件来避免错误密钥,但是如果您需要验证数据,则无法知道用户是否将继续输入或者数据是否准备好进行验证。

4)你应该记住,变化退出事件不会强制用户在文本字段来传递,所以系统需要重新验证并试图退出窗体,不取消时重新计算。

以下代码对于静态表单很简单但有效。

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean) 
Call AutoCalc(Cancel) 
End Sub 

Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean) 
Call AutoCalc(Cancel) 
End Sub 
..... 
Private Sub TextBox25_Exit(ByVal Cancel As MSForms.ReturnBoolean) 
Call AutoCalc(Cancel) 
End Sub 

Private Function Valid 
..... 
End Function 

Private Sub AutoCalc(Canc As Variant) 
If Not Valid() Then Canc=True 
' Calculation 
End Sub 

它你沉迷于节省时间,你可以以产生用于在适合的掩膜的形式与控件事件的代码创建一个通用的VBA程序。此代码可以放在草稿中(直接生成代码比较安全,在某些Excel版本中是错误的),并且可以复制并粘贴到表单模块。

Sub GenerateEvent(Form As String, Mask As String, _ 
    Evento As String, Code As String) 
' Form - Form name in active workbook 
' Mark - String piece inside control name 
' Evento - Event name to form procedure name 
' Code - Code line inside event 
Dim F As Object 
Dim I As Integer 
Dim L As Long 
Dim R As Range 
Dim Off As Long 
Set F = ThisWorkbook.VBProject.VBComponents(Form) 
Set R = ActiveCell ' Destination code 
Off = 0 
For I = 0 To F.Designer.Controls.Count - 1 
    If F.Designer.Controls(I).Name Like "*" & Mask & "*" Then 
     R.Offset(Off, 0) = "Private Sub " & _ 
      F.Designer.Controls(I).Name & "_" & Evento & "()" 
     R.Offset(Off + 1, 0) = "  " & Code 
     R.Offset(Off + 2, 0) = "End Sub" 
     Off = Off + 4 
    End If 
Next I 
End Sub 

Sub Test() 
Call GenerateEvent("FServCons", "tDt", "Exit", _ 
    "Call AtuaCalc(Cancel)") 
End Sub 
0

但是,要知道,TextBox控件没有一个退出事件(该事件实际上是窗体的一部分),所以你真的必须使用更改事件。

我很困惑。也许这是2007年添加的,或者我不明白这些细微差别。我在TextBox控件上使用了Exit事件。当我退出该控件,或者在另一个控件上单击鼠标时,它会触发Exit事件。

0

我有一个类似的问题,我想使用普通例程验证大约48个不同的文本框,并且类模块方法看起来很有趣(很少重复的代码行)。但我不想验证输入的每个字符,我只是想在更新后检查。如果输入的数据无效,我想清除文本框并保留在同一个文本框中,该文本框需要在Exit例程中使用Cancel = True。经过几个小时的尝试,并没有我的AfterUpdate和Exit事件处理程序从来没有触发我发现了为什么。

如果你创建了以下一类:

Private WithEvents MyTextBox As MSForms.TextBox 

Public Property Set** Control(tb As MSForms.TextBox) 

    Set MyTextBox = tb 

End Property 

,然后你进入VBE对象浏览器,选择MyTextBox,你会看到支持不包括更新后或退出枚举事件。如果进入UserForm并使用VBE对象浏览器并查看TextBox的实例,但这些事件似乎是从TextBox所属的控件继承的,则可以使用这些事件。使用MSForms.TextBox定义一个新类不包含这些事件。如果你试图手动定义这些事件处理程序,它们将被编译,看起来它们会工作(但它们不会)。它不是成为类对象的事件处理程序,而只是在VBE对象浏览器下显示在(常规)中的私有子例程,并且从未得到执行。看来创建有效事件处理程序的唯一方法是在VBE对象浏览器中选择类对象,然后从枚举事件列表中选择所需的事件。

经过许多小时的搜索后,我一直无法找到任何引用来展示如何在私有类中构建类似的继承模型,以便AfterUpdate和Exit将显示为创建的类的可用事件。因此,如果您想使用AfterUpdate和/或Exit,那么为UserForm上的每个TextBox单独设置事件处理程序的建议(上文)可能是唯一可行的方法。

0

因此,在论坛中给我的前9行我不记得在哪里。但是我建立在这个基础上,现在我想使用一个命令按钮来重新计算,如果使用改变了这个子部分列出的变量。

<pre><code>'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'</pre></code> 
相关问题