I have found a similar article about my question, as stated below;
How do I track who uses my Excel spreadsheet?
However, I do like the last coloumn of comment >>
"You could also put a time stamp in the next column to show when the spreadsheet was used"
My question is> can anyone guide me the possible step or let me copy the code for doing this please? and how to hide the worksheet without anyone noticing?
My key is, very importantly, everything must done silently which no one else (other users in sharedrive) could find out i m tracking it. The reason is , i have done lot of research worksheets, and i don't have time/impossible to make every single excel worksheet perfect, i need to prioritize them inorder to be efficient with my time by knowing which one is more important to people.
many thanks~!!
In Excel, under the Review tab, you have 'Track Changes'. This should do everything you want.
If you want a VBA script to do this, try one of the following code samples.
Private Sub Worksheet_Change(ByVal Target As Range)
Set t = Target
Set a = Range("A:A")
If Intersect(t, a) Is Nothing Then Exit Sub
Application.EnableEvents = False
t.Offset(0, 7).Value = Environ("username")
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
Dim V As Long
Application.EnableEvents = False
Set rng1 = Application.Union(Range("a1:g1"), Range("H:iv"))
Set rng = Application.Intersect(Target, rng1)
If Not rng Is Nothing Then Exit Sub
V = Target.Offset(0, 12).Value
If Target.Offset(0, 12) = "" Then
With Range("H" & Target.Row)
.Value = Target.Address & ": first entry by " & Application.UserName & " at " & Now()
.ColumnWidth = 60
.Interior.ColorIndex = 33
End With
Target.Offset(0, 12).Value = Target.Value
Application.EnableEvents = True
Exit Sub
End If
Target.Offset(0, 12).Value = Target.Value
With Range("H" & Target.Row)
.Value = Target.Address & " changed from " & V & " to " & Target.Value & " by " & Application.UserName & " at " & Now()
.ColumnWidth = 60
.Interior.Color = vbYellow
End With
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
With Target
If .Count > 1 Then Exit Sub
If Not Intersect(Range("A2:A10"), .Cells) Is Nothing Then
Application.EnableEvents = False
Sheets("Sheet2").Select
If IsEmpty(.Value) Then
.Offset(0, 1).ClearContents
Else
With .Offset(0, 1)
.NumberFormat = "dd mmm yyyy hh:mm:ss"
.Value = Now
End With
End If
Sheets("Sheet1").Select
Application.EnableEvents = True
End If
End With
End Sub
All of these 'Worksheet_Change' scripts are worksheet events. You need to right-click your sheet and click 'View Code' then paste the script into the window that opens. Try one at a time, not all three together.
Related
Could any kind soul help me, I have spent a considerable amount of time searching and trying to get these 2 pieces of code to work, but couldn't.
Is there any way I can combine these 2 snippets? They serve two different purposes.
1.
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("A:A")) Is Nothing Then Exit Sub
If Len(Target) = 10 Then
Range("I" & Target.Row & ":J" & Target.Row & ", K" & Target.Row & ", M" & Target.Row) = "N"
End If
End Sub
2.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 12 And Target.Value = "Y" Then
Target.Offset(0, 1) = Date
End If
End Sub
I am at a loss....
You could use the code below.
Disable events to stop the Change event firing when you update the values in columns I:K & N.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ERR_HANDLE
Application.EnableEvents = False
With Target
If .Column = 12 Then
If .Value = "Y" Then
.Offset(, 1) = Date
End If
ElseIf .Column = 1 Then
If Len(.Value) = 10 Then
Cells(.Row, 9).Resize(, 3) = "N" 'Column I:K
Cells(.Row, 13) = "N" 'Column M
End If
End If
End With
EXIT_PROC:
Application.EnableEvents = True
On Error GoTo 0
Exit Sub
ERR_HANDLE:
Select Case Err.Number
Case 13 'Type mismatch.
Resume EXIT_PROC
Case Else 'Any unhandled errors.
MsgBox "Error " & Err.Number & vbCr & _
Err.Description, vbOKOnly, "Error in " & ThisWorkbook.Name
Resume EXIT_PROC
End Select
End Sub
Edit after accepted:
I've added an error handler to the code at the suggestion of #MathieuGuindon. After dealing with the error the code jumps back to the EXIT_PROC label so there's only a single exit point to the procedure.
You can try this as a combined method:
You need do disable alerts when making changes while you have a worksheet_change event loaded on a macro, else you may find yourself in a infinite loop.
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("A:A")) Is Nothing Or Target.Column <> 12 Then Exit Sub
If Len(Target) = 10 Then
Application.EnableEvents = False
Range(Cells(Target.Row, "I"), Cells(Target.Row, "K")) = "N"
Range("M" & Target.Row) = "N"
Application.EnableEvents = True
End If
If Target.Column = 12 And Target.Value = "Y" Then
Application.EnableEvents = False
Target.Offset(0, 1) = Date
Application.EnableEvents = True
End If
End Sub
I am having problems with a "Worksheet_Change" sub that copies and pastes the whole row into a second worksheet ("Completed") when the column "P" takes on the value "x". It reads like this:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
Application.EnableEvents = False
'If Cell that is edited is in column P and the value is x then
If Target.Column = 16 And Target.Value = "x" Then
'Define last row on completed worksheet to know where to place the row of data
LrowCompleted = Sheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row
'Copy and paste data
Range("A" & Target.Row & ":P" & Target.Row).Copy Sheets("Completed").Range("A" & LrowCompleted + 1)
'Delete Row from Project List
Range("A" & Target.Row & ":P" & Target.Row).Delete xlShiftUp
End If
Application.EnableEvents = True
End Sub
The sub itself works fine but if I copy and paste anywhere in the worksheet, the sub is activated and the row into which I paste is send to my "Completed" sheet.
I have played around with the "if-clause" without any luck so far. E.g.:
If Not Target.Column = 16 And Target.Value = "x" Is Nothing Then
I fear I am missing the obvious and I am grateful for any help.
Thanks and regards
PMHD
If you are concerned with muliple targets, deal with them; don't discard them.
Private Sub Worksheet_Change(ByVal Target As Range)
If not intersect(target, range("p:p")) is nothing then
on error goto meh
Application.EnableEvents = False
dim t as range, lrc as long
lrc = workSheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row + 1
for each t in intersect(target, range("p:p"))
if lcase(t.Value2) = "x" Then
intersect(columns("A:P"), t.rows(t.row)).Copy _
destination:=workSheets("Completed").cells(lrc , "A")
lrc = lrc+1
'Delete Row from Project List
intersect(columns("A:P"), t.rows(t.row)).Delete xlShiftUp
end if
next t
End if
meh:
Application.EnableEvents = true
end sub
Thanks, Jeeped.
The problem arose due to Target referring to multiple cells. It was fixed by excluding cases where Target.Count > 1.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
Application.EnableEvents = False
'Exclude all cases where more than one cell is Target
If Target.Count > 1 Then
'If Cell that is edited is in column P and the value is x then
ElseIf Target.Column = 16 And Target.Value = "x" Then
'Define last row on completed worksheet to know where to place the row of data
LrowCompleted = Sheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row
'Copy and paste data
Range("A" & Target.Row & ":P" & Target.Row).Copy Sheets("Completed").Range("A" & LrowCompleted + 1)
'Delete Row from Project List
Range("A" & Target.Row & ":P" & Target.Row).Delete xlShiftUp
End If
Application.EnableEvents = True
End Sub
I have two working Worksheet_Change code and I would like to use both of them on the same sheet. When I use them individually both of them work but when I use them together they do not. I tried to paste in two different codes but I got an ambiguous name detected error. I also tried to use elseif, next but none of them worked.
The two codes:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Range("O:O"), Target) Is Nothing Then
Cells(Target.Row, 17).Value = Date
End If
End Sub
and
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
If Target.Column = 15 Then
Range("P" & Target.Row).Value = Target.Value + Range("P" & Target.Row).Value
Application.EnableEvents = False
Target.Value = ""
Application.EnableEvents = True
End If
End Sub
Thank you for your help
you can just put both workshett Change events in the same sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Range("O:O"), Target) Is Nothing Then
Cells(Target.Row, 17).Value = Date
End If
If Target.Column = 15 Then
Range("P" & Target.Row).Value = Target.Value + Range("P" & Target.Row).Value
Application.EnableEvents = False
Target.Value = ""
Application.EnableEvents = True
End If
End Sub
Try it like this...
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.CountLarge > 1 Then Exit Sub
If Not Intersect(Range("O:O"), Target) Is Nothing Then
Application.EnableEvents = False
Cells(Target.Row, 17).Value = Date
Range("P" & Target.Row).Value = Target.Value + Range("P" & Target.Row).Value
Target.Value = ""
Application.EnableEvents = True
End If
End Sub
Is there a way I can automate the date or time once the I enter the data from the User_Form via "enter/click" button? I've tried this code but it keeps restarting my excel workbook. On top of that, I have a ton of other codes in the Private Sub Worksheet_Change(ByVal Target As Range)
Is it possible it's overloaded ?
So I'm thinking if I could code it with my Userform_click() I'd be better off?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
For i = 16 To 100
If Cells(i, 3).Value <> "" Then
Cells(i, 1).Value = Date & " "
Cells(i, 1).NumberFormat = "mm/dd/yy"
End If
Next
End Sub
You need to add Application.EnableEvents = False in the beginning of your Sub, otherwise it will keep running it every time a value is changed inside the worksheet (like when you change it inside your For i = 16 To 100 loop).
Code
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
Application.EnableEvents = False
For i = 16 To 100
If Cells(i, 3).Value <> "" Then
Cells(i, 1).Value = Date & " "
Cells(i, 1).NumberFormat = "mm/dd/yy"
End If
Next i
Application.EnableEvents = True '<-- restore to original setting
End Sub
Edit 1: You can write your Sub in another way, that it will enter it only if a cell is changed inside the searched Range("C16:C100"). Only if the modified cell is inside that range, then check each cell if the Value <> "".
Private Sub Worksheet_Change(ByVal Target As Range)
Dim C As Range
Application.EnableEvents = False
If Not Intersect(Range("C16:C100"), Target) Is Nothing Then
For Each C In Intersect(Range("C16:C100"), Target)
If C.Value <> "" Then
C.Offset(, -2).Value = Date & " "
C.Offset(, -2).NumberFormat = "mm/dd/yy"
End If
Next C
End If
Application.EnableEvents = True
End Sub
I'd like to know how to detect if the user is deleting or inserting content into a range. If they are deleting a range say D14:D18. I'd like to then perform a macro that also deletes content in E14:E18. I just wouldn't want to delete E14:E18 if they are entering content into D14:D18.
I've tried:
If Selection.ClearContents Then
MsgBox Target.Offset(0, 3).Style
End If
But this get's me stuck in an infinite loop.
A bit more context:
I have a few hundred cells in D:D for entering quantities for services. Not everything in D:D should be touched. Only cells in D:D with .Style = "UnitInput". In E:E I have data validation that lets the user only enter contractor 1 or contractor 2 But, when content is entered in D:D I run a macro to assign the default contractor (housed in F:F) to E:E. So when the user enters quantities into D:D it correctly assigns the default contractor. And when they delete singular items from D:D I have it handling proper removal of contractors. It's only when they delete a range of items from D:D.
Full code:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
On Error GoTo ErrHandler:
If Selection.Rows.Count * Selection.Columns.Count = 1 Then
If Target.Offset(0, 3).Style = "Contractor" Then
If Target.Value < 1 Then
Target.Offset(0, 3).Value = ""
Else
Target.Offset(0, 3).Value = Target.Offset(0, 2).Value
End If
End If
If Target.Offset(0, 5).Style = "Markup" Then
If Target.Value = "" Then
Target.Offset(0, 5).Value = ""
ElseIf Target.Value <= Target.Offset(0, 14).Value Then
Target.Offset(0, 5).Value = "Redact 1"
ElseIf Target.Value >= Target.Offset(0, 15).Value Then
Target.Offset(0, 5).Value = "Redact 2"
Else
Target.Offset(0, 5).Value = "Redact 3"
End If
End If
Else
'!!!!!! this is where I need to handle multiple deletions. !!!!!!!
End If
Application.ScreenUpdating = True
ErrHandler:
Application.ScreenUpdating = True
Resume Next
End Sub
Based on your comments in chat, here is what I propose
UNTESTED
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, aCell As Range
Dim lRow As Long
'~~> Error handling, Switching off events and Intersect
'~~> As described in
'~~> http://stackoverflow.com/questions/13860894/ms-excel-crashes-when-vba-code-runs
On Error GoTo Whoa
Application.EnableEvents = False
With ActiveSheet
'~~> Find Last Row since data is dynamic
'~~> For further reading see
' http://stackoverflow.com/questions/11169445/error-in-finding-last-used-cell-in-vba
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End If
If lRow > 12 Then
'~~> Set your range
Set rng = Range("D13:D" & lRow)
If Not Intersect(Target, rng) Is Nothing Then
For Each aCell In rng
If Len(Trim(aCell.Value)) = 0 Then
Select Case Target.Offset(0, 3).Style
Case "Contractor"
'~~> Do Your Stuff
Case "Markup"
'~~> Do Your Stuff
'
'~~> And so on
'
End Select
End If
Next aCell
End If
End If
End With
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
Here is an idea -- you have to first select a region to clear its contents. Use selection change to record the number of non-blank cells and then worksheet change to see if it drops to zero. Something like:
Dim NumVals As Long
Private Sub Worksheet_Change(ByVal Target As Range)
Dim NewCount As Long
NewCount = Application.WorksheetFunction.CountA(Target)
If NewCount = 0 And NumVals > 0 Then MsgBox Target.Address & " was cleared"
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
NumVals = Application.WorksheetFunction.CountA(Target)
End Sub
I have this code is Sheet1 and it seems to capture when I highlight a group of cells (which contains at least one value) and then hit the delete key.
You can use the CommandBars Undo Control to determine if the user has actually deleted something.
Bear in mind this will fire if the user any or all of the contents of the Range D14:D18, but can be adjusted in many ways to suit your exact needs. After seeing your edit, this basically means you can adjust the ranges and need be and which cells in column E it affects as well. If you need more guidance on this, let me know.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("D14:D18")) Is Nothing Then
Dim sLastAction As String
sLastAction = Application.CommandBars("Standard").Controls("&Undo").List(1)
Debug.Print sLastAction
'manual delete 'right-click delete 'backspace delete
If sLastAction = "Clear" Or sLastAction = "Delete" Or Left(sLastAction, 9) = "Typing ''" Then
Application.EnableEvents = False
Me.Range("E14:E18").ClearContents
Application.EnableEvents = True
End If
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ChangedRange As Range
Dim Area As Range
Dim Cell As Range
Set ChangedRange = Application.Intersect(Target, Range("D:D"))
If Not ChangedRange Is Nothing Then
Application.EnableEvents = False
For Each Area In ChangedRange.Areas
For Each Cell In Area
If IsEmpty(Cell) Then
Cell.Offset(0, 1).ClearContents
End If
Next
Next
Application.EnableEvents = True
End If
End Sub