Maintain count of each change to cell value - excel

Suppose I have a value in cell A1 and everytime the cell value of A1 changes, the cell on b1 counts the change.
I have a code it works just with A1(value)cell and b1(count on change) cell. i would like to apply this function on cell E2:E709 (value) cells and F2:F709 (count on change) cell.
Private Sub Worksheet_Change(ByVal Target As Range)
Static OldVal As Variant
If Target.Address(False, False) = "A1" Then
Application.EnableEvents = False
If Target.Value <> OldVal Then
Target.Offset(, 1).Value = Target.Offset(, 1).Value + 1
OldVal = Target.Value
End If
Application.EnableEvents = True
End If
End Sub

Try code below:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' If change was outside range we are interested in, then quit Sub
If Intersect(Target, Range("E2:E709")) Is Nothing Then Exit Sub
' store reference to cell in F column for simplicity
Dim c As Range: Set c = Target.Offset(0, 1)
' check if cell in F column has any value, if not, then assign it 1
If c.Value = "" Then
c.Value = 1
' else increment it by one
Else
c.Value = c.Value + 1
End If
End Sub

Consider:
Private Sub Worksheet_Change(ByVal Target As Range)
Static OldVal(2 To 709) As Variant
Dim E As Range, F As Range, r As Range, Intersekt As Range
Dim rw As Long
Set E = Range("E2:E709")
Set F = Range("F2:F709")
Set Intersekt = Intersect(E, Target)
If Intersekt Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In Intersekt
rw = r.Row
If r.Value <> OldVal(rw) Then
r.Offset(0, 1).Value = r.Offset(0, 1).Value + 1
OldVal(rw) = r.Value
End If
Next r
Application.EnableEvents = True
End Sub
We use an array for OldVal rather than a single item.We use a (potentially) multi-cell IntersektRange` to facilitate changing more than one cell at a time.

Related

How to have apply Worksheet_Change to more than one cell?

I implemented code to timestamp a cell whenever a condition in another cell in the same row is manually met:
Private Sub Worksheet_Change(ByVal target As Range)
Dim A As Range: Set A = Range("A2:A2800")
Dim v As String
If Intersect(target, A) Is Nothing Then Exit Sub
Application.EnableEvents = False
v = target.Value
If v = "" Then target.Offset(0, 6) = ""
If v = "Solicitud enviada" Then target.Offset(0, 6) = Date
Application.EnableEvents = True
End Sub
I need to timestamp another cell by a different criteria. I know I can't have two Worksheet_Change subs at the same time, but from what I've investigated trying to have two events at the same time goes beyond me.
Private Sub LeadTimeStamp(ByVal target As Range)
Dim D As Range: Set D = Range("D2:D2800")
Dim b As String
If Intersect(target, D) Is Nothing Then Exit Sub
Application.EnableEvents = False
b = target.Value
If b = "" Then target.Offset(0, 8) = ""
If b = "lead" Then target.Offset(0, 8) = Date
Application.EnableEvents = True
End Sub
b needs to be compared as a string array with the cell, something like b.length <= 10 if this was JavaScript.
I know that VBA uses LEN(), but I do not know how to use it here. For now I have a placeholder condition similar to the one on the original code, to make sure that the code works before I tackle the array condition part.
Your situation is that you need to check for one of several possible changes, so that means an If statement at the Target level. So in outline form it would look like this:
Private Sub Worksheet_Change(ByVal Target As Range)
'--- only deal with single-cell changes. multi-cell
' edits are skipped
If Target.CountLarge > 1 Then Exit Sub
Dim solicitudArea As Range
Dim leadArea As Range
Set solicitudArea = Range("A2:A2800")
Set leadArea = Range("D2:D2800")
Application.EnableEvents = False
If Not Intersect(target, solicitudArea) Is Nothing Then
'--- a request has changed
If Target.Value = vbNullString Then
Target.Offset(0, 6).Value = vbNullString
ElseIf Target.Value = "Solicitud enviada" Then
Target.Offset(0, 6).Value = Date()
End If
ElseIf Not Intersect(target, leadArea) Is Nothing Then
'--- a request has changed
If Target.Value = vbNullString Then
Target.Offset(0, 8).Value = vbNullString
ElseIf Target.Value = "lead" Then
Target.Offset(0, 8).Value = Date()
End If
End If
Application.EnableEvents = True
End Sub
A good practice is to use variable names that match up with what those values represent. It makes the code easier to read and maintain later on.

Error when clearing multiple cells in Excel

I'm using Worksheet_Change to make a value (either 1 or 0) appear in the next cell (Bx) when a value is entered in a range of cells (A1:A10).
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A1:A10")) Is Nothing Then
If Target.Value = 1 Then
Target.Offset(0, 1).Value = 1
Else:
Target.Offset(0, 1).Value = 0
End If
End If
End Sub
The problem occurs when I try to clear the cells in column A.
When I select the cells I want to clear and press "Delete" I get "Run-time error '13' - Type mismatch" on the line "IF Target.Value = 1".
I would also like the cells in the B column to be cleared if I clear cells in the A column. E.g. if I delete cell A2:A5, B2:B5 should be cleared.
From what I understand the problem is that when selecting multiple cells it returns an array as the Target, and this is a mismatch with the Integer.
Is there a way around this problem?
Try this. You need to cater for multiple cells in some way, for the reasons you mention, and add an extra clause to your If.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim r As Range, r1 As Range
Set r = Intersect(Target, Range("A1:A10"))
If Not r Is Nothing Then
For Each r1 In r
If r1.Value = 1 Then
r1.Offset(0, 1).Value = 1
ElseIf r1.Value = vbNullString Then
r1.Offset(0, 1).Value = vbNullString
Else
r1.Offset(0, 1).Value = 0
End If
Next r1
End If
End Sub
In a first step we add the functionality that multiple cells are selected and changed:
Private Sub Worksheet_Change_Var1(ByVal Target As Range)
Dim targetCell As Range
'If Target.Range.count
If Not Intersect(Target, Range("A1:A10")) Is Nothing Then
If Target.Cells.Count > 1 Then
For Each targetCell In Target
If targetCell.Value = 1 Then
targetCell.Offset(0, 1).Value = 1
Else
targetCell.Offset(0, 1).Value = 0
End If
Next targetCell
Else
If Target.Value = 1 Then
Target.Offset(0, 1).Value = 1
Else
Target.Offset(0, 1).Value = 0
End If
End If
End If
End Sub
In the 2nd step we understand that also the "one cell" case can be handled in the same way and we add an if clause for the "cell(s) cleared" case:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim targetCell As Range
'If Target.Range.count
If Not Intersect(Target, Range("A1:A10")) Is Nothing Then
For Each targetCell In Target
If targetCell.Value = 1 Then
targetCell.Offset(0, 1).Value = 1
Else
targetCell.Offset(0, 1).Value = 0
End If
'if cell in col A is empty, then clear cell in col B
If targetCell.Value = "" Then targetCell.Offset(0, 1).ClearContents
Next targetCell
End If
End Sub

Update excel cell with date if a cell in a range is update

I need to update a cell with the date and time stamp (NOW()) if any cell is updated within any cell before it within that same row.
So update cell "CU" with date and time when any cell from "A-CR" is updated.
I have done some searching but I can only seem to find bits that work if only updating a single cell, I'm looking for if anything changes within that range.
I currently have some Vba which does something similar which will update the adjacent cell with time and date which is required but I also need an overall one for the whole process.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("F:F, I:I, L:L, O:O, R:R, U:U, X:X, AA:AA, AB:AB, AE:AE, AH:AH, AK:AK, AN:AN, AQ:AQ, AT:AT, AW:AW, AZ:AZ, BC:BC, BF:BF, BI:BI, BL:BL, BO:BO, BR:BR, BU:BU, BX:BX, CA:CA, CD:CD, CG:CG, CJ:CJ, CM:CM, CP:CP")) Is Nothing Then
On Error GoTo safe_exit
With Application
.EnableEvents = False
.ScreenUpdating = False
Dim trgt As Range, ws1 As Worksheet
'Set ws1 = ThisWorkbook.Worksheets("Info")
For Each trgt In Intersect(Target, Range("F:F, I:I, L:L, O:O, R:R, U:U, X:X, AA:AA, AB:AB, AE:AE, AH:AH, AK:AK, AN:AN, AQ:AQ, AT:AT, AW:AW, AZ:AZ, BC:BC, BF:BF, BI:BI, BL:BL, BO:BO, BR:BR, BU:BU, BX:BX, CA:CA, CD:CD, CG:CG, CJ:CJ, CM:CM, CP:CP"))
If trgt <> vbNullString Then
If UCase(trgt.Value) = "Y" Or UCase(trgt.Value) = "N" Then
Cells(trgt.Row, trgt.Column + 1) = Now()
Cells(trgt.Row, trgt.Column + 2) = Environ("username")
'Select Case trgt.Column
' Case 2 'column B
' Cells(trgt.Row, trgt.Column + 1) = Environ("username")
' Case 4 'column D
' 'do something else
' End Select
Else
trgt = ""
Cells(trgt.Row, trgt.Column + 1) = ""
Cells(trgt.Row, trgt.Column + 2) = ""
End If
End If
Next trgt
'Set ws1 = Nothing
End With
End If
safe_exit:
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
This works for me:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Intersect(Target, Me.Range("A" & Target.Row & ":CR" & Target.Row)) Is Nothing Then GoTo SafeExit
Me.Cells(Target.Row, "CU") = Now()
SafeExit:
Application.EnableEvents = True
End Sub
The below code takes care of:
Clearing the time if the row is blank.
Updating the time only if the values really change from the previous value.
Dim oldValue As String
'Change the range below where your data will be
Const RangeString = "A:CR"
'Below variable decides the column in which date will be displayed
'Change the below value to 1 for column A, 2 for B, ... 99 for CU
Const ColumnIndex = 99
Private Sub Worksheet_Change(ByVal Target As Range)
Dim WorkRng As Range
Dim HorizontalRng As Range
Dim Rng As Range
Dim HorRng As Range
Dim RowHasVal As Boolean
Set WorkRng = Intersect(ActiveSheet.Range(RangeString), Target)
If Not WorkRng Is Nothing Then
If WorkRng.Cells.Count = 1 And WorkRng.Cells(1, 1).Value = oldValue Then
Exit Sub
End If
Application.EnableEvents = False
For Each Rng In WorkRng
Set HorizontalRng = Intersect(ActiveSheet.Range(RangeString), Rows(Rng.Row))
RowHasVal = False
For Each HorRng In HorizontalRng
If Not VBA.IsEmpty(HorRng.Value) Then
RowHasVal = True
Exit For
End If
Next
If Not RowHasVal Then
ActiveSheet.Cells(Rng.Row, ColumnIndex).ClearContents
ElseIf Not VBA.IsEmpty(Rng.Value) Then
ActiveSheet.Cells(Rng.Row, ColumnIndex).Value = Now
ActiveSheet.Cells(Rng.Row, ColumnIndex).NumberFormat = "dd-mm-yyyy, hh:mm:ss"
End If
Next
Application.EnableEvents = True
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target, ActiveSheet.Range(RangeString)) Is Nothing Then
If Target.Cells.Count = 1 Then
oldValue = Target.Value
Else
oldValue = ""
End If
End If
End Sub

Auto-fill the date and time in 2 cells, when the user enters information in an adjacent cell

i have the following code which would auto-fill the date in column B once i add value's in column A.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, B As Range, Inte As Range, r As Range
Set A = Range("A:A")
Set Inte = Intersect(A, Target)
If Inte Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In Inte
If r.Offset(0, 1).Value = "" Then
r.Offset(0, 1).Value = Date & " " & Time = "hh:mm:ss AM/PM"
End If
Next r
Application.EnableEvents = True
End Sub
what im looking for is to also add the current time to column C.
ok so i found what im looking for but it requires little modification where the date and time are being set.
below is the code
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, B As Range, Inte As Range, r As Range
Set A = Range("D:D")
Set Inte = Intersect(A, Target)
If Inte Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In Inte
If r.Value > 0 Then
r.Offset(0, -3).Value = Date
r.Offset(0, -3).NumberFormat = "dd-mm-yyyy"
r.Offset(0, -2).Value = Time
r.Offset(0, -2).NumberFormat = "hh:mm:ss AM/PM"
Else
r.Offset(0, -3).Value = ""
r.Offset(0, -2).Value = ""
End If
Next r
Application.EnableEvents = True
End Sub
to auto-fill column E with date, instead of column A
and auto-fill column F with time, instead of column B
and if possible im trying to have the same process but another cell on the same sheet.
While you might look at using SpecialCells to do this in one hit rather than a loop, a simple mod to your code would be:
one-shot per range area method
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, B As Range, Inte As Range, r As Range
Set A = Range("A:A")
Set Inte = Intersect(A, Target)
If Inte Is Nothing Then Exit Sub
Application.EnableEvents = False
On Error Resume Next
For Each r In Inte.Areas
r.Offset(0, 1).Cells.SpecialCells(xlCellTypeBlanks) = Date
r.Offset(0, 2).Cells.SpecialCells(xlCellTypeBlanks) = Time
Next r
Application.EnableEvents = True
End Sub
initial answer
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, B As Range, Inte As Range, r As Range
Set A = Range("A:A")
Set Inte = Intersect(A, Target)
If Inte Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In Inte
If r.Offset(0, 1).Value = vbNullString Then r.Offset(0, 1).Value = Date
If r.Offset(0, 2).Value = vbNullString Then r.Offset(0, 2).Value = Time
Next r
Application.EnableEvents = True
End Sub
if you want to:
put current Date in Target adjacent column blank cells
put current Time in Target adjacent column blank cells adjacent cells
then go like follows:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Range("A:A"), Target).Address <> Target.Address Then Exit Sub '<--| exit if all target cells aren't in column "A"
Application.EnableEvents = False
If WorksheetFunction.CountBlank(Target.Offset(, 1)) = 0 Then Exit Sub '<--| exit if no blank cells in target adjacent column
With Target.Offset(, 1).SpecialCells(xlCellTypeBlanks) '<--| reference blank cells in target adjacent column
.Value = Date '<--| set referenced cells value to the current date
.Offset(, 1).Value = Time '<--| set referenced cells adjacent ones value to the current time
End With
Application.EnableEvents = True
End Sub
While if you want to:
put current Date in Target adjacent column blank cells
put current Time in Target two columns offset blank cells
then go like follows:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Range("A:A"), Target).Address <> Target.Address Then Exit Sub '<--| exit if all target cells aren't in column "A"
Application.EnableEvents = False
On Error Resume Next
Target.Offset(, 1).SpecialCells(xlCellTypeBlanks).Value = Date '<--| set target adjacent column blank cells to the current date
Target.Offset(, 2).SpecialCells(xlCellTypeBlanks).Value = Time '<--| set target two columns offset blank cells to the current time
Application.EnableEvents = True
End Sub
where the On Error Resume Next is there to avoid two distinct If WorksheetFunction.CountBlank(someRange) Then someRange.SpecialCells(xlCellTypeBlanks).Value = someValue statements
Normally you would avoid On Error Resume Next statement and ensure you're handling any possible errors.
But in this case, being it confined to the last two statements of a sub, I think it's a good trade off in favour of code readability without actually loosing its control

Macro basic multiplication calculation

I am trying to take the value inside cell I-8 Multiplied by the value inside H-8 and have this new value X, replace the contents of cell I-8.
I am trying to do this with every row starting with 8. (I-9 * H-9 etc)
I am already removing cell's will qty 0 inside column I with the following:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Dim c As Range
Dim SrchRng
Set SrchRng = Intersect(ActiveSheet.UsedRange, Range("I:I"))
Do
Set c = SrchRng.Find(0, LookIn:=xlValues)
If Not c Is Nothing Then c.EntireRow.Delete
Loop While Not c Is Nothing
End Sub
You actually need a different event for this to work:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 9 And Target.Cells.Count = 1 And Target.Row > 7 Then
Application.EnableEvents = False
If IsNumeric(Target.Value) Then Cells(Target.Row, 2).Value = Target.Value * Target.Offset(0, -1).Value
Application.EnableEvents = True
End If
End Sub
I feel it needs to pointed out however, that simply entering =H8*I8 into B8 will do the same thing.

Resources