引用在不同模块

时间:2018-05-17 13:45:42

标签: vba excel-vba excel

dr(没有间接细节):我试图引用在另一个模块中以编程方式创建的控件,但它正在抛出

  

"编译错误:未定义变量。"

这是我尝试做的事情(也许我的方法完全错了,如果是这样,请告诉我):我正在尝试创建一个使用列表框作为菜单的用户表单用户可以在其中选择项目并查看与该项目相关的控件(标签,文本框)。例如:item" General Information"提交者姓名,部门,日期等; item"报告请求"将有报告申请,所需的应用程序名称等。选择"一般信息"在列表框上应该.visible = true框架包含" GI"控制和隐藏其他框架,并选择"报告请求" item will .visible = false General Information frame but .visible = true Report Request frame,依此类推。

因为我希望将控件放在userform上的相同位置,所以我在Construct模块中以编程方式创建它们。它看起来像这样:

Public Sub GeneralInformationCreator()

Dim i As Integer
Dim labelGeneral As MSForms.Label
Dim frameGeneral As MSForms.Frame
Dim frameRequestInformation As MSForms.Frame
Dim frameSubmitterInformation As MSForms.Frame
Dim labelAppSelect As MSForms.Label
Dim listAppSelect As MSForms.ListBox
Dim labelDateSelect As MSForms.Label
Dim comboMonthSelect, comboDaySelect, comboYearSelect As MSForms.ComboBox
Dim labelSubmitterFName, labelSubmitterLName As MSForms.Label
Dim inputSubmitterFName, inputSubmitterLName As MSForms.TextBox
Dim labelSponsorFName, labelSponsorLName As MSForms.Label
Dim inputSponsorFName, inputSponsorLName As MSForms.TextBox
Dim labelDepartmentName As MSForms.Label
Dim inputDepartmentName As MSForms.TextBox

Set labelGeneral = formRequestWizard.Controls.Add("Forms.Label.1", "labelGeneral", True)
    With labelGeneral
        .Font.Size = 12
        .Top = 12
        .Left = 192
        .Height = 14.25
        .Width = 42
        .Caption = "General Information"
        .Name = labelGeneral
    End With

Set frameGeneral = formRequestWizard.Controls.Add("Forms.Frame.1", "frameGeneral", True)
    With frameGeneral
        .Top = 30
        .Left = 192
        .Height = 310
        .Width = 384
        .Caption = ""
        .BorderColor = RGB(255, 255, 255)

        Set frameRequestInformation = frameGeneral.Controls.Add("Forms.Frame.1", "frameRequestInformation", True)
            With frameRequestInformation
                .Top = 15
                .Left = 15
                .Height = 100
                .Width = 350
                .Caption = "Request Information"
                .BorderStyle = 1
                .BorderColor = RGB(0, 0, 0)

                Set labelAppSelect = frameRequestInformation.Controls.Add("Forms.Label.1", "labelAppSelect", True)
                    With labelAppSelect
                        .Caption = "Select an application:"
                        .Top = 15
                        .Left = 15
                        .Width = 100
                        .Height = 20
                        .AutoSize = True
                    End With

使用UserForm_Initialize()在运行时调用Construct方法,如下所示:

Private Sub UserForm_Initialize()

Call Construct.GeneralInformationCreator
Call Construct.ApplicationDetailsCreator

With formRequestWizard.listMenu
    .AddItem ("General Information")
    .AddItem ("Application Details")
    '.Selected(0) = True
End With

End Sub

以下是问题:在userform代码中,我有一个listMenu_Change(),如下所示:

Private Sub listMenu_Change()

If (listMenu.Selected(0) = True) Then
    labelGeneral.Visible = True
    frameGeneral.Visible = True
Else
    labelGeneral.Visible = False
    frameGeneral.Visible = False
End If

If (listMenu.Selected(1) = True) Then
    labelAppDetails.Visible = True
    frameAppDetails.Visible = True
Else
    labelAppDetails.Visible = False
    frameAppDetails.Visible = False
End If

End Sub

从列表框中选择一个项会从tl中引发错误; dr:"编译错误:变量未定义"在第一个变量" labelGeneral"。

我已经尝试将所有脚本放入userform代码窗口,并将控件Dims放在Subs之外。我所做的研究主要是讨论如何为运行时创建的控件创建事件处理程序,但我决定通过对象视图放置listMenu对象,所以我不确定& #39;适用。

2 个答案:

答案 0 :(得分:3)

定义所有变量:

Dim comboMonthSelect As MSForms.Combobox
Dim comboDaySelect As MSForms.Combobox 
Dim comboYearSelect As MSForms.ComboBox

使用 Dim comboMonthSelect, comboDaySelect, comboYearSelect As MSForms.ComboBox, 只有comboYearSelect被声明为MSForms.ComboBox,其他两个被声明为Variant

然后编写Option Explicit并确保在每个Sub / Function中定义每个变量。在你的情况下应该是:

Dim labelGeneral As MSForms.Label
发生错误的Sub listMenu_Change中的

MSDN Option Explicit reference

作为一种快速而又肮脏的方式,你可以做这样的事情:

If (listMenu.Selected(0) = True) Then
    Controls("labelGeneral").Visible = True
    Controls("someOtherName").Visible = True
End If

然而,这会完全打破any MVC pattern并可能导致任何非VBA人感到头晕并发布这样的内容:

enter image description here

如果你想以"正确的方式做到这一点",请从ex-StackOverflow文档中阅读本文,这是非常好的: http://www.riptutorial.com/vba/example/19036/best-practices

答案 1 :(得分:3)

GeneralInformationCreator本身应该是一个类 - 即使名称也是如此(它的名称就像 的类/类型一样某事,而不是动词就像某事的过程一样。)

查看您在程序范围内的所有这些声明,在程序范围内实时和死亡 - 它们是局部变量,没有其他人可以看到:

Dim labelGeneral As MSForms.Label
Dim frameGeneral As MSForms.Frame
Dim frameRequestInformation As MSForms.Frame
Dim frameSubmitterInformation As MSForms.Frame
Dim labelAppSelect As MSForms.Label
Dim listAppSelect As MSForms.ListBox
Dim labelDateSelect As MSForms.Label
Dim comboMonthSelect [As Variant], comboDaySelect [As Variant], comboYearSelect As MSForms.ComboBox
Dim labelSubmitterFName [As Variant], labelSubmitterLName As MSForms.Label
Dim inputSubmitterFName [As Variant], inputSubmitterLName As MSForms.TextBox
Dim labelSponsorFName [As Variant], labelSponsorLName As MSForms.Label
Dim inputSponsorFName [As Variant], inputSponsorLName As MSForms.TextBox
Dim labelDepartmentName As MSForms.Label
Dim inputDepartmentName As MSForms.TextBox

通过将它们移动到类模块中的实例级字段(即模块范围变量),您可以公开Public Property Get访问器,使任何拥有该类实例的人都能够访问这些对象。

所以不要这样:

Private Sub UserForm_Initialize()

    Call Construct.GeneralInformationCreator
    Call Construct.ApplicationDetailsCreator

    With formRequestWizard.listMenu
        .AddItem ("General Information")
        .AddItem ("Application Details")
        '.Selected(0) = True
    End With

End Sub

你将拥有:

Private generalInfoControls As New GeneralInformationCreator
Private appDetailsControls As New ApplicationDetailsCreator
Private Sub UserForm_Initialize()

    generalInfoControls.Create Me.Controls
    appDetailsControls.Create Me.Controls

    With Me.listMenu 'don't refer to the default instance in the form's code-behind... EVER!
        .AddItem "General Information"
        .AddItem "Application Details"
        '.Selected(0) = True
    End With

End Sub

Create是一个采用Controls参数的过程 - 通过给它Me.Controls我们传递当前实例的控件集合用户表单,所以方法可以如下所示:

Public Sub Create(ByVal parent As Controls)
    Set labelGeneral = parent.Add("Forms.Label.1", "labelGeneral", True)
        With labelGeneral
            .Font.Size = 12
            .Top = 12
            .Left = 192
            .Height = 14.25
            .Width = 42
            .Caption = "General Information"
            .Name = labelGeneral
        End With
     '...

请注意,它不再与您的formRequestWizard表单的默认实例 耦合,并且可以使用任何表单实例' s Controls收集了它。

我不确定你是否还在按照我的思路进行思考,但这意味着你的处理程序现在可以执行此操作:

Private Sub listMenu_Change()
    generalInfoControls.SetVisibility listMenu.Selected(0)
    appDetailsControls.SetVisibility listMenu.Selected(1)
End Sub

这意味着您的XxxxCreator类的SetVisibility方法看起来像这样:

Public Sub SetVisibility(ByVal isVisible As Boolean)
    labelGeneral.Visible = isVisible
    frameGeneral.Visible = isVisible
    '...