将工作簿声明为全局变量

时间:2015-07-21 10:27:27

标签: excel vba excel-vba scope global-variables

我开始编写一个适用于多个工作簿的代码,但始终使用相同的参考工作簿。代码将有许多子代码,因为我试图避免将变量调暗到每个sub中的参考工作簿,我想将它们声明为Global。

首先我有:

Global Locations As Excel.Workbook
Set Locations = Workbooks.Open("M:\My Documents\MSC Thesis\Italy\Merged\locXws.xlsx")

哪位给了我:

  

“编译错误:外部程序无效”

经过一些谷歌搜索后,我在某处找到了以下代码:

Public Const Locations As Excel.Workbook = "Workbooks.Open("M:\My Documents\MSC Thesis\Italy\Merged\locXws.xlsx")"

哪位给了我:

  

“编译错误:预期:输入名称”

编辑:

使用:

Public Const Locations As Excel.Workbook = "Workbooks.Open('M:\My Documents\MSC Thesis\Italy\Merged\locXws.xlsx')"

(Workbooks.Open语句中的单引号)与使用双引号时的错误结果相同。

谁知道我做错了什么?

EDIT2:

我还试图在this answer之后使用以下内容声明“ThisWorkbook”中的变量:

Private Sub Workbook_Open()
Dim Locations As Excel.Workbook
Dim MergeBook As Excel.Workbook
Dim TotalRowsMerged As String


Locations = Workbooks.Open("M:\My Documents\MSC Thesis\Italy\Merged\locXws.xlsx")
MergeBook = Workbooks.Open("M:\My Documents\MSC Thesis\Italy\Merged\DURUM IT yields merged.xlsm")
TotalRowsMerged = MergeBook.Worksheets("Sheet1").UsedRange.Rows.Count
End Sub

然后它返回

  

“需要对象”

在我的模块中。

EDIT3:

我现在有这个有用,但是有不得不将SET行复制到每个Sub的缺点,必须有更好的方法来做到这一点吗?

Global Locations As Workbook
Global MergeBook As Workbook
Global TotalRowsMerged As String

Sub Fill_CZ_Array()
Set Locations = Application.Workbooks("locXws.xlsx")
Set MergeBook = Application.Workbooks("DURUM IT yields merged.xlsm")
TotalRowsMerged = MergeBook.Worksheets("Sheet1").UsedRange.Rows.Count

11 个答案:

答案 0 :(得分:20)

我认为工作簿全局变量最通用的方法是创建一个带有Public Property Get过程的模块。您可以在不先调用任何代码的情况下参考它,如果文件是否打开,您也不必担心。

以下是其中一个变量的示例模块代码:

Private wLocations As Workbook

Public Property Get Locations() As Workbook
  Const sPath As String = "M:\My Documents\MSC Thesis\Italy\Merged\locXws.xlsx"
  Dim sFile As String

  If wLocations Is Nothing Then
      'extract file name from full path
      sFile = Dir(sPath)

      On Error Resume Next

      'check if the file is already open    
      Set wLocations = Workbooks(sFile)

      If wLocations Is Nothing Then
        Set wLocations = Workbooks.Open(sPath)
      End If

      On Error GoTo 0
  End If
  Set Locations = wLocations
End Property

您可以在代码中的任何位置将其用作全局变量:

Sub Test()
  Debug.Print Locations.Worksheets.Count
End Sub

答案 1 :(得分:6)

您的问题意味着您需要全局工作簿常量,而不是变量。由于VBA不允许在过程之外初始化对象,因此不能使对象保持不变。您可以做的最好的事情是拥有一个在事件中初始化的公共工作簿变量。

您可以声明一个全局变量,但不能执行代码来在过程之外分配值:

Public myBook As Excel.Workbook

Sub AssignWorkbook()
    Set myBook = Workbooks.Open("C:\SomeBook.xlsx") '// <~~ valid, inside sub
End Sub

Sub TestItWorked()
    MsgBox myBook.Name
End Sub

所以在正常模块中你可以:

Public myBook As Excel.Workbook

Workbook_Open()事件中:

Private Sub Workbook_Open()
    Set myBook = Workbooks.Open("C:\SomeOtherBook.xlsx")
End Sub

然后,您可以在代码中的其他地方使用myBook,而无需重新分配。

值得一看Chip Pearson关于VBA变量范围的文章here

答案 2 :(得分:6)

你想要的是某种具有静态属性的Factory,例如在一个单独的模块中

<强> mFactoryWkbs

Private m_WkbLocations           As Workbook
Private m_WkbMergeBook           As Workbook

Public Property Get LOCATIONS() As Workbook
    If m_WkbLocations Is Nothing Then
        Set m_WkbLocations= Workbooks.Open("wherever")
    End If
    Set LOCATIONS = m_WkbLocations
End Property

Public Property Get MERGEBOOK () As Workbook
    If m_WkbMergeBook Is Nothing Then
        Set m_WkbMergeBook = Workbooks.Open("wherever")
    End If
    Set MERGEBOOK = m_WkbMergeBook 
End Property

要使用,只需致电该物业&amp;当你需要它时,不需要额外的变量(或者它们的集合)。

TotalRowsMerged = MERGEBOOK.Worksheets("Sheet1").UsedRange.Rows.Count

答案 3 :(得分:4)

这是迄今为止我能想到的最好的。结果是现在只有一个地方可以更改文件的名称,但是我仍然需要在每个子程序中复制SET函数。不完全理想,但更好,然后没有。

Public Const DESTBOOK = "DURUM IT yields merged.xlsm"

Global Locations As Workbook
Global MergeBook As Workbook
Global TotalRowsMerged As String

Sub Fill_CZ_Array()
Set Locations = Application.Workbooks("locXws.xlsx")
Set MergeBook = Application.Workbooks(DESTBOOK)
TotalRowsMerged = MergeBook.Worksheets("Sheet1").UsedRange.Rows.Count

答案 4 :(得分:4)

每当我遇到这个问题时,我都会将wb声明为公共常量字符串:

"text":"Holiday Celebrate \nHoliday Celebrate........

然后,在整个项目代码中,您可以参考

public wb as string = "c:\location"

答案 5 :(得分:3)

只有当您知道将从引用的工作簿中使用的所有工作表的编号和名称时,此解决方案才有效。

在您的模块中,为所有工作表声明工作表公共变量,如下所示:

Public sht1 As Worksheet
Public sht2 As Worksheet
Public sht3 As Worksheet
...

在应用程序加载事件中实例化这些公共变量。

Sub Workbook_Open()

    Workbooks.Open ("your referenced workbook")

    'Instantiate the public variables
    Set sht1 = Workbooks("Test.xlsm").Sheets("Sheet1")
    Set sht2 = Workbooks("Test.xlsm").Sheets("Sheet2")
    Set sht3 = Workbooks("Test.xlsm").Sheets("Sheet3")

End Sub

现在,您可以在子网中引用这些全局工作表。

例如:

Sub test()
    MsgBox sht1.Range("A1").Value
    MsgBox sht2.Range("A1").Value
    MsgBox sht3.Range("A1").Value
End Sub

答案 6 :(得分:3)

如果您创建一个模块说ExcelMod并且在该模块中您有一个公共函数或子例程Initialize()和另一个名为Terminate()的模块,您可以使用这些例程初始化和终止模块级变量。例如,我之前使用过这个:(注意模块变量是在模块顶部声明的第一件事。)

Dim excelApp As Object, wb As Workbook, ws As Worksheet

Sub Initialize()
    Set excelApp = CreateObject("Excel.Application")
    Set wb = Workbooks.Open("C:\SomeOtherBook.xlsx")
End Sub

Sub Terminate()
    Set excelApp = Nothing
    Set wb = Nothing
End Sub

变量是整个模块的一部分,只能通过这些子程序进行初始化和终止。您可以根据需要将变量传入和传出模块,并在所有这些模块子例程中使用它们,而无需再次设置。如果您需要在另一个模块中使用,则需要像往常一样将其传递给该模块。

正如其他人所提到的,你可以使用workbook_Open事件来调用初始化子来创建对象,并在需要时只设置一次。

这就是你想要的吗?

答案 7 :(得分:3)

您也可以使用类模块并依赖类初始化程序在模块中使用它时为您完成工作:

名为cLocations的类模块:

Public Workbook As Workbook

Private Sub Class_Initialize()
    Set Workbook = Workbooks.Open("C:\Temp\temp.xlsx")
End Sub

您喜欢的模块或任何地方:

Dim Locations As New cLocations

Sub dosomething()
    Locations.Workbook.Sheets(1).Cells(1, 1).Value = "Hello World"
End Sub

然后,您可以使用Locations.Workbook引用位置工作簿,ThisWorkbook引用代码运行的工作簿,ActiveWorkbook引用工作簿有重点。这样,您可以使用位置工作簿(ThisWorkbook)作为参考从一个工作簿(Locations.Workbook)运行代码,并迭代其他工作簿(ActiveWorkbook)以添加另一个级别的自动化

如果单步执行代码,您将看到只有在遇到需要它的代码行时才会初始化类,而不是在加载工作簿时。

我必须补充一下,在这种情况下,我认为如果你给我们一个稍微大一点的图片来说明你想要实现的目标,我们可能能够为你提供一个比你在编码时遇到的更好的问题的解决方案。

您还可以更进一步,抽象到应用程序级别,隐藏位置工作簿,如果您明确知道他们的位置或名称,甚至为命名工作表提供智能感知:

课程模块:

Private App As Application
Public Workbook As Workbook
Public NamedSheet As Worksheet

Private Sub Class_Initialize()
    Set App = New Application
    App.Visible = False
    App.DisplayAlerts = False
    Set Workbook = App.Workbooks.Open("C:\Temp\temp.xlsx") 'maybe open read only too?
    Set NamedSheet = Workbook.Sheets("SomethingIKnowTheNameOfExplicitly")
End Sub

Public Sub DoSomeWork()
    'ThisWorkbook refers to the one the code is running in, not the one we opened in the initialise
    ThisWorkbook.Sheets(1).Cells(1, 1).Value = Wb.Sheets(1).Cells(1, 1).Value
End Sub

Public Function GetSomeInfo() As String
    GetSomeInfo = NamedSheet.Range("RangeIKnowTheNameOfExplicitly")
End Function

然后在你的模块中,第一次使用变量时,它将在一行代码中初始化:

Dim Locations As New cLocations
Dim SomeInfo

Sub DoSomething()
    SomeInfo = Locations.GetSomeInfo 'Initialised here, other subs wont re-initialise

    Locations.Workbook.Sheets(1).Cells(1, 1).Value = _ 
        ThisWorkbook.Sheets(1).Cells(1, 1).Value

    Locations.NamedSheet.Cells(1,1).Value = "Hello World!"

    Locations.Workbook.Save
End Sub

答案 8 :(得分:3)

当我有需要正确初始化的全局变量时,我通常会这样做:

在通用代码模块中输入以下代码:

Public Initialized As Boolean
Public Locations As Workbook

Sub Initialize()
    If Initialized Then Exit Sub
    Const fname As String = "M:\My Documents\MSC Thesis\Italy\Merged\locXws.xlsx"
    On Error Resume Next
        Set Locations = Workbooks(Dir(fname))
    On Error GoTo 0
    If Locations Is Nothing Then
        Set Locations = Workbooks.Open(fname)
    End If
    Initialized = True
End Sub

然后在工作簿的代码模块中输入:

Private Sub Workbook_Open()
    Initialize
End Sub

此外,在可能启动代码的任何“网关”子或函数(例如事件处理程序,UDF等)中,将Initialize(或者可能:If Not Initialized Then Initialize)作为第一行。通常,大多数潜艇不会直接启动,并且可以依赖于呼叫者正确设置的Locations。如果您需要测试在未设置变量时无法正常运行的内容,则可以直接在立即窗口中键入initialize

答案 9 :(得分:2)

如果我正确理解您的问题,那么您创建的代码应该在应用程序级别而不是在工作簿级别上运行。在这种情况下,为什么不创建加载项。

加载项中的所有代码都可以访问应用程序级别的所有打开的工作簿。

答案 10 :(得分:1)

您可能想要创建加载项,或使用类模块来处理属性,...

但是我不确定它是更清洁而不是常规模块中的简单声明,并且在工作簿的开放中调用该过程将会成功也很好。

(我已经使用这种方法已经很多次并且没有受到打扰)

所以你可以在(专用或非专用)常规模块中使用它

'Set the path to your files
Public Const DESTBOOK = "M:\My Documents\MSC Thesis\Italy\Merged\DURUM IT yields merged.xlsm"
Public Const LOCBOOK = "M:\My Documents\MSC Thesis\Italy\Merged\locXws.xlsx"

'Declare all global and public variables
Global Locations As Workbook
Global MergeBook As Workbook
Global TotalRowsMerged As String

'Set all variable (Procedure call from Workbook_Open)
Sub Set_All_Global_Variables()
    Set Locations = Set_Wbk(LOCBOOK)
    Set MergeBook = Set_Wbk(DESTBOOK)
    TotalRowsMerged = MergeBook.Worksheets("Sheet1").UsedRange.Rows.Count
    '...
End Sub

'Function to check if the workbook is already open or not
Function Set_Wbk(ByVal Wbk_Path As String) As Workbook
    On Error Resume Next
        Set Set_Wbk = Workbooks(Dir(Wbk_Path))
    On Error GoTo 0
    If Set_Wbk Is Nothing Then
        Set Set_Wbk = Workbooks.Open(Wbk_Path)
    End If
End Function

并且调用此过程设置ThisWorkbook模块中的所有变量

Private Sub Workbook_Open()
    Set_All_Global_Variables
End Sub