VBA change instance variable from module (excel) - excel

In VBA I need a module sub to tell an instance to set up some variables.
In Module 1 I have:
Sub Load()
ThisWorkbook.SetupVariables
ThisWorkbook.TestVariables
End Sub
In ThisWorkbook I have:
Private Variable1 As Integer
Private Variable2 As String
Private Variable3 As MyUserDefinedObjectType
Public Sub SetupVariables()
Variable1 = 5
Variable2 = "Five"
Set Variable3 = New MyUserDefinedObjectType()
End Sub
Sub TestVariables()
MsgBox Variable1 & " is spelled " & Variable2
Variable3.SomeFunction
End Sub
The call to TestVariables inside Load() yields the correct result, but subsequent calls to TestVariables fail. How can I make Variable1 and Variable2 hold their values? (In my real-world situation, these variables are objects I've defined and cannot be made public variables.)
Sequence of events:
Load is stored in Module1 and is associated with a form button on Worksheet1. This is pressed first.
Subsequently, an ActiveX control in Worksheet2 tells ThisWorkbook to call TestVariables.

It works for me as you have it. Create a Watch (View - Watch Window) for Variable1 and choose Break When Value Changes. You should be able to see when it changes to zero.

Related

Global variable not accessible in different sub?

I declared projname globally at the top of the module 1. It is assigned in the userform, and is successfully accessed in the createWB sub. However, when I go to access it in the addWindow sub also in module 1, it becomes empty (""). I'm unsure of why this is happening because I thought that since the variable was globally declared I should be able to access it in any sub.
Module 1
Option Explicit
Public outputWorkbook As Workbook
Public globalcounter As Integer
Public projname As String
Public projnum As String
createWB()
Dim uf2 As New UserForm2
uf2.Show
Set outputWorkbook = Workbooks.Add(xlWBATWorksheet)
outputWorkbook.SaveAs Filename:=Environ("userprofile") & "\Desktop\" &
Replace(projname, " ", "") & ".xlsx"
outputWorkbook.Activate
Range("B3") = projname
Range("B4") = projnum
End Sub
addWindow()
Workbooks(Replace(projname, " ", "") + ".xlsx").Activate
End Sub
Userform Code
Public Sub CommandButton1_Click()
projname = Me.TextBox1.Text
projnum = Me.TextBox2.Text
Me.Hide
End Sub
Cells B3 and B4 are assigned the correct value, but the addWindow() line causes a subscript out of range error. When I test it with Debug.Print, I see that projname = "". I also simply tried outputWorkbook.Activate, which did not work either.
Avoid Global Pollution
Unless there is a really good reason to use them, try to avoid global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that.
Instead, try passing your parameters through your various methods as they are needed. This helps prevent errors, makes your code easier to follow, and utilizes composition.
Using your userform to store and expose your properties
Try to instantiate your userform using a With statement so that you have a captured instance of it where you have access to its various properties that you expose. In your case ProjectName and ProjectNumber.
Additionally, there should be a property to check if the userform was canceled or the X button was pressed.
You userform would look something like this:
Option Explicit
Private cancelled As Boolean
Public Property Get ProjectName() As String
ProjectName = TextBox1.Value
End Property
Public Property Get ProjectNumber() As Long
ProjectNumber = TextBox2.Value
End Property
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelled
End Property
Private Sub CommandButton1_Click()
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
OnCancel
End If
End Sub
Private Sub OnCancel()
cancelled = True
Hide
End Sub
Instantiating the userform
Here is the example of now calling your userform (P.S. Change the name from Userform2). Notice we are capturing our instance of your userform using the With block. Within this block, we have access to the properties we exposed: ProjectName, ProjectNumber, IsCancelled.
Private Sub createWB()
With New UserForm2
.Show
If Not .IsCancelled Then
' Do neccessaray steps here...
' You have access to ProjectName and Project number.
' Pass this to your addWindow method.
addWindow .ProjectName
End If
End With
End Sub
The ProjectName now can be accessed from your userform and passed as a parameter to you addWindow method.
Private Sub addWindow(ByVal projName As String)
Workbooks(Replace(projName, " ", "") + ".xlsx").Activate
End Sub
For more information on using userforms in this way see this helpful Rubberduck Blog Post.
could you try using Module1 as prefix? , jus like in this code
Public Sub CommandButton1_Click()
Module1.projname = Me.TextBox1.Text
Module1.projnum = Me.TextBox2.Text
Me.Hide
End Sub

Having a public variable available in userform / sheet objects

I've spent the last hour reading pages about variable scope in various flavours of excel vba, and could not find a definite documentation reference addressing my scope problem... even though i'm convinced it is such a classic. Oh well, here goes.
I've got a workbook that contains just one sheet and one userform. I have a list of students sitting in column 1 on my sheet. I would like to :
load this list up into some global Collection variable named students_list (i do this using a Workbook-Open() procedure in the ThisWorkbook object)
use the contents of students_list to initialize a listbox in my userform
remove elements from students_list when a button on my userform is clicked on
All i need is a variable that is seen from within my userform's procedures, as well as from inside the ThisWorkbook object.
I tried declaring it as public, global, in the sheet's code, in the userform, in ThisWorkbook, in a separate module dedicated to globals... I just can't seem to find the right way to have the students_list variable visible from everywhere.
What am I missing ? My apologies for this question that should be so basic and yet beats me :-/
Place the declaration of your Public variables inside a Module (use Insert / Module from the menu to create one, if you don't already have one). The scope will then extend to your whole project.
So in a Module (e.g. Module1) have:
Public foo As Integer
And in the worksheet (e.g. Sheet1) code have:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
foo = 4
MsgBox "foo set to 4"
End Sub
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
MsgBox "foo = " & foo
End Sub
If you were to place the declaration in the code for ThisWorkbook you would need to reference it as Thisworkbook.foo because, although it is accessible from any part of the code, it is a variable specific to that ThisWorkbook object.
So, in the code for ThisWorkbook have:
Public foo As Integer
And in the worksheet (e.g. Sheet1) code have:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
ThisWorkbook.foo = 4
MsgBox "foo set to 4"
End Sub
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
MsgBox "foo = " & ThisWorkbook.foo
End Sub

Calling a userform and returning a value

I have a vba code thats Auto_Open. It does some checks then prompts a userform that asks for username and password. I called this userform with userform_name.show.
My issue is how can I return a Boolean to my Auto_Open sub from the userform code.
I linked the code that verifies if the credentials are correct to the "Login" button on the form. this is the code that produces the Boolean. I need to return it to the Auto_Open.
Private Sub loginbutton()
Dim bool As Boolean
Dim lrup
Dim r As Long
Dim pass As String
loginbox.Hide
'are fields empty
Do While True
If unBox.Text = "" Or pwBox.Text = "" Then
MsgBox ("You must enter a Username and Password")
Else
Exit Do
End If
loginbox.Show
Exit Sub
Loop
'find pw reated to username (if existant)
lrup = UserPass.Range("A1").Offset(UserPass.Rows.Count - 1, 0).End(xlUp).Row
If unBox = "b0541476" And pwBox = "theone" Then
bool = True
Else
MsgBox ("Invalid username or password. Please try again.")
loginbox.Show
Exit Sub
End If
For r = 2 To lrup
If unBox = Cells(r, 1) Then
pass = Cells(r, 2).Value
Exit For
End If
Next
If pass = "" Then
MsgBox ("Invalid username or password. Please try again.")
loginbox.Show
Exit Sub
Else
bool = True
End If
End Sub
You can manage to do this without the use of public variables.
There appears to be a difference between show/hide and load/unload.
If you hide a form while it's still loaded it won't be cleared out, so you can reference the state of the controls on the form.
For example I was using a date picker (called DTPicker1) on a form, my code in the module looks something like this:
Dim NewDay As Date
Load FrmDayPicker
FrmDayPicker.Show
NewDay = FrmDayPicker.DTPicker1.Value
Unload FrmDayPicker
Debug.Print NewDay
On your form you can just use Me.Hide insteaded of Unload Me and this should work
Remove Dim bool As Boolean from the userform code area and declare it in the module as shown below
This is how your Code in the module would look like
Public bool As Boolean
Sub Auto_Open()
'
'~~> Rest of the code
'
UserForm1.Show
If bool = True Then
'~~> Do Something
Else
'~~> Do Something
End If
'
'~~> Rest of the code
'
End Sub
How about using a function instead of a sub?
Function loginbutton()
' your code
loginbutton = bool
End Function
Now in your calling code you can test for true/false
if loginbutton() then
'true responce
else
'false responce
end if
Update:
I was to quick to dismiss public variables. While both methods can work, Pub Vars and directly accessing items, sometimes it's not ideal to access an item directly if say it's a list.
I now have modules specifically for calling UserForms which only declar the public variables and call the userform. I can then call these modules from UserForms or Modules and have access to the public variable after the userform is closed.
Eg: Here is a module I use now, very basic, and all my other needs can just call this module/sub.
Public ColSelectorDic As Object
Public Sub Col_Picker_Sub()
Col_Picker_UserForm.Show
End Sub
It's simplest IMO to use Public Variables declared in the Module calling the UserForm. But, this has the caveat if you wanted to call this userform from separate modules, you will get errors regarding duplicate declarations/ambiguous names.
So, if you know it's only going to be called be the one module, Pub Vars all the way. In my case I was using a "Column Picker" userform, which was very simple and I wanted to be able to utilize it again in unforseen future projects so I attempted to resolve the above caveat.
See this answer for Public Variables, no need to repeat information --> https://stackoverflow.com/a/18966341/5079799
And this answer related to Accessing the Form Variables directly -->
https://stackoverflow.com/a/47919465/5079799 but I felt it could use some expanding.
Also, here is a good article which goes deeper in depth about accessing userform variables directly --> https://gregmaxey.com/word_tip_pages/userform_pass_data.html
So my UserForm looks like this and is named ColPicker:
Private Sub UserForm_Initialize()
Dim i As Long
lCol = Get_lCol(ActiveSheet)
For i = 1 To lCol
ColumnLetter = Col_Letter(i)
Me.ComboBox1.AddItem ColumnLetter
Next
End Sub
Private Sub CommandButton1_Click()
Me.Hide
End Sub
Sub PassVarFromUserForm()
ColPicker.Show
Dim ColLetter As String
ColLetter = ColPicker.ComboBox1.Value
Unload ColPicker
Debug.Print ColLetter
End Sub
Notice how the "Run"/Command Button in the UserForm just hides the form, I then store the values in a variable, THEN unload the form, from the module, via utilizing it's name. (You can only use unload me from within the userform).
The variable is then available inside module and can be declared in the beginning as public, or inside module, it doesn't matter as it can be declared differently in each module, the userform has no idea/reference to what the variable name the information will be stored in.

User form can't pass value to module

I've inserted a user form into a project that already contains a bunch of modules. By playing around with the code in the user form, I've verified that I can return the value from a combo box.
User form code:
Public SelectedPacking As Integer
Private Sub CancelButton_Click()
UserForm1.Hide
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk
'Return list index value to cell C50
Worksheets("Inputs & Results").Range("C50") = SelectedPacking
Unload UserForm1
End Sub
My problem is that I can't pass this value on to any of the macros written in the modules.
Module code:
Public Sub ShowComboBox()
UserForm1.Show
End Sub
Public Sub DemoDialogOk()
ival = SelectedPacking
'Return value of ival (list index value from combo box) to cell C17
Worksheets("Packed bed (Random)").Range("C17") = ival
End Sub
Obviously the module contains more useful code, but I've commented out everything to try and figure out where I'm going wrong. I've been changing some things around, but I still can't get anything to appear in cell C17, so I think I'm missing something fundamental.
I think two options: 1) change DemoDialogueOK to accept variables:
Public Sub DemoDialogOk(SelPack as integer)
' ival = SelectedPacking
Worksheets("Packed bed (Random)").Range("C17") = SelPack
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk(SelectedPacking)
...
End Sub
Or option two: fully qualify the variable from the useform i.e:
Public Sub DemoDialogOk()
ival = ufYourForm.SelectedPacking
...
End Sub
Public variables in userforms don't appear to be as "public" as module level...
Tipping on top of Simon's answer, you could pass the entire userform if you'd like. This would give you access to all the pieces of it and is especially useful if you need to do some validation on, say, different checkboxes being checked or not.
Sub inOurUserForm()
Call inADifferentModule(Me) 'Passes this userform
End Sub
Sub inADifferentModule(ourForm As UserForm1)
'Passed the form, and using it like a class (As whatever the form is called)
If ourForm.chkYes = True Then
'Do something
Else
'Do something else, like
ourForm.chkYes = False 'Because we are passing the object itself _
rather than a copy, at least in my understanding
End If
End Sub
And you don't necessarily need to pass the userform, as you could just reference it as an object itselft e.g.
UserForm1.chkYes
A very easy solution to this would be to declare a variable within the Userform (UserForm1 in this example)
Public Pass as string
This Pass would contain the string where you store the password. Once you store the password, you can hide the form
Me.Hide
Within the module, you can open the Form as modal
UserForm1.Show vbModal
Now after all the code inside the userform is run, the password can be retrieved within the module -
UserForm1.Pass
You can then unload the hidden form
unload UserForm1

reference variable name in excel vba

I have a load of variable names in a spreadsheet column. These variables are defined and have values in the modules I have in my project.
I want to have a code that references the variable name in a spreadsheet, and returns the value that it have in the module and pastes the value in another spreadsheet i.e
Sub code()
dim variable1 as integer
variable1 = 2
End sub
sheet 1: cell A1: variable1
Sub code2()
sheet(2).range("a1").value = sheet(1).range("a1").value
end sub
sheet 2: cell A1: 2
There is no way to ask for a variable by name in VBA during runtime. During compilation all variable names are stripped away, and at runtime the variables are referenced just with memory locations. Also, if the variable is declared within a sub, it only exists while that sub is being executed. If you try to access it later, something else will be using its memory location.
The only way to do this is to declare all the variables at module level, and then have a function which explicitly maps variable names to these variables:
Private variable1 As Integer
Sub code()
variable1 = 2
End Sub
Sub code2()
Sheets(2).Range("a1").Value = VariableByName(Sheets(1).Range("a1").Value)
End Sub
Function VariableByName(VarName As String) As Variant
Select Case VarName
Case "variable1": VariableByName = variable1
End Select
End Function
Actually, your best option is to forget about using variables and use names instead:
Sub code()
Names.Add "variable1", 2, Visible:=False
End Sub
Sub code2()
Sheets(2).Range("a1").Value = Evaluate(Sheets(1).Range("a1").Value)
End Sub
But when you go that route, if you need to access the variable in VBA you can't just say variable1, you need to use code like this:
Sub code3()
Dim variable1 As Integer
variable1 = Evaluate("variable1") 'bring it into a normal variable
variable1 = variable1 * 2 'now you can use it like a normal variable
Names("variable1").RefersTo = variable1 'need to save it when you're done
End Sub
This worked in Excel 2010
variable1 = [variable1].Value
VBA treats [variable1] (with brackets) as a variant that references the named cell.
-mmh

Resources