I am trying to use the following VBA macro to alter the content of a cell when a particular character is entered into it.
Sub replaceWords()
Dim i As Long
Dim r As Integer
For i = 1 To 60
For r = 1 To 60
If Cells(i, r).Value = "`" Then
Cells(i, r).Value = "0,0"
End If
Next r
Next i
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("A1:AA100")) Is Nothing Then
Application.EnableEvents = False
Call replaceWords
Application.EnableEvents = True
End If
End Sub
My understanding is that Worksheet_Change should do this automatically, but it doesn't seem to be working - I have to run the macro manually. What am I doing wrong?
It's not clear from your code, but make sure Worksheet_Change is in the Worksheet object by going to the Project Explorer (Ctrl-R) and right click on the worksheet in question and select View Code. Put that worksheet_change module in there.
Make sure that the function is in the worksheet's module. If that still doesn't work, check to see if Application.EnableEvents is set to True.
You set that value to false as your code makes changes. If your code was interrupted before resetting it to True, the event driven subs won't fire.
Adding some error handling where you exit gracefully and reset EnableEvents to True is generally a good idea.
Related
Excel 365.
When the user changes the value of a cell in a certain column of my Excel table (Listobject), I can use the Worksheet_Change event to trigger more code. I would use something like:
If Not Intersect(Target, Listobjects(1).listcolumns(2).DataBodyRange) Is Nothing Then
...to tell that one of these cells was changed. But how do I tell which cell it was?
On a releated note: Is there a way for Worksheet_Change to tell when a new row or column is added to the Listobject?
Sorry in advance if I misunderstood what you asked.
As Target.Address returns its cell location on the sheet, you can translate it to context of listobject by offsetting it with the location of first cell of the table
With me.Listobjects(1)
Debug.Print .DataBodyRange(Target.Row - .Range.Row + 1, _
Target.Column - .Range.column + 1).Address(0,0)
End with
secondly, if you can store the information of the initial table to some variables when opening the workbook, then you can compare the information every time workbook_change event takes place.
P/S: it is quite risky to leave a sheet that already has worksheet event macros in place like this to be unprotected and changed without restriction.
In a Module,
Dim start_LROW&, start_LCOL& 'Variable is declared first
Sub run_when_Open()
With sheet1.ListObjects(1)
start_LROW = .ListRows.Count
start_LCOL = .ListColumns.Count
End With
End Sub
in Workbook_Open event under ThisWorkbook module,
Private Sub Workbook_Open()
Call Module1.run_when_OPEN
End Sub
in Workbook_Change event under Sheet module,
Private Sub Worksheet_Change(ByVal Target As Range)
With Me.ListObjects(1)
If Not Intersect(Target, .DataBodyRange) is Nothing Then
If .ListRows.Count <> start_LROW Or _
.ListColumns.Count <> start_LCOL Then
Debug.Print "changed" 'Trigger some codes
start_LROW = .ListRows.Count 'update the new information to be compared for next trigger.
start_LCOL = .ListColumns.Count
End If
End If
End With
End Sub
when i change avalue in B2:B10 in need the message "update"
When running Insert_row it should not give that message.
The code works but when I place the procedure Insert_row in another module the public variable is not known ?
How can i solve this.
Public blockchange As Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B2:B10")) Is Nothing And blockchange = False Then
MsgBox "Update"
End If
End Sub
Sub Insert_rows()
blockchange = True
Dim LastRow As Integer
LastRow = 3 * ActiveSheet.UsedRange.Rows.Count
For i = 3 To LastRow Step 3
Cells(i, 1).EntireRow.Insert
Cells(i, 1).EntireRow.Insert
Next i
blockchange = False
End Sub
Some thoughts:
(1) As SJR wrote, put global variables into a regular module.
(2) If you insist, you can have a global variable also in the code of the sheet. In this case you access it by putting the codename of the sheet as prefix, eg Sheet1.blockchange. The codename of a sheet is the internal name of a sheet, you see it in the VBA editor in the project window or if you open the property window (F4), it is displayed in the first row as (name). This name doesn't change if a sheet is renamed, you can change it only in the VBA editor.
(3) For your specific case, it's maybe better not to use a variable but simply to prevent the events to fire while the Insert-code is running. You do this with the statement Application.EnableEvents = False. Just don't forget to put Application.EnableEvents = True at the end of the code, maybe with an error handler to prevent that the statement is not executed when an error occurs.
My Workbook_SheetChange code seems to not be running.
I have tried changing it several different ways, including Setting objects and not Setting objects. No change event seems to trigger.
My current code is below, but I have completely scratched other versions that don't even reference sheet names. None of them seem to work. I have another wb with a Worksheet Change code that works.
I am expecting my summary sheet date and time cell - Worksheets("GL 1500").("B2") - to update with the current date and time any time anything in the range "C31:P46" on the summary tab, or the alternate range on any other tab, changes, but it has not updated with any changes I make to any worksheet.
What stupid mistake am I making?
Code:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Sh.Name = "GL 1500" Then
If Not Intersect(Target, Sh.Range("c31:p46")) Is Nothing Then
Application.EnableEvents = False
With Worksheets("GL 1500")
.Range("b2").Value = Now
End With
Application.EnableEvents = True
End If
Else
If Not Intersect(Target, Sh.Range("e14:k39")) Is Nothing Then
Application.EnableEvents = False
With Worksheets("GL 1500")
.Range("b2").Value = Now
End With
Application.EnableEvents = True
End If
End If
End Sub
I need to call a macro when cell B3 changes, however B3 is an RTD link so when the value changes the formula is still the same and excel doesn't recognise the change. How can I get my macro to run when the value changes but the formula doesn't?
I've tried using a simple Range("B3").Value but that gives me a run-time error "424". I then tried creating a range object to get around this but still got another error message.
Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B3")) Is Nothing Then
Call Copy_Values
End If
End Sub
I'm relatively new to VBA so I know I could be missing something obvious, thanks for any help you can provide
You want Worksheet_Calculate not Worksheet_Change. Use a static var to 'remember' the value from the last calculation cycle.
Sub Worksheet_Calculate()
static beeThree as variant
If Range("B3").value2 <> beeThree Then
application.enableevents = false
application.calculation = xlcalculationmanual
Copy_Values
beeThree = Range("B3").value2
application.calculation = xlcalculationautomatic
application.enableevents = true
End If
End Sub
I have produced some VBA code to resolve this problem :
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Value <> Empty Then
Target.Value = UCase(Target.Value)
End If
End Sub
But when I try to input some data in a field, Excel stops working without a single error message.
Does anyone know where this problem can come from ?
You probably have set Application.EnableEvents = False. Open the Immediate window in the VBA editor and type in application.EnableEvents = True then ENTER to turn them back on.
Also, you need to disable events if you don't want to cause a cycle of changing the sheet and re-triggering the event. The ISEMPTY function is slightly different in VBA and your code could be updated to the following which will also handle changing more than just 1 cell
Option Explicit
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim cell As Variant
Application.EnableEvents = False
For Each cell In Target
If Not IsEmpty(cell.Value) Then
cell.Value = UCase(cell.Value)
End If
Next cell
Application.EnableEvents = True
End Sub
or if you want to restrict this running to 1 cell change only, replace the for each loop with If Target.rows.count = 1 AND Target.columns.count = 1....
You may not have the callback function in the right spot:
From Events And Event Procedures In VBA
For sheet (both worksheet and chart sheet) level events, the event procedure code must be placed in the Sheet module associated with that sheet. Workbook level events must be placed in the ThisWorkbook code module. If an event procedure is not in the proper module, VBA will not be able to find it and the event code will not be executed.