Ensure Cell Value Follows a Specified Character & Number Combination - vba

Im new to vba I have a requirement where in I have restrict input to column A of a worksheet where user can only enter string in following format
'BATCH00_00' numbers can range from 0-99 I tried the below code but it does not work
Private Sub Worksheet_Change(ByVal Target As Range)
'PURPOSE: Checks a specific column and validates that value follow a specified pattern (numbers or letter combinations)
Dim cell As Range, rng As Range
Dim InvalidCount As Long, x As Long
x = 3 'Column to Validate
Set rng = ActiveSheet.UsedRange.Columns(x).Offset(1).Resize(ActiveSheet.UsedRange.Rows.Count - 1, 1)
For Each cell In rng.Cells
If Not UCase(cell.Value) Like "BATCH##_##Then
'Highlight Invalid Cell Yellow
msg "invalid entry please enter In following format BATCH00_00"
Next cell
End Sub
ALSO I have a another code on sheet which checks in column A there should not be any duplicate entries
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, r As Range, msg As String, x As Range
Set rng = Intersect(Columns(1), Target)
If Not rng Is Nothing Then
Application.EnableEvents = False
For Each r In rng
If Not IsEmpty(r.Value) Then
If Application.CountIf(Columns(1), r.Value) > 1 Then
msg = msg & vbLf & r.Address(0, 0) & vbTab & r.Value
If x Is Nothing Then
r.activate
Set x = r
Else
Set x = Union(x, r)
End If
End If
End If
Next
If Len(msg) Then
MsgBox "Duplicate values not allowed Invalid Entry" & msg
x.ClearContents
x.Select
End If
Set rng = Nothing
Set x = Nothing
Application.EnableEvents = True
End If
End Sub
how do I make the 1st code working & combine both to have one Private Sub Worksheet_Change

Try this (coments in code)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim validationError Boolean
validationError = False
'if changed cell was not in A column, then exit sub
If Target.Column <> 1 Then Exit Sub
'check if format is valid BATCH00_00
If Not Target.Value Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
MsgBox "Invalid format!"
validationError = True
Exit Sub
End If
'check for uniqueness in A column
If Application.WorksheetFunction.CountIf(Range("A:A"), Target.Value) > 1 Then
MsgBox "Values must be unique in A column!"
validationError = True
End If
If validationError Then
Application.EnableEvents = False
Target.Value = ""
Application.EnableEvents = True
End If
End Sub

Related

Type Mismatch Error in Worksheet_Change Event

The code below checks for:
duplicate entry in column 1 & restricts them
then performs validation to ensure user can only input value BATCH[0-9]_[0-9] in first column
I am getting the following error for second block of code
error -Runtime error 13 Type mismatch
on the line -If Not Target.Value Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
Would appreciate if anyone can help fix this error
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, r As Range, msg As String, x As Range
Set rng = Intersect(Columns(1), Target)
If Not rng Is Nothing Then
Application.EnableEvents = False
For Each r In rng
If Not IsEmpty(r.Value) Then
If Application.CountIf(Columns(1), r.Value) > 1 Then
msg = msg & vbLf & r.Address(0, 0) & vbTab & r.Value
If x Is Nothing Then
r.activate
Set x = r
Else
Set x = Union(x, r)
End If
End If
End If
Next
If Len(msg) Then
MsgBox "Duplicate values not allowed Invalid Entry" & msg
x.ClearContents
x.Select
End If
Set rng = Nothing
Set x = Nothing
Application.EnableEvents = True
End If
If Target.Column <> 1 Then Exit Sub
'check if format is valid BATCH00_00
If Not Target.Value Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
MsgBox "Invalid format!"
GoTo ValidationError
Exit Sub
End If
Exit Sub
ValidationError:
Application.EnableEvents = False
Target.Value = ""
Application.EnableEvents = True
End Sub
sample data in spread sheet
yes I do have data in my spread sheet
Batch_Code | Batch_Name | Batch_Invoker_Name
BATCH00_01 | DwhEtl_MetaData_Loading start | Invalid
BATCH00_02 | DwhEtl_MetaData_Loading | dwhetl_batch_meta_load.unx
BATCH00_03 | DwhEtl_MetaData_Loading1111 | dwhetl_batch_meta_load.unx
BATCH00_04 | DwhEtl_Reg_Files_R22213123 | dwhetl_batch_meta_load.unx
BATCH00_05 | DwhEtl_Reg_Files_R323131312 | dwhetl_batch_meta_load.unx
BATCH00_06 | DwhEtl_Reg_Files_R323131313 | dwhetl_batch_meta_load.unx
BATCH00_07 | DwhEtl_Reg_Files_R323131314 | dwhetl_batch_meta_load.unx
BATCH00_08 | DwhEtl_Reg_Files_R323131315 | dwhetl_batch_meta_load.unx
BATCH00_09 | DwhEtl_Reg_Files_R323131316 |
with Batch_Code as column 1 to be validated & checked with
--Also I have tried the following code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim validationError Boolean
validationError = False
'if changed cell was not in A column, then exit sub
If Target.Column <> 1 Then Exit Sub
'check if format is valid BATCH00_00
If Not Target.Value Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
MsgBox "Invalid format!"
validationError = True
Exit Sub
End If
'check for uniqueness in A column
If Application.WorksheetFunction.CountIf(Range("A:A"), Target.Value) > 1 Then
MsgBox "Values must be unique in A column!"
validationError = True
End If
If validationError Then
Application.EnableEvents = False
Target.Value = ""
Application.EnableEvents = True
End If
End Sub
And this one:
If Target.Column <> 1 Then Exit Sub
If IsError(Target.Column) Then
'check if format is valid BATCH00_00
If Not Target.Column Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
MsgBox "Invalid format!"
GoTo ValidationError
Exit Sub
End If
End If
Exit Sub
ValidationError:
Application.EnableEvents = False
Target.value = ""
Application.EnableEvents = True
End Sub
The answer above mine should be marked correct.
Using code and input copied directly from the OP and running results in expected behavior. Duplicate entries and invalid format's return expected messages. Further testing reveals that the OP's problem is exactly as described in the question above. This is verified by creating an error in Column A with the formula "=5/0". The returned error, as properly diagnosed in the answer is a run time error, type mismatch.
The OP has clearly not investigated the avenue suggested to him.
Here is a the code I used to test, the only material change would be adding option explicit, debug.print output to test branching, 1 comment line, and putting 1 declaration to a line. No other changes except white space.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Dim r As Range
Dim msg As String
Dim x As Range
Set rng = Intersect(Columns(1), Target)
If Not rng Is Nothing Then
Application.EnableEvents = False
For Each r In rng
If Not IsEmpty(r.Value) Then
If Application.CountIf(Columns(1), r.Value) > 1 Then
msg = msg & vbLf & r.Address(0, 0) & vbTab & r.Value
If x Is Nothing Then
r.Activate
Set x = r
Else
Set x = Union(x, r)
End If
End If
End If
Next
If Len(msg) Then
MsgBox "Duplicate values not allowed Invalid Entry" & msg
x.ClearContents
x.Select
End If
Set rng = Nothing
Set x = Nothing
Application.EnableEvents = True
End If
If Target.Column <> 1 Then Exit Sub
'check if format is valid BATCH00_00
If Not Target.Value Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
MsgBox "Invalid format!"
GoTo ValidationError
Debug.Print "should never be run"
Exit Sub 'what's the point of this ?
End If
Debug.Print "normal exit"
Exit Sub
ValidationError:
Application.EnableEvents = False
Target.Value = ""
Application.EnableEvents = True
Debug.Print "exit from validation error"
End Sub
This is the sample input. Remove the Div/0 error and the program runs.
Batch_Code Batch_Name Batch_Invoker_Name
BATCH00_01 DwhEtl_MetaData_Loading start Invalid
BATCH00_02 DwhEtl_MetaData_Loading dwhetl_batch_meta_load.unx
BATCH00_03 DwhEtl_MetaData_Loading1111 dwhetl_batch_meta_load.unx
BATCH00_04 DwhEtl_Reg_Files_R22213123 dwhetl_batch_meta_load.unx
BATCH00_05 DwhEtl_Reg_Files_R323131312 dwhetl_batch_meta_load.unx
BATCH00_06 DwhEtl_Reg_Files_R323131313 dwhetl_batch_meta_load.unx
BATCH00_07 DwhEtl_Reg_Files_R323131314 dwhetl_batch_meta_load.unx
BATCH00_08 DwhEtl_Reg_Files_R323131315 dwhetl_batch_meta_load.unx
BATCH00_09 DwhEtl_Reg_Files_R323131316
BATCH10_10
#DIV/0!
The error comes, because the Target.Value is some kind of an error. Thus, an error cannot be compared with anything and it throws this Type Mismatch Error. Try this piece of code to replicate:
Public Sub TestMe()
Range("A1") = "=6 / 0" 'Making a #DIV/0! to replicate
If Range("A1") Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
Debug.Print "SOMETHING"
End If
End Sub
Thus, in your code, the easiest you can do is to sanitize the input somehow. E.g., check whether it is not an error. This is easily done with the IsError() function:
Public Sub TestMe()
Range("A1") = "=6 / 0"
If Not IsError(Range("A1")) Then
If Range("A1") Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
Debug.Print "SOMETHING"
End If
End If
End Sub
Thus, in your original code, try the following:
If Not IsError(Target) Then
If Not Target.Value Like "BATCH[0-9][0-9]_[0-9][0-9]" Then
MsgBox "Invalid format!"
GoTo ValidationError
Exit Sub
End If
End If

VBA Macro triggering too often

My worksheet is set up with data validation dropdowns and I am wanting a macro to ONLY trigger when the value of the cell is changed from another value in the dropdown, not from the default "empty" value.
Here is what I am trying to use:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 5 Then
If IsEmpty(Target.Value) = True Then
MsgBox "Test1"
Else
MsgBox "Test2"
End If
End If
exitHandler:
Application.EnableEvents = True
Exit Sub
End Sub
My problem is that this "IsEmpty" command is reading the cell AFTER the selection not before. I want it to read what the cells value was BEFORE the selection not after.
How can I do this?
Example approach:
Const COL_CHECK As Long = 5
Private oldVal
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
Set c = Target.Cells(1) '<< in case multiple cells are changed...
If c.Column = COL_CHECK Then
If oldVal <> "" Then
Debug.Print "changed from non-blank"
Else
Debug.Print "changed from blank"
End If
End If
exitHandler:
Application.EnableEvents = True
Exit Sub
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim c As Range
Set c = Target.Cells(1)
oldVal = IIf(c.Column = COL_CHECK, c.Value, "")
Debug.Print "oldVal=" & oldVal
End Sub
Another approach:
This will need one cell per validation-dropdown:
Function ValChange(Cell2Follow As Range) As String
ValChange = ""
If Len(Application.Caller.Text) = 0 Then Exit Function
If Application.Caller.Text = Cell2Follow.Text Then Exit Function
MsgBox "value of the cell is changed from another value in the dropdown" & vbLf & "not from the default 'empty' value"
End Function
in a different cell, assumed the dropdown is in E6:
=E6&ValChange(E6)
application.caller.text will be the old value
(calculation must be automatic)

Data Validation Allowing More Than One Entries

I have written down the lines that follow in order to allow the user to insert more that one entries in a data validated range. So if the dropdown list contains the elements: x1, x2, x3, .., xn, then, for any cell in the range, one can select and insert at first x1 value, then at the same cell select and insert x3 with the result being: x1, x3, and so it goes.
The problem is that when the user wishes to delete one of the value he gets an excel error saying the user has restricted the values for this cell. Therefore, he must delete the entire content of the cell and then select again the values he wants. Can you help improve this with regard to that?
Here is the code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngDV As Range
Dim oldVal As String
Dim newVal As String
If Target.count > 1 Then GoTo exitHandler
On Error Resume Next
Set rngDV = Cells.SpecialCells(xlCellTypeAllValidation)
On Error GoTo exitHandler
If rngDV Is Nothing Then GoTo exitHandler
If Intersect(Target, rngDV) Is Nothing Then
'Column 7 is the one to which is the code is applied
ElseIf Target.Column = 7 Then
Application.EnableEvents = False
newVal = Target.Value
Application.Undo
oldVal = Target.Value
Target.Value = newVal
If oldVal = "" Then
Else
If newVal = "" Then
Else
Target.Value = oldVal _
& ", " & newVal
End If
End If
End If
exitHandler:
Application.EnableEvents = True
End Sub
Violating validation through VBA is possible, that's why your code works in the first place. Deleting an entry is a manual action where the cell content (a comma separated list) is being compared to your validation list.
You could do one of the following:
use your cell with validation for selection of items only, and write the comma separated selection to a different cell.
write an edit function for the cell - that way the result of the edit will be again written into the cell via VBA.
add the list (and all possible results from deletions into the validation list as well (very messy though)
try with below
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngDV As Range
Dim oldVal As String
Dim newVal As String
If Target.Count > 1 Then GoTo exitHandler
On Error Resume Next
Set rngDV = Range("G:G")
On Error GoTo exitHandler
If rngDV Is Nothing Then GoTo exitHandler
If Intersect(Target, rngDV) Is Nothing Then
ElseIf Target.Column = 7 Then
Application.EnableEvents = False
newVal = Target.Value
Application.Undo
oldVal = Target.Value
Target.Value = newVal
If oldVal = "" Then
Else
If newVal = "" Then
Else
Target.Value = oldVal & ", " & newVal
End If
End If
End If
exitHandler:
Application.EnableEvents = True
End Sub

VBA Record date of row change in specific column

I'm trying to automatically update the "Updated" column of an excel spreadsheet when any cell of that specific row changes to today's date. I was able to do this by hard-coding where the "Updated" column header would be, however, it is now necessary to search for that column header as it may move.
The code I am trying to implement works but immediately gives me the error Automation error - The object invoked has disconnected from it's clients.
Any help would be appreciated. Here is the code I have currently:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
Dim f As Range
Set f = ActiveSheet.Range("A1:DD1").Find("Updated", lookat:=xlWhole)
' f.Row = Range(Target).Row
If Not f Is Nothing Then
Range(Split(f.Address, "$")(1) & Target.Row).Value = Now
Else
MsgBox "'Updated' header not found!"
End If
End If
End Sub
You got into an endless loop.
Try this:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
Dim f As Range
Set f = ActiveSheet.Range("A1:DD1").Find("Updated", lookat:=xlWhole)
' f.Row = Range(Target).Row
If f Is Nothing Then
MsgBox "'Updated' header not found!"
ElseIf Intersect(Target, f.EntireColumn) Is Nothing Then
Intersect(Target.EntireRow, f.EntireColumn).Value = Now
' Else
' MsgBox "We entered this function again because the row above updated the Updated column", vbInformation, "False alarm"
End If
End If
End Sub
To understand what happens,
Uncomment the else and MsgBox
Put a breakpoint on the MsgBox
When you hit it, press [ctrl]-L
In a case such as this, I run into far fewer problems when I simply loop through the available cells to find the column header. Using the .Find method also works, but is less "tunable" to my needs in a custom application.
Public Function FindColumn(header As String) As Long
Dim lastCol As Long
Dim headerCol As Long
Dim i As Long
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("VTO2 Labor")
lastCol = sh.Cells(1, sh.Columns.Count).End(xlToLeft).Column
headerCol = 0
For i = 1 To lastCol
If sh.Cells(1, i).Value = header Then
headerCol = i
End If
Next i
FindColumn = headerCol
End Function
It isn't clear on whether the Updated column header could be in row 1 or if it will always be in row 1, just not in the same location.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
On Error GoTo bm_SafeExit
'TURN OFF EVENTS IF YOU ARE GOING TO WRITE A VALUE INTO THE WORKSHEET!
Application.EnableEvents = False
Dim uCol As Long, f As Range
If Application.CountIf(Rows(1), "updated") Then
uCol = Application.Match("updated", Rows(1), 0)
For Each f In Intersect(Target, Range("A:DX"))
If f.Row > 1 Then _
Cells(f.Row, uCol) = Now
Next f
Else
MsgBox "'Updated' header not found!"
End If
End If
bm_SafeExit:
Application.EnableEvents = True
End Sub
That should survive multiple updates (e.g. when pasting values). The problem I see is that is the Updated column is being shifted around, presumably through inserting columns or the like, then the change routine is going to run.

Data validation select multiple items error message from text entered in non drop down list cells

I'm trying to produce a code whereby the user can multiple items from a drop down menu (separated by a comma in the original cell) and then remove them by selecting the same item again. This pertains to only one column, I in the workbook and the other columns are regular columns where the user will be entering in text. I'm running into a problem in that the code that I have (see below) works on Column I but when the user tries to enter any information into other cells in other columns on the same worksheet, Run time error 1004 Application defined or object defined error pops up. When I press debug, it highlights If Target.Validation.Type = 3 Then. I copied this code from Contextures Inc and experience the same problem when I click on an outside cell in their test spreadsheet.
Any help would be much appreciated!
Option Explicit
' Developed by Contextures Inc.
' www.contextures.com
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngDV As Range
Dim oldVal As String
Dim newVal As String
Dim strVal As String
Dim i As Long
Dim lCount As Long
Dim Ar As Variant
If Target.Count > 1 Then GoTo exitHandler
If Target.Validation.Type = 3 Then
Application.EnableEvents = False
newVal = Target.Value
Application.Undo
oldVal = Target.Value
Target.Value = newVal
If Target.Column = 9 Then
If oldVal = "" Then
'do nothing
Else
If newVal = "" Then
'do nothing
Else
On Error Resume Next
Ar = Split(oldVal, ", ")
strVal = ""
For i = LBound(Ar) To UBound(Ar)
Debug.Print strVal
Debug.Print CStr(Ar(i))
If newVal = CStr(Ar(i)) Then
'do not include this item
strVal = strVal
lCount = 1
Else
strVal = strVal & CStr(Ar(i)) & ", "
End If
Next i
If lCount > 0 Then
Target.Value = Left(strVal, Len(strVal) - 2)
Else
Target.Value = strVal & newVal
End If
End If
End If
End If
End If
exitHandler:
Application.EnableEvents = True
End Sub
Add these lines immediately before If Target.Count > 1 Then GoTo exitHandler:
Set Target = Application.Intersect(Target, Me.Columns(9))
If Target Is Nothing Then Exit Sub
This will restrict the range Target only to cells in Column 9
Then you can remove this test:
If Target.Column = 9 Then
End If