How to set a Master Workbooks Object? - excel

I was thinking if its possible to set a Master Workbook in Excel VBA and access it on all procedures and functions?
Since I am pretty new to VBA I don't understand the syntax VBA is using..
Dim MasterWB as Workbook
Set MasterWB = Workbooks.Open("Path to my Workbook")
But if I want to access it in an another Procedure or Function I'll get a Object Definition Error.
I don't want to declare the Object in every Sub or Function.
Thanks a lot!

There are two ways of doing this:
The good practice:
Option Explicit
Sub Test()
Dim MasterWB As Workbook
Set MasterWB = Workbooks.Open("Path")
UseWb MasterWB
End Sub
Sub UseWb(wb As Workbook)
wb.Close
End Sub
The you shouldn't use practice:
Option Explicit
Public MasterWB As Workbook
Sub Test()
Set MasterWB = Workbooks.Open("Path")
UseWb MasterWB
End Sub
Sub UseWb()
wb.Close
End Sub
The first one allows you to pass the Workbook Variable as parameter to other functions in this case you can't use ByVal but other variables like Integeror Long can be used as ByVal instead of (per default) ByRef.
ByVal means you are only passing the value of that variable, so you won't modify it on the function.
ByRef means you pass the reference, so the other function can modify that original variable.
The second way only allows you to use the variable as a reference so there might be changes on it without noticing.
Hope this clears out a bit your question.

Related

Check if Workbook variable has been set

I have a sub with an optional workbook argument. I want an If to check whether that variable was passed, and if not set the variable to the active worksheet.
Sub SaveWorkbook(NewPathName As String, Optional Workbook As Workbook)
'This is very simplified, the real thing has various other parameters
If IsNull(Workbook) Then Set Workbook = ActiveWorkbook
'Then loads more stuff
End Sub
Things I have tried include:
IsNull(Workbook)
IsEmpty(Workbook)
Workbook = Nothing
None trigger the If statement so the code attempts to continue with Workbook set to Empty, and then hits errors.
Do not use the word "Workbook" as the variable name. Try it like this:
Sub SaveWorkbook(NewPathName As String, Optional wb As Workbook)
If wb Is Nothing Then
MsgBox "workbook not set"
Set wb = ActiveWorkbook
End If
MsgBox wb.Name
End Sub
In VBA the isMissing function only works, if you declare the parameter as Variant. See in the description
This should work:
Sub SaveWorkbook(NewPathName As String, Optional Workbook As Variant)
'This is very simplified, the real thing has various other parameters
If isMissing(Workbook) Then Set Workbook = ActiveWorkbook
'Then loads more stuff
End Sub

Can you call a module from a userform whose variable is declared from a combo box?

I created a userform sub that will allow the user to choose from the open workbook to use as the reference workbook using a combo box. My current script is returning an error that workbooks(wb) is not defined - I assume this is because the variable is defined in two different modules and the combo box is not in the module being called. Ideally would like to use the userform below
Private Sub Go_Click()
If ComboBox1.ListIndex = -1 Then
MsgBox "Please select a workbook name and try again"
Exit Sub
End If
Dim wb As Variant
wb = ComboBox1.List(ComboBox1.ListIndex)
Call GenerateReportUserForm
End Sub
To call this sub:
Sub newMacro()
Dim copyNames As Range, pasteNames As Range, copyAmounts As Range, pasteAmounts As Range, copyDates As Range, pasteDates As Range, _
copyPayment As Range, pastePayment As Range
' For cheques only
Set copyNames = Workbooks(wb).Worksheets(2).Columns("F")
Set copyAmounts = Workbooks(wb).Worksheets(2).Columns("AR")
Set copyDates = Workbooks(wb).Worksheets(2).Columns("AI")
Set copyPayment = Workbooks(wb).Worksheets(2).Columns("AJ")
Set pasteNames = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("A")
Set pasteAmounts = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("C")
Set pasteDates = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("D")
Set pastePayment = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("E")
copyNames.Copy Destination:=pasteNames
copyAmounts.Copy Destination:=pasteAmounts
copyDates.Copy Destination:=pasteDates
copyPayment.Copy Destination:=pastePayment
End sub
Thanks!
wb clearly wants to be a String representing the name of a workbook. Declare it as such.
Dim wbName As String
wbName = ComboBox1.List(ComboBox1.ListIndex)
Side note, name your controls. AvailableFilesBox tells so much more than ComboBox1.
Now, what you want is to pass this variable as an argument; do not use a global variable unless you absolutely MUST.
Call GenerateReportUserForm
Not sure what this is supposed to be doing, but it's not calling newMacro. If you want to make it call newMacro, then change it to this:
NewMacro wbName
Or if you really really want to keep that redundant and distracting Call keyword:
Call NewMacro(wbName)
Note: give that macro a meaningful name that describes what the macro does. "new macro" might be clear now, but not so much once there are 4-5 newer macros in that project - and newMacro2 is NOT an option!
Now, in order to pass wbName as an argument, the procedure needs to declare that it takes a parameter - like this:
Public Sub NewMacro(ByVal wbName As String)
Inside that procedure scope, you don't need to constantly dereference the Workbook object. Do it once, store the object reference into a local variable, then use that variable:
Dim wb As Workbook
Set wb = Workbooks(wbName)
Turns out, that macro doesn't really care for the workbook's name; what it really actually wants is a Workbook object. So, let's make it the caller's responsibility to provide a Workbook.
First we change the signature to take a Workbook parameter:
Public Sub NewMacro(ByVal wb As Workbook)
Then we change the form code to supply it:
Dim wb As Workbook
Set wb = Workbooks(ComboBox1.List(ComboBox1.ListIndex))
NewMacro wb ' or: Call NewMacro(wb)
Remember to always put Option Explicit at the top of every module; Rubberduck can help you find & fix this, and other issues in your code.

Is it possible to return a worksheet type variable in a function?

In this project I'm developing, I created many Subs that all use the same three workbooks. But is there a better way to use these workbooks' sheets without having to write them down everytime I create a new Sub? I tried returning it in a function but it does not work.
Function defineWorksheet() As Worksheet
Dim wk_Base_18 As Excel.Workbook
Dim ws_18 As Excel.Worksheet
Set wk_Base_18 = Excel.Workbooks("2019.01.03.xlsb")
Set ws_18 = wk_Base_18.Worksheets("Planilha1")
ws_18
End Function
error 91
Yes, you can declare them as a global variable.
Public ws1 As Worksheet
Then instantiate the global variable during the application load event of the excel application
Private Sub Workbook_Open()
Set ws1 = ThisWorkbook.Sheets("YourSheetName")
End Sub
And now, you can refer to it via the variable, eg.
Dim x as Integer: x = ws1.Range("B5")
The general idea is sound, you just need to create one function per object:
Function wk_Base_18() as workbook
Set wk_Base_18 = Excel.Workbooks("2019.01.03.xlsb")
End Function
Function ws_18() as worksheet
Set ws_18 = wk_Base_18.Worksheets("Planilha1")
End Function
Then whenever you go to use the variable ws_18 or wk_Base_18, you will be calling these same functions.

Excel VBA pass object ByRef never sems to work

I've been using VBA for many years but in all that time I've never managed to pass a workbook or sheet ByRef, I've had to use string names & set the objects in the partner sub or function... so finally it's time to get some help!
Sub SubOne()
Dim wb as workbook
Dim filepath as string
filepath = "//somepath/somebook.xlsx"
Set wb = application.workbooks.open(filepath)
Call SubTwo(wb)
End Sub
Sub SubTwo(ByRef wb as workbook)
debug.print wb.name
End Sub
Can anyone see why this would trigger a ByRef type mismatch compile error? What am I missing?
Many thanks
You can avoid problems like these by not using the Call Keyword.
Instead of Call SubTwo(wb) use SubTwo wb
Related information: Should I use Call keyword in VB/VBA?
Your original code worked for me but there might have been minor differences
in white space or parentheses that caused the problem. VBA uses parentheses not only to pass arguments to subs / functions but also to evaluate data.
Another point to mention is that ByVal and ByRef should both work for what you are trying to do since Objects are always passed by reference in VBA. ByVal / ByRef only define if the reference itself is passed by value or reference: https://msdn.microsoft.com/en-us/library/ddck1z30.aspx
I want to leave my previous answer here because it is still a valid answer for the posted error message and might help someone in search of a solution.
Previous answer:
My guess is that one of your loaded AddIns is using a Module, ClassModule, Enum etc. named workbook and this causes the compile error.
If you look at your code you will also see that workbook is written lowercase. Usually the VBA Editor would autocorrect this to Workbook unless some other type name is interfering.
To avoid this replace workbook with Excel.Workbook and please try again.
Your code should then look like this:
Sub SubOne()
Dim wb as Excel.Workbook
Dim filepath as string
filepath = "//somepath/somebook.xlsx"
Set wb = application.workbooks.open(filepath)
Call SubTwo(wb)
End Sub
Sub SubTwo(ByRef wb as Excel.Workbook)
debug.print wb.name
End Sub
This works for me:
Sub SubOne()
Dim wb as workbook
Set wb = This.ActiveWorkbook
Call SubTwo(wb)
End Sub
Sub SubTwo(ByRef wb As Workbook)
Debug.Print(wb.Name)
End Sub
I had the same issue. After lot of try and fail, I added option Explicit on top. When I executed the code it showed me that the declaration of the Worksheet variable had a name mismatch with the variable which was passed. That is, shtFSheet was declared and strFSheet was passed. Changing this solved my problem. Hope this helps somebody.

Specifying a range to be applied to all subs in a module?

I have been working on creating a module that has multiple subs and functions that all are applied to the same sheet. In my efforts and research to clean up my code I found that instead of declaring the "Dim" for each sub, I can declare it at the very top of the module by using either "Dim" or "Private".
Sub Sample()
Dim DataSheet As Range
'Only declared for this sub, doesn't apply to other subs
'on the other hand,
Private DataSheet As Range
Sub Sample()
'declares it for each sub in this module.
What I can't figure out is, is there a way to set the value or in this case the exact range that I want to assign to "DataSheet" that will apply to the entire module? Currently each of my subs contains,
Set DataSheet = ThisWorkbook.Sheets(1).Range("A3:FU5002")
which, since this range is constant and never changes, seems a little redundant.
Create a special sub to perform the initialization and run it first:
Dim DataSheet As Range
Sub RunMeFirst()
Set DataSheet = ThisWorkbook.Sheets(1).Range("A3:FU5002")
End Sub
add in a global variable in the ThisWorkbook module and use the workbook open event to set the value.
Public DataSheet As Range
Private Sub Workbook_Open()
Set DataSheet = ThisWorkbook.Sheets(1).Range("A3:FU5002")
End Sub

Resources