【问题标题】:Creating command button and writing code programmatically in a userform module在用户窗体模块中以编程方式创建命令按钮和编写代码
【发布时间】:2015-08-31 05:39:00
【问题描述】:

这就是我想要做的:

1 检查文件夹中文件的版本数,

2 在用户表单中添加与版本数一样多的按钮,

3 为每个按钮编写代码,例如,显示一个 MsgBox

使用下面的代码,版本数被正确检查,按钮也被正确创建,代码被正确添加到用户表单模块中,但是......当我点击任何按钮(为每个版本创建)时,什么都没发生。救命!!!

Dim Boton As MSForms.CommandButton
Dim Fichero_Secundaria_Hoy As String
Dim Versiones_secundaria(8) As Integer
Dim i As Integer
Dim Number_of_versions As Integer
Dim Code As String
Dim j As Integer

Fichero_Secundaria_Hoy = Dir("C:\Prueba\pdvd_" & Format(Date, "yyyymmdd") & "*")

Do While Fichero_Secundaria_Hoy <> ""
    Versiones_secundaria(i) = Mid(Fichero_Secundaria_Hoy, 27, 1)
    Fichero_Secundaria_Hoy = Dir
    i = i + 1
Loop

Number_of_versions = i
Version_secundaria.Height = 18 + 24 * Number_of_versions

With ActiveWorkbook.VBProject.VBComponents("Version_secundaria").CodeModule
    .DeleteLines 1, .CountOfLines
End With

For i = 0 To  Number_of_versions - 1
    Set Boton = Version_secundaria.Controls.Add("Forms.commandbutton.1", "Version" & Versiones_secundaria(i))
    Version_secundaria.Controls("version" & Versiones_secundaria(i)).Caption = Versiones_secundaria(i)

    Code = "Sub " & Boton.Name & "_Click()" & vbCrLf
    Code = Code & "Call Mostrar_secundaria" & vbCrLf
    Code = Code & "Version_secundaria.Hide" & vbCrLf
    Code = Code & "End Sub"

    With ActiveWorkbook.VBProject.VBComponents("Version_secundaria").CodeModule
        .InsertLines .CountOfLines + 1, Codigo_del_boton
    End With

Next i

此代码位于普通模块中,以编程方式编写的代码出现在名为“Version_secundaria”的现有用户窗体模块中。

似乎点击按钮并没有进入Sub Button_click,但不知道为什么!

谢谢!

【问题讨论】:

    标签: excel vba


    【解决方案1】:

    我不相信你想要的东西是可能的。一两年前有一个类似的问题。 OP 的目标与您的非常不同,但也需要在运行时创建源代码。我的回忆是,OP 被告知不可能在同一运行中创建和执行源代码。我在网上找不到任何东西来确认或否认这一限制。我还记得发布了该要求的另一种方法。可能是我,因为我记得考虑如何达到所需的效果。

    您可能还想考虑来自 C Pearson 的警告:

    注意:许多基于 VBA 的计算机病毒通过以下方式自行传播 创建和/或修改 VBA 代码。因此,许多病毒扫描程序可能 自动且没有警告或确认删除模块 引用 VBProject 对象,导致永久且无法恢复 代码丢失。请查阅您的防病毒软件的文档 了解详情。

    下面的代码提供了一种替代方法来实现我相信您寻求的效果。

    我创建了一个用户表单,并在其中填充了 20 个选项按钮、1 个命令按钮和 1 个标签。除了重命名为 cmdExit 的命令按钮外,我已将表单和控件保留为默认名称。我将退出按钮和标签放在我希望它们出现的位置。选项按钮被随机放置并且不可见(属性可见 = False)。这可能不足以满足您的需求,但您可以在表单明显变慢之前拥有更多的控件。

    我创建了一个小程序来显示表单:

    Option Explicit
    Sub DsplForm()
    
      Load UserForm1
      UserForm1.Show vbModal
    
    End Sub
    

    表单中代码的基础是集合Controls,其中包含表单上的所有控件。您可以通过控件名称或控件集合访问控件的属性。例如,如果控件 5 是 Label1,则以下是等价的:

    Label1.Caption = "xxxx"
    Controls(5).Caption = "xxxx"
    

    这允许运行时访问类似的控件,而无需为每个控件指定特定代码。

    我使用表单的初始化例程来准备一切:

    1. 扫描Controls 查找选项按钮并计数。
    2. 根据按钮的数量调整数组 OptButCtrl 的大小。
    3. 记录数组 OptButCtrl 中每个按钮的控件编号。我不关心顺序。在Controls 中很难在OptionButton1 之前获得OptionButton2,但如果你这样做也没关系。
    4. 我初始化数组 OptButDesc()。这是确定显示多少选项按钮的数组。您需要根据版本数调整此数组的大小,并使用版本的名称和详细信息加载此数组。
    5. 适当数量的选项按钮显示在列表中。

    所有选项按钮的单击例程调用一个例程,该例程将所有选项按钮设置为关闭/假,而不是选定的按钮。 Label1 设置为所选按钮的描述。

    如有必要,请提出问题,但我相信我已经为您提供了足够的信息来展示如何实现您所寻求的效果。

    Option Explicit
      Dim OptButCtrl() As Long
      Dim OptButDesc() As String
    Private Sub cmdExit_Click()
    
      Unload Me
    
    End Sub
    ' VB.Net has the functionality to allow one routine handle the events for
    ' several controls.  I can find no equivalent functonality with VBA. This
    ' approach is the best I have found.
    Private Sub OptionButton1_Click()
      Call OptionButtonAny(1)
    End Sub
    Private Sub OptionButton2_Click()
      Call OptionButtonAny(2)
    End Sub
    Private Sub OptionButton3_Click()
      Call OptionButtonAny(3)
    End Sub
    Private Sub OptionButton4_Click()
      Call OptionButtonAny(4)
    End Sub
    Private Sub OptionButton5_Click()
      Call OptionButtonAny(5)
    End Sub
    Private Sub OptionButton6_Click()
      Call OptionButtonAny(6)
    End Sub
    Private Sub OptionButton7_Click()
      Call OptionButtonAny(7)
    End Sub
    Private Sub OptionButton8_Click()
      Call OptionButtonAny(8)
    End Sub
    Private Sub OptionButton9_Click()
      Call OptionButtonAny(9)
    End Sub
    Private Sub OptionButton10_Click()
      Call OptionButtonAny(10)
    End Sub
    Private Sub OptionButton11_Click()
      Call OptionButtonAny(11)
    End Sub
    Private Sub OptionButton12_Click()
      Call OptionButtonAny(12)
    End Sub
    Private Sub OptionButton13_Click()
      Call OptionButtonAny(13)
    End Sub
    Private Sub OptionButton14_Click()
      Call OptionButtonAny(14)
    End Sub
    Private Sub OptionButton15_Click()
      Call OptionButtonAny(15)
    End Sub
    Private Sub OptionButton16_Click()
      Call OptionButtonAny(16)
    End Sub
    Private Sub OptionButton17_Click()
      Call OptionButtonAny(17)
    End Sub
    Private Sub OptionButton18_Click()
      Call OptionButtonAny(18)
    End Sub
    Private Sub OptionButton19_Click()
      Call OptionButtonAny(19)
    End Sub
    Private Sub OptionButton20_Click()
      Call OptionButtonAny(20)
    End Sub
    Private Sub UserForm_Initialize()
    
      Dim InxCtrl As Long
      Dim InxOptBut As Long
      Dim Left As Long
      Dim NumOptButs As Long
      Dim Top As Long
    
      ' Scan all controls and count those that are Option buttons
      NumOptButs = 0
      For InxCtrl = 0 To Controls.Count - 1
        With Controls(InxCtrl)
          If Mid(.Name, 1, 12) = "OptionButton" Then
            NumOptButs = NumOptButs + 1
          End If
        End With
      Next
    
      ' Size array according to number of option buttons found
      ReDim OptButCtrl(0 To NumOptButs - 1)
    
      ' Record control numbers of option buttons in array OptButCtrl()
      InxOptBut = LBound(OptButCtrl)
      For InxCtrl = 0 To Controls.Count - 1
        With Controls(InxCtrl)
          If Mid(.Name, 1, 12) = "OptionButton" Then
            OptButCtrl(InxOptBut) = InxCtrl
            InxOptBut = InxOptBut + 1
          End If
        End With
      Next
    
      ' The lower bound of OptButCtrl and OptButDesc must be the same.
      ' The size of this array controls how many option buttons are displayed.
      ReDim OptButDesc(0 To 5)
    
      OptButDesc(0) = "Desc for Button 1"
      OptButDesc(1) = "Desc for Button 2"
      OptButDesc(2) = "Desc for Button 3"
      OptButDesc(3) = "Desc for Button 4"
      OptButDesc(4) = "Desc for Button 5"
      OptButDesc(5) = "Desc for Button 6"
    
      ' These control the top left corner of the first option button
      Left = 5
      Top = 5
    
      ' Display one button for each description in OptButDesc()
      For InxOptBut = LBound(OptButCtrl) To UBound(OptButCtrl)
        InxCtrl = OptButCtrl(InxOptBut)
        If InxOptBut <= UBound(OptButDesc) Then
          ' This button is required
          With Controls(InxCtrl)
            .Top = Top
            Top = Top + .Height + 5
            .Left = Left
            .Visible = True
            .Caption = OptButDesc(InxOptBut)
          End With
        Else
          ' This button is not required
          Controls(InxCtrl).Visible = False
        End If
      Next
    
    End Sub
    Sub OptionButtonAny(ByVal OptButNum As Long)
    
      Dim InxCtrl As Long
      Dim InxOptBut As Long
    
      ' Find Control for selected Option button.
      ' Set all other option buttons to Off.
      ' Display description of selected button in Label1
      For InxOptBut = LBound(OptButCtrl) To UBound(OptButCtrl)
        InxCtrl = OptButCtrl(InxOptBut)
        With Controls(InxCtrl)
          If .Name = "OptionButton" & OptButNum Then
            Label1.Caption = OptButDesc(InxOptBut)
          Else
            .Value = False
          End If
        End With
      Next
    
    End Sub
    

    我应该补充一点,我不相信这是处理您的要求的最佳方式。我相信我会将所有版本加载到 ListBox 并让用户选择所需的行。

    【讨论】:

    • 非常感谢您的宝贵时间!我想你是对的,这是有道理的。我必须仔细阅读你的答案。我找到的解决方案是创建许多按钮,将它们放在一列中,并调整用户窗体的高度,这样用户就只能看到工作按钮。
    猜你喜欢
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多