I could use some help in understanding using Public vs Dim in a module in Excel 2013 VBA.
First I want to say I did find this great post with excellent definitions (see link below), but no examples and I could use some examples of how I could apply the Public Variables to my project. Also I am a little confused on when I would need to use the Option Private Module; would I need to use that on each module I have or just the module that holds the below code?
stackoverflow descriptions difference between Public/Private
What I would like to do is set this up in a Standard Mod so I dont have to continue setting variables for worksheets through all of my UserForms that use the same naming convention for Worksheets they reference.
Sub PubVar()
Public wb As Workbook
Public wsSI As Worksheet
Public wsRR As Worksheet
Public wsCalcs As Worksheet
Public wsNarr As Worksheet
Public wsEval As Worksheet
Public wsUW As Worksheet
Public wsLVBA As Worksheet
Set wb = Application.ThisWorkbook
Set wsSI = wb.Sheets("SavedInfo")
Set wsCalcs = wb.Sheets("Calcs")
Set wsNarr = wb.Sheets("Narrative")
Set wsEval = wb.Sheets("EvalCL")
Set wsUW = wb.Sheets("UWCL")
Set wsLVBA = wb.Sheets("ListsForVBA")
End Sub
Thank you for your assistance.
You really should only need Option Private Module
if you are making your own Excel Add-In (*.XLAM file). It allows a module's Public
variables to be visible to other modules within the Add-In's own project but keeps them from being visible/callable by other worksheets that use your AddIn.
As for the rest, it's not clear what you are asking. The question you linked has a good explanation of Public vs Dim/Private. Private/Dim variables cannot be seen by the VBA code in different Modules, Forms or Classes in the same VBA project. You use public if you want all of your VBA code to be able to see/call it. You use private/dim if you want it to only be visible/callable from within it's own module.
But generally you want to be judicious with what you make public, both because it can give you too many global names (confusing), can cause problems with duplicate names (problematic, can be addressed with naming standard) and most of all because it can lead to confusing/obscure bugs and horrendous debugging problems (because any public variable change could cause any other code that can see it to behave differently). And some things are worse than others:
Public Subs OK in a module, but better in a Class
Public Function Usually fine, especially if its a true function
Public Const No problem, belongs in modules
Public Variables Very Bad. *UNLESS ...*
Public variables are the ones that good coders worry about. They are to be avoided, UNLESS they are what we call "Read-Only Variables". These are variables that you set only once, and never change again. As long as you follow that rule, they are effectively constants and are OK (though read-only static properties would be better, but VBA has too many limitations in this regard).
Finally If you want to use named variables for all of your worksheets that are visible/useable from all code, but you only have to setup once, that is what Public
is for, but the declaration ("Public ") needs to be at "the Module Level" which means, in a module, but outside of any subroutine or function. Like this:
Public wb As Workbook
Public wsSI As Worksheet
Public wsRR As Worksheet
Public wsCalcs As Worksheet
Public wsNarr As Worksheet
Public wsEval As Worksheet
Public wsUW As Worksheet
Public wsLVBA As Worksheet
' sets all of the Worksheet variables
Sub PubVar()
Set wb = Application.ThisWorkbook
Set wsSI = wb.Sheets("SavedInfo")
Set wsCalcs = wb.Sheets("Calcs")
Set wsNarr = wb.Sheets("Narrative")
Set wsEval = wb.Sheets("EvalCL")
Set wsUW = wb.Sheets("UWCL")
Set wsLVBA = wb.Sheets("ListsForVBA")
End Sub
Option Private Module
Option Private Module
should be used in any standard module that doesn't mean to expose its public members to Excel as macros (i.e. Public Sub
procedures) or User-Defined-Functions (i.e. Public Function
procedures).
Without this option, a standard module's public parameterless Sub
procedures appear in Excel's list of available macros, and public Function
procedures appear in Excel's cell "intellisense" as available worksheet functions.
Note that this merely hides a module's members from the macros list: if you type the exact name of a "hidden" procedure, Excel will still run it.
Dim vs Private vs Public
Dim
is a keyword you use for declaring local variables, inside a procedure scope. The keyword is also legal for declaring private, module-level variables, but then you might as well use Private
.
When used for declaring module-level variables, Private
makes that variable only accessible from within the module it's declared in.
When used for declaring module-level variables, Public
makes that variable accessible from anything that has access to the module it's declared in - in a standard module, that means the variable is effectively Global
. In a class (/document/userform/anything else) module, it means the variable holds instance state and is accessible from anything that has access to an instance of that class. Classes that have a predeclaredId, such as UserForm
classes, all have an instance that's globally accessible: avoid storing instance state in this default instance.
Use Worksheet.CodeName
Set wb = Application.ThisWorkbook
Set wsSI = wb.Sheets("SavedInfo")
Set wsCalcs = wb.Sheets("Calcs")
Set wsNarr = wb.Sheets("Narrative")
Set wsEval = wb.Sheets("EvalCL")
Set wsUW = wb.Sheets("UWCL")
Set wsLVBA = wb.Sheets("ListsForVBA")
ThisWorkbook
is the workbook you're looking at - the one that contains your VBA code. The ThisWorkbook
identifier is globally accessible, and Application.ThisWorkbook
is merely a pointer to that object.
Use ThisWorkbook
over Application.ThisWorkbook
, unless you've declared a local variable and named it ThisWorkbook
- then that local variable would be shadowing the global identifier; don't do that. There shouldn't be any reason to need to qualify ThisWorkbook
with Application
.
Now, if any of these worksheets exist at compile-time in ThisWorkbook
, then you don't need any of these variables. Find each sheet in the Project Explorer (Ctrl+R), then hit F4 and give its (Name)
property a meaningful identifier name.
So if you rename Sheet1
to SavedInfoSheet
, then you can access SavedInfoSheet
from anywhere in the code, and you don't ever need to dereference it from the Workbook.Sheets
(or better, Workbook.Worksheets
) collection. The reason for this is that VBA automatically creates a global-scope identifier by the name of whatever identifier you put as the (Name)
property of a Worksheet
module.
If the sheets don't exist at compile-time (i.e. they're created at run-time), then you don't need these variables either, because the code that created them should already have that reference:
Set theNewSheet = theBook.Worksheets.Add
Then you can (and should) pass these worksheet object references around, as parameters, as needed.
There is no worksheet.
What I would like to do is set this up in a Standard Mod so I dont have to continue setting variables for worksheets through all of my UserForms that use the same naming convention for Worksheets they reference
Your forms are running the show. The code that fires them looks like this:
UserForm1.Show
Like any UI, forms are responsible for collecting user input, and showing data to the user. If you find yourself writing userform code-behind that accesses a dozen worksheets (and/or worse, makes them public fields), you're making your form much, much more complicated than it needs to be, and you're treating a full-fledged object as a mere container for procedures, by making its default instance stateful.
This article goes in details about how to fix that. This article pushes the concept further and allows back-and-forth communication between the view and the presenter, and has a download link with a simple example to study (disclaimer: I wrote these articles, and the accompanying example code).
UserForm code done right, looks extremely simple, and is responsible for so little logic, it's boring. In fact, it's not responsible for any logic beyond presentation - all a UserForm should do, is respond to control events, relay control state to some model, and if application logic needs to be executed before the form is closed (e.g. if a command button is clicked but the form should remain open), then it fires an event, and the calling code ("presenter") handles it by triggering the logic that needs to run.
When the dialog is okayed, or when it relays an event to the presenter, code outside the form's code-behind is executed to to the work: the form never needs to know anything about any worksheet.