在运行时将用户窗体添加到其他工作簿

Add userform to a different workbook at runtime

提问人:Greedo 提问时间:7/27/2018 最后编辑:Mathieu GuindonGreedo 更新时间:7/27/2018 访问量:1184

问:

我打开了一个插件和一个工作簿。加载项是一个 .xlam 文件,在工作簿中我添加了对它的引用。加载项受密码保护。

可以从我的工作簿中运行插件的公共方法。但是,插件中的一种方法用于打开在运行时创建的用户窗体,如下所示VBA.UserForms.Add

假设包含引用的工作簿具有以下内容:myAddin

Private Sub callAddin()
    myAddin.ShowForm ThisWorkbook
End Sub

通常,我的插件中的代码如下所示:

Public Sub ShowForm(CallerWorkbook As Workbook)
    Const vbext_ct_MSForm As Long = 3

    'This is to stop screen flashing while creating form
    Application.VBE.MainWindow.Visible = False

    'Add to ThisWorkbook, not supplied workbook or VBE will crash - ignore CallerWorkbook
    Dim myForm As Object
    Set myForm = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)

    'Create the User Form
    With myForm
        .Properties("Caption") = "Select"
        .Properties("Width") = 300
        .Properties("Height") = 270
    End With

    'Show the form
    Dim finalForm As Object
    Set finalForm = VBA.UserForms.Add(myForm.Name)
    finalForm.Show

    'Remove form
    ThisWorkbook.VBProject.VBComponents.Remove myForm

End Sub

这很好用。但是,当我的插件受密码保护时,不允许尝试向其添加临时用户窗体。没问题,我只是将临时用户窗体添加到调用代码的工作簿中,因为这不会受密码保护

Sub ShowForm(CallerWorkbook As Workbook)
    Const vbext_ct_MSForm As Long = 3

    'This is to stop screen flashing while creating form
    Application.VBE.MainWindow.Visible = False

    'Add to CallerWorkbook instead
    Dim myForm As Object
    Set myForm = CallerWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)

    'Create the User Form
    With myForm
        .Properties("Caption") = "Select"
        .Properties("Width") = 300
        .Properties("Height") = 270
    End With

    'Show the form
    Dim finalForm As Object
    'Now myForm cannot be found and added
    Set finalForm = VBA.UserForms.Add(myForm.Name)
    finalForm.Show

    'Remove form
    CallerWorkbook.VBProject.VBComponents.Remove myForm

End Sub

但是,VBA 似乎看不到指向现在的位置,因此 Add 方法失败,并显示myForm.Name"Run time error 424: Object required"

有没有办法在另一个工作簿中显示运行时创建的表单?

VBA Excel VBE

评论

0赞 Mathieu Guindon 7/27/2018
有趣的是,该类没有方法。实际上,VBA 在运行时会附加许多 MSForms 接口。我不知道这是否可能(刚才尝试并失败了一点),但无论如何......为什么要经历制作 100% 动态用户表单的痛苦?UserFormShow
0赞 Mathieu Guindon 7/27/2018
FWIW 当我尝试执行时,我收到运行时错误 75“路径/文件访问错误”,其中声明了 .Set finalForm = myForm.DesignerfinalFormAs UserForm
1赞 Comintern 7/27/2018
我认为更大的问题是它可能无法在运行时编译新的表单源。我不确定您是否可以使用 VB 应用程序扩展,就像通过 .NET 反射发出 CLR 一样。
0赞 Greedo 7/27/2018
@MathieuGuindon我正在尝试创建一些代码,用户可以将其复制并粘贴到模块中/作为单个模块导入,并使其轻松运行。就我个人而言,我正在从标准工具箱插件中使用它,其中我可以包含模板用户窗体,但我也希望便于在新项目中使用 - 在这种情况下,单个自包含文件是理想的选择。因此 100% 从头开始。
0赞 Mathieu Guindon 7/27/2018
...好吧,你可以这样做。你不能做的是同时运行表单。

答:

8赞 ThunderFrame 7/27/2018 #1

您遇到的问题是,默认情况下,用户窗体是私有实例化的。这意味着一个项目不能引用另一个项目中的用户窗体,如果不能引用该窗体,则无法调用它的方法。Show

您的语句返回 a ,而不是 ,所以这就是为什么你不能使用Set myForm = CallerWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)VbComponentUserFormVBA.UserForms.Add(myForm.Name)

有两种方法可以解决这个问题:

1 - 在外接程序中创建模板用户窗体PublicNotCreatable

用户窗体类似于一个类,因此它可以设置其属性,就像类一样。但是,VBE 不会在用户窗体的“属性”窗口中公开该属性,因此若要设置实例化,需要导出窗体,然后在文本编辑器中编辑 FRM 文件中的属性,然后再再次导入窗体。步骤如下:InstancingInstancingAttribute VB_Exposed

  • 创建在外接程序项目中命名的用户窗体TemplateForm
  • 删除并选择在删除表单之前导出表单TemplateForm
  • 在文本编辑器中打开文件TemplateForm.frm
  • 编辑该行,以便读取Attribute VB_Exposed = FalseAttribute VB_Exposed = True
  • 将更改保存到TemplateForm.frm
  • 导入到加载项中TemplateForm.frm
  • 添加一个公共函数,该函数将 的新实例返回到外接程序。我已使此函数接受工作簿引用,以便加载项可以在窗体上配置任何特定于工作簿的属性:TemplateForm

    Public Function GetTemplateForm(CallerWorkbook As Workbook) As TemplateForm
      Dim frm As TemplateForm
      Set frm = New TemplateForm
      'Set early-bound properties with intellisense
      frm.Caption = "Select"
      frm.Width = 300
      frm.Height = 270
    
      'Configure CallerWorkbook specific form properties here
      '...
      Set GetTemplateForm = frm
    End Function
    
  • 然后,在用户的工作簿中,可以显示 TemplateForm 的实例,而无需动态添加窗体,也无需处理屏幕闪烁或难以调试的代码:

    Sub ShowAddinForm()
        With MyAddin.GetTemplateForm(ThisWorkbook)
            'Do more workbook specific propery setting here...
            '...
            .Show
        End With
    End Sub
    

** 注意 - Rubberduck VBA 加载项很快将能够添加用户窗体。PublicNotCreatable

2 - 让外接程序创建用户窗体组件,但让用户的工作簿管理它

这种方法并不那么优雅。用户需要管理的代码还有很多,并且屏幕闪烁,并且难以调试代码。步骤如下:

  • 将此代码添加到加载项:Add this code to the add-in:

    Public Function GetTempFormName(CallerWorkbook As Workbook) As String
        Const vbext_ct_MSForm As Long = 3
    
        'This is to stop screen flashing while creating form
        Application.VBE.MainWindow.Visible = False
    
        'Add to CallerWorkbook instead
        With CallerWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
            .Properties("Caption") = "Select"
            .Properties("Width") = 300
            .Properties("Height") = 270
            GetTempFormName = .Name
        End With
    End Function
    
    Public Sub RemoveTempForm(CallerWorkbook As Workbook, FormName As String)
        With CallerWorkbook.VBProject.VBComponents
            Dim comp As Object
            Set comp = .Item(FormName)
            .Remove .Item(FormName)
        End With
    End Sub
    
  • 然后,在用户的工作簿中,添加以下代码:

    Sub GetAddinToCreateForm()
        Dim FormName As String
        FormName = MyAddin.GetTempFormName(ThisWorkbook)
        With VBA.UserForms.Add(FormName)
            .Show
        End With
        MyAddin.RemoveTempForm ThisWorkbook, FormName
    End Sub