我开始编写一个适用于多个工作簿的代码,但始终使用相同的参考工作簿。代码将有许多子代码,因为我试图避免将变量调暗到每个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语句中的单引号)与使用双引号时的错误结果相同。
谁知道我做错了什么?
我还试图在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
然后它返回
“需要对象”
在我的模块中。
我现在有这个有用,但是有不得不将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
答案 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