-1

In my code, I have a combobox on a userform. The user selects an option from the drop down and the userform sets the selected value to a global variable "monthYearEntered".

Then i have a sub routine that uses the global variable. However, the value of the global variable is not carrying over to the subroutine. I know because I made a MsgBox of the variable from within the userform and then again in the sub routine. I get the right value in the form but not the routine.

I researched a bit and saw some potential solutions but none of them worked for me. In some past codes, I made a workaround by setting cell to the value and then referencing the cell to set the variable in the subroutine. I would like to avoid that since its just a Band-Aid.

USERFORM CODE

Private Sub CBN_OK_Click()

If YearMonthField = "" Then
    MsgBox "Value cannot be blank. Please enter correct value."
    Exit Sub
Else
    monthYearEntered = YearMonthField.Value
    MsgBox monthYearEntered
    Me.Hide
    Application.Run "DeleteMonth"
End If

End Sub

MODULE

Option Explicit
Dim monthYearEntered As String

Private Sub RangetoDelete()

    Application.DisplayAlerts = False
    
    Dim ws As Worksheet
    Dim tableSalesCombined As ListObject
    Dim cell, rng As Range

    Set ws = ThisWorkbook.Worksheets("Sales Data Combined")
    Set tableSalesCombined = ws.ListObjects("Table_SalesCombined")
    Set rng = ws.Columns("I:I")
    
    DeleteMonthForm.Show 'show user form
    
    'monthYearEntered.ClearContents
    Application.Run "Protect"
    Application.DisplayAlerts = True
    
End Sub

Private Sub DeleteMonth()

    Application.DisplayAlerts = False
    
    Dim ws As Worksheet
    Dim tableSalesCombined As ListObject
    'Dim monthYearValue
    Dim cell, rng As Range

    Set ws = ThisWorkbook.Worksheets("Sales Data Combined")
    Set tableSalesCombined = ws.ListObjects("Table_SalesCombined")
    Set rng = ws.Columns("I:I")
    
    ws.Unprotect Password:=PassW

    'look for value entered by user (monthYearValue)
    Set cell = rng.Find(What:=monthYearEntered, LookIn:=xlFormulas, LookAt:=xlWhole, MatchCase:=False)
    
    MsgBox monthYearEntered
    
    If cell Is Nothing Then
        MsgBox monthYearEntered & "not found in Combined Sales Data.  Please confirm value and try again."
        Exit Sub
    End If
    
    With tableSalesCombined
         .Range.AutoFilter Field:=9, Criteria1:=monthYearEntered
         If Not .DataBodyRange Is Nothing Then
            '.DataBodyRange.SpecialCells(xlCellTypeVisible).Delete
         End If
         '.Range.AutoFilter
    End With

    tableSalesCombined.AutoFilter.ShowAllData
    
    'monthYearEntered.ClearContents
    Application.Run "Protect"
    Application.DisplayAlerts = True
    
End Sub
  
8
  • 1
    Public monthYearEntered As String Commented Feb 26 at 21:51
  • in the module code what does MsgBox monthYearEntered give you? Are you using Option Explicit in all your code modules? Commented Feb 26 at 21:51
  • Yes, i am using 'Option Explicit'. in the module code 'MsgBox monthYearEntered' gives me a blank Commented Feb 26 at 21:52
  • 1
    Are you also using Option Explicit in your userform code module? @braX's comment points to the likely problem. Commented Feb 26 at 21:57
  • 1
    Should always be in every module - set it up in the VBeditor Tools >> Options >> Editor -"Require Variable Declaration" and it will get added whenever you create a new module. Commented Feb 26 at 21:58

1 Answer 1

3

Writing Dim monthYearEntered As String at the top of a module declares the variable monthYearEntered as global, but its scope is limited to the module where the definition is. That means, outside of the module the variable is unknown.

If you want the variable to be accessible outside the module, you need to declare it using the Global keyword, not the Dim keyword.

Global monthYearEntered As String 

As you declared the variable with Dim, the statement monthYearEntered = YearMonthField.Value in the form cannot write into the variable - it is simply unknown there. There are 2 possibilities, but at the end of the day the result in the same:

  • You have a variable monthYearEntered declared in the form. This is a new variable and has nothing to do with the variable you declared in the module.

  • You don't have a variable declared in the form:

    • You don't use Option Explicit in the form. When the Click-routine hits the assignment statement, the VBA runtime will see that at this time, monthYearEntered is unknown at create it on the fly. Again, this is a different variable.
    • You use Option Explicit: The compiler will complain.

You can easily check this in the VBA editor: move the cursor on the variable and press Shift+F2

  • When the variable is declared locally, the VBA editor will jump to the declaration inside the form
  • When the form accesses a globally declared variable, the editor jumps to the declaration inside the module.
  • When the form uses an undeclared variable, you see a message "Identifier under cursor is not recognized".
  • When you move the cursor to something that is part of the VBA environment (a function like Left or Date, or an object like Application), object browser window is displayed.

An example: I put the following code into a module1

Option Explicit

Global globVar As String
Dim dimVar As String

Sub test1()
    globVar = "global var from module1"
    dimVar = "dimmed var from module1"
    
    Debug.Print "Start in test1: ", globVar, dimVar
    test2
    Debug.Print "Back in test1: ", globVar, dimVar
End Sub

Now I create another module and put the following code (note that for the moment, Option Explicit is deactivated):

' Option Explicit
Sub test2()
    Debug.Print "Now in test2: ", globVar, dimVar
    globVar = "overwritten in test2"
    dimVar = "overwritten in test2"
    Debug.Print "Still in test2: ", globVar, dimVar
End Sub

Executing test1, you get the following output:

Start in test1:             global var from module1     dimmed var from module1
Now in test2:               global var from module1     
Still in test2:             overwritten in test2        overwritten in test2
Back in test1:              overwritten in test2        dimmed var from module1

You can see that globVar is displayed in test2, and the assigned new value is seen also when returning back to test1. However, printing dimVar in test2 doesn't show anything: The variable is unknown at that moment and created instantly. You can write into it, but this is not seen in test1 as it is a different variable, only with the same name.

If you now activate the Option Explicit in module2, you will get a compiler error because dimVar is unkwown. If you now add a declaration for dimVar into module2 (to get rid of that compiler error), you declare a new variable - the outcome is the same: globVar is visible in both modules but dimVar of module1 is not.

Option Explicit
Dim dimVar As String
Sub test2()
    Debug.Print "Now in test2: ", globVar, dimVar
    globVar = "overwritten in test2"
    dimVar = "overwritten in test2"
    Debug.Print "Still in test2: ", globVar, dimVar
End Sub

(Same output as before)

Just to make it complete: If you add a declaration for globVar in module2, you would hide the declaration of globVar in module1 and you have 2 different variables.

Option Explicit
Dim globvar As String
Dim dimvar As String
Sub test2()
    Debug.Print "Now in test2: ", globvar, dimvar
    globvar = "overwritten in test2"
    dimvar = "overwritten in test2"
    Debug.Print "Still in test2: ", globvar, dimvar
End Sub

Now you get

Start in test1:             global var from module1     dimmed var from module1
Now in test2:                             
Still in test2:             overwritten in test2        overwritten in test2
Back in test1:              global var from module1     dimmed var from module1

If you want to declare a "global" variable inside a form, you cannot use the keyword Global. The reason is that a form is a class, and you can have multiple instances of a class (you can open a form more than once). To make a variable visible outside the form, use the keyword public.

Form code: Public formVar As String

Private Sub Ok_clicked
    formVar = "I was filled in the form"
    Me.Hide
End Sub

Module code using the default instance of the form:

UserForm1.Show
Debug.Print UserForm1.formVar

or using an explicit instance:

Dim frm as New UserForm1
frm.Show
Debug.Print frm.formVar

Note that you need to use Hide to close the form. If you use the infamous Unload command, the userform object is destroyed (removed from memory) and frm.formVar no longer accessible.


Summary:

  • Using Dim on module level declares a variable on module level that is unknown in other modules
  • Using Global declares a variable that is known in all modules
  • In a form (or a class module), you can't define a Global variable. To make a variable accessible, declare it as public.
  • Always use Option Explicit to avoid that the VBA runtime creates undeclared variables on the fly.
  • In case of a doubt, use Shift+F2 to see where a variable is declared.
Sign up to request clarification or add additional context in comments.

2 Comments

That's almost a whole textbook - helpful & instructive +:)
This response helped me a second time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.