Worksheet_Change Event - Duplication Check, Ignore Blanks - excel

I am using a VBA change event to look for duplicates in column C. The code below works but when i delete all values within the range, blanks are triggered as duplicates so i need to include a way to ignore duplicates from the code. Any ideas?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cell As Range
On Error GoTo ws_exit
Application.EnableEvents = False
With Target
If .Column = 3 Then
With .EntireColumn
Set cell = .Find(What:=Target.Value, AFter:=.Cells(1, 1))
If cell.Address = Target.Address Then
Set cell = .FindNext()
End If
If Not cell.Address = Target.Address Then
MsgBox "This Wall Reference already exists. Please ensure you have a unique reference identifier less than 20 characters in length", vbOKOnly
End If
End With
End If
End With
ws_exit:
Application.EnableEvents = True
End Sub
I expect to be able to ignore blanks but sill have the VBA run a duplication check to return a msgbox only if a duplication is found.

First you must consider that Target is a range of multiple cells and not only one cell. Therefore it is necessary to use Intersect to get all the cell that are changed in column 3 and then you need to loop through these cells to check each of them.
Also I recommend to use WorksheetFunction.CountIf to count how often this value occurs if it is >1 then it is a duplicate. This should be faster then using Find.
Note that the following code looks for duplicates in column 3 only if you want to check if a duplicate exists anywhere in the worksheet replace CountIf(Me.Columns(3), Cell.Value) with CountIf(Me.Cells, Cell.Value)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim AffectedRange As Range
Set AffectedRange = Intersect(Target, Me.Columns(3))
If Not AffectedRange Is Nothing Then
Dim Cell As Range
For Each Cell In AffectedRange
If Application.WorksheetFunction.CountIf(Me.Columns(3), Cell.Value) > 1 Then
MsgBox "This Wall Reference already exists. Please ensure you have a unique reference identifier less than 20 characters in length", vbOKOnly + vbExclamation
End If
Next Cell
End If
End Sub
Instead of using VBA you could also use Conditional Formatting to highlight duplicates in red for example. Could be easier to archieve (use the =CountIf formula as condition). And also it will always highlight all duplicates immediately which makes it easy to determine them.

Thanks for the help K.Davis. I appreciate your time and effort.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Value = vbNullString Then Exit Sub
Dim cell As Range
On Error GoTo ws_exit
Application.EnableEvents = False
With Target
If .Column = 3 Then
With .EntireColumn
Set cell = .Find(What:=Target.Value, AFter:=.Cells(1, 1))
If cell.Address = Target.Address Then
Set cell = .FindNext()
End If
If Not cell.Address = Target.Address Then
MsgBox "This Glazing Reference already exists. Please ensure you have a unique reference identifier less than 20 characters in length", vbOKOnly
End If
End With
End If
End With
ws_exit:
Application.EnableEvents = True
End Sub

Related

Use VBA to enforce specific date format entry on a range of cells

I am trying to enforce a specific date format on a sheet where dates will be manually entered or pasted on a regular basis. Since Excel's built-in data validation does not work for pasted values, I am trying to accomplish this with VBA.
This is the code I have written/hodgepodged together from other Stack posts:
Option Explicit
Function dateCheck(cellval As Date) As Boolean
If IsDate(cellval) And cellval.NumberFormat <> "yyyy-dd-mm" Then
MsgBox "Please use the yyyy-dd-mm date format!"
dateCheck = True
End If
End Function
Private Sub Worksheet_Change(ByVal Target As Range)
Dim AffectedCells As Range
Dim Cell As Range
Dim A As Boolean
'rule 1: column A allows only text length up to 9
Set AffectedCells = Intersect(Target, Target.Parent.Range("collection_dates"))
If Not AffectedCells Is Nothing Then
For Each Cell In AffectedCells
A = dateCheck(Cell.Value)
If A = True Then
Application.Undo 'undo insert
Exit Sub 'stop checking after one invalid data was found.
End If
Next Cell
End If
End Sub
"collection_dates" is a range of cells I named, which I want to enforce formatting on.
The error I'm currently getting is a "Run-time error '13': type mismatch" on the line A = dateCheck(Cell.Value)
I have tried various other iterations of this and gotten various other errors; I think I am just fundamentally missing something about how to properly validate a cell value in VBA.
I took the advice from some of the comments and stopped attempting to enforce the formatting at this level, since what was really important to me was that dates were entered correctly, ie. not typos.
I had to make some changes to accommodate blank cells or else the Application.Undo would loop endlessly if an incorrect value was entered and cleared. This is the final product, which appears to be working:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim AffectedCells As Range
Dim Cell As Range
'rule 1: only allow dates to be entered in affected cells
Set AffectedCells = Intersect(Target, Target.Parent.Range("collection_dates"))
If Not AffectedCells Is Nothing Then
For Each Cell In AffectedCells
If Not IsDate(Cell.Value) Then
If Trim(Cell.Value) = "" Then
GoTo DoNothing
Else
MsgBox "Please enter a valid date!", vbCritical
Application.Undo 'undo insert
Exit Sub 'stop checking after one invalid data was found.
End If
End If
DoNothing:
Next Cell
End If
End Sub

VBA - if cell contains a word, then messagebox just one single time

My idea was to get an alert every time I digit the word "high" in a cell of column A (also if the word is contained in a longer string). This alert should pop up just if i edit a cell and my text contains "high" and I confirm (the alert shows when I press "enter" on the cell to confirm or just leave the edited cell). So I made this code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not IsError(Application.Match("*high*", Range("A:A"), 0)) Then
MsgBox ("Please check 2020 assessment")
End If
End Sub
The code seemed working fine. I digit "high" in a cell of column A and get the alert when I confirm- leave the cell.
The problem is that when i have a single "high" cell, the alert continues to pop up at every modification I do, in every cell. So is impossible to work on the sheet.
I need a code to make sure that, after digiting "high", i get the alert just one time, and then I do not get others when editing any cell, unless i digit "high" in another cell, or i go and modify a cell that already contains "high" and I confirm it again.
What could I do? Thanx!!
This will set a target (monitored range) and check if the first cell changed contains the word
Be aware that if you wan't to check every cell changed when you modify a range (for example when you copy and paste multiple cells), you'r have to use a loop
Private Sub Worksheet_Change(ByVal Target As Range)
' Set the range that will be monitored when changed
Dim targetRange As Range
Set targetRange = Me.Range("A:A")
' If cell changed it's not in the monitored range then exit sub
If Intersect(Target, targetRange) Is Nothing Then Exit Sub
' Check is cell contains text
If Not IsError(Application.Match("*high*", targetRange, 0)) Then
' Alert
MsgBox ("Please check 2020 assessment")
End If
End Sub
Let me know if it works
I tried your code; now, if column "A" has a cell "high", the alert correctly pop up and if then I edit cells in a column other than column "A", I don't get alert, so this is the good news!
The bad news is that if I have one single "high" in column A, when I edit any other cell in column "A" itself, I still get the alert everytime.
A Worksheet Change: Target Contains String
The message box will show only once whether you enter one ore multiple criteria values.
The Code
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const srcCol As String = "A"
Const Criteria As String = "*high*"
Dim rng As Range: Set rng = Intersect(Columns(srcCol), Target)
If rng Is Nothing Then
Exit Sub
End If
Application.EnableEvents = False
Dim aRng As Range
Dim cel As Range
Dim foundCriteria As Boolean
For Each aRng In rng.Areas
For Each cel In aRng.Cells
If LCase(cel.Value) Like LCase(Criteria) Then
MsgBox ("Please check 2020 assessment")
foundCriteria = True
Exit For
End If
Next cel
If foundCriteria Then
Exit For
End If
Next aRng
Application.EnableEvents = True
End Sub
Sub testNonContiguous()
Range("A2,A12").Value = "high"
End Sub

Need help changing cell values when cell in same row changes

I need help automatically changing cells containing a certain value whenever a specific cell on same row changes value.
E.g whenever a cell in B column changes = change TRUE to FALSE on that specific row.
My VBA knowledge is pretty much nonexistent and Im certainly a beginner.
Im fairly sure that Worksheet.Change is what Im looking for and I've been trying out some code I've found here on SO, such as:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Target.Parent.Range("B:B")) Is Nothing Then Exit Sub
For Each x In Target
Cells(x.Row, 3).Value = "False"
Next
End Sub
I know though that this doesn't replace specific values in whatever column the cells are.
I've been trying out silly things like:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Target.Parent.Range("B:B")) Is Nothing Then Exit Sub
For Each x In Target
If Cells(x.Row, x.Column).Value = "TRUE" Then Value = "FALSE"
Next
End Sub
But of course it doesnt work.
Think you could point me out a direction of what I should be researching?
Replace the change event sub on the sheet where you have your data with the code below. I think that should do the trick
Private Sub Worksheet_Change(ByVal Target As Range)
Dim oRng As Range
Dim oCell As Range
' Check if change was in column B
If Intersect(Target, Target.Parent.Range("B:B")) Is Nothing Then Exit Sub
' Turn off events so that when we make a change on the sheet, this event is not triggered again
Application.EnableEvents = False
' Set the range to include all column in Target row
Set oRng = Target.Parent.Range("C" & Target.Row & ":" & Target.Parent.Cells(Target.Row, Target.Parent.UsedRange.Columns.Count).Address)
' Loop through all cells to change the value
For Each oCell In oRng
If Trim(LCase(oCell.Value)) = "true" Then
oCell.Value = "FALSE"
End If
Next
' Enable events again
Application.EnableEvents = True
End Sub

run macro to an entire column

working on this macro on a dependent drop down menu
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$M$10" Then
Range("O10").Value = "--select--"
End If
End Sub
I need to run this macro for all the cells in the column. It just work in the first cell
Can anyone help me please?
thanks!
You would need to use Application.Intersect in combination with .Offset() instead of the Target.Address method.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim AffectedRange As Range
Set AffectedRange = Application.Intersect(Target, Me.Range("B:B")) 'find all cells that were changed in column B
If AffectedRange Is Nothing Then Exit Sub 'exit if nothing in column B was changed
Application.EnableEvents = False 'make sure our value change doesn't trigger another Worksheet_Change event (endless loop)
On Error GoTo ENABLE_EVENTS 'make sure events get enabled even if an error occurs
Dim Cell As Range
For Each Cell In AffectedRange.Cells 'loop through all changed cells in column B
Cell.Offset(ColumnOffset:=1).Value = "" 'move from B one column to the right and reset value
Next Cell
ENABLE_EVENTS: 'in case of error enable events and report the error
Application.EnableEvents = True
If Err.Number <> 0 Then
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End If
End Sub
This will observe column B and delete the value in C whenever a cell in B was changed.

Prevent More than 1 cell From Being Changed While Ignoring if the Cells' Contents Were Deleted

I am trying to create an Excel VBA macro that will detect if more than one cell is being changed at a time. I also want it to ignore the code if a person is only deleting the cells.
This works for checking if more than one cell is being changed and will prevent the change:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Cells.Count > 1 Then
MsgBox "Change only one cell at a time", , "Too Many Changes!"
Application.Undo
End If
Application.EnableEvents = True
End Sub
I am trying to figure out how to get it so it ignores it when the contents of the cells are being deleted. I have tried many combinations of stuff using KeyAscii, Chr, ClearContents, and several other things. I can't seem to get it to work. Below is the last thing I tried.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Cells.Count > 1 Then
If KeyAscii <> vbKeyDelete Then
MsgBox "Change only one cell at a time", , "Too Many Changes!"
Application.Undo
End If
End If
Application.EnableEvents = True
End Sub
Please let me know if someone has any suggestions.
Try this
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cl As Variant
Dim dat As Variant
Application.EnableEvents = False
If Target.Cells.Count > 1 Then
dat = Target.Formula
For Each cl In dat
If cl <> "" Then
MsgBox "Change only one cell at a time", , "Too Many Changes!"
Application.Undo
Exit For
End If
Next
End If
Application.EnableEvents = True
End Sub
Explanation:
dat = Target.Formula copies the Formula's from a range into a 2D variant array.
The .Formula is to avoid treating formulas that happen to return an empty string as blanks
For Each cl in dat iterates each element of the array
If cl is non blank then the user cannot have deleted the range, so trigger then message and undo
I could have used For Each Cl in Target (where cl is Dim'ed as Range), but copying to a variant array is faster that looping over a range.

Resources