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!
Related
The below code is written into the code of a Sheet (called "Start page") rather than connected to a button.
For reference, cell C17 asks the user of the sheet if they're ready to start, and they have a data validation in D17 linking to M2 (Yes) and M3 (No).
The idea is that when the user selects Yes, the code kicks in and auto-advances to the next sheet ("Selection page"). If they select No, a pop-up box will display advising the workbook will close (I've not written this code yet).
The problem is, whilst I can't see what's wrong with this code, whenever the user selects anything from the data validation, nothing happens.
I'd be really grateful if someone could review the code to see what I'm missing. I've tried Range("D17") and Range("D17").Value but still nothing.
Thanks
EDIT - latest code listed below
Private Sub Worksheet_Change()
Dim output As Integer
Application.ScreenUpdating = False
Dim output As Integer
If Sheets("Start page").Range("D17") Is Nothing Then GoTo ExitHandler
If Sheets("Start page").Range("D17") = Sheets("Start page").Range("M2") Then
Sheets("Selection page").Activate
Else
output = MsgBox("This sheet will now close.", vbCritical, "Closing")
Activebook.Close
ExitHandler:
Application.ScreenUpdating = True
End Sub
Currently, you have nothing triggering the code. You probably want to tie this into a Worksheet event, such as Worksheet_Change or Worksheet_SeletionChange
The quick way to check what Events are available is to go to the top of the Code Pane, select the "Worksheet" object from the Left drop-down, and select the Even from the Right drop-down. This will also automatically add the correct Parameter list:
The "Change" event will occur when any cell on the Worksheet is Changed - while the "SelectionChange" event will happen when you select something new, even if you haven't altered the old thing
For the "Change" event of the Worksheet there is 1 Parameter, ByVal Target As Range - this is the Range of Cells that have been changed. So, for example, you can use MsgBox "Row " & Target.Row & " was changed" to display a message telling you which row the changed cell is on.
Since the Event will automatically pass the Argument to the Sub, you need to have the Parameter there to 'catch' it.
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
I have an excel sheet that became very slow.
For some reason, wherever I am in the document, if I set Calculation to manual and refresh the current sheet after any change, it is fast enough and serves my purpose.
This is not very comfortable however.
I would like the current sheet (and not the whole document) to be refreshed whenever a cell is changed. This should be done whatever sheet I'm on. How can I do that?
EDIT: let it be clear that I'm not asking for clues on how to make my workbook faster, it was just contextual info. I'm interested in autorefresh only.
Charles Williams has extensive information, techniques and code on calculation on his website http://decisionmodels.com. Quoting from this page:
Another method uses the worksheet.enablecalculation property. When
this property is changed all the formulae on the worksheet are flagged
as uncalculated, so toggling the property to false and then back to
true for all sheets in all open workbooks will cause the next
calculation to be a full calculation.
> Dim oSht as worksheet Application.Calculation=xlCalculationManual
>
> for each oSht in Worksheets oSht.enablecalculation=false
> osht.enablecalculation=true next osht
>
> Application.calculate
You can also use this method for a single worksheet to do a full sheet
calculate.
You can easily work this technique into the Worsheet_Change event, like this:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.Calculation = xlCalculationManual
Me.EnableCalculation = False
Me.EnableCalculation = True
Me.Calculate
End Sub
This will do a full calculation of the current sheet only and leave all other sheets uncalculated.
I believe the Calculation event can happen at the Application, Worksheet or Range level (but not the Workbook level.)
If you have calculation mode set to manual but want the current worksheet (only) updated when you enter something, you could add a worksheet change event (putting the code in the relevant sheet.)
Private Sub Worksheet_Change(ByVal ChangedRng As Range)
ChangedRng.Worksheet.Calculate
End Sub
This will recalculate just the worksheet and not the rest of the Workbook or any other workbooks.
As the provided answers don't fully satisfy me here is my own, borrowing from their appreciated help. It's possible that I've missed some nuance from teylyn's answer. I'll gladly edit if it's relevant.
There doesn't seem to be a way to handle change events at the APPLICATION level.
So all changes must be handled at the SHEET level.
This can be done by copying and pasting this bit of code in EVERY Sheet object of the workbook (and remember to copy and paste it it whenever you add a new sheet):
Private Sub worksheet_change(ByVal Target As Range)
Me.Calculate
End Sub
However because of the tedious copy pastes we had to do, we may want to turn this feature off easily without going back to each sheet object.
We may also want to avoid entering a loop of hell if we do mass change on one sheet (say through a macro, a drag & drop, or a ctrl+H).
To do this we add a new sheet called WS_refresh where we set 3 values :
in A2: Yes/No to enable/disable our new feature
in A4: time of latest refresh
in A6: min interval to allow refresh, in sec (I've set it to 1)
Now when we change values in several cells in a short amount on time, autorefresh will only work on the first change, avoiding previously mentioned loops of hell. If you like to live dangerously set A6 to 0.
This is what should be copied and pasted in every sheet object:
Private Sub worksheet_change(ByVal Target As Range)
auto_refresh = ThisWorkbook.Worksheets("WS_refresh").Range("A2")
If auto_refresh = "Yes" Then
last_refresh = ThisWorkbook.Worksheets("WS_refresh").Range("A4")
refresh_interval_sec = ThisWorkbook.Worksheets("WS_refresh").Range("A6")
refresh_interval_tv = TimeValue("0:00:" & refresh_interval_sec)
If Application.Calculation = xlCalculationManual And Now() > last_refresh + refresh_interval_tv Then
ThisWorkbook.Worksheets("WS_refresh").Range("A4") = Now()
Me.Calculate
End If
End If
End Sub
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
want to use a Spin button (ActiveX Control) to show a previous or next sheet. When the button is clicked the event is successfully triggered and the desired sheet is activated but it holds some elements (commands, graphs, etc.,) from the original sheet and shows these as an appending picture.
Sample code for Down button event :
Private Sub SpinButton_JumpToWeek_SpinDown()
Dim sh_num As String
Dim tmp_num As Integer
Application.ScreenUpdating = False
Application.EnableEvents = False
SpinButton_JumpToWeek.Value = Range("B27").Value - 1
tmp_num = SpinButton_JumpToWeek.Value
' Activate desired KTx sheet
sh_num = "KT" & tmp_num
Range("F27").Value = "" 'reset to blank
Sheets(sh_num).Activate
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
To override this effect I have to manually select (activate) another sheet and then again select the desired sheet. I tried also to automatize this workaround with a macro, but unfortunately it does not work.
It is interesting that this problem do not occur if I execute code in Debug mode (using breakpoint and the stepping line by line).
Surprisingly, I do not have such problem if I try to show the previous/next sheet by writing a value (sheet name index) into a defined cell (i.e. using the Worksheet_Change event). The desired page is correctly shown. See photos.
Sample code for this evententer image description here:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim sh_num As String
Application.ScreenUpdating = False
Application.EnableEvents = False
If Range("F27").Value > 0 Then
' Activate desired KTx sheet
sh_num = "KT" & Range("F27").Value
Range("F27").Value = "" 'reset to blank
Sheets(sh_num).Activate
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
I need to use the Spin button because it is faster and allow me to skip some sheets (for example if these do not have data).
The problem occurs in both 32 and 64-bit Excel.
Do somebody an idea why this problem is occurring and a workaround? Do I have to change some settings or system properties in Excel? Any help would be highly appreciated.
#mehow
I append my commenst here due to large number of words.
I followed your suggestion and tried the example of a UserForm with inserted active control “Microsoft Office Spreadsheet”. I found out that this would not be a good solution for my case, because the response of such sheet is relatively slow (even on a fast PC like mine) when user inserts values into cells. Furthermore, this would greatly complicate my fairly simple *.xlsm workbook, which has more than 50 sheets (1 sheet for each week, the contents of these sheets are then linked to a main sheet), and completely meets my basic necessities (with the exception of this spin button of course).
In my opinion there is probably necessary to manipulate some kind of system property (like for e.g. Application.ScreenUpdating trick), but I do not have enough VBA knowledge to find it.
To clearly explain my question I would need to share my sample workbook, but I do not know how to upload it to this forum. Unfortunately, I am not able upload/show images successfully on this forum (due to my low rating), which would also help a lot.
BTW, I cannot see images on other questions of this forum, too. . Could this problem occur due to missing plugins in a web browser?
You can use a simple trick... before the "Application.screenupdating = true" you can insert the two lines:
ActiveWindow.SmallScroll Down:=-100
ActiveWindow.SmallScroll Up:=100