Is it possible to have a static variable declared in one procedure, and use this variable in several different procedures using Excel VBA?
i.e.
Public myvar as integer
Sub SetVar()
static myvar as integer
myvar=999
end sub
sub Usevar()
dim newvar as integer
newvar=myvar*0.5
end sub
I need myvar to be seen by other procedures, and not change or get "lost". The code above works if myvar is not declared as a static variable, but more code then the variable is "lost". If the static declaration is used, myvar is not seen by the usevar procedure. And "Public Static myvar as integer" is not accepted by VBA.
Thanks for your help
Zeus
Try this by calling MAIN() :
Public myvar As Integer
Sub MAIN()
Call SetVar
Call UseVar
End Sub
Sub SetVar()
myvar = 999
End Sub
Sub UseVar()
Dim newvar As Variant
newvar = myvar * 0.5
MsgBox newvar
End Sub
If you declare an item Static , its value will be preserved within the procedure or sub.If you declare the item Public , its value will be preserved and it will be visible to other procedures as well.
Although this question was answered over four years ago by #Gary's Student, there's a subtle nuance worth mentioning, since the solution can depend on the data type of myvar.
First of all, as you've noted in the question, Public Static myvar as Integer doesn't work, because Static is only allowed inside a sub or function.
As noted in the comments to the OP by #Patrick Lepelletier, you can easily get around this by declaring a Constant instead (assuming you don't need to change it dynamically): Public Const myvar as Integer = 999. (Or possibly Private Const myvar...)
Another option is to declare myvar as a function instead of a variable or constant, effectively turning it into a pseudo-constant:
Private Function myvar() as Integer
Static intMyvar as Integer
intMyvar = 999
myvar = intMyvar
End function
In this simple example where myvar is an integer, the pseudo-constant approach is obviously unnecessary and adds the overhead of a function call. Simply declaring a Constant does the job. However, using a constant only works if the value is static and not an object. It will not work if myvar is an object, for example a Range. In that case, using pseudo-constants might be useful:
Private Function myvar() as Range
Set myvar = Range("A1")
End Function
Another advantage is that you can use code inside the function to check for certain conditions and assign different values to myvar accordingly.
The pseudo-constant approach can also be combined with naming worksheet ranges: If cell A1 is a named range, say MyRange, then you could write:
Dim strMyString as String
strMyString = "MyRange"
Private Function myvar() as Range
Set myvar = Range(strMyString)
End Function
Now it is possible to move around the content of cell A1 without breaking the code, since the named range follows along if you cut and paste the cell. I find this approach useful in the design stage, when things tend to move around a lot in a worksheet.
Pseudo-constants also help avoiding some problems usually associated with global (or module-level) variables that might be an issue in larger projects.
The key is to use 2 variables. In the code below, myvar is public but not static. stvar is static but not public. Its scope is only within Main(). By assigning myvar=stvar and stvar=myvar, it effectively creates a variable that is both public and static. The value is preserved.
Public myvar As String
Sub Main() 'in module 1
Static stvar As String
myvar = stvar
toInput
stvar = myvar
End Sub
Sub toInput() 'in module2
myvar = InputBox("enter something", "Input", myvar)
End Sub
Related
I am having trouble with losing static variables within an Excel user form.
I have been working on a routine for excel. I am a (very) novice coder.
I am attempting to populate a cell range to an array. I have been able to do this without issue.
However, when I attempt to store the array as a *static * variable, the variable is not retained for as long as I want it to be.
I think the problem occurs when another page is selected in the multipage, the static variable is cleared.
Code is something like this:
Sub UserForm_Initialize ()
static myArray () as variant
dim myRange as range
set myRange = [namedrange]
myArray=myRange
msgbox myArray(0,0) 'this works fine
call OtherSub
end sub
sub OtherSub ()
msgbox myArray(0,0) 'this returns a blank
end sub
The first sub of code shows the array element just fine. The array element is blank in the second sub.
I have tried:
Declaring the array as a public variable, but this returns an error (I think that variables within user forms are private by default and cannot be changed).
using a very small variable (a simple string)
writing code in a module before opening the user form (variable is not retained).
I am aware that I can just write data to a cell range, but this would defeat the purpose. I was hoping to avoid multiple instances of reading large arrays from the worksheet.
This might explain it a bit clearer. Moving MyArray outside of the Procedure will set it's scope to a Module Level, making it usable through other subs within that module. You will generally want to keep the scope of your variables to the lowest level required. The other option would be to pass your variable as a parameter to your other procedure.
Option Explicit
Dim MyArray() As Variant ' Private Module Level Scope
Public ExampleVariable As String ' Public Module Level Scope (Not required, just showing an example.)
Sub UserForm_Initialize()
Dim myRange As Range ' Procedure Level Scope
Set myRange = [namedrange]
MyArray = myRange
MsgBox MyArray(0, 0) 'this works fine
Call OtherSub
End Sub
Sub OtherSub()
MsgBox MyArray(0, 0) 'this returns a blank
End Sub
I am having trouble assigning a value to constant variant declaration. So the declaration is this
Option Explicit
public const Abc as variant = ?
Abc is a Microfocus rumba object which gets initiated when the workbook is open, something like this
sub workbook_open()
Set Abc= session.GetRDEObject()
End sub
But I cannot use this to assign it to the const variable, it says "Circular dependencies". The reason I am wanting to assign value is to ensure the object Abc is alive even after user executes End statement. Is there a way I can do this?.
I have tried creating a separate function and adding its return value to the Abc, but it does not work.
A constant is exactly that, so try something like this:
Option Explicit
Private Abc As Object
Private Sub Workbook_Open()
Set Abc = Session.GetRDEObject()
End sub
Public Function GetAbc() As Object
Set GetAbc = Abc
End Function
In the following code, tests 1,2 and 3 compile, but test 3 fails at run-time with "object doesn't support this property or method": ( why?)
Sub testdrive()
Dim sh As Worksheet
Dim val As Single
Dim myfoo As New CFoo
' test 1
val = 4
myfoo.sub1 (val)
' test 2
Set sh = ThisWorkbook.Sheets(1)
Call myfoo.sub2(sh)
' test 3
myfoo.sub2 (sh)
End Sub
The class module contains only the following:
Public f As Single
Public Sub sub2(sh As Worksheet)
End Sub
Public Sub sub1(s As Single)
End Sub
I cannot find a definitive statement of when I am obliged to use the "call" keyword.
Can anybody enlighten me?
When you parenthesize a parameter without using the keyword Call, in VBA syntax, this means that you want to force evaluating it then passing its Value. For VBA, the only way to achieve this is to evaluate the parameter then pass its value to the subroutine.
This works well if the parameter is a simple type, such as Single, so it works for Public Sub sub1(s As Single) then sub1(something) works fine.
BUT when the parameter is a worksheet, such as in Public Sub sub2(sh As Worksheet), when calling sub2(sh) with parenthesis, you are asking VBA to evaluate the worksheet, which it does not know how to achieve. Basically, Worksheet is not an object that VBA can evaluate. Therefore, it says
this object does not have this property or method
It means: the class Worksheet does not have a default property such as .Value, which on the other hand exists for a Range object, for example.
You cannot parenthesize the worksheet argument, unless you use the keyword Call, which prevents its prior evaluation.
Call myfoo.sub2(sh) '<~~ works fine
myfoo.sub2 sh '<~~ works fine
myfoo.sub2(sh) '<~~ problem, you are asking VBA to evaluate sh and pass its value
Finally, note that this has nothing to do with the fact that your Subs are class methods. It would have been the same if they were normal subroutines, placed in a normal code module.
I am writing a module with several subs, and I need some variables to have the same value in all the modules. I know about declaring variables as
Public varname as vartype
but how can I assign a global value to such a variable?
Thanks
For a worksheet object (any object for that matter) you need to do the following:
In a standard code module:
Public varName As Excel.Worksheet
In the Workbook_Open() event:
Private Sub Workbook_Open()
Set varName = Sheets("mySheet")
End Sub
Then you can refer to varName in any other module for that workbook and it will point to your worksheet object.
From your question/comments, it seems that you actually want some sort of object constant, which can't be done in VBA - see Declare a Workbook as a Global variable for more infromation.
If you're referring to a value data type such as String, Integer or Long then you can use a constant instead of a variable, however a constant's value cannot be changed once it has been declared (kind of the definition of 'constant') i.e.
Public Const someName As String = "Macro Man"
Public Const someNumber As Long = "1234567890"
Public Const someInt As Integer = "1453"
Im having trouble takign my assigned variable and offseting it. What am I doing wrong?
Public Sub SampleBox_Change()
Dim str As Integer
If (SampleBox.ListIndex > -1) Then
str = SampleBox.List(SampleBox.ListIndex)
End If
End Sub
Public Sub Samplesdel(str As Integer)
Range(Range("BA1").EntireColumn, Range("BA1").Offset(0, -str).EntireColumn).Select
End Sub
Public Sub CommandButton1_Click()
Application.Run "Samplesdel"
End Sub
So the variable (str) is a whole number. I would like to use this number to select a certain number of columns (from BA1 to "left however many columns valued as str"). so if the user selects 8 i would like to select BA1 and left 8 columns.
The combobox is in a userform where the code for the assigned variable is set.
I would like to use the assigned variable in a macro (where i used the select function).
so the variable str gets assigned from a userform combobox. I would then like to pass this variable to a macro where i use it in the offset function.
I just used your snippets of code here, I'll assume you know what you're trying to accomplish with this. As mentioned in another answer, I think it's an issue of scope; it doesn't seem like there's a good reason you can't combine your statements as follows:
Public Sub SampleBox_Change()
Dim str As Integer
If (SampleBox.ListIndex > -1) Then
Range(Range("BA1").EntireColumn, Range("BA1").Offset(0, SampleBox.ListIndex).EntireColumn).Select
End If
End Sub
To add some unsolicited feedback, naming a variable that is an integer "str" will be very confusing to anyone else that has to read your code, the name "str" implies "string", a different data type.
James, you are setting the value of str here correct?
Public Sub SampleBox_Change()
Dim str As Integer
If (SampleBox.ListIndex > -1) Then
str = SampleBox.List(SampleBox.ListIndex)
End If
End Sub
The scope for str will only be this function and it will not be able to be accessed by any other function.
What value are you passing to Samplesdel as the parameter?
Application.Run "Samplesdel"