I have a macro (below) that inserts a new row into an un-defined number of Named ranges using ParamArray, it works fine except for when I try to assign the macro with more than 5-6 arguments I get a message box that says "Formula Too Complex to Assign To Object" (see picture above)
(see assignment string below)
'InsertNewRow "ServiceCrewDay_EmployeeList", "SAP_SCD_InPool", "SAP_SCD_OutPool", "SAP_SCD_SecondaryIn", "SAP_SCD_SecondaryOut", "SAP_SCD_ORD","SAP_SCD_THF","SAP_SCD_LH", "SAP_SCD_LH"'
Macro:
Sub InsertNewRow(ParamArray args() As Variant)
Dim ans: ans = MsgBox("WARNING: " & vbNewLine _
& "Action Cannot be undone!" & vbNewLine & "Continue?", vbYesNo, "Warning!")
If ans = vbNo Then: Exit Sub
Call HaltOperations
Call ActiveSheet.Unprotect()
Call Sheets("SAP Timesheet").Unprotect()
On Error GoTo OnError_Exit
'Loop and Check All Named Ranges Exist Before Proceeding
For Each a In args
If RangeExists(a) = False Then
MsgBox ("Named Range: " & a & " Not Defined!" & vbNewLine & "Operation Cancelled")
Exit Sub
End If
Next a
Dim rng As Range
'ADD ROW TO EACH NAMED INPUT RANGE
For Each a In args
Set rng = Range(a)
With rng
.Rows(.Rows.count).EntireRow.Insert
.Rows(.Rows.count - 2).EntireRow.Copy
.Rows(.Rows.count - 1).EntireRow.PasteSpecial (xlPasteFormulasAndNumberFormats)
On Error Resume Next: .Rows(.Rows.count - 1).EntireRow.PasteSpecial (xlPasteFormats)
End With
Next a
On Error GoTo OnError_Exit
'ADJUST HEIRACHY NUMBERS ON FIRST INPUT RANGE (MANNING TAB)
Set rng = Range(args(0))
Dim col As Integer
col = rng.Column
Cells(rng.Row + rng.Rows.count - 2, col).Offset(0, -1).Value _
= Cells(rng.Row + rng.Rows.count - 3, col).Offset(0, -1).Value + 1
Cells(rng.Row + rng.Rows.count - 1, col).Offset(0, -1).Value _
= Cells(rng.Row + rng.Rows.count - 3, col).Offset(0, -1).Value + 2
Call ResumeOperations
Application.CutCopyMode = False
Call ActiveSheet.Protect()
Call Sheets("SAP Timesheet").Protect()
Exit Sub
OnError_Exit:
Call ResumeOperations
Application.CutCopyMode = False
Call ActiveSheet.Protect()
Call Sheets("SAP Timesheet").Protect()
End Sub
Private Function RangeExists(rng As Variant) As Boolean
Dim Test As Range
On Error Resume Next
Set Test = Range(rng)
RangeExists = Err.Number = 0
End Function
Private Sub HaltOperations()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
End Sub
Private Sub ResumeOperations()
ResumeOps:
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
End Sub
The Macro itself runs as expected it's just the assigning the named ranges that is causing the issue.
is there a better way to do this?
or is there a way to get around the Formula is too complex method?
and if there is will that need to be done on all end user pc's or just on mine and the settings will carry over?
What I have thought about doing was just taking in 2 Named ranges and then for the following ranges Just offsetting those by the Row Count of the previous range so if Range2 = Sheets().Range("A1:A10") then Range3 = Range2.Offset(Range2.Rows.Count,0) then the assingment input would only need to be Range1 as string, Range2 as string, NumberOfExtraRanges as integer the reason I need atleast two ranges is because every range after range 1 is on a different tab and is essentially a raw data version of all pay info hours etc. in the first tab which will be Range1_EmployeeList
which I will play around with while I wait for a response.
TIA
Not a Complete answer but I did find that inside the ParamArray I could just assign One Input Range using a , to seperate each defined range. I haven't tested the limitations doing it this way but it does seem to atleast let me use a few extra inputs.
Example (Not Working):
Note: Each Defined Range is a Separate Input
'InsertNewRow "ServiceCrewDay_EmployeeList", "SAP_SCD_InPool" ," SAP_SCD_OutPool","SAP_SCD_SecondaryIn", "SAP_SCD_SecondaryOut"'
Example (Working):
Note Each Defined Range is passed as 1 input
'InsertNewRow "ServiceCrewDay_EmployeeList", "SAP_SCD_InPool, SAP_SCD_OutPool,SAP_SCD_SecondaryIn,SAP_SCD_SecondaryOut"'
I am trying to implement multiple functions into one worksheet_change. I was able to integrate 3 functions before (all pertaining to hiding/unhiding rows), however, am having trouble adding a function that allows multiples selections within a dropdown.
I have tried to add the new multiple selection code to the previously existing code and it does not give me errors, however it wont run. In a perfect world, it would keep the hiding/unhiding functions, as well as allow for multiple selections in the identified rows.
Private Sub Worksheet_Change(ByVal Target As Range)
ActiveSheet.Activate
If Not Application.Intersect(Range("C10:AA10"), Range(Target.Address))
Is Nothing Then
Select Case Target.Value
Case Is = "Select One": Rows("14:58").EntireRow.Hidden = True
Rows("10").EntireRow.Hidden = False
Case Is = "1": Rows("17:58").EntireRow.Hidden = True
Rows("14:16").EntireRow.Hidden = False
Case Is = "2": Rows("20:58").EntireRow.Hidden = True
Rows("14:19").EntireRow.Hidden = False
Case Is = "3": Rows("23:58").EntireRow.Hidden = True
Rows("14:22").EntireRow.Hidden = False
Case Is = "4": Rows("26:58").EntireRow.Hidden = True
Rows("14:25").EntireRow.Hidden = False
Case Is = "5": Rows("29:58").EntireRow.Hidden = True
Rows("14:28").EntireRow.Hidden = False
Case Is = "6": Rows("32:58").EntireRow.Hidden = True
Rows("14:31").EntireRow.Hidden = False
Case Is = "7": Rows("35:58").EntireRow.Hidden = True
Rows("14:34").EntireRow.Hidden = False
Case Is = "8": Rows("38:58").EntireRow.Hidden = True
Rows("14:37").EntireRow.Hidden = False
Case Is = "9": Rows("41:58").EntireRow.Hidden = True
Rows("14:40").EntireRow.Hidden = False
Case Is = "10": Rows("44:58").EntireRow.Hidden = True
Rows("14:43").EntireRow.Hidden = False
Case Is = "11": Rows("47:58").EntireRow.Hidden = True
Rows("14:46").EntireRow.Hidden = False
Case Is = "12": Rows("50:58").EntireRow.Hidden = True
Rows("14:49").EntireRow.Hidden = False
Case Is = "13": Rows("30:58").EntireRow.Hidden = True
Rows("14:52").EntireRow.Hidden = False
Case Is = "14": Rows("56:58").EntireRow.Hidden = True
Rows("14:55").EntireRow.Hidden = False
Case Is = "15": Rows("14:58").EntireRow.Hidden = False
End Select
End If
If Not Intersect(Range("C66:AA66"), Target) Is Nothing Then
Select Case Target.Value
Case "GBP", "USD", "Yuan", "EUR", "LRD", "Select One"
Rows("67").Hidden = True
Case "Other"
Rows("67").Hidden = False
End Select
End If
If Not Intersect(Range("C11:AA11"), Target) Is Nothing Then
Select Case Target.Value
Case "$"
Rows("13").Hidden = True
Rows("12").Hidden = False
Case "%"
Rows("13").Hidden = False
Rows("12").Hidden = True
Case "Select One"
Rows("13").Hidden = True
Rows("12").Hidden = True
End Select
End If
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Row = "15",”18”,”21” Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & ", " & Newvalue
Else:
Target.Value = Oldvalue
End If
End If
End If
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
End Sub
I want this to be able to continue hiding/unhiding the given rows based upon selections, as well as allow for multi-selects from the drop downs in the rows outlined in the code. The code does not give me errors, but the multi-select does not run
I think I see what you're trying to do and I hope these remarks can help your code. So a few comments...
Always use Option Explicit. No matter what example code you find on the webz, using this habit will be a big help to you in the future.
It's a BIG help to use intermediate variables in your code that makes the code self-documenting. There is no penalty for assigning interim values and objects, so use this to your advantage.
Separate logic blocks into separate subroutines or functions. This makes your code "functionally isolated" -- meaning that each block of code has a specific focus and if you need to change it, you're only changing it in one location. It also makes your code easier to read without scrolling up and down to get a sense of the overall logic.
In the case of your Worksheet_Change event code, I can reduce the logic into a much easier to understand flow:
Option Explicit
Private Sub Worksheet_Change(ByVal target As Range)
Dim groupsRange As Range
Dim currencyRange As Range
Dim valuesRange As Range
Set groupsRange = ActiveSheet.Range("C10:AA10")
Set currencyRange = ActiveSheet.Range("C66:AA66")
Set valuesRange = ActiveSheet.Range("C11:AA11")
If Not Intersect(groupsRange, target) Is Nothing Then
ShowActiveGroups target
ElseIf Not Intersect(currencyRange, target) Is Nothing Then
ShowCurrency target
ElseIf Not Intersect(valuesRange, target) Is Nothing Then
ShowValues target
End If
If target.Count > 1 Then Exit Sub
If (target.Row = 15) Or (target.Row = 18) Or (target.Row = 21) Then
CheckMultiSelect target
End If
End Sub
Clearly, I may not be getting the "point" of your ranges (using "groups", "currency", "values") but you should use descriptive names that make it easier to understand WHAT and WHY the logic is working on certain sections.
The code for the Subs called in the Worksheet_Change event are placed into a separate module and all of them are tagged as Public. Each of them have similar logic and there are a few things working here.
In each of the logic blocks (i.e. in the Sub code in this case) you should go through the steps of establishing exactly which worksheet is being referenced. It's critical to always fully qualify your range references (see #5). The easiest way to do that (without very long, compound statements) is to use intermediate variables.
So in each of the "Show" routines called above I'm setting up a reference to the Worksheet of the target cell (the cell that caused the Worksheet_Change event).
Dim targetWS As Worksheet
Set targetWS = target.Parent
Try to define constants for seemingly "random" numbers or values that have no real meaning outside the context of your worksheet.
In your case, you are referencing many different rows and hiding/unhiding them. I have no idea why. But if you could "name" the rows in your code, it could make more sense. Here are some examples I used:
Const RED_GROUP_1 As String = "14:58"
Const RED_GROUP_2 As String = "10"
Const GREEN_GROUP_1 As String = "17:58"
Const GREEN_GROUP_2 As String = "14:16"
So the first three "Show" routines could look something like this:
Public Sub ShowActiveGroups(ByRef target As Range)
Dim targetWS As Worksheet
Set targetWS = target.Parent
Const RED_GROUP_1 As String = "14:58"
Const RED_GROUP_2 As String = "10"
Const GREEN_GROUP_1 As String = "17:58"
Const GREEN_GROUP_2 As String = "14:16"
With targetWS
Select Case target.Value
Case "Select One"
.Rows(RED_GROUP_1).EntireRow.Hidden = True
.Rows(RED_GROUP_2).EntireRow.Hidden = False
Case 1
.Rows(GREEN_GROUP_1).EntireRow.Hidden = True
.Rows(GREEN_GROUP_2).EntireRow.Hidden = False
Case 2
.Rows("20:58").EntireRow.Hidden = True
.Rows("14:19").EntireRow.Hidden = False
' ...
Case Else
'--- what should we do if it's not a valid value?
End Select
End With
End Sub
Public Sub ShowCurrency(ByRef target As Range)
Dim targetWS As Worksheet
Set targetWS = target.Parent
Const CURRENCY_LINE As String = "67"
With targetWS
Select Case target.Value
Case "GBP", "USD", "Yuan", "EUR", "LRD", "Select One"
.Rows(CURRENCY_LINE).EntireRow.Hidden = True
Case "Other"
.Rows(CURRENCY_LINE).EntireRow.Hidden = False
Case Else
'--- what should we do if it's not a valid value?
End Select
End With
End Sub
Public Sub ShowValues(ByRef target As Range)
Dim targetWS As Worksheet
Set targetWS = target.Parent
Const MONEY_LINE As String = "13"
Const PERCENT_LINE As String = "12"
With targetWS
Select Case target.Value
Case "$"
.Rows(MONEY_LINE).EntireRow.Hidden = True
.Rows(PERCENT_LINE).EntireRow.Hidden = False
Case "%"
.Rows(MONEY_LINE).EntireRow.Hidden = False
.Rows(PERCENT_LINE).EntireRow.Hidden = True
Case "Select One"
.Rows(MONEY_LINE).EntireRow.Hidden = True
.Rows(PERCENT_LINE).EntireRow.Hidden = True
Case Else
'--- what should we do if it's not a valid value?
End Select
End With
End Sub
Finally, I always had trouble with the data-validation/multi-select code that you found on the webz. So I'm tossing in the one I use that has a couple slight mods. This code goes in the regular code module as well.
Public Sub CheckMultiSelect(ByRef target As Range)
Dim targetWS As Worksheet
Set targetWS = target.Parent
On Error Resume Next
Dim dvCheck As Range
Set dvCheck = targetWS.Cells.SpecialCells(xlCellTypeAllValidation)
If dvCheck Is Nothing Then Exit Sub
Application.EnableEvents = False
'--- only allow multi-select if the cell has defined data validation
If Not Intersect(dvCheck, target) Is Nothing Then
Dim currentValue As String
Dim oldValue As String
currentValue = target.Value
Application.Undo
oldValue = target.Value
If oldValue = vbNullString Then
target.Value = currentValue
Else
If InStr(1, oldValue, currentValue) = 0 Then
target.Value = oldValue & "," & currentValue
Else
If currentValue = vbNullString Then
target.Value = vbNullString
Else
target.Value = oldValue
End If
End If
End If
End If
Application.EnableEvents = True
End Sub
Using the code above in both the worksheet module and a regular code module, I sucessfully was able to perform some of the operations in your original question.
The below code will not work because the Set isect line is too long and I cannot figure out how to make it a multiple line code. I have tried space (_) and enter.
If I make the line multiple lines starting with Set isect = Application.Intersect (Target, Range ()) it will only work on the last line of code.
The intent of the Excel sheet is to make a popup requiring data input if the selected cell has no as an answer. The required remarks would go into the cell to the right.
How do I split the Set isect line into multiple lines?
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Rows.Count > 1 Or Target.Columns.Count > 1 Then
Exit Sub
End If
Dim com As String
Dim comm1 As String
Set isect = Application.Intersect(Target, Range("C10:C14, C21:C47, F10:F14, F21:F47, I10:I14, I21:I47, L10:L14, L21:L47, O10:O14, O21:O47, r10:R14, r21:R47, U10:U14, U21:U47, X10:X14, X21:X47, AA10:AA14, AA21:AA47, AD10:AD14, AD21:AD47, AG10:AG14, AG21:AG47, AJ10:AJ14, AJ21:AJ47, AM10:AM14, AM21:AM47, AP10:AP14, AP21:AP47, AS10:AS14, AS21:AS47, AV10:AV14, AV21:AV47, AY10:AY14, AY21:AY47, BB10:BB14, BB21:BB47, BE10:BE14, BE21:BE47, BH10:BH14, BH21:BH47, BK10:BK14, BK21:BK47, BN10:BN14, BN21:BN47, BQ10:BQ14, BQ21:BQ47, BT10:BT14, BT21:BT47, BW10:BW14, BW21:BW47, BZ10:BZ14, BZ21:BZ47, CC10:CC14, CC21:CC47, CF10:CF14, CF21:CF47, CI10:CI14, CI21:CI47, CL10:CL14, CL21:CL47, CO10:CO14, CO21:CO47, CR10:CR14, CR21:CR47, CU10:CU14, CU21:CU47, CX10:CX14, CX21:CX47, DA10:DA14, DA21:DA47, DA10:DA14, DA21:DA47, DD10:DD14, DD21:DD47, DG10:DG14, DG21:DG47, DJ10:DJ14, DJ21:DJ47, DM10:DM14, DM21:DM47, DP10:DP14, DP21:DP47, DS10:DS14, DS21:DS47, DV10:DV14, DV21:DV47, DY10:DY14, DY21:DY47, EB10:EB14, EB21:EB47, EE10:EE14, EE21:
EE47 , EH10: EH14 , EH21: EH47 , EK10: EK14 , EK21: EK47 , EN10: EN14 , EN21: EN47 , EQ10: EQ14 , EQ21: EQ47 , ET10: ET14 , ET21: ET47 "))"
If isect Is Nothing Then
Else
If Target.Value = "No" Then
com = "Enter comment in " & Target.Offset(0, 1).Address(RowAbsolute:=False, columnabsolute:=False)
Do While comm1 = ""
comm1 = Application.InputBox(prompt:=com, Type:=2)
On Error GoTo myloop
If comm1 = False Then
comm1 = ""
End If
myloop:
On Error GoTo -1
Loop
Target.Offset(0, 1).Value = comm1
Else
Target.Offset(0, 1).Value = ""
End If
End If
End Sub
There are a few ways to skin this cat.
The easiest but least sustainable way to do what you're doing is to simply break up the line across rows.
Dim strRange As String
strRange = "C10:C14, C21:C47, F10:F14, F21:F47, I10:I14, I21:I47, L10:L14, L21:L47, O10:O14, O21:O47, r10:R14, r21:R47, U10:U14, U21:U47, X10:X14"
strRange = strRange & ", X21:X47, AA10:AA14, AA21:AA47, AD10:AD14, AD21:AD47, AG10:AG14, AG21:AG47, AJ10:AJ14, AJ21:AJ47, AM10:AM14, AM21:AM47, AP10:AP14"
strRange = strRange & ", AP21:AP47, AS10:AS14, AS21:AS47, AV10:AV14, AV21:AV47, AY10:AY14, AY21:AY47, BB10:BB14, BB21:BB47, BE10:BE14, BE21:BE47"
strRange= strRange & ... etc.
set isect = Application.Intersect(Target, Range(strRange))
... or you could create a named range within your workbook with all of those cells contained within it and then simply reference that in your code.
It keeps the maintenance of the range and the code separate, might not be for you though.
set isect = Application.Intersect(Target, Range("ValidateRange"))
Another way, looking at your cells, is to create an array and a loop (which could be enhanced to be even better) so that it's building the string up with an element of dynamicness.
Dim strRange As String, arrColumns(), i As Long, strCol As String
arrColumns = Array("C", "F", "I", "L", etc ...)
For i = 0 To UBound(arrColumns)
If i > 0 Then strRange = strRange & ","
strCol = arrColumns(i)
strRange = strRange & strCol & "10:" & strCol & "14," & strCol & "21:" & strCol & "47"
Next
Set isect = Application.Intersect(Target, Range(strRange))
Like I said, there are a few ways to do what you're wanting to do. If you're not wanting anything too complicated then go for the first solution.
I hope it helps.
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
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