Excel Data Validation (Text Length) [closed] - excel

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
This will probably have to be something that is done in VBA, which is fine.
Some background information: I am wanting to use the text length option in Excel's Data validation. I am needing to limit to 60 characters or less. This is the easy part, however.
My question is, if a user has exceeded this 60-character threshold, I do not want my error alert to remain static and give a generic response saying "you must keep below 60 characters..." I want it to actually count the number of characters the user attempted to place in the cell, then on the Error Alert popup I would like it to be more specific, such as: You have exceeded the 60-character limit by ## characters. Please shorten the input and try again. Anyone know of a solution?

I wrote something similar to the code provided by K Davis in another answer. Beyond coding style, here are major functional differences.
        •  handles multiple target columns
        •  adds address to message
        •  selects the rogue cell after notification
        •  loops backwards so that in the event of multiple rogue inputs, the user ends up at the first
        •  declaration and assignment of variables are reserved to when they are actually required
        •  critical stop message box
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Union(Columns("B"), Columns("D"))) Is Nothing Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
Dim mssg As String, iLimit As Long, a As Long, t As Long, trgt As Range
iLimit = 60
Set trgt = Intersect(Target, Union(Columns("B"), Columns("D")))
'loop backwards through multiples so we end up at the first rogue entry
For a = trgt.Areas.Count To 1 Step -1
For t = trgt.Areas(a).Cells.Count To 1 Step -1
If Len(trgt.Areas(a).Cells(t).Value2) > iLimit Then
mssg = "You have exceeded the " & iLimit & "-character limit by " & _
Len(trgt.Areas(a).Cells(t).Value2) - iLimit & " characters in " & _
trgt.Areas(a).Cells(t).Address(0, 0) & ". Please shorten the input and try again."
MsgBox mssg, vbCritical + vbOKOnly, "Bad Input"
trgt.Areas(a).Cells(t).ClearContents: trgt.Areas(a).Cells(t).Select
End If
Next t
Next a
Set trgt = Nothing
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
You might want to split the sentences with one or two vbLF. To my eye, it makes the alert message more effective; particularly so with the cell address added.
I had thought about putting all of the rogue input cell addresses into a single message but that would preclude the specific overdrawn character count.
Sample text courtesy of Lorem Ipsum Generator

Thanks to a suggestion made by Jeepeed, I performed an internet search and stumbled upon this site. I have modified this person's code a bit to accomplish what I needed in my original question, and would like to share it in the event anyone else comes across my same issue.
Here is the code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Dim rCell As Range
Dim iChars As Integer
On Error GoTo ErrHandler
'Change these as desired
iChars = 60
Set rng = Me.Range("B:B")
If Not Intersect(Target, rng) Is Nothing Then
Application.EnableEvents = False
For Each rCell In Intersect(Target, rng)
If Len(rCell.value) > iChars Then
MsgBox "You have exceeded the 60-character" & _
" limit by " & Len(rCell.value) - iChars & _
" characters." & vbCrLf & "Please shorten" & _
" your input and try again.", vbRetryCancel
End If
Next
End If
ExitHandler:
Application.EnableEvents = True
Set rCell = Nothing
Set rng = Nothing
Exit Sub
ErrHandler:
MsgBox Err.Description
Resume ExitHandler
End Sub

I think that would be better with an Excel formula next to the cell:
= IF( LEN( B2 ) > 60, "You have exceeded the 60-character limit by "
& ( LEN( B2 ) - 60 ) & " characters. Please shorten your input and try again.", "" )

Related

VBA listbox updates

i have below VBA code to update long list(1000) of part userform listbox with constant changes to design.
i need help with below 2 issues i am facing with code,
1)somehow, it is only updating only 1st selected item under multiselect listbox. can you pl help to check what is the issue with it to get all selected items updated by command button?
also, there are number of duplicates that i want to updates as well. however, below code updates only one and not other duplicate. can you pl help to correct code so it can update duplicates as well?
Private Sub cmdaction_Click()
Dim t, t1 As String
Dim vrech As Range, lColumn As Range
Dim sh As Worksheet
Dim i As Long
Dim selItem As String
Set sh = ThisWorkbook.Sheets("part bump")
Set lColumn = sh.Range("P1:AZA1").Find(Val(txtchangenumber.Value), , xlValues, xlWhole)
'Set lcolumn1 = sh.Range("F4:F1000")
If UserForm3.txtchangedescrption.Value = "" Then
MsgBox "Please enter Change Description"
Exit Sub
End If
If UserForm3.txtchangenumber.Value = "" Then
MsgBox "Please enter Change Number"
Exit Sub
End If
If UserForm3.cmbaction.Value = "" Then
MsgBox "Please Select part Action"
Exit Sub
End If
If lColumn Is Nothing Then
MsgBox "Change number not found"
Exit Sub
End If
With UserForm3.lstDatabase
For i = 0 To UserForm3.lstDatabase.ListCount - 1
If UserForm3.lstDatabase.Selected(i) = True Then
Set vrech = sh.Range("H4:H250").Find(.Column(7, i), , xlValues, xlWhole)
If Not vrech Is Nothing Then
Select Case cmbaction.Value
Case "RP"
t = Chr(Asc(Mid(.List(i, 7), 2, 1)) + 1)
t1 = Mid(.List(i, 7), 1, 2) & t & Mid(.List(i, 7), 4, 1)
Intersect(vrech.EntireRow, lColumn.EntireColumn) = t1
MsgBox "Selected parts 'RP' Action completed"
Case "RV"
Intersect(vrech.EntireRow, lColumn.EntireColumn) = .List(i, 7)
MsgBox "Selected parts 'RV' Action completed"
Case "DP"
Intersect(vrech.EntireRow, lColumn.EntireColumn) = "Deleted"
vrech.EntireRow.Font.Strikethrough = True
MsgBox "Selected parts 'DP' Action completed"
End Select
End If
End If
Next i
End With
End Sub
Upon further investigation I found that your handling of the Selected property is correct. I have deleted my advice in this regard and apologize for my hasty comment.
I have also re-examined your code and regret, I can't find a reason why it shouldn't deal with all selected items. without access to your workbook i don't have the ability to test and can't help you further.
Your second complaint is caused by this line of code.
Set vrech = sh.Range("H4:H250").Find(.Column(7, i), , xlValues, xlWhole)
It will find the first instance and no others. If you want the search to be repeated a loop will be required that repeats the search. Look up "VBA Find & FindNext MSDN" and you will find code samples how to construct the loop.
Note that in Dim t, t1 As String only t1 is a string. t is defined as a variant by virtue of not having a specified data type. This doesn't appear to be your intention.
I also noted your unusual use of Application.Intersect. Intersect(vrech.EntireRow, lColumn.EntireColumn) should be the equivalent of the simpler Sh.Cells(vrech.Row, lColumn), and it's recommended to specify the Value property when assigning a value to it.

Find a value within a column, then change a value elsewhere on the same row

I am working on a project with an Excel "database" to keep track of production of items. There have been cases of people scanning an item more than once, which causes problems when it's time to send to the client.
I have an alert popup, to prevent people from scanning an item more than once unless dealing with a rework. If the item is present in the "database", there is a MsgBox with vbYesNo buttons. If people click "Yes", it's a rework. If people click "No", it's an error and they exit the sub.
I need a way to handle the rework and have it change the values of the cells in the same row as the original item.
Here's the code I have so far.
Private Sub gbatchd_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) 'Check DB for duplicate value
Set depo = dbgrids.Sheets("Deposition")
Set found = depo.Columns("A").Find(what:=valuetofind, LookIn:=xlValues, lookat:=xlWhole)
valuetofind = gbatchd.Text
FR = depo.Range("A" & Rows.Count).End(xlUp).Row
If KeyCode = 13 Then
For i = 1 To FR
If gbatch.Cells(i, 1).Value = valuetofind Then
MsgBox "This batch has already been deposited!" & vbCrLf & "Rework?", vbYesNo, "Rework?"
If answer = vbNo Then
Exit Sub
Else
depo.Cells(found.Row, 5).Value = "Rework"
End If
End If
Next
End If
End Sub
Setting aside any criticism of the solution's architecture, what your code is missing is the assignment to the answer variable:
answer = MsgBox("This batch has already been deposited!" & vbCrLf & "Rework?", vbYesNo, "Rework?")
Use Option Explicit and declare your variables with the appropriate types (check out this article).
A cleaned-up version of your code might look something like this:
Option Explicit 'At the very top of your module.
'... Other code ...
Private Sub gbatchd_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) 'Check DB for duplicate value
Dim wsDepo As Excel.Worksheet
Dim rngFound As Excel.Range
Dim sValueToFind As String
Dim answer As VbMsgBoxResult
If KeyCode = KeyCodeConstants.vbKeyReturn Then
'Barcode reader has sent the Enter (Return) key.
'Attempt to find the value.
sValueToFind = Trim(gbatchd.Text)
Set wsDepo = dbgrids.Worksheets("Deposition")
Set rngFound = wsDepo.Columns(1).Find(What:=sValueToFind, LookIn:=xlValues, LookAt:=xlWhole)
If Not rngFound Is Nothing Then
'Value was found. Ask whether it is a rework.
answer = MsgBox("This batch has already been deposited!" & vbCrLf & "Rework?", vbYesNo, "Rework?")
If answer = VbMsgBoxResult.vbYes Then
wsDepo.Cells(rngFound.Row, 5).Value = "Rework"
End If
End If
'Cleanup.
Set rngFound = Nothing
Set wsDepo = Nothing
End If
End Sub

Method 'Color' of object 'Font' failed

I'm getting the title error message in my Excel 2010 VBA code. I've looked at this question and this question which both look similar, but nether seems to address the issue.
My code parses through all the conditional formatting on the current worksheet and dumps it as text to another (newly created) worksheet - the ultimate goal is to load those same conditions to a nearly identical worksheet (thus I can't just copy the base worksheet).
The code is:
Public Sub DumpExistingRules()
'portions of the code from here: http://dailydoseofexcel.com/archives/2010/04/16/listing-format-conditions/
Const RuleSheetNameSuffix As String = "-Rules"
Dim TheWB As Workbook
Set TheWB = ActiveWorkbook
Dim SourceSheet As Worksheet
Set SourceSheet = TheWB.ActiveSheet
Dim RuleSheetName As String
RuleSheetName = SourceSheet.Name & RuleSheetNameSuffix
On Error Resume Next 'if the rule sheet doesn't exist it will error, we don't care, just move on
Application.DisplayAlerts = False
TheWB.Worksheets(RuleSheetName).Delete
Application.DisplayAlerts = True
On Error GoTo EH
Dim RuleSheet As Worksheet
Set RuleSheet = TheWB.Worksheets.Add
SourceSheet.Activate
RuleSheet.Name = RuleSheetName
RuleSheet.Range(RuleSheet.Cells(1, CellAddrCol), RuleSheet.Cells(1, OperatorCodeCol)).Value = Array("Cell Address", "Rule Type", "Type Code", "Applies To", "Stop", "Font.ColorRGB", "Formula1", "Formula2", _
"Interior.ColorIndexRGB", "Operator Type", "Operator Code")
Dim RuleRow As Long
RuleRow = 2
Dim RuleCount As Long
Dim RptCol As Long
Dim SrcCol As Long
Dim RetryCount As Long
Dim FCCell As Range
For SrcCol = 1 To 30
Set FCCell = SourceSheet.Cells(4, SrcCol)
For RuleCount = 1 To FCCell.FormatConditions.Count
RptCol = 1
Application.StatusBar = "Cell: " & FCCell.Address
PrintValue RuleSheet, RuleRow, CellAddrCol, FCCell.Address
PrintValue RuleSheet, RuleRow, RuleTypeCol, FCTypeFromIndex(FCCell.FormatConditions.Item(RuleCount).Type)
PrintValue RuleSheet, RuleRow, RuleCodeCol, FCCell.FormatConditions.Item(RuleCount).Type
PrintValue RuleSheet, RuleRow, AppliesToCol, FCCell.FormatConditions.Item(RuleCount).AppliesTo.Address
PrintValue RuleSheet, RuleRow, StopCol, FCCell.FormatConditions.Item(RuleCount).StopIfTrue
If FCCell.FormatConditions.Item(RuleCount).Type <> 8 Then
PrintValue RuleSheet, RuleRow, Formula1Col, "'" & Right(FCCell.FormatConditions.Item(RuleCount).Formula1, Len(FCCell.FormatConditions.Item(RuleCount).Formula1) - 1) 'remove the leading "=" sign
If FCCell.FormatConditions.Item(RuleCount).Type <> 2 And _
FCCell.FormatConditions.Item(RuleCount).Type <> 1 Then
PrintValue RuleSheet, RuleRow, Formula2Col, "'" & Right(FCCell.FormatConditions.Item(RuleCount).Formula2, Len(FCCell.FormatConditions.Item(RuleCount).Formula2) - 1) 'remove the leading "=" sign
End If
End If
RetryCount = 0
RetryColor:
PrintValue RuleSheet, RuleRow, FontColorCol, "'" & GetRGB(FCCell.FormatConditions(RuleCount).Font.Color)
PrintValue RuleSheet, RuleRow, IntColorIdxCol, "'" & GetRGB(FCCell.FormatConditions.Item(RuleCount).Interior.Color)
If FCCell.FormatConditions.Item(RuleCount).Type = 1 Then
PrintValue RuleSheet, RuleRow, OperatorTypeCol, OperatorType(FCCell.FormatConditions.Item(RuleCount).Operator)
PrintValue RuleSheet, RuleRow, OperatorCodeCol, FCCell.FormatConditions.Item(RuleCount).Operator
End If
RuleRow = RuleRow + 1
Next
Next
RuleSheet.Rows(1).AutoFilter = True
CleanExit:
If RuleRow = 2 Then
PrintValue RuleSheet, RuleRow, RptCol, "No Conditional Formatted cells were found on " & SourceSheet.Name
End If
On Error Resume Next
Set SourceSheet = Nothing
Set TheWB = Nothing
Application.StatusBar = ""
On Error GoTo 0
MsgBox "Done"
Exit Sub
EH:
If Err.Number = -2147417848 Then
MsgBox "Font.Color = " & FCCell.FormatConditions(RuleCount).Font.Color
If RetryCount < 5 Then
RetryCount = RetryCount + 1
Resume RetryColor
Else
MsgBox "RetryCount = " & RetryCount
Resume Next
End If
Else
MsgBox "Error Number: " & Err.Number & vbCrLf & _
" Description: " & Err.Description & vbCrLf & _
"Cell Address: " & FCCell.Address & vbCrLf
Resume Next
End If
End Sub
The line in question is the one immediately following the RetryColor: label. When that line of code is executed for a Unique Values conditional formatting rule (i.e. highlight duplicates), I get err.number = -2147417848' and err.description = "Method 'Color' of object 'Font' failed". The code drops to EH:, falls into the first IF statement, and displays the MsgBox without any problem.
Why is it that the statement FCCell.FormatConditions(RuleCount).Font.Color fails the first time, but executes perfectly the second time in the error handler? Once I've clicked the OK button on the MsgBox, execution resumes at the RetryColor: label, the statement executes correctly, and all is good.
To make sure this is clear, if I comment out the
MsgBox "Font.Color = " & FCCell.FormatConditions(RuleCount).Font.Color
line in EH:, the code will error 5 times without ever outputting the RGB code to my output worksheet, then continue on its way. If that line is in EH: (as shown above), I get the MsgBox and the .Font.Color will now be read in the main code and execution will continue as expected without error.
UPDATE: It seems that after letting this code sit for a week while I worked on something else, that it's now slightly more broken. In the error handler, I now get the titular error message popping, up. If I hit F5, it will execute and display the MsgBox with the color code.
So now, it will fail twice, then execute properly the 3rd time.
For completeness, here's the code for GetRGB:
Private Function GetRGB(ByVal ColorCode As Variant) As String
Dim R As Long
Dim G As Long
Dim B As Long
If IsNull(ColorCode) Then
GetRGB = "0,0,0"
Else
R = ColorCode Mod 256
G = ColorCode \ 256 Mod 256
B = ColorCode \ 65536 Mod 256
GetRGB = R & "," & G & "," & B
End If
End Function
I have to pass the parameter as a Variant because when the .Font.Color is set to Automatic in the color chooser, I get a NULL returned, thus the If statement in GetRGB.
Another Update: After letting this code sit for a few more weeks (it's to make my life easier, not an official project, therefore it's at the bottom of the priority list), it seems that it will generate the error on every call now, instead of just sometimes. However, the code will execute properly in the immediate window!
The yellow highlighted line is the one that generated the error, yet you can see the results in the immediate window.
Also (I realize this should really be another question), if anybody happens to quickly see any reason for the SourceSheet.Activate line, please let me know - I was getting random errors without it, so I put that in. Usually these errors are because of unqualified references working on the currently active sheet (which would be RuleSheet as soon as it's created), but I thought I had all my references qualified. If you see something I missed, please pipe up! Otherwise, I'll probably head over to CodeReview to have them take a look at what I missed once I get this working properly.
I think I've reduced this to a root cause.
I manually added 2 different types of FormatConditions in cell Sheet1.A1:
And here's my code, in the same workbook.
Sub foo()
Dim rng As Range
Set rng = Sheet1.Range("A1")
Dim fc As Object
On Error Resume Next
Sheet2.Activate
Set fc = rng.FormatConditions(1)
Debug.Print ActiveSheet.Name, TypeName(fc), fc.Type
Debug.Print , fc.Font.Color
Set fc = rng.FormatConditions(2)
Dim fnt As Font2
Debug.Print ActiveSheet.Name, TypeName(fc), fc.Type
Debug.Print , fc.Font.Color
Sheet1.Activate
Set fc = rng.FormatConditions(1)
Debug.Print ActiveSheet.Name, TypeName(fc), fc.Type
Debug.Print , fc.Font.Color
Set fc = rng.FormatConditions(2)
Debug.Print ActiveSheet.Name, TypeName(fc), fc.Type
Debug.Print , fc.Font.Color
End Sub
And here's the output:
Sheet2 FormatCondition 1
3243501
Sheet2 Top10 5
Sheet1 FormatCondition 1
3243501
Sheet1 Top10 5
13998939
So the FormatConditions.Item method will not always return a FormatCondition
I can't reproduce your Immediate Window behavior, so maybe you inadvertently activated the sheet?
If I remove the On Error Resume, and break at the error for the Top10.Font.Color call, and then query in the debug window, I get:
Run-time error '-2147417848 (80010108)':
Automation error
The object invoked has disconnected from its clients.
For which Google takes me to Error or Unexpected Behavior with Office Automation When You Use Early Binding in Visual Basic
Based on my results, when the FormatConditions.Item returns a Top10 (and maybe other types, including your UniqueValues type), it isn't possible to access the Font.Color property unless the range's sheet is active.
But it looks like you have it active? I wonder if you're changing the active sheet in PrintValue?
Regarding your second question:
I have always have had problems with setting cells that are not in an active sheet, the most probable cause for the problem in doing SourceSheet.Activate relies on the fact of the Set range later:
Set FCCell = SourceSheet.Cells(4, SrcCol)
I've found that, if the sheet is not active, it would fail within the cells() argument, I think the best approach for this is using Range before Cells.
This may be the case.
So for this example I would do something like:
With SourceSheet:Set FCCell = .Range(.Cells(4,SrcCol):End With

How to deal with a dash in an Excel VBA input variable?

I'm having some trouble with an Excel VBA macro and was hoping you could give me some advice on how to fix it. In the code below, when a user clicks a command button, an InputBox pops up and the user inputs a number in the form XXX-XXXXXX (e.g. 111-222222). Then, the macro takes the value from the column adjacent to button and uses the input variable to replace a certain part of the adjacent column's value. However, when I tried to run the macro and input a number such as 123-456789, nothing happens. I believe it has something to do with the dash that the user inputs, however I'm not sure how to fix it. Please help!
Sub CommandButtonTitleXXXdashXXXXXX_Click()
Application.ScreenUpdating = False
On Error Resume Next
Dim n As Integer
n = Worksheets("REVISIONS").Range("D3:D17").Cells.SpecialCells(xlCellTypeConstants).Count
If n = 15 Then
If MsgBox("Title revision box full. Add manually.", vbOKOnly, "Error") = vbOK Then
Exit Sub
End If
End If
Dim rs As Integer
rs = ActiveSheet.Shapes(Application.Caller).TopLeftCell.Row
Dim amount As String
Application.ScreenUpdating = True
amount = Application.InputBox("Enter case number:", "")
Application.ScreenUpdating = False
If amount = False Then
Exit Sub
Else
Dim newCell As String
newCell = Replace(Worksheets("TITLE").Range("A" & rs).Value, "XXX-XXXXXX", amount)
Worksheets("REVISIONS").Range("D17").End(xlUp).Offset(1, 0) = newCell
End If
End Sub
I would take your code to an extra step.
No need to declare amount as String. You can keep it as a Variant. Also like I mentioned in the comment above
Can your Case number be like #D1-1%#456? If not then you have an additional problem to handle ;)
See this example. I have commented the code so that you will not have a problem understanding it. Still if you do lemme know :) The other way would be to use REGEX to validate your Case ID. Let me know if you want that example as well.
Code
Sub Sample()
Dim amount As Variant
' 123-$456789 <~~ Invalid
' 123-4567890 <~~ Valid
' ABC-&456789 <~~ Invalid
' 456-3456789 <~~ Valid
amount = Application.InputBox("Enter case number:", "")
'~~> Check if user pressed cancel
If amount = False Then Exit Sub
'~~> Check if then Case ID is valid
If IsValidCaseNo(amount) Then
MsgBox amount
Else
MsgBox "Invalid case ID"
End If
End Sub
Function IsValidCaseNo(sAmount) As Boolean
Dim s As String
Dim i As Long, j As Long
s = sAmount
'
'~~> Initial basic checks
'
'~~> Check if the length is 11 characters
If Len(Trim(s)) <> 11 Then GoTo Whoa
'~~> Check if the string contains "-"
If InStr(1, s, "-") = 0 Then GoTo Whoa
'~~> Check if the 4th character is a "-"
If Mid(s, 4, 1) <> "-" Then GoTo Whoa
'~~> Loop through 1st 3 characters and check
'~~> If they are numbers
For i = 1 To 3
Select Case Asc(Mid(s, i, 1))
Case 48 To 57
Case Else: GoTo Whoa
End Select
Next
'~~> Loop through last 6 characters and check
'~~> If they are numbers
For i = 5 To 11
Select Case Asc(Mid(s, i, 1))
Case 48 To 57
Case Else: GoTo Whoa
End Select
IsValidCaseNo = True
Next
Whoa:
End Function
If you Dim amount as String, you can test it as a string:
Sub GetDash()
Dim amount As String
amount = Application.InputBox(Prompt:="Enter case number", Type:=2)
If amount = "False" Then
MsgBox "You cancelled"
End If
End Sub

Is it possible to increase the 256 character limit in excel validation drop down boxes?

I am creating the validation dynamically and have hit a 256 character limit. My validation looks something like this:
Level 1, Level 2, Level 3, Level 4.....
Is there any way to get around the character limit other then pointing at a range?
The validation is already being produced in VBA. Increasing the limit is the easiest way to avoid any impact on how the sheet currently works.
I'm pretty sure there is no way around the 256 character limit, Joel Spolsky explains why here: http://www.joelonsoftware.com/printerFriendly/articles/fog0000000319.html.
You could however use VBA to get close to replicating the functionality of the built in validation by coding the Worksheet_Change event. Here's a mock up to give you the idea. You will probably want to refactor it to cache the ValidValues, handle changes to ranges of cells, etc...
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ValidationRange As Excel.Range
Dim ValidValues(1 To 100) As String
Dim Index As Integer
Dim Valid As Boolean
Dim Msg As String
Dim WhatToDo As VbMsgBoxResult
'Initialise ValidationRange
Set ValidationRange = Sheet1.Range("A:A")
' Check if change is in a cell we need to validate
If Not Intersect(Target, ValidationRange) Is Nothing Then
' Populate ValidValues array
For Index = 1 To 100
ValidValues(Index) = "Level " & Index
Next
' do the validation, permit blank values
If IsEmpty(Target) Then
Valid = True
Else
Valid = False
For Index = 1 To 100
If Target.Value = ValidValues(Index) Then
' found match to valid value
Valid = True
Exit For
End If
Next
End If
If Not Valid Then
Target.Select
' tell user value isn't valid
Msg = _
"The value you entered is not valid" & vbCrLf & vbCrLf & _
"A user has restricted values that can be entered into this cell."
WhatToDo = MsgBox(Msg, vbRetryCancel + vbCritical, "Microsoft Excel")
Target.Value = ""
If WhatToDo = vbRetry Then
Application.SendKeys "{F2}"
End If
End If
End If
End Sub

Resources