For some reason, when using a Select Case statement in VBA, I seem to be losing my variable value once the statement completes.
For context, I am trying to capture a double click action on two specific cells, in a specific order. The point is for a hidden function, so to run the function a user must first double click on cell U35, which should set the egg variable to 1. Then, if the user double clicks on cell AF35, AND the egg variable is set to 1, then the function will run.
The problem I am having is that after double clicking cell U35, the egg variable gets set to null, hence the second Case statement is never reached.
Private Sub Worksheet_Activate()
Dim egg
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Select Case egg
Case Is = 0
If Not Intersect(Target, Range("U35")) Is Nothing Then
MsgBox ("First action captured")
egg = 1
End If
Case Is = 1
If Not Intersect(Target, Range("AF35")) Is Nothing Then
MsgBox ("Second action captured")
egg = 0
End If
End Select
End Sub
You need to globally declare your variable egg, out of the macro Private Sub Worksheet_Activate(), so that it keeps its value even once you get out of the local context.
Dim egg As Integer '<-- global declaration (on top of module)
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
'rest of your code
End Sub
You can declare a global variable, and set it on the Worksheet Activate, and fix the Select Case a little:
Dim egg As Long
Private Sub Worksheet_Activate()
egg = 0
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Debug.Print (egg)
Select Case egg
Case Is = 0
If Not Intersect(Target, Range("U35")) Is Nothing Then
MsgBox ("First action captured")
egg = 1
End If
Case Is = 1
If Not Intersect(Target, Range("AF35")) Is Nothing Then
MsgBox ("Second action captured")
' Do things here when they're clicked back-to-back
End If
egg = 0
End Select
End Sub
Problem
"Worksheet_BeforeDoubleClick" is unable to recognize "egg" from "Worksheet_Activate" because they are private subs.
There are 2 types of subs in VBA, private and public. There are some exceptions, generally speaking information inside a private sub cannot be accessed from another sub.
Solution(s)
A) define the variable as a global object
B) define the variable inside the sub it will be used
A) define the variable as a global object
Public egg as Integer
Private Sub Worksheet_Activate()
egg = 0
'this is not necessary, you can set it inside BeforeDoubleClick
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
*your code*
End Sub
B) define the variable inside the sub it will be used
Private Sub Worksheet_Activate()
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim egg as Integer
egg = 0
*your code*
End Sub
Related
I have code on a userform that contains several checkboxes and several DTPickers.
The code looks like so:
Private Sub CheckBox11_Click()
If CheckBox11.Value = True Then
DTPicker22.Enabled = True
Else
DTPicker22.Enabled = False
End If
End Sub
Private Sub CheckBox12_Click()
If CheckBox12.Value = True Then
DTPicker24.Enabled = True
Else
DTPicker24.Enabled = False
End If
End Sub
The Userform contains a lot of checkboxes that have clauses next to them. Upon their completion the DTPicker will enable entering the date of completion.
Whilst this does what I want, it only enables one DTPicker when the checkbox is ticked per private sub. There has to be some way to make this so I wouldn't need to create different private subs for every checkbox click event.
Could you also tell me where to put it, as in, what event?
A "control array" is the typical approach for something like this.
See:
http://www.siddharthrout.com/index.php/2018/01/15/vba-control-arrays/
eg:
Class module clsEvents
Option Explicit
'Handle events for a checkbox and a date control, associated with a worksheet cell
Private WithEvents m_CB As MSForms.CheckBox
Private WithEvents m_DP As DTPicker
Private m_dateCell As Range
'set up the controls and the cell
Public Sub Init(cb As MSForms.CheckBox, dp As DTPicker, rng As Range)
Set m_CB = cb
Set m_DP = dp
Set m_dateCell = rng
If rng.Value > 0 Then
cb.Value = True
m_DP.Value = rng.Value
Else
cb.Value = False
End If
m_DP.CustomFormat = "dd/MM/yyyy"
End Sub
Private Sub m_CB_Change()
m_DP.Enabled = (m_CB.Value = True)
End Sub
Private Sub m_DP_Change()
m_dateCell.Value = m_DP.Value 'update the cell
End Sub
Userform:
Option Explicit
Dim colObj As Collection 'needs to be a Global to stay in scope
Private Sub UserForm_Activate()
Dim obj As clsEvents, i As Long, ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set colObj = New Collection
'loop over controls and create a class object for each set
' 3 pairs of controls on my test form...
For i = 1 To 3
Set obj = New clsEvents
obj.Init Me.Controls("CheckBox" & i), _
Me.Controls("DTPicker" & i), _
ws.Cells(i, "B")
colObj.Add obj
Next i
End Sub
The first thing I'd recommend is following a proper naming convention. "CheckBox11" and "DTPciker1" are really vague and once you get further into your code, you'll forget which control is which. I would recommend naming them something that relates the two control together, like "firstDate" and "firstDateDTP". My alternate answer below uses this approach.
You could make a public function that enables the DTPicker based upon the checkbox's value.
Public Function EnableDTPicker(myPicker as String, enableBool as Boolean)
UserFormName.Controls(myPicker).Enabled = enableBool
End Function
Then, you can call the function in your CheckBox123_Click() subs like this:
Private Sub CheckBox123_Click()
EnableDTPicker("thePickerName", CheckBox123.Value)
End Sub
Alternatively, you could make a timer event that runs x number of seconds that just loops through the controls and performs the checks as needed. See this page on how to set up the timer. Using the code in the link shown, You could do something along the lines of:
'Put this in Workbook events
Private Sub Workbook_Open()
alertTime = Now + TimeValue("00:00:01")
Application.OnTime alertTime, "EventMacro"
UserForm1.Show
End Sub
'Put this in a Module
Public Sub EventMacro()
With UserForm1
For each ctrl in .Controls
If TypeName(ctrl) = "CheckBox" Then
'The code below assumes the naming convention outlined above is followed
.Controls(ctrl.Name & "DTP").Enabled = ctrl.Value
End If
Next ctrl
End With
alertTime = Now + TimeValue("00:00:01")
Application.OnTime alertTime, "EventMacro"
End Sub
I have a Worksheet in excel 2013, with 25 activex buttons on it. depending on a cell value for each button, i would like it to be visible or hidden. In my case the value of cell U6 makes my commandbutton1 visible, U7 would make commandButton2 visible.... Only my CommandButton1 works properly. I have tried different combinations of code without succes.
Private Sub CommandButton1_Click()
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
'ActiveX button1
If Range("U6") = 1 Then
Sheets("Feuil1").CommandButton1.Visible = True
Else
Sheets("Feuil1").CommandButton1.Visible = False
End If
End Sub
If Range("U6") = 1 Then
Shouldn't that check if the Target (i.e. the modified cell) is in column U?
Sheets("Feuil1").CommandButton1.Visible = True
That road leads to pastaland, you don't want to go there: extract a method. You'll want to query the OLEObjects collections to get the ActiveX control by name, rather than hard-coding the button names 25+ times.
Private Sub SetControlVisibility(ByVal controlName As String, ByVal isVisible As Boolean)
Dim axControl As OLEObject
On Error Resume Next 'next statement may throw error #9
Set axControl = Me.OLEObjects(controlName)
If axControl Is Nothing Then Exit Sub
On Error GoTo 0 'restore error handling
axControl.Object.Visible = isVisible
End Sub
Now you have a method that can toggle the visibility of any ActiveX control on the sheet, given its name.
So in the Worksheet_Change handler, you now just need to work out the name of the ActiveX control, and whether or not you want it visible:
Private Sub Worksheet_Change(ByVal Target As Range)
'bail out if the modified cell isn't interesting, or if it's more than 1 cell:
If Intersect(Target, Me.Range("U6:U31") Is Nothing Or Target.Count <> 1 Then Exit Sub
Dim buttonName As String
buttonName = "CommandButton" & Target.Row - 5
Dim isVisible As Boolean
isVisible = Target.Value = 1
SetControlVisibility buttonName, isVisible
End Sub
Or something like it. Note: code written in the answer box, untested & for illustrative purposes only. Copy-pasta at your own risk.
Purpose: Click on a cell in a range (Range: Column K:K on excel worksheet). Once you click on a specific cell in column K, userform pops up with cell value using following code:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error Resume Next
If Target.Cells.Count > 1 Then Exit Sub
If Not Intersect(Target, Range("K:K")) Is Nothing Then
Credit_Information.TextBox1.Value = Target.Value
Credit_Information.Show
End If
End Sub
My question, is depending on where I click on column K, I want to use two buttons on my userform (Previous and Next) to move up and down column K and see the values of the cell dynamically change on my userform. Is this possible? Please let me know if any clarification is needed.
Just add the two command buttons to your userform.
Name one of the buttons cmdNext and give it a caption of "Next".
Name the other button cmdPrev and give it a caption of "Previous".
Then, in the userform code module, place these routines:
Private Sub cmdNext_Click()
ActiveCell(2).Select
End Sub
Private Sub cmdPrev_Click()
If ActiveCell.Row > 1 Then ActiveCell(0).Select
End Sub
That's it.
Note: if you want you can add code to ensure that the ActiveCell is in column K before allowing the new selections:
If ActiveCell.Column = 11 Then ...
Perfect, Thanks!
I also found out that using Offset worked for me too in this manner. I'm not sure however if I'm breaking any conventions by doing this.
Private Sub CommandButton1_Click()
ActiveCell.Offset(-1).Activate
End Sub
Private Sub CommandButton2_Click()
ActiveCell.Offset(1).Activate
End Sub
It is possible, but I would create another procedure for that. What you could do is declare a public variable in your userform & set it equal to the range Target. Then you could call another procedure from the userform on each button click and redefine the selected range after each click.
So, at the top of your userform do this:
Public selected_cell as Range
Then for the up button:
Private Sub ButtonUp.Click()
If selected_cell.Row < 2 Then Exit Sub
selected_cell.Rows(0).Select
Set selected_cell = selected_cell.Rows(0)
me.TextBox1.Value = selected_cell
End Sub
And the down button would be:
Private Sub ButtonDown.Click()
selected_cell.Rows(2).Select
Set selected_cell = selected_cell.Rows(2)
me.TextBox1.Value = selected_cell
End Sub
Now let's make your code like this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error Resume Next
If Target.Cells.Count > 1 Then Exit Sub
If Not Intersect(Target, Range("K:K")) Is Nothing Then
With Credit_Information
Set .selected_cell = target
.TextBox1.Value = Target.Value
.Show
End With
End If
End Sub
I have a Excel workbook calculator dependent on a few parameters. I want the client to be able to insert those parameters into an appropriate "client input" cell on every spreadsheet so he doesn't have to jump back and forth between spreadsheets.
Is there a good way to do it? I tried the following scheme, but it's buggy for reasons unknown to me:
make a VBA module declaring variables to hold my parameters
initialize them with appropriate initial values on the Workbook_Open event
make the specific sheets write those values into "client input" cells on Worksheet_Activate event
in a Worksheet_Deactivate event, if the "client input" cells are different among each other - update the VBA variables
This works sometimes, but not always. Is there a better way to do this?
EDIT:
This is my "GM" Module:
Option Explicit
Public perspective As String
Public RSS As String
Public Payback As Double
This is my "ThisWorkbook:
Private Sub Workbook_Open()
GM.perspective = Worksheets("Hidden variables").Range("A1").Value
GM.RSS = Worksheets("Hidden variables").Range("B2").Value
GM.Payback = Worksheets("Hidden variables").Range("C3").Value
End Sub
Private Sub Workbook_Close()
Worksheets("Hidden variables").Range("A1") = GM.perspective
Worksheets("Hidden variables").Range("B2") = GM.RSS
Worksheets("Hidden variables").Range("C3") = GM.Payback
End Sub
This is in my worksheet 1 (in worksheet 2 there is an analogous code):
Option Explicit
Private Sub Worksheet_Activate()
'SIMULTANEOUS UPDATE p.1
Worksheets("1").Range("I32") = GM.Payback
Worksheets("1").Range("I29") = GM.RSS
Worksheets("1").Range("I26") = GM.perspective
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
'BASIC PRICE CALCULATION
If Target.Count > 1 Then Exit Sub
If Target = Range("I32") _
Or Target = Range("I29") _
Or Target = Range("I26") _
Or Target = Worksheets("Intro").Range("price") _
Then
Worksheets("Hidden variables").Range("condition") = 2
Worksheets("Hidden variables").Range("basic_price") = Worksheets("Intro").Range("price").Value
Range("M44").GoalSeek Goal:=0, ChangingCell:=Worksheets("Hidden variables").Range("basic_price")
If Worksheets("Hidden variables").Range("basic_price").Value < 0 Then
Range("M46") = "Error"
Else
Range("M46") = Worksheets("Hidden variables").Range("basic_price").Value
End If
Worksheets("Hidden variables").Range("condition") = 1
End If
End Sub
Private Sub Worksheet_Deactivate()
'SIMULTANEOUS UPDATE p.2
GM.Payback = Worksheets("1").Range("I32").Value
GM.RSS = Worksheets("1").Range("I29").Value
GM.perspective = Worksheets("1").Range("I26").Value
End Sub
To avoid infinite loop, you can use a global variable
Outside a function (at the begin of your module)
Public isUpdating As Double
inside your Worksheet_change
Private Sub Worksheet_Change(ByVal Target As Range)
' Check if an update is in progress. If so, exit the change
if isUpdating then
exit sub
end if
' Begin of the update
isUpdating = true
' Here your update
' End of the update
isUpdating = false
End sub
What I am trying to accomplish is fairly simple. When a user selects a sheet, I would like a message box to appear. Meaning: I'm currently viewing Sheet1, I click on the Sheet2 tab and a message pops up before I can do anything. I can't seem to find the event that fires when moving to a different sheet.
Events I've tried: Workbook_SheetActivate and Worksheet_Activate
Private Sub Workbook_SheetActivate(ByVal sh As Object)
MsgBox ("Example Message")
End Sub
Or
Private Sub Worksheet_Activate()
MsgBox ("Example Message")
End Sub
I've done some googling and most things are about when cell values change or the cell selection changes.
This should work:
Private Sub Worksheet_Activate()
MsgBox "you never visit...you never call....you never write"
End Sub
However:
code must be in the worksheet code area
macros must be enabled
events must be enabled
You could use the following in the "ThisWorkbook" module to fire a message whenever you change sheets within the workbook.
Sub Workbook_SheetActivate(ByVal Sh As Object)
MsgBox Sh.Name & " activated!"
End Sub
This will solve the problem without having to add Private Sub Worksheet_Activate() to every worksheet's code module.
Here is a link to all the worksheet events available in Excel:
http://dmcritchie.mvps.org/excel/event.htm
Private Sub Worksheet_Activate()
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean) -- (additional examples)
Cancel = True 'turn off Edit mode when using “Edit directly in a cell”
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True 'turn off Edit mode when using “Edit directly in a cell”
Private Sub Worksheet_Calculate()
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False 'should be part of Change macro
Application.EnableEvents = True 'should be part of Change macro
Private Sub Worksheet_Deactivate()
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Doesn't Worksheet_Activate work for you?
You need to have the event in the worksheet that is being activated - that is, if you put it is sheet 2, it will fire only when that sheet is opened.
This worked in sheet 2 of my workbook.
Sub worksheet_activate()
MsgBox "activated!"
End Sub