【问题标题】:Create a series of buttons by clicking the previous in Excel VBA通过单击 Excel VBA 中的上一个来创建一系列按钮
【发布时间】:2020-03-19 19:48:59
【问题描述】:

我正在用 Excel VBA 练习一个类模块。

我手动插入了一个空白用户表单。从现在开始,将不再手动添加任何控件。

一旦你初始化了用户窗体,它就会在窗体的左上角出现一个命令按钮。

现在,单击该按钮,您将在第一个按钮的正下方看到另一个命令按钮。
单击新创建的按钮应在前一个按钮下方创建另一个按钮。只要您继续单击新创建的命令按钮,该过程就会继续进行。

问题
我在初始化用户窗体时创建了第一个按钮,并在下面创建了一个新按钮。
之后,单击第二个/新创建的按钮时没有任何反应。

用户表单代码

Dim A As New Class2       ' Create an object of the Class (where we declared the events).

Private Sub UserForm_Initialize()

    ' Create and add the button control.
    Dim btEx As MSForms.CommandButton
    Set btEx = UserForm1.Controls.Add("Forms.CommandButton.1")
    
    With btEx
        .Top = 12
        .Left = 12
        .Width = 72
        .Height = 36
        .Caption = "Click Me"
    End With
    
    Set A.btEvents = btEx
    
End Sub

类模块

Public WithEvents btEvents As MSForms.CommandButton

Private Sub btEvents_click()
        
    ' Create and add the button control.
    Dim btEx As MSForms.CommandButton
    Set btEx = UserForm1.Controls.Add("Forms.CommandButton.1")
    
    With btEx
        .Top = 30
        .Left = 30
        .Width = 72
        .Height = 36
        .Caption = "Click Me"
    End With
        
End Sub

我无法将事件分配给类模块中动态创建的命令按钮。

是我遗漏了什么还是在平台上不可能?

【问题讨论】:

  • 你需要有一个集合来保存你需要的所有类 每个按钮 1 个,我会在你设置后进行更新。btEvents 类似Dim a() as Class2 你可以也可以使用这个数组的大小来帮助你在课堂上的间距。
  • @Nathan_Sav 非常感谢您的评论。我已经阅读了很多关于这方面的文章和解决方案,同样,我确实在概念上理解了你的建议,但我在这里看到的只是,它将如何在语法上呈现!请。
  • @Nathan_Sav:非常感谢,先生。对于您在评论中的陈述,“有一个集合来保存每个按钮所需的所有类”。这导致我完善自己的解决方案。再次感谢您。

标签: excel vba


【解决方案1】:

实现一个类的集合,其中每个类都包含一个按钮,是完成这项工作的关键。这种常用方法的缺点是类包含对 UserForm 的引用,并且事件在类本身中处理。理想情况下,我们希望将所有按钮代码返回到用户窗体中,包括事件处理。

由于不能将 Collections 声明为 WithEvents,如何做到这一点?这可以通过全局通知机制来完成:

用户表单

Option Explicit

Private WithEvents MyNotifier As Notifier
Private MyControls As Collection
    
Private Sub UserForm_Initialize()
   Set MyNotifier = GetNotifier()
   Set MyControls = New Collection
   AddButton
End Sub

Private Sub MyNotifier_Click()
   AddButton
End Sub

Private Sub AddButton()
   Dim c As MSForms.CommandButton
   Set c = UserForm1.Controls.Add("Forms.CommandButton.1")
   c.Width = 72
   c.Height = 36
   c.Top = UserForm1.Controls.Count * c.Height
   c.Left = 12
   c.Caption = "Click Me"

   Dim mc As MyControl
   Set mc = New MyControl
   mc.Add c
   MyControls.Add mc
End Sub

此架构有 3 个支持模块。第一个是一个包含全局通知器的模块。我们通过 GetNotifier 方法创建和访问这个通知程序。这种方法保证只有一个实例。

模块

Option Explicit

Private m_Notifier As Notifier

Public Function GetNotifier() As Notifier
   If m_Notifier Is Nothing Then Set m_Notifier = New Notifier
   
   Set GetNotifier = m_Notifier
End Function

第二个是定义通知器的类。这个单例类是一个事件协调器。它允许重定向事件,在本例中是从 MyControl 类返回到 UserControl。

通知程序类

Option Explicit

Public Event Click()

Public Function Click()
   RaiseEvent Click
End Function

第三个是持有按钮的类。对按钮单击事件的响应是调用我们的通知程序的方法,该方法反过来将事件返回给 UserControl:

MyControl 类

Option Explicit

Private MyNotifier As Notifier
Private WithEvents btEx As MSForms.CommandButton

Public Sub Add(ByVal c As MSForms.CommandButton)
   Set MyNotifier = GetNotifier()
   Set btEx = c
End Sub

Private Sub btEx_Click()
   MyNotifier.Click
End Sub

【讨论】:

  • 感谢您提供出色的解决方案!在消除了经典方法的所有缺点之后,这可能是最好的方法。如果您可以为通知程序类写一个解释,那么它将清除有关您的方法的所有疑问!请。
  • @KamalBharakhda 我用更多解释更新了我的帖子。希望这对您的理解有所帮助。
  • 尊敬的先生,非常感谢您为此解决方案所做的努力。在所有同样正确的答案中,我已将您的答案选为最有帮助的答案。我认为你的方法值得最好的方法!再次感谢你。如果你问我,在我读过的任何文章中,我从未见过像你这样的方法。请给我一些文章或教程,让我可以更深入地了解 VBA 的类编程?因为我不知道将事件分配给控件的 RaiseEvent 方法。我想了解更多。请。
  • @KamalBharakhda 我没有可推荐的具体资源。但请记住,Google 是您的朋友!这就是我学习技术和知识的方式,可能也是我多年前学习这种技术的方式。我从一个我在生产中使用了很长时间的应用程序中提取了示例代码,简化了解释架构的答案。很高兴您发现它很有用。
【解决方案2】:

你可以试试这个。有点匆忙,所以如果出现问题也不会感到惊讶,但如果出现问题,明天会回来。

表格后面

Private col As Collection

Private Sub UserForm_AddControl(ByVal Control As MSForms.Control)

Dim cl As Class2
Dim ctl As MSForms.CommandButton

Set col = New Collection

For Each ctl In Me.Controls
    Set cl = New Class2
    Set cl.btEvents = ctl
    col.Add cl
Next ctl

End Sub

Private Sub UserForm_Initialize()

Dim btEx As MSForms.CommandButton
Set btEx = UserForm1.Controls.Add("Forms.CommandButton.1")

With btEx
    .Top = 12
    .Left = 12
    .Width = 72
    .Height = 36
    .Caption = "Click Me"
End With

End Sub

类模块 这里唯一的改变是添加一些间距,这样按钮就不会出现在彼此的顶部。

Public WithEvents btEvents As MSForms.CommandButton

Private Sub btEvents_click()

Dim btEx As MSForms.CommandButton
Set btEx = UserForm1.Controls.Add("Forms.CommandButton.1")

With btEx
    .Top = 30 * UserForm1.Controls.Count
    .Left = 30
    .Width = 72
    .Height = 36
    .Caption = "Click Me"
End With

End Sub

【讨论】:

  • 非常感谢您的解决方案! Nathan_Sav 的评论已经消除了我的疑虑,然后我再次开始完善我的代码,我找到了与您类似的解决方案。我错的是,没有将变量放在全局模块中!但你的解决方案已经证实了我的方式!非常感谢您的努力。我相信我会更多地测试你的代码,在你明天编辑你的答案之后,我会给出对这个问题最有帮助的答案
  • 认为主要问题是初始化事件中的代码,因为在该阶段结束时您只有一个按钮。
猜你喜欢
  • 2018-04-28
  • 2019-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-23
  • 2013-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多