在运行时将用户窗体添加到不同的工作簿

Add userform to a different workbook at runtime

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

我的工作簿中的插件的 运行 public 方法是可能的。然而,插件中的一种方法利用 VBA.UserForms.Add 打开在 运行 时间 like this

创建的用户窗体

假设包含对 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 似乎无法 看到 其中 myForm.Name 指向现在,因此 Add 方法失​​败 "Run time error 424: Object required"

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

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

你的 Set myForm = CallerWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm) 语句 returns 是 VbComponent,而不是 UserForm,所以这就是你不能使用 VBA.UserForms.Add(myForm.Name)[=32= 的原因]

有两种解决方法:

1 - 在您的加载项中创建一个 PublicNotCreatable 模板用户窗体

用户窗体就像 class,因此它可以设置 Instancing 属性,就像 class 一样。但是,VBE 不会在用户窗体的属性 Window 中公开 Instancing 属性,因此要设置实例化,您需要导出窗体,然后编辑 Attribute VB_Exposed 属性在文本编辑器中的 FRM 文件中,然后再次导入表单。以下是步骤:

  • 在您的加载项项目中创建一个名为 TemplateForm 的用户窗体
  • 删除 TemplateForm 并在删除之前选择导出表单
  • 在文本编辑器中打开 TemplateForm.frm 文件
  • 编辑 Attribute VB_Exposed = False 行,使其变为 Attribute VB_Exposed = True
  • 将更改保存到 TemplateForm.frm
  • TemplateForm.frm 导入您的加载项
  • 将 public 函数添加到您的外接程序 returns 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 UserForm。

2 - 让加载项创建用户窗体组件,但让用户的工作簿管理它

这种方法远没有那么优雅。用户需要管理的代码多了很多,屏幕闪烁,代码难以调试。以下是步骤:

  • 将此代码添加到加载项:

    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