Use a variable to refer to other variables? - excel

So my macro uses many string constants to store the filenames of workbooks I want to edit. Each variable uses the naming convention template and then a 3 digit number (e.g. template001). Is there a way I can use an integer variable to refer to which constant I want to use? I'm imagining something like this:
Public Const filepath = "C:\Templates\"
Public Const template001 = "001.xls"
Sub printTemplate()
Dim num As Integer: num = 001
Dim template As Workbook
Set template = Workbooks.Open(filepath & GetVar("template" & num)) 'Complete guess at what the function is called
template.PrintOut
template.Close False
End Sub
Is there any such function that does this?

Related

Call constant using literals in runtime

I have declared constants like the below in the module,
Public Const strFolderA1 = "C:\ABCD\One"
Public Const strFolderA2 = "C:\ABCD\two"
I am trying to call this in a loop,
For i = 1 To 3
strFile = Dir(strFolderA & i & "\" & filenm)
Loop
The above Dir code is wrong, but I want to call the constant based on the looping integer.
Could someone help?
Please let me know if the question is not clear.
VBA does not provide any method for concatenating a string to be used as a dynamic variable name. You could create a string constant with a delimiter then split it before use.
Option Explicit
Public Const strFolderA As String = "C:\ABCD\One|C:\ABCD\Two|C:\ABCD\Three"
Sub abcs()
Dim i As Long, fldrs As Variant
fldrs = Split(strFolderA, "|")
For i = LBound(fldrs) To UBound(fldrs)
Debug.Print fldrs(i)
Next i
End Sub

Excel VBA - calling global variables by concatenated name

I have these
Public Const WS1 = "SheetNameA"
Public Const WS2 = "SheetNameB"
Public Const WS3 = "SheetNameC"
Public Const WS4 = "SheetNameD"
Public Const WS5 = "SheetNameE"
Public Const WS6 = "SheetNameF"
Where SheetNameA-B-C are the names of sheets in a workbook.
And I would like to activate worksheets one after the other using a For loop 1 to 6like so:
For i = 1 to 6
Workbooks([wbkname]).Sheets(WS & i).Activate
[some more code doing stuff]
Next i
Now I know that doesn't work, I'm just trying to explain what I want.
Is that possible? To call a public constant by a concatenated name (to make that public constant string the name of the sheet we're activating)?
I hope that makes sense.
Edit: On second thought, maybe I'll just do a Select Case, which I know is not pretty, but I think, will be fine for my purpose.
For i = 1 to 6
Select Case i
Case 1
somevariable = [name of one sheet]
Case 2
somevariable = [name of another sheet]
....
End Select
Workbooks([wbkname]).Sheets(somevariable).Activate
Just use an array. First run MAIN(), then run marine():
Public WS(1 To 6) As String
Sub MAIN()
WS(1) = "SheetNameA"
WS(2) = "SheetNameB"
WS(3) = "SheetNameC"
WS(4) = "SheetNameD"
WS(5) = "SheetNameE"
WS(6) = "SheetNameF"
End Sub
Sub marine()
For i = 1 To 6
Workbooks("Book1").Sheets(WS(i)).Activate
[some more code doing stuff]
Next i
End Sub
I'm not aware of an "indirect" function in VBA, what you can do is to define a public array and store the values there, here you can get the values by index. As its not possible to define array constants in VBA you need to initialize your array before use (e.g. In the workbook's load event).

read constant from a cell

I am writing a VBA code to read some text files, I would like the file names to be read directly from cells in excel.
I used the following code
Sub ImportFile()
Const textFilePath As String = "C:\Desktop\"
Const textFileName As String = "File1.txt"
Const newTextFileName As String = "NewFile1.txt"
I want to have something like
Sub ImportFile()
Const textFilePath As String = Range("A2").value
Const textFileName As String = Range("A3").value
Const newTextFileName As String = Range("A4").value
I tried different combinations but it does not seem to like the idea of assigning a constant to a cell value, any work around?
Thanks
P.S I don't know how to write the code in a correct way in this website, I tried to add 4 spaces before the code lines but did not seem to work, any idea why?
I don't think you can assign a constant from a range because the range value could change during your code execution. I tried your declarations in this bit of code:
Option Explicit
Sub ImportFile()
Const textFilePath As String = Range("A2").Value
Const textFileName As String = Range("A3").Value
Const newTextFileName As String = Range("A4").Value
End Sub
and on Debug..Compile in the IDE I got this error
If you change your code to
Option Explicit
Sub ImportFile()
Dim textFilePath As String
Dim textFileName As String
Dim newTextFileName As String
textFilePath = Range("A2").Value
textFileName = Range("A3").Value
newTextFileName = Range("A4").Value
End Sub
You'll find it compiles OK.
Unless you're talking "Universal (physical) constants" like PI,c or G for example, constants rarely are constant. I've found that having a named range, possibly on a hidden sheet, and populating a variable from that named range makes for easier maintenance by users with little no VBA experience. Sure, you need to keep track of those "constants" to maintain them but at least you don't need VBA experience to do so.
You can't do that in vba since your constant variables might change. However, I think that you want to declare and initialize your path variables once so that later if you decide to change them you can do that in one place in the same row where the variable was initialized. So If I were you I would use something like
Option Explicit
sub macro()
Dim FILEPATH As String: FILEPATH = Range("A1").Value
MsgBox FILEPATH
end sub

Can I Evaluate An Excel VB Constant That Is In String Format?

Is it possible to Evaluate a String which contains a valid Excel VB Constant's Name
to return that Constant's Value?
eg
Dim ConstantName as String
Dim ConstantValue as Long
ConstantName="xlValues"
ConstantValue= UnknownFunction(ConstantName)
'would set ConstantValue=-4163
Fun!
Option Explicit
Function getConstantValue(constStr As String) As Variant
Dim oMod As VBIDE.CodeModule
Dim i As Long, _
num As Long
Set oMod = ThisWorkbook.VBProject.VBComponents("Module1").CodeModule
For i = 1 To oMod.CountOfLines
If oMod.Lines(i, 1) = "Function tempGetConstValue() As Variant" Then
num = i + 1
Exit For
End If
Next i
oMod.InsertLines num, "tempGetConstValue = " & constStr
getConstantValue = Application.Run("tempGetConstValue")
oMod.DeleteLines num
End Function
Function tempGetConstValue() As Variant
End Function
All code must be in a module called Module1. That can be changed pretty simply by changing the text "Module1" in the routine.
You'll need to add a reference to Microsoft Visual Basic for Applications Extensibility x.x
There are a number of ways this could fail. Let me know if you have any problems with it :)
Instead of using constants, you could use a dictionary
Dim dict As Object
Sub InitialiseDict()
Set dict = CreateObject(Scripting.Dictionary)
dict("xlValues") = -4163
dict("const1") = value1
...
dict("constN") = valueN
End Sub
ConstValue = dict("xlValues")
Is using the string value necessary?
Dim anyConstant as Long
anyConstant = xlValues
msgbox anyConstant
Set anyConstant to any xl constant you please, they are all enumerated Long values.
The first solution offered is indeed much more fun however.

How do I declare a global variable in VBA?

I wrote the following code:
Function find_results_idle()
Public iRaw As Integer
Public iColumn As Integer
iRaw = 1
iColumn = 1
And I get the error message:
"invalid attribute in Sub or Function"
Do you know what I did wrong?
I tried to use Global instead of Public, but got the same problem.
I tried to declare the function itself as `Public, but that also did no good.
What do I need to do to create the global variable?
You need to declare the variables outside the function:
Public iRaw As Integer
Public iColumn As Integer
Function find_results_idle()
iRaw = 1
iColumn = 1
This is a question about scope.
If you only want the variables to last the lifetime of the function, use Dim (short for Dimension) inside the function or sub to declare the variables:
Function AddSomeNumbers() As Integer
Dim intA As Integer
Dim intB As Integer
intA = 2
intB = 3
AddSomeNumbers = intA + intB
End Function
'intA and intB are no longer available since the function ended
A global variable (as SLaks pointed out) is declared outside of the function using the Public keyword. This variable will be available during the life of your running application. In the case of Excel, this means the variables will be available as long as that particular Excel workbook is open.
Public intA As Integer
Private intB As Integer
Function AddSomeNumbers() As Integer
intA = 2
intB = 3
AddSomeNumbers = intA + intB
End Function
'intA and intB are still both available. However, because intA is public, '
'it can also be referenced from code in other modules. Because intB is private,'
'it will be hidden from other modules.
You can also have variables that are only accessible within a particular module (or class) by declaring them with the Private keyword.
If you're building a big application and feel a need to use global variables, I would recommend creating a separate module just for your global variables. This should help you keep track of them in one place.
To use global variables, Insert New Module from VBA Project UI and declare variables using Global
Global iRaw As Integer
Global iColumn As Integer
The question is really about scope, as the other guy put it.
In short, consider this "module":
Public Var1 As variant 'Var1 can be used in all
'modules, class modules and userforms of
'thisworkbook and will preserve any values
'assigned to it until either the workbook
'is closed or the project is reset.
Dim Var2 As Variant 'Var2 and Var3 can be used anywhere on the
Private Var3 As Variant ''current module and will preserve any values
''they're assigned until either the workbook
''is closed or the project is reset.
Sub MySub() 'Var4 can only be used within the procedure MySub
Dim Var4 as Variant ''and will only store values until the procedure
End Sub ''ends.
Sub MyOtherSub() 'You can even declare another Var4 within a
Dim Var4 as Variant ''different procedure without generating an
End Sub ''error (only possible confusion).
You can check out this MSDN reference for more on variable declaration and this other Stack Overflow Question for more on how variables go out of scope.
Two other quick things:
Be organized when using workbook level variables, so your code doesn't get confusing. Prefer Functions (with proper data types) or passing arguments ByRef.
If you want a variable to preserve its value between calls, you can use the Static statement.
If this function is in a module/class, you could just write them outside of the function, so it has Global Scope. Global Scope means the variable can be accessed by another function in the same module/class (if you use dim as declaration statement, use public if you want the variables can be accessed by all function in all modules) :
Dim iRaw As Integer
Dim iColumn As Integer
Function find_results_idle()
iRaw = 1
iColumn = 1
End Function
Function this_can_access_global()
iRaw = 2
iColumn = 2
End Function
Also you can use -
Private Const SrlNumber As Integer = 910
Private Sub Workbook_Open()
If SrlNumber > 900 Then
MsgBox "This serial number is valid"
Else
MsgBox "This serial number is not valid"
End If
End Sub
Its tested on office 2010
The best way I find is to assign a property to the Workbook
It's scope remains valid as long as the workbook is open
Public WhenOpened As Date
Private Sub Workbook_Open()
ThisWorkbook.WhenOpened = Now()
End Sub
Create a public integer in the General Declaration.
Then in your function you can increase its value each time.
See example (function to save attachements of an email as CSV).
Public Numerator As Integer
Public Sub saveAttachtoDisk(itm As Outlook.MailItem)
Dim objAtt As Outlook.Attachment
Dim saveFolder As String
Dim FileName As String
saveFolder = "c:\temp\"
For Each objAtt In itm.Attachments
FileName = objAtt.DisplayName & "_" & Numerator & "_" & Format(Now, "yyyy-mm-dd H-mm-ss") & ".CSV"
objAtt.SaveAsFile saveFolder & "\" & FileName
Numerator = Numerator + 1
Set objAtt = Nothing
Next
End Sub
A good way to create Public/Global variables is to treat the Form like a class object and declare properties and use Public Property Get [variable] to access property/method. Also you might need to reference or pass a Reference to the instantiated Form module. You will get errors if you call methods to forms/reports that are closed.
Example: pass Me.Form.Module.Parent into sub/function not inside form.
Option Compare Database
Option Explicit
''***********************************''
' Name: Date: Created Date Author: Name
' Current Version: 1.0
' Called by:
''***********************************''
' Notes: Explain Who what when why...
' This code Example requires properties to be filled in
''***********************************''
' Global Variables
Public GlobalData As Variant
''***********************************''
' Private Variables
Private ObjectReference As Object
Private ExampleVariable As Variant
Private ExampleData As Variant
''***********************************''
' Public properties
Public Property Get ObjectVariable() As Object
Set ObjectVariable = ObjectReference
End Property
Public Property Get Variable1() As Variant
'Recommend using variants to avoid data errors
Variable1 = ExampleVariable
End property
''***********************************''
' Public Functions that return values
Public Function DataReturn (Input As Variant) As Variant
DataReturn = ExampleData + Input
End Function
''***********************************''
' Public Sub Routines
Public Sub GlobalMethod()
'call local Functions/Subs outside of form
Me.Form.Refresh
End Sub
''***********************************''
' Private Functions/Subs used not visible outside
''***********************************''
End Code
So in the other module you would be able to access:
Public Sub Method1(objForm as Object)
'read/write data value
objForm.GlobalData
'Get object reference (need to add Public Property Set to change reference object)
objForm.ObjectVariable
'read only (needs Public property Let to change value)
objForm.Variable1
'Gets result of function with input
objForm.DataReturn([Input])
'runs sub/function from outside of normal scope
objForm.GlobalMethod
End Sub
If you use Late Binding like I do always check for Null values and objects that are Nothing before attempting to do any processing.

Resources