I'm trying to record the value that changes every one minute from cell "B2" into cell "D2". When the values are recorded to "D2" in a row, I want to add the date and time at the same time it recorded into cell "E". Here, below is my code.
Private Sub Worksheet_Calculate()
Application.EnableEvents = False
Me.Range("D" & Me.Rows.Count).End(xlUp).Offset(1).Value = Me.Range("B2").Value
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim targetRng As Range
Dim rng As Range
Dim c As Integer
Set targetRng = Intersect(Application.ActiveSheet.Range("D:C"), Target)
c = 1
If Not targetRng Is Nothing Then
Application.EnableEvents = False
For Each rng In targetRng
If Not VBA.IsEmpty(rng.Value) Then
rng.Offset(0, c).Value = Now
rng.Offset(0, c).NumberFormat = "dd/mm/yyyy, hh:mm:ss AM/PM"
Else
rng.Offset(0, c).ClearContents
End If
Next
Application.EnableEvents = True
End If
End Sub
It seems that every time the value has recorded, the date and time in cell "E" do not appear to work together.
Any solution here?
I recommend creating a seperate Sub that is not directly hit by an event. Rewrite code below for your purposes.
Private Sub Worksheet_Calculate()
SharedSheetEvent()
end sub
Private Sub Worksheet_Change(ByVal Target As Range)
EditingSheet = true
call SharedSheetEvent()
editingsheet = false
end sub
global EditingSheet as bool
public Sub SharedSheetEvent()
if (EditingSheet) Then
do some stuff
else
do some other stuff
end if
end sub
The code below will do what you want. No two procedures are needed but if you don't specify the sheet, meaning you let it work on the ActiveSheet, it would be a bit of a lose cannon.
Private Sub Worksheet_Calculate()
Dim LastRecord As Range ' cell last written to
Dim NewValue As Variant ' current value in B2
Debug.Print "calculate"
With Worksheets("Sheet1") ' change to suit
Set LastRecord = .Cells(.Rows.Count, "D").End(xlUp)
NewValue = .Cells(2, "B").Value
With LastRecord
If .Value <> NewValue Then ' skip if no change
Application.EnableEvents = False
.Offset(1).Value = NewValue
With .Offset(1, 1)
.Value = Now()
.NumberFormat = "dd/mm/yyyy, hh:mm:ss AM/PM"
End With
Application.EnableEvents = True
End If
End With
End With
End Sub
The question is not, however, how the code works but when. I presume that B2 is changed by a program that works on a timer. The change generated by it doesn't trigger the Worksheet's Change event. You did find out, however, that it triggers the Calculate event. That is my presumption and I couldn't test it. If that is so my procedure will solve your problem.
I have programmed a similar thing recently using a timer of my own to trigger running my procedure. It's just a timer that runs at the same interval as the other and checks every minute (for example) if B2 has changed and records the change if there was one. That works. But if your updater triggers the Calculate event that looks like a neater idea.
Related
I have a cell that states the status of a project, and this status will change frequently.
Whenever the status gets changed, I would like a row to state the time the status was changed and the name of the new status.
I have next to no experience with VBA, so any assistance would be greatly appreciated. So far I have this:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 4 And Target.Row = 4 Then
Target.Offset(10, 3) = Format(Now(), "YYYY-MM-DD HH:MM:SS")
End If
End Sub
This code successfully lists the time in cell G7 whenever the status contained in cell D4 changes, but it always repopulates the same cell, I would like each successive status change to list the date stamp in cell G8, then G9, then G10, and so on.
It also doesn't list what the status cell D4 is changed too, ideally I would like that to be listed in F7, then F8, then F9, and so on.
If you are only interested in a Worksheet_Change on cell D4, you can use the Intersect method shown below
To start a running list, you will need to determine that last used cell in Column G and offset accordingly
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("D4")) Is Nothing Then
Dim LR As Long: LR = Range("G" & Rows.Count).End(xlUp).Offset(1).Row
Target.Offset(LR - Target.Row, 3) = Format(Now(), "YYYY-MM-DD HH:MM:SS")
Target.Offset(LR - Target.Row, 4) = Target
End If
End Sub
Please try this.
Private Sub Worksheet_Change(ByVal Target As Range)
Const Tgt As String = "D4" ' monitored cell
Const FirstRecord As Long = 7 ' change as required
Const Fmt As String = "yyyy-mm-dd hh:mm:ss"
Dim Rl As Long ' last used row
If Target.Address = Range(Tgt).Address Then
Application.EnableEvents = False
Rl = Application.WorksheetFunction.Max( _
Cells(Rows.Count, "F").End(xlUp).Row + 1, FirstRecord)
With Cells(Rl, "G")
.Value = Now()
.NumberFormat = Fmt
Target.Copy Destination:=.Offset(0, -1)
End With
Application.EnableEvents = True
End If
End Sub
I'm fairly new to VBA and looking for any advice on how to manually control the change event for the below.
Column "F" has a Vlookup that returns "Fail" or "0", and rather that having each individual code to hide the row when the single cell in column F changes to 0, I found the below to work the best.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myRow As Long
If Target.Column = 6 Then
' Loop through rows 5-160
For myRow = 5 To 160
' Hide row in entry in column F is "0"
Rows(myRow).Hidden = (Cells(myRow, "F") = "0")
Next myRow
End If
End Sub
I have tried to use the below with the event change but it crashes the program and always restarts. Any suggestions would be greatly appreciated.Thanks!
Private Sub Worksheet_Calculate()
Worksheet_Change Range("F:F")
End Sub
This is my version of what you are trying to accomplish. If the values returned by the formulas in F5:F160 change, the changed values are caught by Worksheet_Calculate and only those changed values are processed by Worksheet_Change.
Caveat: This method of capturing changed values from formulas does not work well when volatile functions are in the workbook. Volatile functions include TODAY(), NOW(), OFFSET(...), etc.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("F5:F160")) Is Nothing Then
Application.EnableEvents = False
On Error GoTo meh
Dim t As Range
Debug.Print "chg: " & Intersect(Target, Range("F5:F160")).Address(0, 0)
For Each t In Intersect(Target, Range("F5:F160"))
't.EntireRow Hidden = CBool(LCase(t.Value2) = "fail" or t.Value2=0)
t.EntireRow.Hidden = CBool(LCase(t.Value2) = "fail")
Next t
End If
meh:
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Calculate()
Static effs As Variant
Dim f As Long, t As Range
If IsEmpty(effs) Then
effs = Range("F1:F160").Value2
For f = 5 To 160
If IsError(effs(f, 1)) Then effs(f, 1) = vbNullString
Next f
Else
For f = 5 To 160
If Not IsError(Cells(f, "F")) Then
If effs(f, 1) <> Cells(f, "F").Value2 Then
If Not t Is Nothing Then
Set t = Union(t, Cells(f, "F"))
Else
Set t = Cells(f, "F")
End If
effs(f, 1) = Cells(f, "F").Value2
End If
End If
Next f
If Not t Is Nothing Then
Debug.Print "calc: " & t.Address(0, 0)
Worksheet_Change t
End If
End If
End Sub
This seems to run well on a test workbook. Additional error and looping control may be necessary in your own situation.
I am currently trying to add a script into excel. excuse my terminology, I am not that hot with programming!
I do all of my accounting on excel 2003, and I would like to be able to add the value of say cells f6 to f27 to the cells e6 to e27, respectively. The thing is, I want the value of the "f" column to reset every time.
So far I have found this code, which works if I copy and paste it into VBA. but it only allows me to use it on one row:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Address = Range("f7").Address Then
Range("e7") = Range("e7") + Range("f7")
Range("f7").ClearContents
End If
Application.EnableEvents = True
End Sub
would somebody be kind enough to explain how I can edit this to do the same through all of my desired cells? I have tried adding Range("f7",[f8],[f9] etc.. but i am really beyond my knowledge.
First, you need to define the range which is supposed to be "caught"; that is, define the range you want to track for changes. I found an example here. Then, simply add the values to the other cell:
Private Sub Worksheet_Change(ByVal Target as Range)
Dim r as Range ' The range you'll track for changes
Set r = Range("F2:F27")
' If the changed cell is not in the tracked range, then exit the procedure
' (in other words, if the intersection between target and r is empty)
If Intersect(Target, r) Is Nothing Then
Exit Sub
Else
' Now, if the changed cell is in the range, then update the required value:
Cells(Target.Row, 5).Value = Cells(Target.Row, 5).Value + Target.Value
' ----------------^
' Column 5 =
' column "E"
' Clear the changed cell
Target.ClearContents
End if
End Sub
Hope this helps
Try this
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ErrHandler
Application.ScreenUpdating = False
Application.EnableEvents = False
If Intersect(Target, Range("B1:B5,F6:F27")) Then 'U can define any other range
Target.Offset(0, -1) = Target.Offset(0, -1).Value + Target.Value ' Target.Offset(0,-1) refer to cell one column before the changed cell column.
'OR: Cells(Target.row, 5) = Cells(Target.row, 5).Value + Target.Value ' Where the 5 refer to column E
Target.ClearContents
End If
ErrHandler:
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
I need to add or update timestamp, to excel workbook, if row has been changed. I am doing data import, but I need to see which row was updated/added and on which date.
So far I have found and adjusted the following code:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
With Target
If .Count > 1 Then Exit Sub
If Not Intersect(Range("A2:BL9999"), .Cells) Is Nothing Then
Application.EnableEvents = False
With .Cells(1, 65)
.NumberFormat = "yyyy.mm.dd"
.Value = Now
End With
Application.EnableEvents = True
End If
End With
End Sub
The problem is, timestamp is always added relative to the row where the changes have been made + 65 rows, not in column BM (index 65).
Can you advise me, which function should I use or change?
As well as the fix for column BM better to
process all the rows that may have changed rather than exit withput any record
turn-off ScreenUpdating for speed
code
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Intersect(Target, Range("A2:BL9999"))
If rng1 Is Nothing Then Exit Sub
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
For Each rng2 In rng1.Cells
With Cells(rng2.Row, 65)
.NumberFormat = "yyyy.mm.dd"
.Value = Now
End With
Next
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
To change your reference relative to the entire row, use .EntireRow.
So that line should read: With .EntireRow.Cells(1, 65)
Note that you can still use A1 style references even when working with a single row. This can keep you from having to count columns. For instance, and in this case, .EntireRow.Range("BM1") means the exact same thing as .EntireRow.Cells(1, 65).
Got a workbook with two sheets in it. The first is where the data is, and the second has been set up as a "corrections" page. This workbook is sent out to users who are to review it and note inconsistencies/discrepencies. Right now it's set up to highlight the cell via double-click then forward the active cell to a cell at the end of the same row. As it turns out people want more room for comments so I've decided to go with a second sheet that works as a comments sheet. I've got the userform and everything with it done except the "submit" button. When the user double-clicks now the cell is still highlighted, but instead of forwarding to the end of row it opens the user form for comments. I'm trying to get the submit button to do two things:
First, I want it to place the row# of the cell that was highlighted into the first column; and second, I want what the user puts in the textbook to be placed into the second column.
I can get it to enter a value in the first row for the textbox, but I don't know where to start for the row#'s (maybe ActiveCell.Row ?); also, I don't know how to go about getting it set to move down to the next row if the first row already has comments in it (need something with a Row +1 I guess? It's just this one last button that's slowing me up; got the rest done, but I could use some advice on this part of the userform coding. Thanks!
Here's how I'd do it (rough draft):
Private Sub Worksheet_Beforedoubleclick(ByVal Target As Range, Cancel As Boolean)
Const CLR_INDX As Integer = 6
If Target.Interior.ColorIndex = xlNone Then 'If cell is clear
With frmCorrections
Set .CellRange = Target
.HiliteColorIndex = CLR_INDX
.Show
End With
'Or Else if cell is already yellow
ElseIf Target.Interior.ColorIndex = CLR_INDX Then
Target.Interior.ColorIndex = xlNone 'Then clear the background
End If
Cancel = True
End Sub
and the user form code:
Dim m_rng As Range
Dim m_index As Integer
Public Property Set CellRange(rng As Range)
Set m_rng = rng
End Property
Public Property Let HiliteColorIndex(indx As Integer)
m_index = indx
End Property
Private Sub cmdCancel_Click()
Me.Hide
End Sub
Private Sub cmdOK_Click()
Dim cmt As String, NextCell As Range
cmt = Me.txtComment.Text
If Len(cmt) > 0 Then
Set NextCell = ThisWorkbook.Sheets("Corrections").Cells( _
Rows.Count, 1).End(xlUp).Offset(1, 0)
With NextCell
.Parent.Hyperlinks.Add Anchor:=NextCell, Address:="", _
SubAddress:=m_rng.Address(False, False, , True), _
TextToDisplay:=m_rng.Address(False, False)
.Offset(0, 1).Value = cmt
End With
m_rng.Interior.ColorIndex = m_index
End If
Me.Hide
End Sub
Private Sub UserForm_Activate()
Me.txtComment.Text = ""
Me.lblHeader.Caption = "Enter comment for cell: " & _
m_rng.Address(False, False)
End Sub
EDIT:
This is what I finally came up with to get it working the way I wanted. On the first worksheet the user can double click on the cell, which then highlights the cell and prompts with the user form. If the user cancels then the highlight is removed and the user can keep working; if they enter anything in the box and submit it then the cell addressis placed in one row on the "Comments" page and the text is enteredone column over in the row corresponding to the original cell's address so I can see where the correction is and what their justification was. Anyways the codes are below.
I use the following for highlighting and calling the form:
Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
Application.EnableEvents = False
Dim TargRow As Variant
Dim TargCol As Variant
TargRow = Target.Row
TargCol = Target.Column
Header = 8
FirstCol = 0
LastCol = 13
CommentCol = 13
If TargRow > Header And TargCol > FirstCol And TargCol < LastCol Then
'If the cell is clear
If Target.Interior.ColorIndex = xlNone Then
Cancel = True
'Then change the background to yellow
Target.Interior.ColorIndex = 6
Corrections.Show
'Else if the cell background color is already yellow
ElseIf Target.Interior.ColorIndex = 6 Then
'Then clear the background
Target.Interior.ColorIndex = xlNone
End If
End If
'This is to prevent the cell from being edited when double-clicked
Cancel = True
Application.EnableEvents = True
End Sub
And I use this for the user form itself:
Private Sub UserForm_Initialize()
TextBox.Value = ""
End Sub
Private Sub CommandButton2_Click()
Unload Corrections
ActiveCell.Interior.ColorIndex = xlNone
End Sub
Private Sub CommandButton1_Click()
Dim PrevCell As Range
Set PrevCell = ActiveCell
ActiveWorkbook.Sheets("Comments").Activate
Range("A6").Select
Do
If IsEmpty(ActiveCell) = False Then
ActiveCell.Offset(1, 0).Select
End If
Loop Until IsEmpty(ActiveCell) = True
ActiveCell.Value = PrevCell.Address
ActiveCell.Offset(0, 1) = TextBox.Value
Unload Corrections
ActiveWorkbook.Sheets("DataPage").Activate
End Sub