【问题标题】:Excel VBA how to link a class and a control?Excel VBA如何链接一个类和一个控件?
【发布时间】:2016-11-20 08:41:41
【问题描述】:

我正在使用带有 VBA 的 Excel 2003,我正在工作表上动态创建复选框控件,并希望将 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 并从控件列表中选择创建的复选框之一,然后从过程下拉列表中选择单击,它将插入回调代码如果我在其中添加一个消息框,当我单击同一个复选框时可以工作......那么我怎样才能在代码中实现这一点?我已经尝试录制一个宏来执行此操作,但没有录制任何内容。

【问题讨论】:

  • objCheckboxes 在哪里声明?
  • objCheckboxes 只是一个集合。
  • 我意识到这一点,但我偶尔会犯错误,即在填充它的 sub 中声明它,而不是作为全局....

标签: excel vba


【解决方案1】:

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

由于一些奇怪的原因,VBA 不会在添加它们的同一执行周期中连接 Sheet 的 ActiveX 控件的事件。因此,我们需要退出添加控件的循环,然后在下一个循环中调用事件添加过程。 Application.OnTime 在这里有帮助。

这似乎有点矫枉过正,但它确实有效:)

Option Explicit

 Dim collChk         As Collection
 Dim timerTime

 Sub master()

        '/ Add the CheckBoxes First
        Call addControls

        '<< Due to some weird reason, VBA doesn't hook up the events for Sheet's ActiveX control in the same
        'execution cycle in which they were added. So, we need to come out of the cycle which added the controls
        'and then invoke the event adding proc in next cycle. >>

        '/ Start Timer. Timer will call the sub to add the events
        Call StartTimer
 End Sub

Sub addControls()
    Dim ctrlChkBox      As MSForms.CheckBox
    Dim objCell         As Range
    Dim i               As Long

    'Intialize the collection to hold the classes
    Set collChk = New Collection

    '/ Here Controls are added. No Events, yet.
    For i = 1 To 10
        Set objCell = Sheet1.Cells(i, 1)
        Set ctrlChkBox = Sheet1.OLEObjects.Add( _
                          ClassType:="Forms.CheckBox.1" _
                        , Left:=1 _
                        , Top:=(objCell.Top + 2) _
                        , Height:=objCell.Height _
                        , Width:=100).Object
        ctrlChkBox.Name = "chkbx" & objCell.Row
     Next

End Sub

Sub addEvents()

    Dim ctrlChkBox      As MSForms.CheckBox
    Dim objCBclass      As clsCheckBox
    Dim x               As Object


    'Intialize the collection to hold the classes
    Set collChk = New Collection

    '/ Here we assign the event handler
     For Each x In Sheet1.OLEObjects
       If x.OLEType = 2 Then

        Set ctrlChkBox = x.Object

        Set objCBclass = New clsCheckBox
        Set objCBclass.cbBox = ctrlChkBox

        collChk.Add objCBclass
        Debug.Print x.Name
       End If
    Next

    '/ Kill the timer
    Call StopTimer

End Sub

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

Sub StopTimer()
    On Error Resume Next
    Application.OnTime EarliestTime:=timerTime, Procedure:="addEvents", _
        Schedule:=False
End Sub

类模块:clsCheckBox

    Option Explicit

    Public WithEvents cbBox As MSForms.CheckBox

    Private Sub cbBox_Change()
        MsgBox "_CHANGE"
    End Sub

    Private Sub cbBox_Click()
        MsgBox "_CLICK"
    End Sub

编辑继续...

类(clsCheckbox):

    Option Explicit

    Public WithEvents cbBox As MSForms.checkbox

    Private Sub cbBox_Click()
        MsgBox "_CLICK"
    End Sub

模块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

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

【讨论】:

  • 谢谢,今晚我会实施并回帖。
  • 我在这一行遇到类型不匹配: Set ctrlChkBox = x.Object 我已经检查了代码,并且确定我已经正确复制了它。
  • 嗯,,,,,不知道为什么会发生在你身上。刚刚测试了代码,它工作。您在 addControls 和 addEvents 中使用相同的声明 Dim ctrlChkBox As MSForms.CheckBox
  • 只需将整个代码集复制到新的工作簿中,看看它是否有效。希望您在复制粘贴时错过了一些东西。 :)
  • 我刚刚这样做了,它立即出错:Dim ctrlChkBox As MSForms.Checkbox with "User-defined type not defined"
【解决方案2】:

您当前正在使用 ActiveX 控件。然而,ActiveX 控件绑定到特定的命名约定。例如:如果您在工作表上插入一个 ActiveX 按钮并将其命名为 btnMyButton,则子必须命名为 btnMyButton_Click。这同样适用于复选框。如果您插入名称为 CheckBox2 的新复选框,则子名称必须为 CheckBox2_Click。简而言之,不能有一个名为 cbBox_Change 的 sub 关联到任何 ActiveX 复选框。

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

如果您愿意改用form controls,则更简单的方法是。

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

Sub Insert_CheckBox()

Dim chk As CheckBox

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

End Sub

由于from control 正在调用子tmpSO,您可以在该子中使用Application.Caller,从而知道哪个复选框一直在调用此子。

Sub tmpSO()

Debug.Print Application.Caller

End Sub

这将返回CheckBox 的名称。因此,您可以为所有复选框使用这个子选项,任何根据它们的名称动态处理它们(可能使用Case Select)。

这是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

【讨论】:

  • 谢谢,有些帖子用同样的方式创建的按钮做了一些关闭的事情,但我看不到这些如何与用户类一起工作,我自己尝试过,我无法让它触发事件。
  • 可以使用VBA扩展库来使用VBA代码编写sheet代码。不过,您必须在安全设置中信任访问 VBA 项目。 Excel vba add code to sheet module programmatically 给出了一个具体的例子,Chip Pearson 的Programming The VBA Editor 给出了更一般的讨论
  • @SPlatten 我不确定您的问题/问题是什么。只需复制子Insert_CheckBox 和子tmpSO。然后运行Insert_CheckBox。之后,您将在哪个工作表处于活动状态时有一个复选框,如果您单击该复选框,那么您将获得 debug.printMsgBox(取决于您复制的任何 tmpSO 子)。而已。完毕。没有类模块,没有要编码的额外事件。就这么简单。
  • 问题是一切都是在代码中创建的,在运行时没有任何东西放在工作表上,因此我需要能够在运行时创建事件处理程序。到目前为止,没有任何效果。
  • 上面的子Insert_CheckBox没有创建复选框?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-22
  • 2021-11-28
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多