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

时间:2018-07-26 18:07:40

标签: excel vba excel-vba vbe

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

可以从我的工作簿中运行插件的公共方法。但是,插件中的一种方法利用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"

是否可以在其他工作簿中显示在运行时创建的表单?

1 个答案:

答案 0 :(得分:6)

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

您的Set myForm = CallerWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)语句返回VbComponent,而不是UserForm,因此这就是为什么您不能再使用VBA.UserForms.Add(myForm.Name)

的原因

有两种解决方法:

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

UserForm就像一个类,因此可以像设置类一样设置其Instancing属性。但是,VBE不会在UserForms的“属性”窗口中显示Instancing属性,因此要设置实例化,您需要导出表单,然后在FRM文件中编辑Attribute VB_Exposed属性。文本编辑器,然后再次导入表单。步骤如下:

  • 在外接程序项目中创建一个名为TemplateForm的用户窗体
  • 删除TemplateForm,然后选择导出表单,然后再删除
  • 在文本编辑器中打开TemplateForm.frm文件
  • 编辑行Attribute VB_Exposed = False,使其读为Attribute 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-由外接程序创建UserForm组件,但由用户的工作簿进行管理

这种方法几乎不那么优雅。用户需要管理更多代码,屏幕闪烁,难以调试代码。步骤如下:

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

    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