Use regex to enforce cell validation? - regex

Using Excel 2010. I want to only allow values in a cell that fit a given regex pattern. So I created a UDF module as follows:
Public re as RegExp
Public Function isValidRegex(rng As Range, pattern As String) As Boolean
If re Is Nothing Then
Set re = New RegExp
End If
re.pattern = pattern
isValidRegex = re.Test(rng.value)
End Function
I created a named range called THIS_CELL, so that the current cell can be passed to isValidRegex(), as follows:
=INDIRECT(ADDRESS(ROW(),COLUMN()))
I set a custom validation for the cell, using this formula:
=isValidRegex(THIS_CELL,"(my|regex)patt[ern]")
This generated the following error:
A named range you specified cannot be found.
According to this article, UDFs cannot be used in Custom validation formulas. The solution suggested in the article (putting the formula in another cell, making that cell into a named range, and referencing that cell in the Custom formula) won't work, because I need to be able to pass THIS_CELL as an argument to the function.
I also tried creating a named range called isValidRegexPattern, defining it as =isValidRegex(THIS_CELL,"(my|regex)patt[ern]"), and setting the Custom formula to =isValidRegexPattern, but this didn't work either; putting a breakpoint in isValidRegex() showed that the function wasn't even being called.
So, how can I use a UDF for cell validation?

You can use a static variable with the Worksheet_Change event to keep a snapshot of the prior values
The code below tracks the values in A1:A10 and uses a Regexp like yours to reject any non-numeric entries
The example below tries top copy and paste B1:B10 over A1:A10, only A6and A8 are allowed as they are numeric
to set the range initially change a cell outside the range of interest to trigger If IsEmpty(X) Then X = [a1:a10].Value2
change event
Private Sub Worksheet_Change(ByVal Target As Range)
Static X As Variant
Dim rng2 As Range
Dim rng3 As Range
If IsEmpty(X) Then X = [a1:a10].Value2
Set rng2 = Intersect([a1:a10], Target)
If rng2 Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each rng3 In rng2
If Not isValidRegex(rng3, "\d+") Then rng3.Value = X(rng3.Row, 1)
Next
Application.EnableEvents = True
X = [a1:a10].Value2
End Sub
regexp
Function isValidRegex(rng As Range, pattern As String) As Boolean
Dim re As Object
Set re = CreateObject("vbscript.regexp")
re.pattern = pattern
isValidRegex = re.Test(rng.Value)
End Function

You seem to be reluctant to move over to a WorksheetChange event macro because you believe it does not 'capture the pre-change state of the cell'. That may be correct in the strictest definition but that doesn't mean you cannot capture the changed state, undo the change, determine whether the change is valid and only re-apply the change if it meets criteria.
I'm not going to produce a full regex validating function. This simply tests if the number typed into column E is less than zero or blank. If not then the cell reverts to its prechange state.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Count > 1 Then Exit Sub
If Not Intersect(Target, Columns(5)) Is Nothing Then
If Not IsEmpty(Target) Then
On Error GoTo Safe_Exit
Application.EnableEvents = False
Dim vNEW As Variant
vNEW = Target.Value
Application.Undo
If bIs_It_Valid(vNEW) Then
Target = vNEW
Else
' put stuff like idiot warnings here
End If
End If
End If
Safe_Exit:
Application.EnableEvents = True
End Sub
Private Function bIs_It_Valid(val As Variant) As Boolean
If IsNumeric(val) Then _
bIs_It_Valid = CBool(val < 0)
Debug.Print bIs_It_Valid
End Function
That Worksheet_Change could probably be adjusted to work on a range of cells if pasting a number of values is important.

Here's how I accomplished this without using the Worksheet_Change event
Define a Public REGEX Function in a new Module
'Public REGEX Formula
Public Function REGEX(pattern As String, cel As Range) As Boolean
Dim re As New RegExp
re.pattern = pattern
REGEX = re.Test(cel.Value)
End Function
I added this Sub to a module I named Validations. This Sub requires not only the range to validate and the regular expression pattern, but also another range to apply the REGEX formula to. The actual validation applied actually only checks that separate cell for a True or False value. This is a simplified version that assumes the validationColumn is an entire column.
'Validations Module
Sub regexValidation(cells As Range, pattern As String, validationColumn As Range, defaultValue As String)
Dim cel As Range, regexFormula As String, validationCell As Range
cells.Value = defaultValue
'Need to match true on default value or validation will always fail
pattern = "(" & defaultValue & ")|(" & pattern & ")"
For Each cel In cells
regexFormula = "=REGEX(""" & pattern & """," & cel.address & ")"
Set validationCell = validationColumn.cells(cel.Row, 1)
validationCell.Formula = regexFormula
cel.Validation.Delete
cel.Validation.Add xlValidateCustom, Formula1:="=" & Trim(validationCell.address)
Next cel
End Sub
This is how I'm calling it. In my case, this is a UserForm with a TextBox called regexPattern that contains the regular expression to apply.
'Form
Private Sub applyRegexValidation(cells As Range)
Validations.regexValidation cells, regexPattern.text, validationColumn:=cells.Parent.Range("AA:AA"), defaultValue:="Required Custom"
End Sub

Related

How to Use RegEx for Sheet Names in Excel?

I'm trying to duplicate a sheet. The user inputs the sheet name with an Input Box.
I'm trying to figure out how to interrupt the process if the user inputs a sheet name that already exists.
My plan is to use RegEx to match the string against all of the sheets in the workbook, and determine if the inputted name is already in use.
Here is my current code:
Dim NewSheetName As String
SheetInput: NewSheetName = InputBox("Insert the new sheet name.")
'Ends procedure if user does not input anything.
If NewSheetName = "" Then
Exit Sub
End If
'Ends procedure if the sheet name is already in use.
Dim RegEx As Object
Set RegEx = CreateObject("VBScript.RegExp")
With RegEx
.Pattern = NewSheetName
End With
For i = 1 To Sheets.Count
If RegEx.Test(Sheet(i).Name) = False Then
MsgBox ("This name is already used on a different sheet. Please use a different name.")
GoTo SheetInput
End If
Next i
I get the message
"Compile Error: Sub or Function not defined"
with Sheet in Sheet(i).Name highlighted. I assumed the name of the sheet would be inputted into the RegEx function as a string, but this doesn't seem to be happening.
There are many ways to check if a sheet exists. Here is one. I am sure there may be better ones.
Public Function worksheetExists(ByVal wb As Workbook, ByVal sheetNameStr As String) As Boolean
On Error Resume Next
worksheetExists = (wb.Worksheets(sheetNameStr).Name <> "")
On Error GoTo 0
End Function
Here are a couple of ways to call this function:
If worksheetExists(ThisWorkbook, "sheetNameOne") then ...
If Not worksheetExists(ThisWorkbook, "sheetNameOne") then ...
Adding a call to a couple of user defined functions that are reusable, and restructuring your code to use a Do Loop instead of GoTo see the following:
Public Sub AddNewWrkSht()
Dim SheetName As String
SheetName = GetValidSheetName()
Dim NewWrkSht As Worksheet
Set NewWrkSht = ThisWorkbook.Worksheets.Add
NewWrkSht.Name = SheetName
End Sub
Private Function GetValidSheetName() As String
Dim NewSheetName As String
'keep asking the user for a valid sheet name
'until it is valid
Do
NewSheetName = InputBox("Insert the new sheet name.")
Loop Until IsValidSheetName(NewSheetName)
GetValidSheetName = NewSheetName
End Function
Private Function IsValidSheetName(ByVal SheetName As String) As Boolean
If IsValueEmpty(SheetName) Then
MsgBox "You must Provide a sheet Name."
Exit Function
End If
If WorkSheetExists(SheetName) Then
MsgBox "This name is already used on a different sheet. " & _
"Please choose a different name."
Exit Function
End If
'if we make it here without exiting, then the sheet name is valid
IsValidSheetName = True
End Function
Private Function WorkSheetExists(ByVal SheetName As String) As Boolean
On Error Resume Next
WorkSheetExists = Not ActiveWorkbook.Worksheets(SheetName) Is Nothing
End Function
Private Function IsValueEmpty(ByVal varValue As Variant) As Boolean
IsValueEmpty = (Len(RemoveAllWhiteSpace(varValue)) = 0)
End Function
Private Function RemoveAllWhiteSpace(ByRef varStringIn As Variant, _
Optional ByRef RegExpIn As Object) As String
'Create if not instantiated
If RegExpIn Is Nothing Then Set RegExpIn = CreateObject("VBScript.RegExp")
With RegExpIn
.Pattern = "\s"
.MultiLine = True
.Global = True
RemoveAllWhiteSpace = CStr(.Replace(varStringIn, vbNullString))
End With
End Function

Why does this regular expression test give different results for what should be the same body text?

Here's the pertinent code, which is giving different results on the regular expression test for the message body depending on whether I launch it using TestLaunchURL or the message is passed to it by Outlook when an incoming message arrives:
Public Sub OpenLinksMessage(olMail As Outlook.MailItem)
Dim Reg1 As RegExp
Dim AllMatches As MatchCollection
Dim M As Match
Dim strURL As String
Dim RetCode As Long
Set Reg1 = New RegExp
With Reg1
.Pattern = "(https?[:]//([0-9a-z=\?:/\.&-^!#$;_])*)"
.Global = True
.IgnoreCase = True
End With
PlayTheSound "Speech On.wav"
RetCode = Reg1.Test(olMail.Body)
MsgBox "The RetCode from Reg1.Test(olMail.Body) equals" + Str(RetCode)
' If the regular expression test for URLs in the message body finds one or more
If RetCode Then
PlayTheSound "chimes.wav"
' Use the RegEx to return all instances that match it to the AllMatches group
Set AllMatches = Reg1.Execute(olMail.Body)
For Each M In AllMatches
strURL = M.SubMatches(0)
' Don't activate any URLs that are for unsubscribing; skip them
If InStr(1, strURL, "unsubscribe") Then GoTo NextURL
' If the URL ends with a > from being enclosed in darts, strip that > off
If Right(strURL, 1) = ">" Then strURL = Left(strURL, Len(strURL) - 1)
' The URL to activate to accept must contain both of the substrings in the IF statement
PlayTheSound "tada.wav"
If InStr(1, strURL, ".com") Then
PlayTheSound "TrainWhistle.wav"
' Activate that link to accept the job
RetCode = ShellExecute(0, "Open", strURL)
Set Reg1 = Nothing
Exit Sub
End If
NextURL:
Next
End If
Set Reg1 = Nothing
End Sub
Private Sub TestLaunchURL()
Dim currItem As MailItem
Set currItem = ActiveExplorer.Selection(1)
OpenLinksMessage currItem
End Sub
The test IF Reg1.Test(olMail.Body) always returns a 0 when invoked from an Outlook rule on an incoming message and always returns a -1 when I use the debugger to trigger it for that same message from my inbox.
The code is acting almost as though it has a null message body when it is triggered by an Outlook rule versus having the message body when kicked off by me from exactly the same message once it's in my inbox.
I am completely flummoxed, as I can't understand how one and the same message, with one and the same body, can give 2 different results depending on who hands the message to the subroutine.
Additional Debugging Information:
Since the issue appears to surround the value of the Body of the message, I added the following code, that also examines the HTMLBody as well:
If IsNull(olMail.Body) Then
MsgBox "The message body is null!!"
Else
MsgBox "BODY: " + "|" + olMail.Body + "|"
End If
If IsNull(olMail.HTMLBody) Then
MsgBox "The message HTMLbody is null!!"
Else
MsgBox "BODY: " + "|" + olMail.HTMLBody + "|"
End If
When the script is triggered by the Outlook rule on a message with the content, and only the content, "http://britishtoolworks.com", when it arrives these are the two message boxes:
[I am being forbidden to post images for some reason. These show absolutely nothing between the two pipe characters for BODY and some text, but nothing with the URL in it, for the HTMLBody]
while these are the message boxes if I trigger the script via TestLaunchURL after that very same message is sitting in my inbox:
[Shows the actual expected content. I am forbidden from posting more images.]
If anyone can explain this discrepancy, please do.
Here is the code that finally works. It's clear that the .Body member of olMail is not available until some sort of behind the scenes processing has had time to occur and if you don't wait long enough it won't be there when you go to test using it. Focus on the Public Sub OpenLinksMessage which is where the problem had been occurring.
The major (and only) change that allowed the expected processing of olMail.Body to take place, apparently, was the addition of the line of code: Set InspectMail = olMail.GetInspector.CurrentItem. The time it takes for this set statement to run allows the .Body to become available on the olMail parameter that's passed in by the Outlook rule. What's interesting is that if you immediately display InspectMail.Body after the set statement it shows as empty, just like olMail.Body used to.
Option Explicit
Private Declare Function ShellExecute _
Lib "shell32.dll" Alias "ShellExecuteA" ( _
ByVal hWnd As Long, _
ByVal Operation As String, _
ByVal Filename As String, _
Optional ByVal Parameters As String, _
Optional ByVal Directory As String, _
Optional ByVal WindowStyle As Long = vbMinimizedFocus _
) As Long
Public Sub OpenLinksMessage(olMail As Outlook.MailItem)
Dim InspectMail As Outlook.MailItem
Dim Reg1 As RegExp
Dim AllMatches As MatchCollection
Dim M As Match
Dim strURL As String
Dim SnaggedBody As String
Dim RetCode As Long
' The purpose of the following Set statement is strictly to "burn time" so that the .Body member of
' olMail is available by the time it is needed below. Without this statement the .Body is consistently
' showing up as empty. What's interesting is if you use MsgBox to display InspectMail.Body immediately after
' this Set statement it shows as empty.
Set InspectMail = olMail.GetInspector.CurrentItem
Set Reg1 = New RegExp
With Reg1
.Pattern = "(https?[:]//([0-9a-z=\?:/\.&-^!#$;_])*)"
.Global = True
.IgnoreCase = True
End With
RetCode = Reg1.Test(olMail.Body)
' If the regular expression test for URLs in the message body finds one or more
If RetCode Then
' Use the RegEx to return all instances that match it to the AllMatches group
Set AllMatches = Reg1.Execute(olMail.Body)
For Each M In AllMatches
strURL = M.SubMatches(0)
' Don't activate any URLs that are for unsubscribing; skip them
If InStr(1, strURL, "unsubscribe") Then GoTo NextURL
' If the URL ends with a > from being enclosed in darts, strip that > off
If Right(strURL, 1) = ">" Then strURL = Left(strURL, Len(strURL) - 1)
' The URL to activate to accept must contain both of the substrings in the IF statement
If InStr(1, strURL, ".com") Then
' Activate that link to accept the job
RetCode = ShellExecute(0, "Open", strURL)
Set InspectMail = Nothing
Set Reg1 = Nothing
Set AllMatches = Nothing
Set M = Nothing
Exit Sub
End If
NextURL:
Next
End If
Set InspectMail = Nothing
Set Reg1 = Nothing
Set AllMatches = Nothing
Set M = Nothing
End Sub
Special thanks to niton for his patience and assistance on other questions that formed the basis for this one. He led me to the solution.
Addendum: Another individual assisting me elsewhere brought up something that deserves noting here, as I think she's got it right. I am using Gmail via IMAP access to download my messages. What appears to be happening is that once the header information is populated into the MailItem object, the Outlook Rule is immediately being triggered. The rest of the members of that object, including .Body, appear to be being populated asynchronously behind the scenes. The speed of processing in your script versus the speed of population processing can lead to situations where the script is triggered with the header information and gets to the point where it accesses the .Body before it's been populated by Outlook itself. What's interesting is when this occurred, and that was most of the time until this solution was found, .Body was not considered to be NULL. The IsNull test never passed, but the content when printed was nothing, as in absolutely nothing between the two pipe characters I used as delimiters. What is "nothing that takes up any characters" but that also is not NULL?
Clearly the whole MailItem passed would not pass the "Is Nothing" test, and I would not think to test an individual member of an object with "Is Nothing."
For myself, I consider this to be buggy. Before a MailItem object is ever handed off for script processing it would be the logical presumption that all Members of that object that can be prepopulated will be prepopulated by Outlook before the handoff. It just doesn't appear to be happening that way, and this is under Outlook 2010 on my machine and Outlook 2016 on another. If you get a member that has not yet been populated it should always have the NULL value, as that should be what everything is initialized to prior to the population process taking place.

VBA: Filtering by multiple criteria (more than 2) using wildcards [duplicate]

Right now I am doing coding to set a filter for a data chart. Basically, I don't know how to post the data sheet up here so just try to type them ):
(starting from the left is column A)
Name * BDevice * Quantity * Sale* Owner
Basically I need to filter out for 2 column:
-The BDevice with any word contain "M1454" or "M1467" or "M1879" (It means that M1454A or M1467TR would still fit in)
-The Owner with PROD or RISK
Here is the code I wrote:
Sub AutoFilter()
ActiveWorkbook.ActiveSheet..Range(B:B).Select
Selection.Autofilter Field:=1 Criteria1:=Array( _
"*M1454*", "*M1467*", "*M1879*"), Operator:=xlFilterValues
Selection.AutoFilter Field:=4 Criteria1:="=PROD" _
, Operator:=xlOr, Criteria2:="=RISK"
End Sub
When I run the code, the machine returns error 1004 and the part which seems to be wrong is the Filter part 2 ( I am not sure about the use of Field, so I can not say it for sure)
Edit; Santosh: When I try your code, the machine gets error 9 subscript out of range. The error came from the with statement. (since the data table has A to AS column so I just change to A:AS)
While there is a maximum of two direct wildcards per field in the AutoFilter method, pattern matching can be used to create an array that replaces the wildcards with the Operator:=xlFilterValues option. A Select Case statement helps the wildcard matching.
The second field is a simple Criteria1 and Criteria2 direct match with a Operator:=xlOr joining the two criteria.
Sub multiWildcardFilter()
Dim a As Long, aARRs As Variant, dVALs As Object
Set dVALs = CreateObject("Scripting.Dictionary")
dVALs.CompareMode = vbTextCompare
With Worksheets("Sheet1")
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
'build a dictionary so the keys can be used as the array filter
aARRs = .Columns(2).Cells.Value2
For a = LBound(aARRs, 1) + 1 To UBound(aARRs, 1)
Select Case True
Case aARRs(a, 1) Like "MK1454*"
dVALs.Add Key:=aARRs(a, 1), Item:=aARRs(a, 1)
Case aARRs(a, 1) Like "MK1467*"
dVALs.Add Key:=aARRs(a, 1), Item:=aARRs(a, 1)
Case aARRs(a, 1) Like "MK1879*"
dVALs.Add Key:=aARRs(a, 1), Item:=aARRs(a, 1)
Case Else
'no match. do nothing
End Select
Next a
'filter on column B if dictionary keys exist
If CBool(dVALs.Count) Then _
.AutoFilter Field:=2, Criteria1:=dVALs.keys, _
Operator:=xlFilterValues, VisibleDropDown:=False
'filter on column E
.AutoFilter Field:=5, Criteria1:="PROD", Operator:=xlOr, _
Criteria2:="RISK", VisibleDropDown:=False
'data is filtered on MK1454*, MK1467* or MK1879* (column B)
'column E is either PROD or RISK
'Perform work on filtered data here
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
dVALs.RemoveAll: Set dVALs = Nothing
End Sub
If exclusions¹ are to be added to the filtering, their logic should be placed at the top of the Select.. End Select statement in order that they are not added through a false positive to other matching criteria.
                                Before applying AutoFilter Method
                                After applying AutoFilter w/ multiple wildcards
¹ See Can Advanced Filter criteria be in the VBA rather than a range? and Can AutoFilter take both inclusive and non-inclusive wildcards from Dictionary keys? for more on adding exclusions to the dictionary's filter set.
For using partial strings to exclude rows and include blanks you should use
'From Jeeped's code
Dim dVals As Scripting.Dictionary
Set dVals = CreateObject("Scripting.Dictionary")
dVals.CompareMode = vbTextCompare
Dim col3() As Variant
Dim col3init As Integer
'Swallow row3 into an array; start from 1 so it corresponds to row
For col3init = 1 to Sheets("Sheet1").UsedRange.Rows.count
col3(col3init) = Sheets("Sheet1").Range(Cells(col3init,3),Cells(col3init,3)).Value
Next col3init
Dim excludeArray() As Variant
'Partial strings in below array will be checked against rows
excludeArray = Array("MK1", "MK2", "MK3")
Dim col3check As Integer
Dim excludecheck as Integer
Dim violations As Integer
For col3check = 1 to UBound(col3)
For excludecheck = 0 to UBound(excludeArray)
If Instr(1,col3(col3check),excludeArray(excludecheck)) <> 0 Then
violations = violations + 1
'Sometimes the partial string you're filtering out for may appear more than once.
End If
Next col3check
If violations = 0 and Not dVals.Exists(col3(col3check)) Then
dVals.Add Key:=col3(col3check), Item:=col3(col3check) 'adds keys for items where the partial strings in excludeArray do NOT appear
ElseIf col3(col3check) = "" Then
dVals.Item(Chr(61)) = Chr(61) 'blanks
End If
violations = 0
Next col3check
The dVals.Item(Chr(61)) = Chr(61) idea came from Jeeped's other answer here
Multiple Filter Criteria for blanks and numbers using wildcard on same field just doesn't work
Try below code :
max 2 wildcard expression for Criteria1 works. Refer this link
Sub AutoFilter()
With ThisWorkbook.Sheets("sheet1").Range("A:E")
.AutoFilter Field:=2, Criteria1:=Array("*M1454*", "*M1467*"), Operator:=xlFilterValues
.AutoFilter Field:=5, Criteria1:="=PROD", Operator:=xlOr, Criteria2:="=RISK"
End With
End Sub

How to validate data insertions and restrict them in Excel cells

I have an Asp.Net web application to manage certain tables in the database. I'm using Grid to insert, update the Database. In addition to this, the requirement is that, user should be able to insert into database from Excel(by uploading the Excel, sort of like Import from Excel into Database).
So, I'm reusing the code for insertions(which i used for Insert in Grid) for each row in the Excel.
And I have Regular expression validators for certain fieldsin Grid in Asp.Net as follows:
Id: can be combination of numbers,alphabets. Regex is:"^[a-zA-Z0-9_]{1,50}$"
Formula: can have arithmetic operators and dot. Regex is: "^[ A-Za-z0-9%._(/*+)-]*$"
Sort Order: must be nuber with some max size Regex is: "^[0-9]{1,5}$"
Weight: real number with max size Regex is : "^[0-9]+(?:\.\d{1,2})?$"
Domain UserName: username with domain name Regex is: "^[a-zA-Z\\._]{1,200}$"
I wanted to have this validators in the Excel cells too. I've searched if Excel allows Regular expressions and found that it should be done through vba or any third party tool. I don't know Vb.net and neither want to use any external tool.
And i don't know much about Excel too. Is there any way to do the validations. If so, will there be some formats for setting formula for regex.
Can anyone suggest me how to do this. Thanks In Advance.
You can use the Regex engine that comes with VBScript:
Dim User_ID As String
User_ID = InputBox("Enter User ID:")
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "^[\w]{1,50}$"
If .Test(User_ID) Then '// Check pattern matches User_ID string
Range("B" & Rows.Count).End(xlUp).Offset(1, 0).Value = User_ID
Else
MsgBox("Invalid ID, please try again!")
End If
End With
I got the answer. I've wrote worksheet_Change event with if else
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Target.Row = 1 Then Exit Sub '// Only look at header row
Application.EnableEvents = False '// Disable events, prevent infinite loop.
If Cells(1, Target.Column).Value = "Attribute_Id" Then
Target.Value = AttributeId(Target.Value)
ElseIf Cells(1, Target.Column).Value = "Attribute_Name" Then
Target.Value = AttributeName(Target.Value)
End If
Application.EnableEvents = True '// Turn Events back on
End Sub
And these are the functions:
Function AttributeId(Attribute_Id As String) As String
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "^[a-zA-Z0-9_]{1,50}$"
.IgnoreCase = True
If Not .Test(Attribute_Id) Then
MsgBox ("Invalid Attribute ID, please try again!")
Exit Function
End If
End With
AttributeId = Attribute_Id
End Function
And
Function AttributeName(Attribute_Name As String) As String
If Attribute_Name = "" Then MsgBox ("Attribute Name is a Mandatory field!")
AttributeName = Attribute_Name
End Function
No need to bind the functions to the cells.
-- Thank you #S O for the help..

Dynamic Depending Lists in Separated WorkSheets in VBA (2)

I'm working with 7 dynamic dependent lists, and I thought the best way to automate the process and avoid to arrange anything in a future if I modify the lists was a VBA code.
The VBA code that I started to work on it is posted on: Dynamic Depending Lists in Separated WorkSheets in VBA
That code is just for the 2 first lists.
That's the main table that I have. I want pick lists for the first row only for the yellow columns:
That's the table that I have the lists (they must be dynamic):
The relations between my lists are:
Responsible list and Site list are related with Project list.
The other lists are related with the site list.
Okay. I've got what you are looking for. I solved this issue a few months back in another project. Basically, indirect is no good here because it doesn't work on dynamic named ranges, because they don't produce an actual result, just a formula reference.
First, set up your named ranges on a sheet like so. It's very important that the named ranges be named in the manner I described, as this will feed the code into making your dynamic lists. Also, note, I only wrote out SamplePoints for X1 and T2. If you select other options, the code won't work until you add those named ranges in.
Then assuming input sheet is set up like below:
Place this code in the worksheet change event of your input sheet. What it does is take the value selected in one cell and then appends the appropriate column name to feed that list. So, if Project A is selected and you want to pick a responsible party for project A, it sets the validation in Range("B(whatever row you are on)" to be A_Responsible, thus giving you that list.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim wks As Worksheet
Dim strName As String, strFormula
Dim rng As Range
Set wks = ActiveSheet
With wks
If Target.Row = 1 Then Exit Sub
Select Case Target.Column
Case Is = .Rows(1).Find("Project", lookat:=xlWhole).Column
Set rng = Target.Offset(, 1)
strName = Target.Value
strFormula = "=" & Replace(strName, " ", "_") & "_Responsible"
AddValidation rng, 1, strFormula
'add any more cells that would need validation based on project selection here.
Case Is = .Rows(1).Find("Responsible", lookat:=xlWhole).Column
Set rng = Target.Offset(, 1)
strName = Target.Value
strFormula = "=" & Replace(strName, " ", "_") & "_SamplePoint"
AddValidation rng, 1, strFormula
'add any more cells that would need validation based on responsible selection here.
'Case Is = add any more dependenices here ... and continue with cases for each one
End Select
End With
You will also need this function in a standard module somewhere in your workbook.
Function AddValidation(ByVal rng As Range, ByVal iOperator As Integer, _
ByVal sFormula1 As String, Optional iXlDVType As Integer = 3, _
Optional iAlertStyle As Integer = 1, Optional sFormula2 As String, _
Optional bIgnoreBlank As Boolean = True, Optional bInCellDropDown As Boolean = True, _
Optional sInputTitle As String, Optional sErrorTitle As String, _
Optional sInputMessage As String, Optional sErrorMessage As String, _
Optional bShowInput As Boolean = True, Optional bShowError As Boolean = True)
'==============================================
'Enumaration for ease of use
'XlDVType
'Name Value Description
'xlValidateCustom 7 Data is validated using an arbitrary formula.
'xlValidateDate 4 Date values.
'xlValidateDecimal 2 Numeric values.
'xlValidateInputOnly 0 Validate only when user changes the value.
'xlValidateList 3 Value must be present in a specified list.
'xlValidateTextLength 6 Length of text.
'xlValidateTime 5 Time values.
'xlValidateWholeNumber 1 Whole numeric values.
'AlertStyle
'xlValidAlertInformation 3 Information icon.
'xlValidAlertStop 1 Stop icon.
'xlValidAlertWarning 2 Warning icon.
'Operator
'xlBetween 1 Between. Can be used only if two formulas are provided.
'xlEqual 3 Equal.
'xlGreater 5 Greater than.
'xlGreaterEqual 7 Greater than or equal to.
'xlLess 6 Less than.
'xlLessEqual 8 Less than or equal to.
'xlNotBetween 2 Not between. Can be used only if two formulas are provided.
'xlNotEqual 4 Not equal.
'==============================================
With rng.Validation
.Delete ' delete any existing validation before adding new one
.Add Type:=iXlDVType, AlertStyle:=iAlertStyle, Operator:=iOperator, Formula1:=sFormula1, Formula2:=sFormula2
.IgnoreBlank = bIgnoreBlank
.InCellDropdown = bInCellDropDown
.InputTitle = sInputTitle
.ErrorTitle = sErrorTitle
.InputMessage = sInputMessage
.ErrorMessage = sErrorMessage
.ShowInput = bShowInput
.ShowError = bShowError
End With
End Function