Excel VBA - Error in using 'For Each' loop and 'If' statement.
I have an excel workbook; which has two sheets:
The first sheet is: 'Jurisdictions' Which has three columns:
Country (Column B), State (Column C) and City (Column D)
This sheet has single entry for each city.
But, as each city is listed on separate row, the names of state and country(To which cities belong) can get repeated on multiple rows.
For Ex:
Col B | Col C | Col D |
--------------------------------
U.S. | New York | Buffalo |
U.S. | New York | Manhattan |
(These are my two rows)
I have another sheet: Sheet1;
Here also I have same three columns; (and 20 some other columns)
These three columns I shall validate with three columns in 'Jurisdictions' sheet. (Only few 'Jurisdictions' are listed in Sheet1; and those can be in any order and can be for any country)
The validation Rules are:
1) For Country
2) State
3) City
I have the following code which validates Countries, States and Cities correctly. (linguistically - spelling.)
ie
Col B | Col C | Col D |
-----------------------------------------
U.S. | New York | Buffalo |
U.S. | New York | Manhattan; Buffalo |
India | Karnataka| Bangalore |
Also it validates the hierarchy correctly.
Dim nLastRow As Long
Dim nLastRowSheet2 As Long
Dim rngFnder As Range
Dim strFndAddress As String
Dim stString As String
Dim stArray() As String
'Get the last row
'Dim lastRow As Integer
nLastRow = Sheets("Sheet1").Cells(Rows.Count, 2).End(xlUp).Row
nLastRowSheet2 = Sheets("Jurisdictions").Cells(Rows.Count, 2).End(xlUp).Row
Dim c As Range
Dim d As Range
Dim e As Variant
'Turn screen updating off to speed up macro code.
'User won't be able to see what the macro is doing, but it will run faster.
Application.ScreenUpdating = False
For Each c In Worksheets("Sheet1").Range("D2:D" & nLastRow)
stString = c
stArray() = Split(stString, ";")
For Each e In stArray()
e = Trim(e)
strFndAddress = ""
On Error Resume Next
Set rngFnder = Sheets("Jurisdictions").Range("D2:D" & nLastRowSheet2).Find(e)
If rngFnder Is Nothing And c <> "All" Then
c.Interior.Color = vbRed
Else
strFndAddress = rngFnder.Address
Do
If c.Offset(, -1) = rngFnder.Offset(, -1) And c.Offset(, -2) = rngFnder.Offset(, -2) Then
strFndAddress = ""
Exit Do
Else
Set rngFnder = Sheets("Jurisdictions").Range("D2:D" & nLastRowSheet2).FindNext(rngFnder)
End If
Loop While Not rngFnder Is Nothing And rngFnder.Address <> strFndAddress
End If
If rngFnder.Address = strFndAddress Then
c.Interior.Color = vbRed
End If
On Error GoTo 0
Set c = Nothing
strFndAddress = ""
Next
Next
Now I have other requirements
To implement the above requirements I added following code
For Each c In Worksheets("Sheet1").Range("D2:D" & nLastRow)
If c.Offset(, -1) = "All" And c = "All" Then
c.Interior.Color = vbWhite
End If
Next
and...
For Each c In Worksheets("Sheet1").Range("D2:D" & nLastRow)
If c = "All" Then
stString = c.Offset(, -1)
stArray() = Split(stString, ";")
End If
For Each e In stArray()
e = Trim(e)
strFndAddress = ""
On Error Resume Next
Set rngFnder = Sheets("Jurisdictions").Range("C2:C" & nLastRowSheet2).Find(e)
If rngFnder Is Nothing And e <> "All" Then
'c.Interior.Color = c.Interior.Color (Do Nothing)
Else
strFndAddress = rngFnder.Address
Do
If c.Offset(, -2) = rngFnder.Offset(, -2) Then
strFndAddress = ""
c.Interior.Color = vbWhite
Exit Do
Else
Set rngFnder = Sheets("Jurisdictions").Range("C2:C" & nLastRowSheet2).FindNext(rngFnder)
End If
Loop While Not rngFnder Is Nothing And rngFnder.Address <> strFndAddress
End If
If rngFnder.Address = strFndAddress Then
'c.Interior.Color = c.Interior.Color (Do Nothing)
End If
On Error GoTo 0
Set c = Nothing
strFndAddress = ""
Next
Next
The last code is little erroneous.
If the 'State' cell has a single state and adjacent 'City' cell has value "All" :it validates it correctly.
But; If 'State' cell has multiple states separated with a semicolon; and adjacent 'City' cell has value "All" then code do not validate it correctly.
Can anyone help me where I am wrong? and also how I can increase the performance of code...
Looking at your code, it seems you are making this really really complicated, where it does not really have to be.
If I gather this correctly, users will be entering data in your excelworkbook and you want to make sure they do this correctly, following some rules. If they break the rules you will color the cell red.
The easier and faster way to do this is use the conditional formatting built in to excel. This will also allow you to split your code, and only check data that has actually changed: so no need to loop through all the cells.
Quick example:
To add this rule you select the first cell of column C, and you choose "Conditional Formatting" from the Home tab in Excel;s ribbon. Then you create a new rule, and choose to use a formula to determine which cells to format. The formula you need is "=NOT(ISERROR(FIND(";";B1)))" Then you set the format to red Fill and save the rule.
This rule will now work for the first row, in order to make it work for the entire column, you can go to manage rules and adjust the "applies to" field to $C:$C or you can copy the formatting to the cells you want to use.
You can create a Conditional formatting rule for every rule you have on your workbook. You can also code functions for specific rules that you can not express easily in Excel's native funtions.
This will make your checks simple and easier to maintain, but it will also make your user feedback instantanious: as soon as they enter a value, the rules are applied.
First, let me state that the approach in my previous answer can easily be applied to a workbook containing data, and is the best way forward.
But as you ask for input on your code, maybe you should look at the second part of your else. If State has multiple states, and the state can be found on the jurisdictions tab, your code will go to the following else block:
strFndAddress = rngFnder.Address
Do
If c.Offset(, -2) = rngFnder.Offset(, -2) Then
strFndAddress = ""
c.Interior.Color = vbWhite
Exit Do
Else
Set rngFnder = Sheets("Jurisdictions").Range("C2:C" & nLastRowSheet2).FindNext(rngFnder)
End If
Loop While Not rngFnder Is Nothing And rngFnder.Address <> strFndAddress
This block does nothing with the "All" value.
Also, this line confuses me (more than once):
c.Interior.Color = c.Interior.Color
Other tips you might enjoy:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.