VBA-在动态创建的文本框上捕获事件

VBA-在动态创建的文本框上捕获事件,vba,excel,events,userform,Vba,Excel,Events,Userform,我正在用Excel编写一个VBA应用程序。我有一个Userform,它根据其中一个工作表中包含的数据动态构建自己。 创建各种组合框、文本框和标签的所有代码都在工作。 我创建了一个类模块来捕获组合框的OnChange事件,同样,它也能按预期工作。 现在,我需要为一些文本框捕获OnChange事件,因此我创建了一个新的类模块,用于为组合框捕获事件 Public WithEvents tbx As MSForms.TextBox Sub SetTextBox(ctl As MSForms.TextB

我正在用Excel编写一个VBA应用程序。我有一个Userform,它根据其中一个工作表中包含的数据动态构建自己。 创建各种组合框、文本框和标签的所有代码都在工作。 我创建了一个类模块来捕获组合框的OnChange事件,同样,它也能按预期工作。 现在,我需要为一些文本框捕获OnChange事件,因此我创建了一个新的类模块,用于为组合框捕获事件

Public WithEvents tbx As MSForms.TextBox

Sub SetTextBox(ctl As MSForms.TextBox)
Set tbx = ctl
End Sub

Public Sub tbx_Change()
Dim LblName As String

MsgBox "You clicked on " & tbx.Name, vbOKOnly

End Sub
消息框只是为了在我继续之前确认它是否工作。 我遇到的问题是UserForm代码模块:

Dim TBox As TextBox
Dim tbx As c_TextBoxes

'[...]

Set TBox = lbl
Set tbx = New c_TextBoxes
tbx.SetTextBox lbl
pTextBoxes.Add tbx
这会在
Set TBox=lbl
处引发类型不匹配错误。正是精确的相同的代码可以很好地用于组合框,只需为变量指定适当的名称即可。我已经盯着这个看了两个小时了。 有人有什么想法吗?谢谢你的指点

编辑-以下是我遇到问题的完整userform模块:

Private Sub AddLines(FrameName As String, PageName As String)
Dim Counter As Integer, Column As Integer
Dim obj As Object
Dim CBox As ComboBox
Dim cbx As c_ComboBox
Dim TBox As TextBox
Dim tbx As c_TextBoxes
Dim lbl As Control

Set obj = Me.MultiPage1.Pages(PageName).Controls(FrameName)
If pComboBoxes Is Nothing Then Set pComboBoxes = New Collection
If pTextBoxes Is Nothing Then Set pTextBoxes = New Collection

For Counter = LBound(Vehicles) To UBound(Vehicles)
     For Column = 1 To 8
     Select Case Column
     Case 1
         Set lbl = obj.Add("Forms.Label.1", "LblMachine" & FrameName & Counter, True)
    Case 2
        Set lbl = obj.Add("Forms.Label.1", "LblFleetNo" & FrameName & Counter, True)
    Case 3
        Set lbl = obj.Add("Forms.Label.1", "LblRate" & FrameName & Counter, True)
    Case 4
        Set lbl = obj.Add("Forms.Label.1", "LblUnit" & FrameName & Counter, True)
    Case 5
        Set lbl = obj.Add("Forms.ComboBox.1", "CBDriver" & FrameName & Counter, True)
    Case 6
        Set lbl = obj.Add("Forms.Label.1", "LblDriverRate" & FrameName & Counter, True)
    Case 7
        Set lbltbx = obj.Add("Forms.TextBox.1", "TBBookHours" & FrameName & Counter, True)
    Case 8
        Set lbl = obj.Add("Forms.Label.1", "LblCost" & FrameName & Counter, True)
    End Select
    With lbl
        Select Case Column
        Case 1
            .Left = 1
            .Width = 60
            .Top = 10 + (Counter) * 20
            .Caption = Vehicles(Counter).VType
        Case 2
            .Left = 65
            .Width = 40
            .Top = 10 + (Counter) * 20
            .Caption = Vehicles(Counter).VFleetNo
        Case 3
            .Left = 119
            .Width = 50
            .Top = 10 + (Counter) * 20
            .Caption = Vehicles(Counter).VRate
        Case 4
            .Left = 163
            .Width = 30
            .Top = 10 + (Counter) * 20
            .Caption = Vehicles(Counter).VUnit
        Case 5
            .Left = 197
            .Width = 130
            .Top = 10 + (Counter) * 20
            Set CBox = lbl 'WORKS OK
            Call CBDriver_Fill(Counter, CBox)
            Set cbx = New c_ComboBox
            cbx.SetCombobox CBox
            pComboBoxes.Add cbx
        Case 6
            .Left = 331
            .Width = 30
            .Top = 10 + (Counter) * 20
        Case 7
            .Left = 365
            .Width = 30
            .Top = 10 + (Counter) * 20
            Set TBox = lbl 'Results in Type Mismatch
            Set tbx = New c_TextBoxes
            tbx.SetTextBox TBox
            pTextBoxes.Add tbx
        Case 8
            .Left = 400
            .Width = 30
            .Top = 10 + (Counter) * 20
        End Select
    End With
    Next
Next
obj.ScrollHeight = (Counter * 20) + 20
obj.ScrollBars = 2

End Sub
下面是c_Combobox类模块:

Public WithEvents cbx As MSForms.ComboBox

Sub SetCombobox(ctl As MSForms.ComboBox)
    Set cbx = ctl
End Sub

Public Sub cbx_Change()
Dim LblName As String
Dim LblDriverRate As Control
Dim i As Integer


    'MsgBox "You clicked on " & cbx.Name, vbOKOnly
    LblName = "LblDriverRate" & Right(cbx.Name, Len(cbx.Name) - 8)
    'MsgBox "This is " & LblName, vbOKOnly

    'Set obj = Me.MultiPage1.Pages(PageName).Controls(FrameName)
    Set LblDriverRate = UFBookMachines.Controls(LblName)
    For i = LBound(Drivers) To UBound(Drivers)
        If Drivers(i).Name = cbx.Value Then LblDriverRate.Caption = Drivers(i).Rate
    Next
End Sub
Public WithEvents tbx As MSForms.TextBox

Sub SetTextBox(ctl As MSForms.TextBox)
    Set tbx = ctl
End Sub

Public Sub tbx_Change()
Dim LblName As String
    'Does nothing useful yet, message box for testing
    MsgBox "You clicked on " & tbx.Name, vbOKOnly

End Sub
最后,这里是c_文本框类模块:

Public WithEvents cbx As MSForms.ComboBox

Sub SetCombobox(ctl As MSForms.ComboBox)
    Set cbx = ctl
End Sub

Public Sub cbx_Change()
Dim LblName As String
Dim LblDriverRate As Control
Dim i As Integer


    'MsgBox "You clicked on " & cbx.Name, vbOKOnly
    LblName = "LblDriverRate" & Right(cbx.Name, Len(cbx.Name) - 8)
    'MsgBox "This is " & LblName, vbOKOnly

    'Set obj = Me.MultiPage1.Pages(PageName).Controls(FrameName)
    Set LblDriverRate = UFBookMachines.Controls(LblName)
    For i = LBound(Drivers) To UBound(Drivers)
        If Drivers(i).Name = cbx.Value Then LblDriverRate.Caption = Drivers(i).Rate
    Next
End Sub
Public WithEvents tbx As MSForms.TextBox

Sub SetTextBox(ctl As MSForms.TextBox)
    Set tbx = ctl
End Sub

Public Sub tbx_Change()
Dim LblName As String
    'Does nothing useful yet, message box for testing
    MsgBox "You clicked on " & tbx.Name, vbOKOnly

End Sub

经过一些快速测试,如果我将
TBox声明为TextBox
,我就能够重现您的错误。如果将
TBox声明为MSForms.TextBox
,则不会出现错误。我建议使用
MSForms
限定符声明所有
TextBox
变量

测试代码与您的测试代码类似。我有一个
多页
,其中有一个
框架
,我正在添加一个
控件

Private Sub CommandButton1_Click()

    Dim obj As Object
    Set obj = Me.MultiPage1.Pages(0).Controls("Frame1")

    Dim lbl As Control
    Set lbl = obj.Add("Forms.TextBox.1", "txt", True)

    If TypeOf lbl Is TextBox Then
        Debug.Print "textbox found1" 'does not execute
    End If

    If TypeOf lbl Is MSForms.TextBox Then
        Debug.Print "textbox found2"

        Dim txt1 As MSForms.TextBox
        Set txt1 = lbl 'no error
    End If

    If TypeOf lbl Is MSForms.TextBox Then
        Debug.Print "textbox found3"

        Dim txt As TextBox
        Set txt = lbl 'throws an error
    End If

End Sub

我不确定为什么
TextBox
ComboBox
需要限定符。正如您在上面所看到的,一个很好的测试是
If-TypeOf。。。是然后
测试哪些对象是哪些类型。我包括了第一个块,以表明
lbl
不是一个“裸的”
TextBox
,但我也不知道为什么会这样。可能还有另一种类型的
TextBox
覆盖默认声明?

哪里定义了
lbl
,类型是什么?这段代码也在哪里运行?变量声明可以在UserForm代码模块中,但是
集合和下面的行呢?它们是在事件中执行还是在其他地方执行?Lbl在userform模块中定义。第二个代码段也在userform模块中。我忘了说ptextbox是一个集合。正如我所提到的,所有这些都可以完美地用于组合框,代码运行在同一个userform模块中,这就是为什么我不理解文本框为什么会有问题。谢谢你的帮助。如果看不到更完整的代码图,就有点难说了<代码>类型不匹配
应该通过添加断点并查看正在处理的类型来轻松调试。我假设
lbl
被声明为
文本框
,以便类型匹配
TBox
?归根结底,
lbl
不是您所认为的,或者您在上面错误地定义了一个变量。调试将告诉您前者。如果是完全相同的代码,希望您将所有的
组合框
引用更改为
文本框
。。。这肯定是类型不匹配。@Byron,谢谢,根据请求,我已编辑了我的问题,以包括完整的userform模块以及两个自定义类模块。我确实尝试过将一个新变量声明为Textbox,并在案例7和抛出类型不匹配的行中使用它来生成Textbox,但这仍然导致了类型不匹配,这真是令人困惑和恼火……太棒了,有了这个更改,它对我也适用。我发现VBA充满了这样的小特性(或者它们只是bug:)。例如,我在代码中调整了一个列表框的大小,但我不知道为什么会忽略宽度设置(没有抛出错误,只是没有做任何事情。Top、.Height等都按预期工作。因此,我将该行移到UserForm Initialize子行,它可以工作,尽管这种解决方法可能并不总是切实可行。无论如何,再次感谢您花时间帮我解决这个问题,这是一个很好的学习,因为它总是值得尝试另一种方法!如果这就解决了问题,将其标记为答案(选中投票按钮下的复选标记)帮助其他人了解这一点。我同意VBA可能会有点混乱。它允许声明变量、后期绑定对象、隐式转换可能被滥用的类型等方面有很大的灵活性。它还可以在Excel上工作,这使得交互具有双重性:这是VBA问题还是Excel问题,还是交叉点中的某个问题?其中一个f“gotchas”(像这一个)似乎加起来真的拖累了语言。不幸的是,也有很多东西看起来像是“gotcha”,但一旦你知道它就有意义了。知道哪个是真正的困难。