2011-05-09 30 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> 
相關問題