How to apply an Excel event handler to all sheets? - excel

I've created VBA that manipulates the cells in my current sheet (triggered by a SelectionChange event handler).
How can I make this trigger in any of the sheets in my workbook? (The action should only react to, and modify, the sheet that's currently active.)
Here's an example: It takes the value of the current cell, and copies it to "A1":
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Cells(1, 1).Value = Cells(Target.Row, Target.Column).Value
End Sub
(The real code is rather more complex —and constant WiP—, so I don't really want to copy it to each and every sheet.)

Using Workbook_SheetSelectionChange instead of Worksheet_SelectionChange seems to work.
It should be noted though, that the script then has to be placed into the ThisWorkbook code window (instead of the one for Sheet1).

Related

Macro "Doesn't Work" From Call on Separate Sheet

This macro works as intended, from a button on the Batch Input sheet:
Sub BatchTriggerOFF()
Sheets("Batch Input").Unprotect
Sheets("Batch Input").Range("G3:J3").Value = "Off"
Sheets("SQL LOGIC").Calculate
Sheets("Batch Input").Range("A12").Select
Sheets("Batch Input").Shapes.Range(Array("Group 12")).ZOrder msoSendToBack
Sheets("Batch Input").Protect
End Sub
However, when BatchTriggerOFF is called from a different sheet in the same workbook, the macro neither changes the Range("G3:J3").Value nor Shapes.Range(Array("Group 12")).ZOrder msoSendToBack. There is no error message.
If Sheets("SQL LOGIC").Range("B1") = "On" Then Call BatchTriggerOFF
I've tried unprotecting the Batch Input sheet beforehand, messing with Sheets("Batch Input").Activate, Sheets("Batch Input").Select, and even tried pasting the BatchTriggerOFF line by line VBA directly into the second macro, to no avail.
What is causing BatchTriggerOFF to seemingly not run when called from the second macro/sheet?
[...] something is inherently wrong with the second code I've provided, likely not actively running when the value in Range("B1") is changed?
Exactly. A procedure that's in a standard module needs something, somewhere to invoke it. Could be a shape or button on the worksheet, could be other VBA code, but something needs to invoke it somehow.
No procedure is going to just know to run when Range("B1") is changed on Sheets("SQL LOGIC"): you need to have code that's "triggered" when a cell is changed on that sheet.
The way to do this, is to handle the worksheet module's Change event. Find your "SQL LOGIC" sheet in the VBE's Project Explorer (Ctrl+R), double-click it. In the code-behind module for that specific worksheet, select Worksheet from the left-side dropdown at the top of the code pane; the right-side dropdown should say SelectionChange, and the VBE should have added a private procedure that looks like this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
End Sub
Select Change from the right-side dropdown; the VBE creates a private procedure that looks like this:
Private Sub Worksheet_Change(ByVal Target As Range)
End Sub
Now delete the SelectionChange handler, you don't need it unless you need to track cells that the user has selected. Since we want to track cells that have changed, we'll use the Change worksheet event. This procedure will be invoked whenever the user or your code changes anything on that sheet.
Since we only care to run code when a specific cell is changed, we need a condition here, involving the Target parameter. Using the Application.Intersect function, we can get a Range object reference that's Nothing if the two specified ranges don't intersect; we can use this information to bail out if it's not B1 that changed:
If Application.Intersect(Me.Range("B1"), Target) Is Nothing Then Exit Sub
Any code written after that condition inside the Worksheet.Change event handler procedure, will only run after the value of cell B1 was modified - either by the user typing in a value, or by any other code writing to that cell (you need to toggle Application.EnableEvents off if you have to prevent firing that event when it's code doing the changes and you don't want the handler to run).
Now, it looks like cell B1 isn't going to change, rather, it looks like it contains a formula whose result might change after making changes to the "Batch Input" sheet.
If that's the case, then the Change event will not be fired when B1 recalculates and now evaluates to a new value, because the cell didn't change, only its result.
If that's your scenario, then you want to handle the Calculate worksheet event, and have that be your trigger:
Private Sub Worksheet_Calculate()
If Me.Range("B1").Value = "On" Then BatchTriggerOFF
End Sub
If you need your sub to be called from any (sheet) module, move it in a module! The function/sub in the sheet module cannot be called without specifying the module name where it belongs, like you will be able to do in a module.

Activation event getting triggered by PasteSpecial operation

While programming a module in VBA that operates on copy-pasting a range based on formulae on a sheet, the currently activated sheet's Worksheet_Activate event is triggered.
I know that I can use Application.EnableEvents = False to disable the trigger, however, I would like to understand why the PasteSpecial operation for a Range object triggers the Worksheet_Activate method, as I cannot find any mention of the same on the Range.PasteSpecial documentation, or what actions trigger the Worksheet_Activate event.
For a reproducible example, please create a workbook with two sheets, Sheet1 and Sheet2, and insert code in Sheet1 as so:
Public Sub Worksheet_Activate()
MsgBox "Sheet 1 has been activated."
End Sub
Add a separate module with code as so:
Public Sub copy_and_paste_in_sheet2()
Set Rng1 = ThisWorkbook.Worksheets("Sheet2").Range("A1:A10")
Rng1.Copy
Set rng2 = ThisWorkbook.Worksheets("Sheet2").Range("B2:B12")
rng2.PasteSpecial xlPasteFormulas
End Sub
While Sheet1 is activated, run the copy_and_paste_in_sheet2 macro, and it will be evident that the Worksheet_Activate event for Sheet1 is triggered despite no apparent code in the second module explicitly doing so.
I would expect that notwithstanding any use of Select operations that require the Activate command to work, the PasteSpecial operation should not ideally trigger the Worksheet_Activate event. Could you please direct me to the relevant documentation for this behaviour?
It seems to be one of the odd things you may have when dealing with Excel: PasteSpecial quickly "activates" the sheet you are pasting to. You can see the screen flicker (at least I can, using a VM that is rather slow when it comes to screen updating). Also, if you put similar event code to sheet2, you will see that it is triggered also.
The strange thing is that in the triggered sheet you see still sheet1 as ActiveSheet. So the following code (as event routine of Sheet2) will print "activate 2 Sheet1"
Public Sub Worksheet_Activate()
Debug.Print "activate 2 " & ActiveSheet.Name
End Sub
I doubt that you will find any documentation for that behaviour. And your best bet is to accept this and (as you already wrote) use Application.EnableEvents = False

Code for automatic updating Userform

I'd like to know on how to code a userform (VBA Excel) with automatically updating itself when the cell values has changed.
I have produced a button that will show the userform with labels and text boxes. But whenever i click it yes it shows up but i need to click the userform in order for me to see the values.
Need help.
Thank you in advance,
Tramyer
The Worksheet.Change event is fired everytime you change the contents of a cell on a given worksheet.
You can create an event handler in the worksheet module (usually labeled Sheet1, Sheet2 etc. in the VBA editor) that is called every time the event is fired:
Private Sub Worksheet_Change(ByVal Target As Range)
Debug.Print Target.Address
End Sub
This example just outputs the address of the cell that was changed, but you can adapt this to update your userform with the changed values instead.

How can I run a VBA code each time a cell get is value changed and where do I put it the code?

I tried running the private sub code:
Private Sub Worksheet_Calculate()
Dim target As Range
Set target = Range("G3")
If Not Intersect(target, Range("G3")) Is Nothing Then
End If
End Sub
I am pulling a value from one sheet and putting it into another cell on a separate sheet. I want the VBA to run when the cell is automatically changed. When I tried the code above, nothing happened when I updated the cell.
From the sounds of things the issue seems to be that you're trying to put the code in a Module like a regular macro. To have it run based on a worksheet event, you need to have the code in that worksheet's code window (in the VBA window, there's the "Microsoft Excel Objects" folder, inside is the list of worksheets, double-click the worksheet to open it's code).
Once you've opened the worksheet's code, at the top of the window you should see two drop-downs. The left one should show "(General)". In that drop-down select "Worksheet" (should be the only option).
In the drop-down to the right of that, select "Change". Then you need to validate the Target is the right cell (If Target.Address = "$<Column letter>$<row #>"). Inside that If statement is where you'd nest your code to copy the value to the second worksheet

Automatically Run Macro When Data Is Pasted VBA

I have a worksheet where in the first three columns (A,B,C) I store data. These values are used in a macro.
I would like to know how it is possible to make this macro run automatically after data is pasted onto these columns. I am almost sure that I will use the Worksheet-Change module, but as for the code I am clueless.
Thanks in advance.
A simple implementation:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A1:C" & ThisWorkbook.Worksheets(1).UsedRange.Rows.Count)) Is Nothing Then
'Call your Macro to do stuff
End If
End Sub
Intersect checks if Target is in the range you want to monitor. So if something changes in columns past C, Intersect will return Nothing and your macro won't be called. Just keep in mind that the Worksheet_Change event fires on any change, even double clicking into the cells. If all you are doing in this Worksheet is copy&pasting data and then running your Macro, this should be fine, but if you are manipulating your data further, you might have to look at more sophisticated solutions. Examples include mirroring your Worksheet and comparing it pre/post Worksheet Changed Event. You can read more on this here: Detect whether cell value was actually changed by editing

Resources