when using the below code my excel freezes with no error (i have to pause code and end to allow excel to be used again. - the weird thing is i can run each of the updates individually and it wll work fine. but when all of them are active it stops working.
any advice?
Private Sub Worksheet_Change(ByVal Target As Range)
With ThisWorkbook
Dim Country As String
Country = .Sheets("Views").Range("C2").Text
Select Case Country
Case Is = "Australia"
.Sheets("Views").Range("C5").Formula = "=Volume!B7+Volume!H7+Volume!N7+Volume!T7+Volume!Z7+Volume!AF7"
.Sheets("Views").Range("C6").Formula = "=Volume!C7+Volume!I7+Volume!O7+Volume!U7+Volume!AA7+Volume!AG7"
.Sheets("Views").Range("C7").Formula = "=Volume!D7+Volume!J7+Volume!P7+Volume!V7+Volume!AB7+Volume!AH7"
.Sheets("Views").Range("C8").Formula = "=Volume!E7+Volume!K7+Volume!Q7+Volume!W7+Volume!AC7+Volume!AF7"
Case Is = "China"
'do china
End Select
End With
End Sub
thanks Variatus, you were 100% correct.
Presuming that the code is in the code sheet of Sheets("Views") the Change event, once triggered, would cause Change events by the changes it creates on the sheet, resulting in an endless loop which would, probably, eventually end in "out of memory". To avoid that, set Application.EnableEvents = False before any changes are made and set the property back to True after all changes have been completed. – yesterday
I also found that adding the following at the start also helped, this way the code only trigered updates when the specific Cell i wanted changed was changed i.e. the Country selection
If Not Application.Intersect(Range("C2"), Range(Target.Address)) Is Nothing Then
'code you want exicuted
End If
Related
I have a worksheet_change macro embedded in two sheets within my workbook. They are there to prevent anyone making changes to the sheets. However, I still want the data within the sheets to be refreshed every so often. This does not work.
Two sheets within the workbook are connected via a query to another workbook. Essentially those sheets are a copy of the sheets within the other workbook.
I have embedded Code1 into the two worksheets. This is to prevent anyone making changes to the worksheet but still allow them to view the sheet and copy data from it. It brings up an message box and then undoes the change made by the user. This works fine and I am happy with it.
At the same time I want to be able to refresh the workbook so that the connected sheets are up to date with respect to the other workbook that they are connected to.
To do this I have added a button into the workbook called "Refresh". This button calls Code2. This was done with the intention of disabling events so that the worksheet_change macro is paused to allow for the data to be refreshed.
However, this does not work as the worksheet_change macro still works. I.e after clicking the button, the workbook is refreshed and then any update is undone and the message box is displayed - which isn't what I need.
CODE1
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("A1:Z1000")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
With Application
.EnableEvents = False
.Undo
.EnableEvents = True
End With
' Display a message when one of the designated cells has been
' changed.
' Place your code here.
MsgBox "DO NOT MODIFY THIS SHEET - Any necessary modifications should be made in 'Master Invoice Template' and this sheet will automatically be updated!"
End If
End Sub
CODE2
Sub refresh()
On Error GoTo ErrorHandler
Application.EnableEvents = False
ThisWorkbook.RefreshAll
ErrorHandler:
Application.EnableEvents = True
End Sub
I have scoured the internet for a solution and pretty much everything that I find points me in the direction of enableevents=false, but as described in my post this does not work. Do I need to change the method of solving my problem or am I doing something wrong within my code?
I suspect the undo line of code is causing the problem, but I am not sure!
Any help would be greatly appreciated!
I think I have figured out what was wrong with the code; correct me if I am wrong. The data was taking too long to refresh when Code2 was ran. This meant that the Application.EnableEvents = Ture in Code2 took effect before the data could be fully refreshed and when it finally did complete its update, the Worksheet_Change event was triggered.
I tried using DoEvents after the RefreshAll command but this didn't work either. I have used what I found in this post to work around the problem and the refresh button now works!
Specifically the code that helped is below: I replaced Code2 with this:
Sub Refresh_All_Data_Connections()
For Each objConnection In ThisWorkbook.Connections
'Get current background-refresh value
bBackground = objConnection.OLEDBConnection.BackgroundQuery
'Temporarily disable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = False
'Refresh this connection
objConnection.Refresh
'Set background-refresh value back to original value
objConnection.OLEDBConnection.BackgroundQuery = bBackground
Next
MsgBox "Finished refreshing all data connections"
End Sub
Please let me know if my logic in explaining why the code didn't work is correct - I am still new to VBA and would like to understand the problem fully!
I have VBA code which includes the functions Worksheet_Calculate() and Worksheet_Change(ByVal Target As Range) because I want to update some cells when any recalculation takes place as well as when I modify some particular cells in the sheet. Both of these subroutines call exactly the same subroutines. However, one of them works properly (Worksheet_Change) and the other (Worksheet_Calculate) does not, even though they both call exactly the same functions.
The next step I took was to set up breakpoints in places where I thought things went wrong when any recalculation took place, and to my surprise, the code executed by Worksheet_Calculate() worked properly this time (when stepping through the code by using breakpoints, etc). Aditionally, it will ocasionally work properly even in normal mode (not in debug), but this is very random. I have no idea what is causing this. Below is a shortened version of my subs (MakeVisible, UpdateBaseline, UpdateDerivative1... are all subroutines I defined later):
Private Sub Worksheet_Calculate()
MakeVisible
UpdateBaseline
If ([F4] > 0) Then
UpdateDerivative1
End If
If ([F4] > 1) Then
UpdateDerivative2
End If
If ([F4] > 2) Then
UpdateDerivative3
End If
If ([F4] > 3) Then
UpdateDerivative4
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Application.Intersect(Sheets("Main").Range("C7:C61"), Range(Target.Address)) Is Nothing Then
MakeVisible
UpdateBaseline
If ([F4] > 0) Then
UpdateDerivative1
End If
If ([F4] > 1) Then
UpdateDerivative2
End If
If ([F4] > 2) Then
UpdateDerivative3
End If
If ([F4] > 3) Then
UpdateDerivative4
End If
End If
End Sub
After spending some more time debugging, I believe that there is some race condition since the code in UpdateBaseline, UpdateDerivative1, etc. is actually resizing and moving some shape objects (such as stars, straight connectors, etc) and these get moved to the wrong place even though the variables I'm using to position them seem to have the correct value. My intuition tells me that moving shapes or changing their properties takes some computation that might be causing some race condition, but this is just a wild guess, it could be completely unrelated.
Thank you!
I was able to identify the problem thanks to #Slai. The bug is indeed cause by a dependency of the code on the currently active sheet, but it wasn't fixed after I specified the sheet for particular cells (I changed [F4] to [Main!F4] and all other ocurrences.) I am still not able to figure out what the problem is since I made sure that all of my code specifically works with either [Main!<Cell>] or Sheets("Main").Range("<Cell>"), which would make any dependency on the currently active sheet dissappear. However, I added code to make the currently active sheet go back to "Main" when Worksheet_Calculate() gets triggered and this caused correct behavior. The code I added was Worksheets("Main").Activate on the first line of Worksheet_Calculate().
Thank you!
You can try something like this
Dim ignoreEvents As Boolean
Private Sub Worksheet_Calculate()
If ignoreEvents = True Then Exit Sub
ignoreEvents = True
' your code here
ignoreEvents = False
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If ignoreEvents = True Then Exit Sub
ignoreEvents = True
' your code here
ignoreEvents = False
End Sub
hi there im creating a spreadsheet to use for clocking in and out of work and have a simple wee GUI set up already. along with this i have the current time showing as soon as i start up the worksheet but it shows on every page i was wondering how to stop this?? sample code below:
Global clockOn As Boolean
Sub runClock()
Range("M15").Value = Now()
If clockOn = True Then
Application.OnTime Now + TimeValue("00:00:01"), "runClock"
End If
End Sub
Sub Auto_Open()
clockOn = True
runClock
End Sub
Sub stopClock()
clockOn = False
End Sub
on top of this i will be doing a macro to put the information onto a specific page all going well as this will be depending on the day selected from the drop down menu any help with this would be greatly appriciated :) as in maybe an if statement in the VBA code to select the correct page and leave the data there.
You need to specify the sheet that you are putting the value on; currently you only specify the range, but you don't specify the sheet. Can be done multiple ways, such as:
Sheets(1).Range("M15").Value = Now()
This selects the first sheet in the workbook. If you have multiple workbooks, you will need to specify that somehow as well (by referring to the active workbook, or the name of a workbook, etc.). For example:
Activeworkbook.sheets(1).Range("M15").Value = Now()
I want to run the bellow macro whenever cell F3 increases. I need this to happen without manual intervention because F3 is increasing due to incoming RTD server data. As it stands, unless I manually update something in my sheet the macro does not run.
Public Prev_Val As Long
Private Sub Worksheet_Change(ByVal Target As Range)
'using a Public variable to store the previous value
If Range("F3") <> Prev_Val Then
Application.EnableEvents = False
Range("I3") = Range("F3") - Prev_Val
Prev_Val = Range("F3")
Application.EnableEvents = True
End If
End Sub
I've tried using:
If Target.Address = "$F$3" Then
'code here
But that does not seem to work.
Context: I'm using RTD with a stock simulator to automatically populate fields in Excel. Several calculations are done on the incoming data, but I cant do any of them without having Cell I3 work properly!
This might work, instead of the Worksheet_Change event, use the Worksheet_Calculate event. This procedure runs every time the sheet calculates, so you will need to have Automatic calculation enabled (this is normal default setting).
This is basically your exact code, slightly tweaked:
Public Prev_Val As Long
Private Sub Worksheet_Calculate()
Dim rngF3 As Range
Set rngF3 = Range("F3")
If rngF3 <> Prev_Val Then
Application.EnableEvents = False
Range("I3") = rngF3 - Prev_Val
Prev_Val = rngF3
Application.EnableEvents = True
End If
End Sub
Now one limit I think is that after you end the session, save/close the file, etc., the value of Prev_Val will not persist. There are a number of ways you might work around this limitation using a hidden worksheet to store the value, or a Name variable in the worksheet/workbook, or CustomDocumentProperties (I think I recently wrote an answer here to another Q about how to use CustomDocumentProperties, and I'm certain I have a few about using Names for this sort of approach, too...).
But maybe the simplest would be to do something like this in the Worksheet_Activate event
Private Sub Worksheet_Activate()
If Prev_Val = 0 Then Prev_Val = [F3]
End Sub
I haven't really tested that too thoroughly, though, and it may not be a perfect fit for you, so it might be better to use the CustomDocumentProperties, here is an example:
Alternatives to Public Variables in VBA
Several of the other possible methods are posted as answers in that Q, too.
Hope this helps!
I have a workbook with multiple Worksheet_Change events in the sheet.
Each of these macroes are related to an active x combobox.
The problem is that when I change one of the comboboxes the macro fires (as expected), which in turn start another macro (which is not suppose to run). I have already set the Application.EnableEvents = False
But the issue might be that I am changing a cell.value, which is linked to another combobox and hence also linked to another worksheet_change event.
A workaround as I see it, might be to only run the macro, if the combobox is the one actually selected, but here comes the second problem. I can't find a way to have vba return the name of the active combobox.
Please note that these comboboxes is not connected to a userform, they are simply placed directly on the worksheet.
Is there anybody who has any idea on how to solve this??
Any help is much appreciated,
I see that there are 2 possible solutions...
1 - as stated by Gary's Student, you may have toggled the Application.EnableEvents somewhere unknowingly and you could try and trap where that happens.
2 - Maybe set a global boolean variable called, say DontRunMacros and set it to TRUE at the start of the first macro and to FALSE at the end. Then you simply have each other macro start with If DontRunMacros Then Exit Sub - That will prevent the others running regardless of the events that fire...
Hope that helps!
You could also set a global variable handling events. You check it at the beginning of each change event.
Dim ufEventsDisabled As Boolean
Sub YourSub()
ufEventsDisabled = False
Range("A1").Value=1 'This triggers the event
ufEventsDisabled = True
Range("A1").Value=1 'This doesn't trigger the event
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If ufEventsDisabled=True Then Goto ExitEvent:
'Your regular worksheet code
ExitEvent:
ufEventsDisabled=False
End Sub
There is probably an error somewhere that is re-Enabling Events...........You can always insert:
MsgBox Application.EnableEvents
at points in your code to trap this.
Thanks a lot for the quick responses.
I ended up using a named cell in the worksheet, similar to what hstay sugested.
If ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 0 Or Me.Name <> ActiveSheet.Name Then Exit Sub
However as I need the worksheet to calculate some cells while the code is running, I can't set application.Calculation = xlManual.
So the code still tries to execute a bunch of other change events, but the code above stops them from running more than just the first line. This however still takes a lot of time, which is quite frustrating. Guess I'll just need to take this in to account another time.
This is how I begin and end all my worksheet_chnage events:
If ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 0 Or Me.Name <>
ActiveSheet.Name Then Exit Sub
ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 0
Application.ScreenUpdating = False
Application.EnableEvents = False
'some random code that trigger other change_events
Application.EnableEvents = True
Application.ScreenUpdating = True
ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 1