Private Sub Workboook_Open()
'Auto Dating Test (1)'
If Range("i4").Value = "CR" Then
Range("I5").Value = Date
End If
Range("I5").Value = Date
End Sub
So I'm trying to get it to work with any variable/information in i4 aswell Delete the date if the value has been deleted, and not automatically update. so it only pulls the date from when the information was entered on i4. so if going back to the document days later it doesn't update automatically to the day the document was opened again. I can't get the thing to run automatically aswell as I haven't even figured out where to begin to delete the date in i5 say if the information in i4 is deleted. nor just a universal detection if somethings in i4 and not just a specific string.
A 'True' Worksheet Change Backed Up By a Workbook Open
These codes have to be copied into three different modules. Additionally, the sheet code name (Sheet1) in PopulateVariables has to be adjusted.
ThisWorkbook
Option Explicit
Private Sub Workboook_Open()
PopulateVariables
End Sub
Sheet Module e.g. Sheet1
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
DateChange Target
End Sub
Standard Module e.g. Module1
Option Explicit
Private Const CHANGE_CELL As String = "I4"
Private Const DATE_CELL As String = "I5"
Private ChangeCellValue As Variant
Private DateCellValue As Variant
' Called by the Workbook Open event.
' Note that the worksheet has to be specified. It's preferable to use
' the code name to allow changing the (tab) name.
Sub PopulateVariables()
ChangeCellValue = Sheet1.Range(CHANGE_CELL).Value ' adjust the sheet!
DateCellValue = Sheet1.Range(DATE_CELL).Value ' adjust the sheet!
End Sub
' Called by the Worksheet Change event.
' If you select a cell and click into the formula bar and press enter
' or you double-click the cell and press enter, you haven't changed
' the value but the Worksheet Change event got triggered anyway.
' That is the meaning of an invalid change.
Sub DateChange(ByVal Target As Range)
On Error GoTo ClearError
Dim cCell As Range, dCell As Range, HaveValuesChanged As Boolean
With Target.Worksheet
Set cCell = .Range(CHANGE_CELL)
Set dCell = .Range(DATE_CELL)
End With
Dim Today As Date: Today = Now ' when done testing, use 'Date'
' Handle change in Change cell.
If Not Intersect(cCell, Target) Is Nothing Then ' change detected
If CStr(cCell.Value) <> CStr(ChangeCellValue) Then ' valid change
Application.EnableEvents = False ' to not retrigger the event
If IsEmpty(cCell) Then
DateCellValue = Empty
dCell.Value = DateCellValue
Else
If CStr(dCell.Value) <> CStr(Today) Then ' date is different
DateCellValue = Today
dCell.Value = DateCellValue
'Else ' date is the same; do nothing
End If
End If
ChangeCellValue = cCell.Value
HaveValuesChanged = True
'Else ' invalid change; do nothing
End If
'Else ' no change detected; do nothing
End If
' Handle change in Date cell.
If Not HaveValuesChanged Then ' previous invalid change or no change
If Intersect(dCell, Target) Is Nothing Then Exit Sub ' no ch. detected
If CStr(dCell.Value) <> CStr(DateCellValue) Then ' valid change
Application.EnableEvents = False ' to not retrigger the event
dCell.Value = DateCellValue
'Else ' invalid change; do nothing
End If
End If
ProcExit:
On Error Resume Next
If Not Application.EnableEvents Then Application.EnableEvents = True
On Error GoTo 0
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "':" & vbLf & Err.Description
Resume ProcExit
End Sub
did you mean like if A4 gets deleted so does A5 and if A4 gets edited the date is shown in A5? try below
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.CountLarge > 1 Then Exit Sub
If Not Intersect(Target, Range("A4")) Is Nothing Then
If Target.Value = "" Then
Range("A5") = "" 'if A4 gets deleted same goes for A5
Else
Range("A5") = Date 'if A4 gets edited with any value show the date in A5
End If
End If
End Sub
Related
So I have a dropdown to select the year; 2015, 2016, 2017, etc, but based on which year is selected, I want to populate cells from a specific worksheet. So for example if 2015 is selected, cell K3 in the current worksheet equals cell E12 from the 2015 worksheet. Any help would be greatly appreciated, thanks!
Edit:
So far I have the following VBA code:
Option Explicit
Sub Worksheet_Change(ByVal Target As Range)
If Range("J2") = "2016" Then
Range("K3") = ActiveWorkbook.Worksheets("2016").Range("E12")
Else
Range("K3") = "0"
End If
End Sub
...but keep getting this error:
Run-time error '1004':
Method 'Range' of object '_Worksheet' failed
...and then Excel restarts.
A Drop-Down Worksheet Change
In the initial solution, when writing to the destination cell, the event would get re-triggered. Although it would finish with the If Intersect... line, I consider it as unacceptable (wrong). Study the following solution how this is avoided.
To see (prove) the difference, you could add e.g. the line MsgBox "Entering Change Event" at the beginning of each code which would show that the wrong solution shows the message box twice on each change in the drop-down cell.
Corrected and Improved
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const sAddress As String = "E12" ' Source Cell (read from)
Const dddAddress As String = "J2" ' Destination Drop-Down Cell
Const dAddress As String = "K3" ' Destination Cell (written to)
Const dValNoWorksheet As Long = 0 ' source worksheet not found
Const dValBlank As Long = 0 ' source cell blank i.e. [Empty],[=""],['],...)
Dim dddCell As Range: Set dddCell = Range(dddAddress)
' This will prevent the succeeding code to run if there was no change
' in the drop-down cell.
If Intersect(Target, dddCell) Is Nothing Then Exit Sub ' not ddd cell
' 'Me' is the worksheet containing this code, while 'Me.Parent' is
' its workbook which is also 'ThisWorbook'.
On Error Resume Next ' defer error trapping
Dim sws As Worksheet: Set sws = Me.Parent.Worksheets(CStr(dddCell.Value))
On Error GoTo 0 ' enable error trapping ('Err.Number = 0')
' The following line prevents triggering the event again when writing
' to the destination cell ('dCell').
Application.EnableEvents = False
' Immediately after the previous line start an error-handling routine
' to prevent exiting the procedure with events disabled. Its flow
' is self-explanatory but study it carefully.
On Error GoTo ClearError ' enable error trapping
' Now you do your thing. If something goes wrong, the error handler
' will make make sure that the procedure will exit only after enabling
' events.
Dim dCell As Range: Set dCell = Range(dAddress)
If sws Is Nothing Then ' worksheet doesn't exist
dCell.Value = dValNoWorksheet
Else ' worksheet exists
Dim sCell As Range: Set sCell = sws.Range(sAddress)
If Len(CStr(sCell.Value)) = 0 Then ' blank
dCell.Value = dValBlank
Else ' not blank
dCell.Value = sCell.Value
End If
End If
SafeExit:
' Be careful, if an error occurs here, it will trigger an endless loop,
' since the error handler is still active.
Application.EnableEvents = True
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume SafeExit ' the error handler stays active('Err.Number = 0')
End Sub
Wrong
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("J2")) Is Nothing Then Exit Sub ' not dd cell
On Error Resume Next
Dim ws As Worksheet: Set ws = Me.Parent.Worksheets(CStr(Range("J2").Value))
On Error GoTo 0
If ws Is Nothing Then ' worksheet doesn't exist
Range("K3").Value = 0
Else ' worksheet exists
If Len(CStr(ws.Range("E12").Value)) = 0 Then ' blank
Range("K3").Value = 0
Else ' not blank
Range("K3").Value = ws.Range("E12").Value
End If
End If
End Sub
The following code successfully changes the sheet name based on the value in cell "E26" i.e. if the value in "E26" is 'Test', sheet name will be named 'Test'.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Set Target = Range("E26")
If Target = "" Then Exit Sub
Application.ActiveSheet.Name = VBA.Left(Target, 31)
Exit Sub
End Sub
Problem is, if I copy the sheet but want to keep the value in cell "E26" the same, the copied sheet name becomes "Test (1)" but I get a Run time error '1004': That name is already taken. Try a different one. obviously.
Question: How can I automatically add a number after each subsequent copy of the sheet i.e. Test (1), Test (2), etc. to avoid getting the error while still keeping the value in cell "E26" the same i.e. Test?
I don't know if I understood the idea, but try to count the current sheets in your book and concat to the name you want
Private Sub Worksheet_SelectionChange(ByVal Target As Range)`
Set Target = Range("E26")
Dim nSheets As Double
nSheets = ThisWorkbook.Sheets.Count
If Target = "" Then Exit Sub
'+1 is optional in order you have a secuence
Application.ActiveSheet.Name = Target & "(" & nSheets + 1 & ")"
End Sub
Let me suggest a bit more complex solution that hopefully also addesses all sort of end cases.
First, let's create a helper function that can answer the question "Is there a sheet with the name x?".
Function sheetExists(name) As Boolean
sheetExists = False
For i = 1 To Application.Worksheets.Count
shtName = Application.Worksheets(i).name
If shtName = name Then
sheetExists = True
Exit Function
End If
Next
End Function
Now that we have that option, let's build the actual code:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
MaxTabs = 10 ' Adjust this number as needed.
BaseName = Range("E26").Value
If (BaseName = "") Then
Exit Sub
End If
name = BaseName
i = 0
For i = 1 To MaxTabs
If (Not sheetExists(name)) Then
Application.ActiveSheet.name = name
Exit Sub
End If
name = BaseName + " (" + Trim(Str(i)) + ")"
Next
End Sub
Worksheet Change
You could rather use the Worksheet_Change event and suppress the error(s) by using On Error.
The Code
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rg As Range: Set rg = Intersect(Range("E26"), Target)
If Not rg Is Nothing Then
Application.EnableEvents = False
On Error GoTo clearError
Me.Name = Left(rg.Value, 31)
Application.EnableEvents = True
End If
Exit Sub
clearError:
'Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume Next
End Sub
I have two columns, I & Q, both with formulas in them. If the formula in Q returns an error, I want to unlock the cell in column I of that row. Here's what I have and it doesn't work:
Private Sub UnlockError(ByVal Target As Range)
Application.EnableEvents = False
If Intersect(Target, Sheet2.Range("Q:Q")) Is Nothing Then GoTo ResetEvents
If IsError(Target) Then
ActiveSheet.Unprotect Password:="#Pentagon2020"
Target.Offset(0, -8).Locked = False
ActiveSheet.Protect Password:="#Pentagon2020"
End If
ResetEvents:
Application.EnableEvents = True
End Sub
The error that triggers the unlocking of another cell must be within a procedure, as demonstrated in the Change event procedure below.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim MyError As Double
If Not Application.Intersect(Target, Range("A:A")) Is Nothing Then
On Error Resume Next
MyError = 100 / 0
If Err.Number = 11 Then
With Cells(Target.Row, "B")
.Locked = False
.Select
End With
ElseIf Err.Number Then
MsgBox "An unidentified error occurred.", _
vbInformation, "Error messsage"
Else
' continue with your program
Target.Value = MyError
End If
End If
End Sub
If you want your procedure to look at a cell and determine if it contains an error this code will do it.
If IsError(Cells(2, "A") Then
With Cells(2, "B")
.Locked = False
.Select
End With
End If
The problem with this code is that you would either need a button to call it or embed it in another procedure that is called by pressing a button.
Worksheet_Calculate
The following is a rather un-efficient solution. It will be triggered each time the worksheet gets calculated i.e. a change will happen 'due to' a formula. It will first lock all cells in the Lock Range and then unlock the desired ones. If you don't have thousands of rows, this may serve you well.
A better solution may be to to write all the values in the Source Range to a public array to keep track of the changes. Then each time the worksheet is calculated, the values of a new array would be checked against the values in the public array. Appropriate changes would be made, and in the end the new array would be written to the public array. Keep in mind that the first writing of the public array could be done by using the Workbook_Open event and that rows may get added or deleted.
The best solution, if possible, would be to steer this whole operation towards the Worksheet_Change event i.e. trace back the formulas to the cells that are changed manually or via VBA and are 'causing' the resulting error values. Then checking only the values that got changed would increase efficiency. Keep in mind that in this event Application.EnableEvents is often necessary and should be 'accompanied' by error handling.
Adjust the values in the constants section.
Standard Module e.g. Module1
Option Explicit
Sub unlockErrorValuesInit( _
ws As Worksheet)
Const sFirst As String = "Q2"
Const lFirst As String = "I2"
Const MyPassWord As String = "" '"#Pentagon2020"
unlockErrorValues ws.Range(sFirst), ws.Range(lFirst), MyPassWord
End Sub
Sub unlockErrorValues( _
srcFirstCell As Range, _
lockFirstCell As Range, _
Optional ByVal MyPassWord As String = "")
' Validate inputs.
If srcFirstCell Is Nothing Then Exit Sub
If lockFirstCell Is Nothing Then Exit Sub
' Calculate offsets.
Dim rOffs As Long: rOffs = lockFirstCell.Row - srcFirstCell.Row
Dim cOffs As Long: cOffs = lockFirstCell.Column - srcFirstCell.Column
' Define Source (Column) Range.
Dim srg As Range
With srcFirstCell
Set srg = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If srg Is Nothing Then Exit Sub
Set srg = .Resize(srg.Row - .Row + 1)
End With
Application.ScreenUpdating = False
srg.Worksheet.Unprotect MyPassWord
' Reset Lock (Column) Range (lock all cells).
With srg.Offset(rOffs, cOffs)
.Locked = True
.Interior.Color = xlNone ' to better visualize
End With
' Define Destination Range of Source Range (cells containing error values).
On Error Resume Next
Dim drg As Range: Set drg = srg.SpecialCells(xlCellTypeFormulas, xlErrors)
On Error GoTo 0
' Unlock cells in Destination Range of Lock Range.
If Not drg Is Nothing Then
With drg.Offset(rOffs, cOffs)
.Locked = False
.Interior.Color = vbYellow ' to better visualize
End With
End If
srg.Worksheet.Protect MyPassWord
Application.ScreenUpdating = True
End Sub
Sheet Module e.g. Sheet1 (the name in parentheses in the VBE Project Explorer)
Option Explicit
Private Sub Worksheet_Calculate()
unlockErrorValuesInit Me
End Sub
I'm trying to make use of the Worksheet_Change event in VBA to return the value of the adjacent cell if current cell value is nothing, within provided range. I.e. IF current cell F3 is empty, then return contents in cell G3. This formula only applies to cells in range F3 to F37.
Here is my current code for which when any cell in range is empty, the code doesn't seem to evaluate (i.e. copy data from adjacent cell), and remains empty.
Any help would be greatly appreciated. Thanks!
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myCell As Range
Set myCell = Range("F3:F37")
If Not Application.Intersect(myCell, Range(Target.Address)) Is Nothing Then
Target.Value = Cell.Offset(0, 1).Value
End If
End Sub
Modified to:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myCell As Range
Application.DisplayAlerts = False
Application.EnableEvents = False
Set myCell = Range("F3:F37")
If Not Application.Intersect(myCell, Range(Target.Address)) Is Nothing Then
If Target.Value = "" Then
Target.Value = Target.Offset(0, 1).Value
End If
End If
Application.EnableEvents = True
Application.DisplayAlerts = True
End Sub
If you use Application.EnableEvents = False in an event make sure you use proper error handling and enable events again in case of any error within the event (VBA Error Handling – A Complete Guide). Otherwise your events will stay turned off in case of an error until you close the Excel application completely.
Note that Application.EnableEvents affects the whole application that means all Excel files that are opened in that instance of the application. So not having proper error handling here might have a bigger impact on other projetcts than you think.
Another trap you fell into, is that Target can be a Range (not only a single cell). So for example if you copy/paste a range that affects multiple cells in F3:F37 your Target is not a single cell and therefore Target.Value = "" does not work. You need a loop through all the affected cells:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim CheckRange As Range
Set CheckRange = Me.Range("F3:F37") 'Make sure you use "Me" to refer to the same worksheet as Target (and the change event is in)
Dim AffectedCells As Range 'get the cells of CheckRange that were changed
Set AffectedCells = Application.Intersect(CheckRange, Target)
Application.EnableEvents = False
On Error GoTo ENABLE_EVENTS 'make sure you never end up in a condition where events stay disabled
If Not AffectedCells Is Nothing Then
Dim Cell As Range
For Each Cell In AffectedCells 'loop throug all the affected cells
If Cell.Value = "" Then
Cell.Value = Cell.Offset(0, 1).Value
End If
Next Cell
End If
'no exit sub here!
ENABLE_EVENTS:
Application.EnableEvents = True
If Err.Number <> 0 Then 'make sure to re-raise an error message if there was an error (otherwise you won't ever notice that there was one), because the `On Error GoTo` statement muted the error message.
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
'above line to raise the original error message
'or at least show a message box:
'MsgBox "There was an error. Tell your Developer to fix it.", vbCritical
End If
End Sub
Note that I removed Application.DisplayAlerts because there is nothing in the code that would display any alerts, so I see no need to use it in this case here.
Please can someone help with the following code. it gives me an error at the following line:
Set range = "C5:L14"
This is the complete code:
Private Sub Worksheet_Change(ByVal Target As Excel.range)
Dim ws As Worksheet
Dim range As Worksheet
Set ws = Application.ActiveSheet
Set range = "C5:L14"
If Not Application.Intersect(Target, range("C5:L14")) Is Nothing Then
If range("C5:L14").Value = "" Then Exit Sub
If range("C5:L14").Date = "< today()" Then Exit Sub
If range("C5:L14").Date = "> today()" Then MsgBox ("Future dates not allowed!")
Else
MsgBox ("Please enter date as follows yyyy-mm")
End If
End Sub
The date is formatted to "2013 Jan" on the cells. Future dates are not allowed and the user should only type in the date as "2013-01". The format should change it correctly. If they type in "2013 Jan" the Conditional formatting does not pick it up. Have tried DATA VALIDATION but it only limits me to one.
I need the macro to make sure a user doesn't enter an incorrect date in the cells specified.
What you are trying can be solved without VBA as well. However I am showing you both the methods. Take your pick
NON VBA
Select the cell where you want to apply Data Validation and then follow these steps.
Step 1
Step 2
Step 3
Step 4
In Action
VBA
I have commented the code so you will not have any problem in understanding it
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
Dim rng As Range
Dim aCell As Range
'~~> The below two lines are required. Read up more on
'~~> http://stackoverflow.com/questions/13860894/ms-excel-crashes-when-vba-code-runs/13861640#13861640
On Error GoTo Whoa
Application.EnableEvents = False
'~~> Set your range
Set rng = Range("C5:L14")
If Not Application.Intersect(Target, rng) Is Nothing Then
'~~> Loop through all cells in the range
For Each aCell In rng
If aCell.Value <> "" Then
If aCell.Value > Date Then
aCell.ClearContents
MsgBox "Future date not allowed in cell " & aCell.Address
ElseIf IsDate(aCell.Value) = False Then
aCell.ClearContents
MsgBox "Incorrect date in cell " & aCell.Address
Else
aCell.Value = Format(aCell.Value, "yyyy-mm")
End If
End If
Next
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
Hope this helps?
EDIT:
A slight change. In the Step 4 of the Non VBA Method, I typed "yyyy mm" by mistake. Change that to "yyyy-mm"