web-dev-qa-db-ja.com

ワークブックをグローバル変数として宣言する

複数のワークブックに適用できるようになるが、常に同じ参照ワークブックを使用するコードを書き始めています。コードには多くのサブがあり、すべてのサブで参照ワークブックへの変数を暗くしないようにしているので、それらをグローバルに宣言したいと思います。

最初に私は持っていました:

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:

「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

しかし、それはそれを返します

"必要なオブジェクト"

私のモジュール内。

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
16
Luuklag

ワークブックのグローバル変数の最も普遍的な方法は、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
20
BrakNicku

あなたの質問は、変数ではなくグローバルワークブック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

6
Sam

あなたが欲しいのは、例えば別のモジュールのような静的なプロパティを持つある種のファクトリです

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
6
Berryl

これに遭遇するたびに、wbをパブリック定数文字列として宣言します。

public wb as string = "c:\location"

次に、プロジェクトのコード全体で、

workbooks(wb).anything
4
user3476534

これは私が今まで思いつくことができる最高のものです。その結果、ファイルの名前を変更する場所は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
4
Luuklag

このソリューションは、参照するブックから使用するすべてのワークシートの数と名前がわかっている場合にのみ機能します。

モジュールで、次のようにすべてのワークシートのワークシートパブリック変数を宣言します。

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
3
ManishChristian

これは、適切に初期化する必要のあるグローバル変数がある場合に、私が通常行うことです。

一般的なコードモジュールに次のコードを追加します。

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と入力します。

3
John Coleman

また、クラスモジュールを使用してそれを行い、モジュールで使用されるときにクラスイニシャライザーに依存して作業を行うこともできます。

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
3
user1641172

モジュールを作成して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回だけ設定できます。

これはあなたが求めているものですか?

3
Clint Street

私があなたの質問を正しく理解していれば、ワークブックレベルではなく、アプリケーションレベルで機能するコードを作成しています。この場合、なぜアドインを作成しないのでしょうか。

アドイン内のすべてのコードは、アプリケーションレベルで開いているすべてのワークブックにアクセスできます。

2
cyboashu

アドインを作成したり、クラスモジュールを使用してプロパティを操作したりすることができます...

しかし、それは通常のモジュールでの単純な宣言とワークブックのそのプロシージャへの呼び出しよりも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
1
R3uK