Excel VBA如何链接类和控件?

Excel VBA如何链接类和控件?,excel,vba,Excel,Vba,我将Excel 2003与VBA一起使用,在工作表上动态创建复选框控件,并希望将VBA控件链接到类,以便当用户单击复选框时触发事件,以便我可以执行某些操作 从我所读到的内容来看,创建用户类似乎是一个解决方案,但尝试了这个方法后,我无法让它工作 我的用户类如下所示: Option Explicit Public WithEvents cbBox As MSForms.checkbox Private Sub cbBox_Change() MsgBox "

我将Excel 2003与VBA一起使用,在工作表上动态创建复选框控件,并希望将VBA控件链接到类,以便当用户单击复选框时触发事件,以便我可以执行某些操作

从我所读到的内容来看,创建用户类似乎是一个解决方案,但尝试了这个方法后,我无法让它工作

我的用户类如下所示:

    Option Explicit

    Public WithEvents cbBox As MSForms.checkbox

    Private Sub cbBox_Change()
        MsgBox "_CHANGE"
    End Sub

    Private Sub cbBox_Click()
        MsgBox "_CLICK"
    End Sub
创建复选框的我的代码:

    For Each varExisting In objColumns
    'Insert the field name
        objColumnHeadings.Cells(lngRow, 1).Value = varExisting
    'Insert a checkbox to allow selection of the column
        Set objCell = objColumnHeadings.Cells(lngRow, 2)
        Dim objCBclass As clsCheckbox
        Set objCBclass = New clsCheckbox
        Set objCBclass.cbBox = ActiveSheet.OLEObjects.Add( _
                                  ClassType:="Forms.CheckBox.1" _
                                , Left:=300 _
                                , Top:=(objCell.Top + 2) _
                                , Height:=10 _
                                , Width:=9.6).Object
        objCBclass.cbBox.Name = "chkbx" & lngRow
        objCBclass.cbBox.Caption = ""
        objCBclass.cbBox.BackColor = &H808080
        objCBclass.cbBox.BackStyle = 0
        objCBclass.cbBox.ForeColor = &H808080
        objCheckboxes.Add objCBclass
        lngRow = lngRow + 1
    Next
这些复选框在工作表中可见,但当我单击它们时,没有显示任何消息框,因此指向该类的链接似乎不起作用

为什么?


编辑…如果在添加复选框后,我进入VB IDE并从控件列表中选择一个已创建的复选框,然后从过程下拉列表中选择“单击”,它将插入回拨代码,如果我在其中添加一个消息框,当我单击同一复选框时,该代码将起作用…那么我如何在代码中实现这一点?我尝试录制宏来执行此操作,但未录制任何内容。

您当前正在使用ActiveX控件。然而,ActiveX控件受特定命名约定的约束。例如:如果在工作表上插入ActiveX按钮并将其命名为
btnMyButton
,则必须将子按钮命名为
btnMyButton\u单击
。这同样适用于复选框。如果插入名为
CheckBox2
的新复选框,则子项的名称必须为
CheckBox2\u单击
。简而言之,名称为
cbBox\u Change
的子项不能与任何ActiveX复选框关联

因此,您真正需要(使用ActiveX控件)的是一种更改工作表上VBA代码的方法。但到目前为止,我从未遇到过任何这样的代码(在工作表上更改VBA代码的VBA代码)

如果你愿意改用,一条更容易的路线会是

下面的子项将创建一个(表单控件)复选框并
tmpSO
。sub
tmpSO
(与ActiveX控件的sub不同)不需要驻留在工作表上,但可以位于任何模块中

Sub Insert_CheckBox()

Dim chk As CheckBox

Set chk = ActiveSheet.CheckBoxes.Add(390.75, 216, 72, 72)
chk.OnAction = "tmpSO"

End Sub
由于控件中的
正在调用sub
tmpSO
,因此您可以在该sub中使用
Application.Caller
,从而知道哪个复选框调用了此sub

Sub tmpSO()

Debug.Print Application.Caller

End Sub
这将返回
复选框的名称。因此,您可以将此子项用于所有复选框,根据它们的名称动态处理它们(可能使用
案例选择

下面是
tmpSO
的另一个示例:

Sub tmpSO()

With ThisWorkbook.Worksheets(1).CheckBoxes(Application.Caller)
    MsgBox "The checkbox " & Application.Caller & Chr(10) & _
        "is currently " & IIf(.Value = 1, "", "not") & " checked."
End With

End Sub

由S.Platten编辑,跳到底部了解这是如何帮助我解决问题的

由于某些奇怪的原因,VBA没有在添加事件的同一执行周期中连接Sheet ActiveX控件的事件。因此,我们需要走出添加控件的循环,然后在下一个循环中调用事件添加过程<代码>应用程序.OnTime
在此提供帮助

这似乎有点过分,但效果不错:)

编辑继续

类(clsCheckbox):

模块1

    Public objCheckboxes As Collection
    Public tmrTimer

    Public Sub addEvents()
        Dim objCheckbox As clsCheckbox
        Dim objMSCheckbox As Object
        Dim objControl As Object

        Set objCheckboxes = New Collection
        For Each objControl In Sheet1.OLEObjects
            If objControl.OLEType = 2 _
            And objControl.progID = "Forms.CheckBox.1" Then
                Set objMSCheckbox = objControl.Object
                Set objCheckbox = New clsCheckbox
                Set objCheckbox.cbBox = objMSCheckbox
                objCheckboxes.Add objCheckbox
            End If
        Next
        Call stopTimer
    End Sub

    Public Sub startTimer()
        tmrTimer = Now + TimeSerial(0, 0, 1)
        Application.OnTime EarliestTime:=tmrTimer _
                         , Procedure:="addEvents" _
                         , Schedule:=True
    End Sub

    Public Sub stopTimer()
        On Error Resume Next
        Application.OnTime EarliestTime:=tmrTimer _
                         , Procedure:="addEvents" _
                         , Schedule:=False
    End Sub
工作表中添加控件的代码:

    Dim objControl As MSForms.checkbox
    For Each varExisting In objColumns
    'Insert the field name
        objColumnHeadings.Cells(lngRow, 1).Value = varExisting
    'Insert a checkbox to allow selection of the column
        Set objCell = objColumnHeadings.Cells(lngRow, 2)
        Set objControl = ActiveSheet.OLEObjects.Add( _
                                  ClassType:="Forms.CheckBox.1" _
                                , Left:=300 _
                                , Top:=(objCell.Top + 2) _
                                , Height:=10 _
                                , Width:=9.6).Object
        objControl.Name = "chkbx" & lngRow
        objControl.Caption = ""
        objControl.BackColor = &H808080
        objControl.BackStyle = 0
        objControl.ForeColor = &H808080
        lngRow = lngRow + 1
    Next

这不是整个项目,但足以演示工作原理。

谢谢,有些帖子用同样的方式创建的按钮进行了关闭,但是我看不出这些如何与用户类一起工作,而且我自己也尝试过,我无法让它触发事件。您可以使用VBA扩展性库使用VBA代码来编写工作表代码。但是,您必须在安全设置中信任对VBA项目的访问。在Chip Pearson的@Splaten上给出了一个具体的例子,并进行了更一般性的讨论。我不确定您的问题是什么。只需复制sub
Insert_复选框和sub
tmpSO
。然后运行
插入\u复选框
。之后,您将有一个复选框,其中“ever sheet”处于活动状态,如果您单击该复选框,您将得到一个
debug.print
MsgBox
(取决于您复制的哪个tmpSO sub)。就这样。完成。无类模块,无需编码的其他事件。就这么简单,问题是所有东西都是用代码创建的,运行时没有任何东西放在工作表上,因此我需要能够在运行时创建事件处理程序。到目前为止什么都不起作用。上面的sub
Insert_CheckBox
没有创建复选框?objcheckbox声明在哪里?objcheckbox只是一个集合。我意识到,但我偶尔会犯错误,在填充它的sub中声明它,而不是作为全局…谢谢,今晚我将实现这一点并发回。我在这一行得到一个类型不匹配的结果:Set ctrlChkBox=x。Object我已经检查了代码,并且我确定我已经正确地复制了它。嗯,,,,,不确定为什么会发生在你身上。刚刚测试了代码,它就成功了。您正在使用与MSForms相同的声明
Dim ctrlChkBox。addControls和addEvents中的复选框
?只需将整个代码集复制到新的工作手册中,然后查看它是否有效。希望您在复制粘贴时错过了一些内容。:)我刚才做了这件事,它立即出现错误:Dim ctrlChkBox作为MSForms.Checkbox带有“用户定义类型未定义”
    Option Explicit

    Public WithEvents cbBox As MSForms.checkbox

    Private Sub cbBox_Click()
        MsgBox "_CLICK"
    End Sub
    Public objCheckboxes As Collection
    Public tmrTimer

    Public Sub addEvents()
        Dim objCheckbox As clsCheckbox
        Dim objMSCheckbox As Object
        Dim objControl As Object

        Set objCheckboxes = New Collection
        For Each objControl In Sheet1.OLEObjects
            If objControl.OLEType = 2 _
            And objControl.progID = "Forms.CheckBox.1" Then
                Set objMSCheckbox = objControl.Object
                Set objCheckbox = New clsCheckbox
                Set objCheckbox.cbBox = objMSCheckbox
                objCheckboxes.Add objCheckbox
            End If
        Next
        Call stopTimer
    End Sub

    Public Sub startTimer()
        tmrTimer = Now + TimeSerial(0, 0, 1)
        Application.OnTime EarliestTime:=tmrTimer _
                         , Procedure:="addEvents" _
                         , Schedule:=True
    End Sub

    Public Sub stopTimer()
        On Error Resume Next
        Application.OnTime EarliestTime:=tmrTimer _
                         , Procedure:="addEvents" _
                         , Schedule:=False
    End Sub
    Dim objControl As MSForms.checkbox
    For Each varExisting In objColumns
    'Insert the field name
        objColumnHeadings.Cells(lngRow, 1).Value = varExisting
    'Insert a checkbox to allow selection of the column
        Set objCell = objColumnHeadings.Cells(lngRow, 2)
        Set objControl = ActiveSheet.OLEObjects.Add( _
                                  ClassType:="Forms.CheckBox.1" _
                                , Left:=300 _
                                , Top:=(objCell.Top + 2) _
                                , Height:=10 _
                                , Width:=9.6).Object
        objControl.Name = "chkbx" & lngRow
        objControl.Caption = ""
        objControl.BackColor = &H808080
        objControl.BackStyle = 0
        objControl.ForeColor = &H808080
        lngRow = lngRow + 1
    Next