I have a workbook with multiple sheets with an Activate sub and one sheet without, let's call it Sheet0. I want to, when switching from Sheet0 to any other sheet, check that some condition is met on Sheet0 before switching and stay on Sheet0 if the condition isn't met. I added a Deactivate sub in Sheet0 so that, when switching sheets, the condition is checked and if is not met, pops a message box and ends execution before the Activate sub from the other sheet runs.
Private Sub Worksheet_Deactivate()
If blnCondition Then
MsgBox ("[...]")
Me.Activate
End
End If
End Sub
I used the End statement but for some reason, it doesn't work as I thought it would. It ends execution of the Deactivate sub but still jumps to the other sheet's Activate sub, same as if I used an Exit Sub. My understanding of the End statement was that it was the ultimate stopping method in VBA.
Is my comprehension of the End statement wrong or am I missing something else?
Workbook SheetDeactivate Event
Workbook.SheetDeactivate Event
Copy the following code into the ThisWorkbook module. Sheet1 is the name of the worksheet that you don't want to deactivate if a condition is not met, in this example, if cell A1 is empty.
Option Explicit
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
If Sh.Name = "Sheet1" Then
If IsEmpty(Sh.Range("A1")) Then
Sh.Activate
Sh.Range("A1").Select
MsgBox "Cell 'A1' is empty.", vbCritical
End If
End If
End Sub
EDIT
To prevent triggering the Worksheet Activate events of the remaining worksheets you will need to use a global (public) variable that will indicate if it is safe to activate them i.e. the condition in e.g. Sheet1 is met. At the beginning of each of the Worksheet Activate event codes of the remaining worksheets, you will need to add an If statement checking for the value of the global variable.
Relevant Sheet Module, e.g. Sheet1 (code name, the name not in parentheses)
Option Explicit
Public IsNotMet As Boolean
Private Sub Worksheet_Deactivate()
If IsEmpty(Range("A1")) Then
IsNotMet = True
Me.Activate
Range("A1").Select
MsgBox "Cell 'A1' is empty.", vbCritical
Else
IsNotMet = False
End If
End Sub
All Other Sheet Modules
Option Explicit
Private Sub Worksheet_Activate()
If Sheet1.IsNotMet Then Exit Sub
' Your code, e.g.:
MsgBox "Worksheet '" & Me.Name & "' activated.", vbinformation
End Sub
Related
I have a worksheet with:
Private Sub Worksheet_Activate()
MsgBox "Ran"
End Sub
I have a button on my toolbar that I made. What I want it to do is trigger this method on the currently selected WorkSheet.
I figured I could do Call ActiveWorksheet.Activate or Call Worksheet.Activate but while these seem to execute without errors, the method is not called.
As a workaround I considered adding a public DoActivate method, but it seems a bit lame and I would likely have to fiddle with CallByName to get it to work (and developers would have to remember to implement this method on every worksheet).
Is there a reason why my Activate method is not calling manually via the above code, or a suitable workaround to get what I'm looking for?
Move your code to a new Sub called OnActivate
Private Sub Worksheet_Activate()
OnActivate
End Sub
Private Sub OnActivate() 'or Public if you call from another module
MsgBox "Ran"
End Sub
The Worksheet_Activate() event handler can be called manually from inside the module by Worksheet_Activate like any other sub (although this is IMO not a nice way to do it)
If you want to ensure all worksheets have the same method, then you can make them Implement an interface: e.g.
Class module: IActivateHandler
Public Sub OnActivate()
End Sub
Then in Sheet1, 2, 3 etc:
Implements IActivateHandler
Private Sub IActivateHandler_OnActivate()
MeOnActivate
End Sub
Private Sub Worksheet_Activate()
MeOnActivate
End Sub
Private Sub MeOnActivate()
MsgBox "Ran"
End Sub
And the button:
Private Sub Button1_Click()
Dim sheetToCall As IActivateHandler
' Debug.Assert TypeOf ActiveSheet Is IActivateHandler
Set sheetToCall = ActiveSheet 'gets the IActivateHandler of the sheet, ensuring it is defined. Will error if it isn't
sheetToCall.OnActivate 'runs the IActivateHandler_OnActivate() method of sheet1
End Sub
You can call the activating event of any active sheet (without knowing its name) in this way:
Create the next event in ThisWorkbook code module. Or, simple copy the following code. Take care that such an event does not already exist:
Public Sub Workbook_SheetActivate(ByVal Sh As Object)
MsgBox Sh.Name & " activated..."
'the necessary code here...
End Sub
Then, call it from a standard module in the next way:
ThisWorkbook.Workbook_SheetActivate ActiveSheet
If you want excepting some sheets, you can adapt the event code to do it:
If sh.Name <> "MySheetName" then
MsgBox Sh.Name & " activated..."
'the necessary code here...
End if
If many sheets should be excepted, an array of sheet names should be built and use Application.Match to differentiate between the sheets to use their event and the other ones.
Edited:
If you need a piece of code written in an add-in (or any macro enabled workbook), able to catch the Activate event of a sheet in any (other) open workbook, you should proceed in the next way:
Copy the next declaration on top of the add-in ThisWorkbook code module (in the declarations area):
Public WithEvents appEvHandler As Application
In the same code module, copy the next code:
Private Sub appEvHandler_SheetActivate(ByVal sh As Object)
If sh.Parent.Name <> ThisWorkbook.Name Then
MsgBox sh.Parent.Name & " workbook, sheet " & sh.Name & " activated..."
Else
Debug.Print "changed in this workbook..."
End If
End Sub
Copy also the next Sub, which will activate the event:
Sub activateAppEvHandler()
Set appEvHandler = Application 'It can be placed in `Workbook_Open` event to be run when workbook opens
End Sub
If you want to inactivate it (for some reason...), use the next Sub:
Sub InactivateAppEvHandler()
Set appEvHandler = Nothing
End Sub
Please, test it and send some feedback. I must confess I am not sure I correctly understood what you need. I was asking for a scenario to be followed, but I tried imagining that this is what you want...
Private Sub Worksheet_Deactivate()
Msgbox(worksheet.Name)
End Sub
How can I get the last deactivated Sheet once I press on any sheet other than the sheet of interest.
You firstly create a Public variable on top of ThisWorkbook code module (in the declarations area):
Public lastSheetName As String
Put the next code in the Workbook_SheetDeactivate event (also in ThisWorkbook code module):
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
lastSheetName = Sh.name
End Sub
Then you can return the name of the last deactivated sheet with a simple Sub or inside another event code. Try pasting the next code in a standard module and run it. Of course, after you deactivated at least a sheet...
Sub LastDeactivatedSheet()
MsgBox ThisWorkbook.lastSheetName
End Sub
3.a Or put the same code in the Workbook_SheetActivate event , in this way:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
MsgBox "You are coming from " & ThisWorkbook.lastSheetName
End Sub
Each time you activate another sheet, it will inform you from which sheet you come...
I have created a macro that allows a user to select an option from a dropdown list which unhides selected rows that corresponds with their selection. The problem is when I protect the sheet I get the following error "Run-time error '1004'" when I select an option for the drop down list. I need this sheet to be protected so the user cannot touch the data sets shown. Here is a sample code (original version is very long):
Private Sub Worksheet_Change(ByVal Target As Range)
ActiveSheet.Activate
If Not Application.Intersect(Range("C15"), Range(Target.Address)) Is Nothing Then
Select Case Target.Value
Case Is ="Option 1"
Rows("17:75").EntireRow.Hidden = True
Case Is ="Option 2"
Rows("17:28").EntireRow.Hidden = False
End Select
End If
End Sub
I've been reading other threads and I've come across a few options that said I need to Unprotect and Protect my sheet but I'm not to sure how to add this to the code above. And if this is the best option for what I am trying to accomplish
Option 1
Sub UnprotectAll()
Dim sh As Worksheet
For Each sh In ActiveWorkbook.Worksheets
sh.Unprotect Password:=yourPassword
Next sh
End Sub
Sub ProtectAll()
Dim sh As Worksheet
For Each sh In ActiveWorkbook.Worksheets
sh.Protect Password:=yourPassword
Next sh
End Sub
Option 2 - Adding this code somewhere below
UserInterFaceOnly:=True
Any suggestions in how I can accomplish this? And what the full code would look like?
Thanks so much!
#ExcelNoob I’ve made the following assumptions based on your question:
Only the active sheet is relevant
You formatted cell C15 as not Locked (when the sheet is unprotected, right click C15 /format/protection and uncheck ‘locked’ & ‘hidden’
You have indicated the correct rows you want hidden or not (seem a bit odd?)
There are only 2 options
That being the case, the minimum code below will do what you ask. If you want to use a specific password, just put it between the double quotation marks.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Range("C15"), Target) Is Nothing Then
ActiveSheet.Unprotect Password:=""
If Range("C15").Value = "Option 1" Then
Rows("17:75").Hidden = True
ElseIf Range("C15").Value = "Option 2" Then
Rows("17:28").Hidden = False
End If
ActiveSheet.Protect Password:=""
End If
End Sub
As per above, but if you don't want to protect/unprotect, put your drop down in a form and use :
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Sheet1.Protect "Password", UserInterfaceOnly:=True
End Sub
Where "password" is changed to whatever password you want.
This is just an example of what I want to do. I have an elaborate macro that I want to do different things depending on whether it was called by another macro or not.
sub Example()
Call MyCode
end sub
sub MyCode()
If Called by Example GoTo SkipNextLine
Do these things
exit sub
SkipNextLine:
Do other things
end sub
You can create hidden name (which, actually, isn't tied to range). Think of it as global variable. The difference between global variable and this name is that name is persisted in workbook when you close it. When you open workbook again - you can start using it without any initialization. As a bonus, this name won't be displayed in Name Manager. The defining of name is required only once.
Sub SetHiddenName()
Names.Add Name:="Caller", RefersTo:="StartValue", Visible:=False
End Sub
Sub FF()
Names("Caller").Value = "FF"
Call SS
End Sub
Sub SS()
Select Case [Caller]
Case "FF": MsgBox "Called by FF" '...
Case "ZZ": MsgBox "Called by ZZ"
End Select
End Sub
A simple approach would be to use arguments and parameters.
Sub Example()
Call MyCode("Example")
End Sub
Sub Example2()
Call MyCode("Example2")
End Sub
Sub MyCode(Origin as String)
Select Case Origin
Case "Example"
'Do stuff here
Case "Example2"
'Do other stuff here
End Select
End Sub
I made my way to this post wanting a macro that changed things on the sheet, but not wanting to kick off event driven macros. In case it's also useful for someone else, it's possible to turn these off in excel using Application.EnableEvents in the parent macro using:
Sub parentMacro()
Application.EnableEvents = False
'Do stuf here
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
'this now only is called on worksheet changes outside of parent macro,
'as it's disabled while parent runs
'Note: This disables all event macros running, so might not be perfect for all cases
End Sub
I'm curious as to whether it's possible to pass the protection status of an excel worksheet to a cell of that worksheet.
e.g.
Sheet1 is locked for editing...cell A1 would be programmed to say "locked"
Sheet1 is unlocked...cell A1 would say "unlocked".
A button on the sheet would be used to toggle worksheet protection on and off.
My sheet will be locked upon opening using a workbook_open event.
This is for a sheet where I don't want the formulae getting all mucked up upon use, but where full access might be required. Its more as a reminder to the user that they are in "Unlocked" Mode so to be extra careful.
Is using VBA a foregone conclusion?
I'm a VBA noob but don't mind using code as a solution for this
Any thoughts or suggestions welcome
You could use code in an ActiveX button on Sheet1 to do this simply
Const strPAss = "test"
Private Sub CommandButton1_Click()
If ActiveSheet.ProtectContents Then
ActiveSheet.Unprotect strPAss
[a1].Value = "unlocked"
Else
[a1].Value = "locked"
ActiveSheet.Protect strPAss
End If
End Sub
Put this in the worksheet's code module, which will place a reminder in the Status Bar (this avoids needing to lock/unlock the sheet in order to write the status in to cell A1).
Put this in Sheet1 code module. The macro will execute every time sheet1 is activated.
Private Sub Worksheet_Activate()
If ActiveSheet.ProtectContents then
Application.StatusBar = "This sheet is protected"
Else:
Application.StatusBar = "This sheet is unprotected"
End If
End Sub
Private Sub Worksheet_Deactivate()
Application.StatusBar = False
End Sub
To protect/unprotect the worksheet you could add this to an Insert>Module. Then attach these macros to separate command buttons, or run from the Developer>Macros ribbon.
Const myPassword as String = "password" '<-- replace "password" with your password
Sub Sht1Protect()
Sheet1.Protect myPassword
End Sub
Sub Sht1Unprotect()
Sheet1.Unprotect myPassword
End Sub
To ensure the sheet is always protected when you close the file, insert this in the Workbook code module
Private Sub Workbook_Close()
Sht1Protect
End Sub
You may need additional handling to control whether the file is saved/not saved etc.