VBA/Excel Speed up macro adding checkboxes - vba

I need to add checkbox for each row in few documents and I have script for that which working, and that's ok but...
If I have 10k rows then this script it's very slowly. How I can speed up it?CODE:
Sub AddCheckBoxes()
Dim chk As CheckBox
Dim myRange As Range, cel As Range
Dim ws As Worksheet
Set ws = Sheets("") 'adjust sheet to your need
Set myRange = ws.Range("A65:A75") ' adjust range to your needs
For Each cel In myRange
Set chk = ws.CheckBoxes.Add(cel.Left, cel.Top, 30, 6) 'you can adjust left, top, height, width to your needs
With chk
.Caption = "Valid"
.LinkedCell = cel.Range("B65:B75").Address
End With
Next
End Sub
Thanks!

Let's try this and see if it fits. Please paste the following code into a normal code module (by default 'Module1') of a blank workbook you create for this purpose. It is a module which doesn't exist in a new workbook. Don't use any of the existing.
Option Explicit
Enum Nws ' Worksheet rows & columns
' 20 Apr 2017
NwsFirstDataRow = 2 ' adjust as required
' Columns:
NwsMainData = 1 ' (= A) Test for used range
NwsCheck = 7 ' (= G) column for Check cell
End Enum
Enum Nck ' CheckBox
' 20 Apr 2017
NckFalse
NckTrue
NckNotSet ' any value other than True or False
End Enum
Sub SetCheckCell(Target As Range)
' 20 Apr 2017
Dim TgtVal As Nck
With Target
If Len(.Value) Then
Select Case .Value
Case True
TgtVal = NckFalse
Case False
TgtVal = NckTrue
Case Else
TgtVal = NckNotSet
End Select
Else
TgtVal = NckNotSet
End If
If TgtVal = NckNotSet Then
SetBorders Target
TgtVal = NckFalse
End If
.Value = CBool(Array(0, -1)(TgtVal))
With .Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = Array(xlThemeColorAccent6, xlThemeColorAccent3)(TgtVal)
.TintAndShade = 0.399945066682943
.PatternTintAndShade = 0
End With
.Offset(0, -1).Select
End With
End Sub
Private Sub SetBorders(Rng As Range)
' 12 Apr 2017
Dim Brd As Long
For Brd = xlEdgeLeft To xlInsideHorizontal
SetBorder Rng, Brd
Next Brd
Rng.Borders(xlDiagonalDown).LineStyle = xlNone
Rng.Borders(xlDiagonalUp).LineStyle = xlNone
End Sub
Private Sub SetBorder(Rng As Range, _
Brd As Long)
' 12 Apr 2017
With Rng.Borders(Brd)
.LineStyle = xlContinuous
.ThemeColor = 1
.TintAndShade = -0.349986266670736
.Weight = xlMedium
End With
End Sub
In column A, enter something - anything - in row 10 (or thereabouts). This is the last "used" row in your worksheet.
Now paste the following code in the code sheet of the worksheet on which you created a last "used" row. It must be exactly that code sheet - no other. It is a sheet which already exists. You recognise it by the tab's name in the VBE's project explorer window.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' 20 Apr 2017
Dim Rng As Range ' used range (almost)
Dim Rl As Long ' last row
Application.EnableEvents = False
With Target.Worksheet
Rl = .Cells(.Rows.Count, NwsMainData).End(xlUp).Row
Set Rng = .Range(.Cells(NwsFirstDataRow, NwsCheck), .Cells(Rl, NwsCheck))
If Not Application.Intersect(Target, Rng) Is Nothing Then
SetCheckCell .Cells(Target.Row, NwsCheck)
End If
End With
Application.EnableEvents = True
End Sub
Now you are all set to test but understand the mechanics first. At the top of the first batch of code you have Enum Nws which specifies one row and two columns. The specified row is NwsFirstDataRow with an assigned value of 2. It means that row 1 is outside the scope of this code. The code will not run when you click in row 1 (presumably a caption row). You could set NwsFirstDataRow to a value of 3, thereby creating 2 header rows which the code won't touch.
The two columns are NwsMainData and NwsCheck. NwsMainData is the column where the last row is measured. If you click below the last row the code will not run. You might find that column A doesn't suit your needs. You can set any other value, thereby specifying another column. The number you set is used for no other purpose but to find the last row. In your test, make sure the column actually has a used row.
NwsCheck is the column where you will have your "check box". You can specify any column. Try it out in a little while. The point is that the code will not run if you click any other column. Therefore, the code will run if you click in the NwsCheck column, at or below NwsFirstDataRow and at or above the last "used" row. Go ahead an click.
Since the cell is empty it will be coloured as a checkbox and filled with the word "False". Click it again and it will change colour and value will be True. It continues to toggle. The cursor moves to the left to allow you to toggle.
You could move the cursor right or up or down. You could change the colours to any colour Excel has on offer. You can change the frame from the one I have selected. You can change the words which are shown. In fact, there is very little you couldn't change - and it isn't difficult.
The question is whether the idea can be adapted to do the job you want a check box to do.

Here is a variation of the above. Instead of writing TRUE or FALSE, it actually gives you a checkbox-character which is either checked or not. The code displays a message box informing you of the status, but the idea is to execute whatever code you want to run instead, based on whether the box is checked or not.
To test this code, add this procedure to the normal code module. Some of the above code will be required for this solution. For the purpose of testing, just have the entire previous code installed. Then add this.
Function SetCheck(Cell As Range) As Boolean
' 21 Apr 2017
Dim Fun As Integer
Dim Chars() As Variant
Dim Mark As Integer ' character current displayed
Chars = Array(168, 254) ' unchecked / checked box
With Cell
If Len(.Value) Then Mark = AscW(.Value)
Fun = IIf(Mark = Int(Chars(0)), 1, 0)
With .Font
.Name = "Wingdings"
.Size = 11
End With
.Value = ChrW(Chars(Fun))
.Offset(0, 1).Select
End With
SetCheck = CBool(Fun)
End Function
Replace the existing event procedure with the one below. The difference is minute, but for quick testing, just replace all of it.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' 21 Apr 2017
Dim Rng As Range ' used range (almost)
Dim Rl As Long ' last row
Dim Chk As Boolean
Application.EnableEvents = False
With Target.Worksheet
Rl = .Cells(.Rows.Count, NwsMainData).End(xlUp).Row
Set Rng = .Range(.Cells(NwsFirstDataRow, NwsCheck), .Cells(Rl, NwsCheck))
If Not Application.Intersect(Target, Rng) Is Nothing Then
' SetCheckCell .Cells(Target.Row, NwsCheck)
Chk = SetCheck(Target.Cells(1))
MsgBox "The checkbox is now " & IIf(Chk, "", "un") & "checked"
End If
End With
Application.EnableEvents = True
End Sub

Related

VBA - Changing the colour of multiple worksheet tabs depending on a cell value

I am trying to create a button that upon one click, will check a specific cell in 10 sheets of a workbook, and recolour the tabs depending on a cell value.
For example,
If cell E15 > 18, then the tab colour should turn green.
If cell E15 < 18, then the tab colour should turn red.
All 10 tabs should be evaluated and recoloured upon a single button click.
So far my macro looks like this, giving just three sheets for an example. It's very crude but I am very new to VBA (1 day).
My main issue is that it works for the first tab, but then opens the second tab and says "Object Required"
Sub Update_Tab_Colour_One_Click()
Sheets(4).Activate
If Cells(13, 11).Value > 18 Then
With ActiveWorkbook.ActiveSheet.Tab
.Color = vbGreen
End With
Else
With ActiveWorbook.ActiveSheet.Tab
.Color = vbRed
End With
End If
Sheets(5).Activate
If Cells(13, 11).Value > 18 Then
With ActiveWorkbook.ActiveSheet.Tab
.Color = vbGreen
End With
Else
With ActiveWorbook.ActiveSheet.Tab
.Color = vbRed
End With
End If
Sheets(6).Activate
If Cells(13, 11).Value > 18 Then
With ActiveWorkbook.ActiveSheet.Tab
.Color = vbGreen
End With
Else
With ActiveWorbook.ActiveSheet.Tab
.Color = vbRed
End With
End If
End Sub
See if this works for you:
Sub Update_Tab_Colour_One_Click()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
If ws.Index = 4 Or ws.Index = 5 Or ws.Index = 6 Then
If ws.Cells(13, 11).Value > 18 Then
ws.Tab.Color = vbGreen
Else
ws.Tab.Color = vbRed
End If
End If
Next ws
End Sub
It checks to see if it's the 4, 5, or 6th indexed sheet, then checks the cell value and colors the tab accordingly.
Something like this would probably do it.
Dim sh as Worksheet
Dim s as Long
For s = 4 to 13 ' Modify if needed
Set sh = ThisWorkbook.Worksheets(s)
With sh
.Tab.Color = IIF(.Cells(13,11).Value > 18, vbGreen, vbRed)
End With
Next
Here we've created a For/Next loop over the sheets indexed 4:13 (10 sheets, incremented by 1). Then, we set a Worksheet variable (sh) to represent the current sheet (note that it is not needed to be Active), then set the sh.Tab.Color based on the boolean expression in the IIF function to return either vbGreen or vbRed.
Info:
For..Next statement reference
IIF function reference
How to avoid using Select in Excel VBA
Looping over non-consecutive and named sheets
This alternative lets you loop over sheets which aren't consecutive (so 2,4,7 not just 1,2,3) and by their names (like "Sheet1", "Sheet2"). So it is much more flexible!
It happens to be just as short to write as a straight forward loop, we are just looping over an array of sheet names or numbers instead.
I have added comments to explain what each line does, see below:
Sub Update_Tab_Colour_One_Click()
' Declare array of sheet numbers and/or names
Dim mySheets As Variant
mySheets = Array(2, 4, "Sheet1")
' Loop over sheet numbers / names
Dim i As Long
For i = LBound(mySheets) To UBound(mySheets)
' Use With so we don't have to repeatedly say we are within this sheet
With ThisWorkbook.Sheets(mySheets(i))
' Use IIF to concisely assign a conditional value
.Tab.Color = IIf(.Cells(13, 11).Value > 18, vbGreen, vbRed)
End With
Next i
End Sub

Finding all cells that have been filled with any color and highlighting corresponding column headers in excel vba

My problem:
I've made a large (2,000 line) macro that runs on our company's template and fixes some common issues and highlights other issues we have prior to importing. The template file always has 150 columns and is in most instances 15,000+ rows (sometimes even over 30,000). The macro works well, highlighting all the cells that contain errors according to our data rules, but with a file with so many columns and rows I thought it'd be convenient to add a snippet to my macro that would have it find all of the cells that have been highlighted and then highlight the column headers of the columns that contain those highlighted cells.
Methods I've found while searching for a solution:
SpecialCellsxlCellTypeAllFormatConditions only works for conditional formatting, so that isn't a plausible method for my situation
Rick Rothstein's UDF from here
Sub FindYellowCells()
Dim YellowCell As Range, FirstAddress As String
Const IndicatorColumn As String = "AK"
Columns(IndicatorColumn).ClearContents
' The next code line sets the search for Yellow color... the next line after it (commented out) searches
' for the ColorIndex 6 (which is usually yellow), so use whichever code line is applicable to your situation
Application.FindFormat.Interior.Color = vbYellow
'Application.FindFormat.Interior.ColorIndex = 6
Set YellowCell = Cells.Find("*", After:=Cells(Rows.Count, Columns.Count), SearchFormat:=True)
If Not YellowCell Is Nothing Then
FirstAddress = YellowCell.Address
Do
Cells(YellowCell.Row, IndicatorColumn).Value = "X"
Set YellowCell = Cells.Find("*", After:=YellowCell, SearchFormat:=True)
If YellowCell Is Nothing Then Exit Do
Loop While FirstAddress <> YellowCell.Address
End If
End Sub
This would be perfect with a few tweaks, except our files can have multiple colorfills. Since our template is so large I've learned that it takes quite some time to run one instance of Find to find just one colorfill in the UsedRange.
Using filtering, maybe cycling through all the columns and checking each if they contain any cell that has any colorfill. Would that be any faster though?
So, my question:
How could I accomplish finding all columns that contain any colorfilled cells? More specifically, what would be the most efficient (fastest) way to achieve this?
The most performant solution would be to search using recursion by half-interval.
It takes less than 5 seconds to tag the columns from a worksheet with 150 columns and 30000 rows.
The code to search for a specific color:
Sub TagColumns()
Dim headers As Range, body As Range, col As Long, found As Boolean
' define the columns for the headers and body
Set headers = ActiveSheet.UsedRange.Rows(1).Columns
Set body = ActiveSheet.UsedRange.Offset(1).Columns
' iterate each column
For col = 1 To headers.Count
' search for the yellow color in the column of the body
found = HasColor(body(col), vbYellow)
' set the header to red if found, green otherwise
headers(col).Interior.color = IIf(found, vbRed, vbGreen)
Next
End Sub
Public Function HasColor(rg As Range, color As Long) As Boolean
If rg.DisplayFormat.Interior.color = color Then
HasColor = True
ElseIf VBA.IsNull(rg.DisplayFormat.Interior.colorIndex) Then
' The color index is null so there is more than one color in the range
Dim midrow&
midrow = rg.Rows.Count \ 2
If HasColor(rg.Resize(midrow), color) Then
HasColor = True
ElseIf HasColor(rg.Resize(rg.Rows.Count - midrow).Offset(midrow), color) Then
HasColor = True
End If
End If
End Function
And to search for any color:
Sub TagColumns()
Dim headers As Range, body As Range, col As Long, found As Boolean
' define the columns for the headers and body
Set headers = ActiveSheet.UsedRange.Rows(1).Columns
Set body = ActiveSheet.UsedRange.Offset(1).Columns
' iterate each column
For col = 1 To headers.Count
' search for any color in the column of the body
found = VBA.IsNull(body(col).DisplayFormat.Interior.ColorIndex)
' set the header to red if found, green otherwise
headers(col).Interior.color = IIf(found, vbRed, vbGreen)
Next
End Sub
Before:
Running this short macro:
Sub FindingColor()
Dim r1 As Range, r2 As Range, r As Range
Dim nFirstColumn As Long, nLastColumn As Long, ic As Long
Set r1 = ActiveSheet.UsedRange
nLastColumn = r1.Columns.Count + r1.Column - 1
nFirstColumn = r1.Column
For ic = nFirstColumn To nLastColumn
Set r2 = Intersect(r1, Columns(ic))
For Each r In r2
If r.Interior.ColorIndex <> xlNone Then
r2(1).Interior.ColorIndex = 27
Exit For
End If
Next r
Next ic
End Sub
produces:
I just don't know about the speed issue. If the colored cells are near the top of the column, the code will run super fast; if the colored cells are missing or near the bottom of the column, not so much.
EDIT#1:
Please note that my code will not find cells colored conditionally.
The Range.Value property actually has three potential optional xlRangeValueDataType parameters. The default is xlRangeValueDefault and that is all (by omission) most anyone ever uses.
The xlRangeValueXMLSpreadsheet option retrieves an XML data block which describes many of the properties that the cell maintains. A cell with no Range.Interior property beyond xlAutomatic will have the following XML element,
<Interior/>
... while a cell with an .Interior.Color property will have the following XML element,
<Interior ss:Color="#FF0000" ss:Pattern="Solid"/>
It's been well established that dumping a worksheet's values into a variant array and processing in-memory is substantially quicker than looping through cells so retrieving the .Value(xlRangeValueXMLSpreadsheet) and performing an InStr function on the single blob of XML data should prove much faster.
Sub filledOrNot()
Dim c As Long, r As Long, vCLRs As String
appTGGL bTGGL:=False
With Worksheets("30Kdata")
With .Cells(1, 1).CurrentRegion
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
For c = 1 To .Columns.Count
vCLRs = .Columns(c).Cells.Value(xlRangeValueXMLSpreadsheet)
If CBool(InStr(1, vCLRs, "<Interior ss:Color=", vbBinaryCompare)) Then _
.Cells(0, c).Interior.Color = 49407
Next c
End With
End With
Debug.Print Len(vCLRs)
End With
appTGGL
End Sub
Public Sub appTGGL(Optional bTGGL As Boolean = True)
With Application
.ScreenUpdating = bTGGL
.EnableEvents = bTGGL
.DisplayAlerts = bTGGL
.AutoRecover.Enabled = bTGGL 'no interruptions with an auto-save
.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
.CutCopyMode = False
.StatusBar = vbNullString
End With
Debug.Print Timer
End Sub
I ran this against 30K rows by 26 columns. While each column was examined, I had only seeded every third column with an .Interior.Color property somewhere randomly within the 30K rows. It took about a minute and a half.
Each column of 30K rows produced an XML record that was almost 3Mbs in size; a length of 2,970,862 was typical. Once read into a variable, it was searched for the fingerprint of a set interior fill.
    
Discarding the read into the string type var and performing the InStr directly on the .Value(xlRangeValueXMLSpreadsheet) actually improved the time by about two seconds.
My proposal using AutoFilter method of Range object
it runs quite fast
Option Explicit
Sub FilterByFillColor()
Dim ws As Worksheet
Dim headerRng As Range
Dim iCol As Long, RGBColor As Long
Set ws = ThisWorkbook.Worksheets("HeadersToColor") '<== set it to your actual name of the data worksheet
Set headerRng = ws.Range("headers") '<== I set a named range "headers" in my test sheet addressing the cells that cointains all headers. but you may use explicit address ie: 'ws.Range("B2:EU150")' for a 150 columns header range
RGBColor = RGB(255, 0, 0)
Application.ScreenUpdating = False
headerRng.Interior.Color = vbGreen
With headerRng.CurrentRegion
For iCol = 1 To .Columns.Count
.AutoFilter Field:=iCol, Criteria1:=RGBColor, Operator:=xlFilterNoFill
If .Columns(iCol).SpecialCells(xlCellTypeVisible).Count < .Rows.Count Then headerRng(iCol).Interior.Color = vbRed
.AutoFilter
Next iCol
End With
Application.ScreenUpdating = True
End Sub

Select a column by letter from activeCell (without activeCell.EntireColumn)

First and foremost, the below works as expected. I'm trying to make the macro mimic one we have in word. Our word macro will select the entire column simply to display which column is currently being processed (the selection object is not used for any actual processing).
In excel, when I attempt to select the column (activecell.entirecolumn.select) if there is a merged cell it will show multiple columns. I need it only to select the letter column (pretty much the same as clicking the letter at the top) of the active cell. I'm hoping for a method that wont require me to parse the address of the cell if possible (I feel like string parsing is sloppy).
Sub setwidths()
Dim rangeName As String
Dim selectedRange As range
Dim tempRange As range
Dim x As Integer
'If only 1 cell is selected, attempt to find the correct named range
If Selection.Cells.Count = 1 Then
rangeName = Lib.getNamedRange(Selection) 'Built in function from my lib (works I promise)
If rangeName <> "" Then
Application.Goto reference:=rangeName
End If
End If
Set selectedRange = Selection
'Go column by column asking for the width
'Made to mimic a word MACRO's behavior and moving backwards served a point in word
For x = selectedRange.Columns.Count To 1 Step -1
Set tempRange = selectedRange.Columns(x)
tempRange.Cells(tempRange.Cells.Count, 1).Select
'This is where the code should go to select the column
tempRange.ColumnWidth = InputBox("This columns?")
Next
End Sub
Is there anyway to select a column by letter (range("A:A").select for instance) from within an active cell?
Edit:
Record MACRO shows that columns("A:A").select is used when clicking the letter at the top; however, entering that same line into the immediate window will select all columns that merged cells are merged across same as with range("A:A").select and activecell.selectcolumn
Sub NSTableAdjust()
Dim rangeName As String
Dim selectedRange As range
Dim tempRange As range
Dim cellsColor() As Long
Dim cellsPattern() As XlPattern
Dim cellsTaS() As Long
Dim cellsPTaS() As Long
Dim result As String
Dim abort As Boolean
Dim x As Integer
Dim y As Integer
'Delete the block between these comments and run macro on 10x10 grid in excel to test
If Selection.Cells.Count = 1 Then
rangeName = Lib.getNamedRange(Selection)
If rangeName <> "" Then
Application.Goto reference:=rangeName
End If
End If
'Delete the block between these comments and run macro on 10x10 grid in excel to test
Set selectedRange = Selection
ReDim cellsArr(1 To selectedRange.Rows.Count)
ReDim cellsColor(1 To UBound(cellsArr))
ReDim cellsPattern(1 To UBound(cellsArr))
ReDim cellsTaS(1 To UBound(cellsArr))
ReDim cellsPTaS(1 To UBound(cellsArr))
abort = False
For x = selectedRange.Columns.Count To 1 Step -1
Set tempRange = selectedRange.Columns(x)
tempRange.Cells(tempRange.Cells.Count, 1).Select
For y = 1 To UBound(cellsColor)
With tempRange.Cells(y, 1).Interior
cellsColor(y) = .Color
cellsPattern(y) = .Pattern
cellsTaS(y) = .TintAndShade
cellsPTaS(y) = .PatternTintAndShade
.Color = 14136213
End With
Next
result = InputBox("This Column?")
If IsNumeric(result) Then
tempRange.ColumnWidth = result
Else
abort = True
End If
For y = 1 To UBound(cellsColor)
With tempRange.Cells(y, 1).Interior
.Color = cellsColor(y)
.Pattern = cellsPattern(y)
.TintAndShade = cellsTaS(y)
.PatternTintAndShade = cellsPTaS(y)
End With
Next
If abort Then
Exit Sub
End If
Next
End Sub
My current solution to simply shade the cells and then restore their original shading after processing the column.
After an obviously lengthy discussion in the comments on the post. It appears the answer to my question is simply "Not Possible."
The solution I settled on in an attempt to get as close to the "Look" I was searching for is below:
For x = selectedRange.Columns.Count To 1 Step -1
Set tempRange = selectedRange.Columns(x) 'Range of the column
'Our standards dictate the last cell in the range will not be merged
With tempRange.Cells(tempRange.Cells.Count, 1)
.Select 'Selecting here will for excel to make sure the range is in view
'Very simple/basic conditional formatting rule
Set fCondition = .EntireColumn.FormatConditions. _
Add(Type:=xlExpression, Formula1:="=True")
fCondition.Interior.Color = 15123099
'Make sure it is the highest priority rule
fCondition.Priority = 1
End With
'Get user input
result = InputBox("This Column?")
'Delete rule
fCondition.Delete
'Validate user input
If IsNumeric(result) Then
tempRange.ColumnWidth = result
Else
abort = True
End If
If abort Then
Exit Sub
End If
Next

Excel 2010 VB Script – Highlight Row Issue

I was wondering if someone had any suggestions to this. I want the row to highlight below row 6 when a cell is clicked on. So if I click on A7, then row 7 will highlight. If I then click on B9, row 7 will have the highlight removed and row 9 will then highlight. I did find code that does work for what I need and have customized it a little. Everything works exactly the way I need it to work, except for when Excel is saved, closed out, and reopened.
If row 9 is highlighted, and the spreadsheet is saved, closed, and reopened, row 9 will remain highlighted (even when another cell is clicked on). So now I have 2 rows highlighted. In order to fix this once the spreadsheet is opened back up is to click on a different row and then click back on row 9. Then it will be back to 1 highlighted row.
Does anyone have a solution for this? Below is the code that I am using.
Thanks for any help someone can provide,
Chris
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
ActiveSheet.Unprotect
Static rr
If rr <> "" Then
With Rows(rr).Interior
.ColorIndex = xlNone
End With
End If
r = Selection.Row
rr = r
With Rows(r).Interior
.ColorIndex = 20
.Pattern = xlSolid
End With
ActiveSheet.Protect
End Sub
The following combination of code seems to be working; I'm highlighting the entire row each time.
Private lastRow As Long
Private Sub Worksheet_Activate()
lastRow = ActiveCell.Row
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If lastRow <> 0 Then
Rows(lastRow).EntireRow.Interior.ColorIndex = xlNone
If Target.Row > 6 Then
Target.Rows(1).EntireRow.Interior.ColorIndex = 20
End If
lastRow = Target.Row
Else
lastRow = Target.Row
End If
End Sub
Actually, it probably needs a bit of work. However, it might be a starting point for you.
Your static rr variable is a Variant and will not have a default value of "". So, when you re-open the file, the cursor will be in the row it was in previously, and because rr is not equal to "" it will not remove the highlight from this line. (In fact, I'm not sure how it is removing the highlight currently.)
Anyway, try:
Static rr
If IsEmpty(rr) Then
rr = ""
End If
Alternatively, give rr the data-type of Integer or Long, which will assume a default value of 0.
I wrote my own code instead of trying to work with the code I found. This works a lot better. It also allows the user to specify their own range of rows to highlight.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Application.EnableEvents = False
ActiveSheet.Unprotect
Dim iFirstCol As Integer
Dim iLastCol As Integer
Dim iFirstRow As Integer
Dim iLastRow As Integer
Dim iColor As Integer
'''Only adjust the below numbers to fit your desired results.'''
iFirstCol = 1 'Change this number to the number of the first column that needs to be highlighted. Column A = 1.
iLastCol = 15 'Change this number to the number of the last column that needs to be highlighted. Column A = 1.
iFirstRow = 7 'Change this number to the number of the first row that needs to be highlighted.
iLastRow = 500 'Change this number to the number of the last row that needs to be highlighted.
iColor = 20 'Change this number to use a different highlight color.
'''End of changes, do not change anything else.'''
'The row highlight will only be applied if the selected range is within this if statement criteria.
If Target.Row > iFirstRow - 1 And Target.Row < iLastRow + 1 And Target.Column > iFirstCol - 1 And Target.Column < iLastCol + 1 Then
'Resets the color within the full range when cell selection changed.
ActiveSheet.Range(ActiveSheet.Cells(iFirstRow, iFirstCol), ActiveSheet.Cells(iLastRow, iLastCol)).Interior.Color = xlNone
'Applies the colors to the row.
For counter = iFirstCol To iLastCol
With ActiveSheet.Cells(Target.Row, iFirstCol).Interior
.ColorIndex = iColor
.Pattern = xlSolid
End With
iFirstCol = iFirstCol + 1
Next counter
End If
ActiveSheet.Protect
Application.EnableEvents = True
End Sub
I often highlight rows in tables on selection. While I might be over-simplifying things it seems so much easier then the code you have provided above.
Here is what I do;
I use just a tiny big of code in the Worksheet selection change for the range that should have the highlighting rows in effect, such as this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target, Range("D8:R10000")) Is Nothing Then
Range("B1").Value = ActiveCell.Row
End If
End Sub
Then I use a Conditional formatting for B1 and the range, with any type of formatting you might like for the selected row. A Conditional formatting formula for the above would be:
=$B$1=ROW()
with an Applied To range of: =$D$8:$R$10000
That's it. No other coding is required and formats can be changed simply.
What are your thoughts on this?

Using VBA to Select and Highlight Excel Rows

How can I tell Excel to highlight rows by their row number. For instance, let's say I wanted row 6, 10, 150, 201 highlighted. Thanks.
Here is another one based on Mote's .EntireRow.Interior.ColorIndex
This one doesn't restrict you to enter the row numbers but gives the user the flexibility to choose the rows at runtime.
Option Explicit
Sub Sample()
Dim Ret As Range
On Error Resume Next
Set Ret = Application.InputBox("Please select the rows that you would like to color", "Color Rows", Type:=8)
On Error GoTo 0
If Not Ret Is Nothing Then Ret.EntireRow.Interior.ColorIndex = 6
End Sub
FOLLOWUP
Is there a way to write the macro to read the row numbers from a list and highlight the rows?
Yes there is a way. Let's say the list in Cell A1 to A10 then you can use this code
Option Explicit
Sub Sample()
Dim i As Long, sh As Worksheet
On Error GoTo Whoa
Application.ScreenUpdating = False
'~~> Set this to the sheet where the rows need to be colored
Set sh = Sheets("Sheet2")
'~~> Change Sheet1 to the sheet which has the list
With Sheets("Sheet1")
For i = 1 To 10
If Not Len(Trim(.Range("A" & i).Value)) = 0 And _
IsNumeric(.Range("A" & i).Value) Then _
sh.Rows(.Range("A" & i).Value).Interior.ColorIndex = 3 '<~~ Red
Next i
End With
LetsContinue:
Application.ScreenUpdating = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
As an alternative to Motes' answer, you can use conditional formatting.
Eg: select A1:J500, Conditional formatting >> New rule >> Use a formula...
For the formula enter: =OR(ROW()=6, ROW()=10, ROW()=150, ROW()=201)
For basic VBA code, you can always start recording a macro, perform the action, stop recording, look at what code was generated, and then clean that up to do what you want. For example, recording the action of highlighting a row (setting the value of Interior.Color) gives you:
Rows("13:13").Select
Range("C13").Activate
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
.PatternTintAndShade = 0
End With
The selection commands and extraneous Interior properties can be removed giving you:
Rows("13:13").Interior.Color = 65535
Adding in the row multi-select:
Rows("6:6,10:10,150:150,201:201").Interior.Color = 65535
Summary:
Record macro
View Excel's version
Use/Edit what code you need
objWB.Cells(rowNum,201).EntireRow.Interior.ColorIndex = 6
etc
Update: Didn't realize the date on this, but thought I'd add this in since it was relevant to the chosen answer.
In addition to Siddharth Rout's answer, since I don't have enough rep to comment yet, you can dynamically figure out how many rows there are in your worksheet with these two lines. xlCellTypeConstants could be changed to another XlCellType constant that you need, and the range can always be changed to accommodate to your spreadsheet.
Dim numRows As Integer
numRows = Range("A2", Range("A1048576").End(xlUp)).SpecialCells(xlCellTypeConstants).Cells.Count
Sorry if it is not as concise or elegant as other answers, but it gets the job done. When I was writing code for my own application I needed to loop through my code. Also, instead of highlighting the entire row, I only needed to highlight a portion of the rows.
Sub Highlight()
Dim ThisWB As Workbook
Dim ThisWS As Worksheet
Dim rows(0 To 3) As Integer
Dim test As String
Set ThisWB = ActiveWorkbook
Set ThisWS = ThisWB.Sheets("Sheet1")
rows(0) = 6
rows(1) = 10
rows(2) = 150
rows(3) = 201
For i = 0 To 3
test = "A" & rows(i) & ":H" & rows(i)
ThisWS.Range(test).Interior.ColorIndex = 15
Next i
End Sub
You might be able to achieve the same thing using conditional formatting
put list of values in a column (I use a separate tab and give the list a name)
under conditional formatting - New Rule - "use a formula to determine with cells to format"
read this article http://www.howtogeek.com/howto/45670/how-to-highlight-a-row-in-excel-using-conditional-formatting/
the rule uses vlookup in the formula- =$A2=VLOOKUP($A2,list,1,FALSE)