Trying to create a script which detects when cell B2" in another workbook changes.
Once the change is detected run the macro RUNALL which has already been created and is working. RUNALL will run a number of macros, which saves as a pdf and sends an email to the customer.
Sub Worksheet_Changes(ByVal Target As Range)
' Run the code when cell B2 is changed
If Target.Address = Workbook("M:\Wholesale\Test.xlsx").Sheet("Sheet1").Range("B2").Address Then
Call RUNALL
End If
End Sub
Read up on Application events: https://learn.microsoft.com/en-us/office/troubleshoot/excel/create-application-level-event-handler
In a class module clsAppEvents:
Option Explicit
Private WithEvents app As Excel.Application
Private cellToMonitor As Range
Private Sub app_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Sh.Parent.Name = cellToMonitor.Worksheet.Parent.Name Then
If Sh.Name = cellToMonitor.Worksheet.Name Then
If Not Application.Intersect(Target, cellToMonitor) Is Nothing Then
Debug.Print "Changed " & cellToMonitor.Address & " on " & _
Sh.Name & " in " & Sh.Parent.Name
End If
End If
End If
End Sub
Private Sub Class_Initialize()
Set app = Application
End Sub
Property Set TheCell(c As Range)
Set cellToMonitor = c
End Property
In a regular module:
Option Explicit
Private obj
Sub Tester()
Set obj = New clsAppEvts
Set obj.TheCell = Workbooks("Book2").Sheets(1).Range("A3") 'for example
End Sub
As BigBen has pointed out, the Worksheet_Changes is triggered only when there is a change in the sheet where your script is specifically located, therefore you should move the macro to the sheet's code located on the other workbook (the one that suffers the change).
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$2" Then
Application.Run ("'M:\Wholesale\My_Book.xlsm'!RUNALL")
End If
End Sub
Since the RUNALL macro will be located in a different workbook, you should use the Application.Run() method as findwindow also pointed out to make the reference to a different workbook. In this case "My_book.xlsm" is the one containing the RUNALL macro.
Moreover, note that Workbook("M:\Wholesale\Test.xlsx").Sheet("Sheet1").Range("B2").Address will only return $B$2 making no difference between the two workbooks.
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...
When the column is refreshing nothing is happening but when I go to the cell and change the value then it is changing.
I want when cells update through refresh it should run.
The column updates but the code doesn't trigger the macro.
Also tried Worksheet_Calculate().
The column is linked with online stock data from NSE website.
Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("B:B")) Is Nothing Then
MsgBox "Cell Value Changed"
Call MyMacro()
End If
End Sub
On internet just told to use Worksheet_Calculate().
Also if trying to update the cell which is equal to a cell in Range("B:B"), the value changes but macro doesn't trigger.
Maybe give this a try by using Workbook_SheetChange instead of Worksheet_Change
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("B:B")) Is Nothing Then
MsgBox "Cell Value Changed"
Call MyMacro()
End If
End Sub
Note that you need to put your code in ThisWorkbook and not your module
Edit : to test the Answer :
Sub TryMe()
For i = 1 To 100
Cells(2, 2).Value = i
Next
End Sub
TryMe Should be added inside a module like below
Workbook_SheetChange hould be added inside ThisWorkbook like below
When we execute test module we should have stuff like this:
and so on..
EDIT 2 If Value are changed by formula :
Give this a try :
This code should be placed in the sheet you are using (in my exemple Sheet1)
Private Sub Worksheet_Calculate()
Dim rng As Range
Set rng = Range("B:B")
If Not Intersect(rng, Range("B:B")) Is Nothing Then
MsgBox "Cell Value Changed"
End If
End Sub
In a Module execute this code once :
Sub TryMe()
ActiveWorkbook.RefreshAll
Application.Calculation = xlAutomatic
End Sub
then this should work
You can use events of QueryTable object behind the table linked to a web source. To do that, you first need to create a class module. Let's call it clsQryTebleEvents. In that module place a WithEvents variable of type Excel.QueryTable and set it to the QueryTable for which you want to capture events. Here's the code for clsQryTableEvents:
Option Explicit
Private WithEvents qryTable As Excel.QueryTable
Private Sub Class_Initialize()
'QueryTable connected to a webpage is on Sheet1, and it's the only table on that sheet, so we can access it with ListObjects(1)
Set qryTable = Sheet1.ListObjects(1).QueryTable
End Sub
Private Sub Class_Terminate()
'Free Memory
Set qryTable = Nothing
End Sub
'You can use other events as well
Private Sub qryTable_BeforeRefresh(Cancel As Boolean)
MsgBox "Refresh is about to start!", vbInformation
End Sub
Next, you need to initialize a variable of this class. You can declare a Public variable inside a standard module and the use Workbook_Open event to instantiate it. Code in a standard module:
Option Explicit
Public objQryTable As clsQryTableEvents
Code in ThisWorkbook:
Option Explicit
Private Sub Workbook_Open()
Set objQryTable = New clsQryTableEvents
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'Free memory
Set objQryTable = Nothing
End Sub
All done! Next time you open the workbook, the objQryTable will be initialised and will start listening to refresh events.
I created a macro that filters based on a cell value which works fine.
Range("A1:L1").AutoFilter Field:=4, Criteria1:=Range("U1")
I need this macro to run everytime the cell value changes.
I wrote a macro but it is not working i dont get any errors just nothing happens.
I tried:
Private Sub Worksheet_Tabelle1(ByVal Target As Range)
If Target.Address = "$U$1" Then
Application.EnableEvents = False
Range("A1:L1").AutoFilter Field:=4, Criteria1:=Range("U1")
Application.EnableEvents = True
End If
End Sub
This version should just execute the code and not call a macro. I changed the Worksheet_xxxxx to the sheet name and tried other things.
I also tried:
Private Sub Worksheet_Arbeitstabelle(ByVal Target As Range)
If Target.Address = "$U$1" Then
Call Macro1
End If
End Sub
This version should call the following Macro:
Sub Macro1()
Range("A1:L1").AutoFilter Field:=4, Criteria1:=Range("U1")
End Sub
I put all the Private Sub macros on the Worksheet and the Macro1 in a modul.
The file is .xlsm and doesnt have any problem running other macros so i dont know why its not working. My guess is i probably did something wrong with the names so here are the names:
Try this:
Dim KeyCells As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("A1:C10")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
' Display a message when one of the designated cells has been
' changed.
' Place your code here.
MsgBox "Cell " & Target.Address & " has changed."
End If
End Sub
I changed:
Private Sub Worksheet_Arbeitstabelle(ByVal Target As Range)
To:
Private Sub Worksheet_Change(ByVal Target As Range)
#Siddharth Rout linked me to:
Why MS Excel crashes and closes during Worksheet_Change Sub procedure?
Here it is explained that its not necessary to add the name of the Sheet after Worksheet_
because the code is stored in the sheet so no need to tell it where to do stuff since its not in a module
In my Excel sheet, I have a range "plot" that triggers a sub-routine upon change. I used the following code for that:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = Range("plot").Address Then
auto_save_data (current_plot) ' <- doesn't work
restore_data
End If
End Sub
This code has to first save the data from the current worksheet to a specific range, that defined by current_plot in another worksheet (let's call it "DATA"), by calling auto_save_data (current_plot).
Then it restores the data from a specific range in "DATA" that is defined by Range("plot"), by calling restore_data.
The restore_data sub-routine above work as expected, but auto_save_data doesn't. The problem is that when the user change the value of "plot" I need to somehow know what was the value before the change, so I can save the data to the correct place before restoring the data from "DATA" for the value after update, and by that deleting the data in the current sheet.
I tried to use the Worksheet_SelectionChange event, as described here:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim current_plot As Variant
If Target.Address = Range("plot").Address Then
current_plot = Target.Value
End If
End Sub
But it has 2 problems:
It didn't work. The sub-routine Worksheet_Change didn't seem to recognize the value of the variable current_plot, albeit, it didn't throw an error.
I tried another method from the question above, that save the old value to a hidden sheet. That worked, except when the user changes the value in "plot" without selecting another range first (then the value in the hidden sheet does not update).
So my question is: What is the simplest method (I'm very new to VBA) to use the value that was in Target before the routine Worksheet_Change was triggered?
EDIT: I changed "plot" to be a single cell range ($P$2), to focus the question on the real problem.
Assuming "Plot" is a single-cell range in the worksheet which has Name of "DATA", place the following code in the code module of Worksheets("DATA"):
'Declare a variable with global scope, i.e. accessible in this worksheet
'and in other code modules.
Public current_plot As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = Range("plot").Address Then
'Use "current_plot" whenever the cell is changed
MsgBox "Old value: " & current_plot & vbCrLf & _
"New value: " & Target.Value
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Address = Range("plot").Address Then
'Update "current_plot" whenever the cell is selected
current_plot = Target.Value
End If
End Sub
and place this code in the ThisWorkbook code module:
Private Sub Workbook_Open()
'Initialise "current_plot" when the workbook is opened
Worksheets("DATA").current_plot = Worksheets("DATA").Range("Plot").Value
End Sub
Assuming you don't want to know what used to be in the cell, but instead you actually want to know what was in the cell the last time you made use of the cell's value, you can simplify this a bit by using the following:
'Declare a variable with global scope, i.e. accessible in this worksheet
'and in other code modules.
Public current_plot As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = Range("plot").Address Then
'Use "current_plot" whenever the cell is changed
MsgBox "Old value: " & current_plot & vbCrLf & _
"New value: " & Target.Value
'...
'... process whatever needs to be processed
'...
'Save the value we used this time
current_plot = Target.Value
End If
End Sub
and place this code in the ThisWorkbook code module:
Private Sub Workbook_Open()
'Initialise "current_plot" when the workbook is opened
Worksheets("DATA").current_plot = Worksheets("DATA").Range("Plot").Value
End Sub