複数のワークブックに適用できるようになるが、常に同じ参照ワークブックを使用するコードを書き始めています。コードには多くのサブがあり、すべてのサブで参照ワークブックへの変数を暗くしないようにしているので、それらをグローバルに宣言したいと思います。
最初に私は持っていました:
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ステートメント内の単一引用符)は、二重引用符を使用する場合と同じエラーになります。
私が間違っていることを誰が知っていますか?
「ThisWorkbook」で変数を宣言しようとしました this answer を使用して、次のようにしています:
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
ワークブックのグローバル変数の最も普遍的な方法は、Public Property Get
プロシージャ。最初にコードを呼び出さなくても参照でき、ファイルが開いているかどうかを心配する必要はありません。
以下は、変数の1つのサンプルモジュールコードです。
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
あなたの質問は、変数ではなくグローバルワークブックconstantが必要であることを意味します。 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
を使用できます。コードを再度割り当てる必要はありません。
VBAの変数スコープに関するチップピアソンの記事を一読する価値があるかもしれません here
あなたが欲しいのは、例えば別のモジュールのような静的なプロパティを持つある種のファクトリです
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
使用するには、必要なときに&だけプロパティを呼び出します。追加の変数(またはそれらのセット)は必要ありません。
TotalRowsMerged = MERGEBOOK.Worksheets("Sheet1").UsedRange.Rows.Count
これに遭遇するたびに、wbをパブリック定数文字列として宣言します。
public wb as string = "c:\location"
次に、プロジェクトのコード全体で、
workbooks(wb).anything
これは私が今まで思いつくことができる最高のものです。その結果、ファイルの名前を変更する場所は1つだけになりましたが、すべてのサブルーチン内で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
このソリューションは、参照するブックから使用するすべてのワークシートの数と名前がわかっている場合にのみ機能します。
モジュールで、次のようにすべてのワークシートのワークシートパブリック変数を宣言します。
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
これは、適切に初期化する必要のあるグローバル変数がある場合に、私が通常行うことです。
一般的なコードモジュールに次のコードを追加します。
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
と入力します。
また、クラスモジュールを使用してそれを行い、モジュールで使用されるときにクラスイニシャライザーに依存して作業を行うこともできます。
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
を使用してフォーカス。このように、場所のワークブック(Locations.Workbook
)を参照として使用して1つのワークブック(ThisWorkbook
)からコードを実行し、他のワークブック(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
そして、あなたのモジュールでは、変数を初めて使用するときに、1行のコードで初期化されます。
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
モジュールを作成して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イベントを使用して初期化サブルーチンを呼び出してオブジェクトを作成し、必要に応じて1回だけ設定できます。
これはあなたが求めているものですか?
私があなたの質問を正しく理解していれば、ワークブックレベルではなく、アプリケーションレベルで機能するコードを作成しています。この場合、なぜアドインを作成しないのでしょうか。
アドイン内のすべてのコードは、アプリケーションレベルで開いているすべてのワークブックにアクセスできます。
アドインを作成したり、クラスモジュールを使用してプロパティを操作したりすることができます...
しかし、それは通常のモジュールでの単純な宣言とワークブックのそのプロシージャへの呼び出しよりもcleanerになるかどうかはわかりませんopenでもトリックがうまくいきます。
(私はかなり長い間この方法を使用しており、気になりませんでした)
したがって、これを(専用モジュールかそうでないか)通常のモジュールで使用する:
'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