I would like to loop all the worksheets of a workbook changing the color of a cell with a specific string in it.
I use .Replace (I need MatchCase and lookat).
It replaces the text without regarding Case. (e.g. if in the array it is lowercase and the string found is uppercase it will be changed to lowercase). The only way to bypass this is to use MatchCase:= false and list all options, and it could be really inefficient.
Could I perform the action using .Find or another function?
Sub CellMarked()
Dim fndlist As Variant, x As Integer, sht as worksheet
fndlist = Array("Column1", "Column2")
For Each sht In ActiveWorkbook.Worksheets
With sht
For x = LBound(fndlist) To UBound(fndlist)
.Cells.Replace What:=fndlist(x), Replacement:=fndlist(x), _
lookat:=xlPart, SearchOrder:=xlByRows, MatchCase:=True, _
SearchFormat:=False, ReplaceFormat:=True
Application.ReplaceFormat.Font.Color = 255
Next x
End With
next sht
End Sub
you could use Find() method and build a helper Function:
Function GetCellsWithValue(sht As Worksheet, val As Variant, foundCells As Range) As Boolean
Dim found As Range
Dim firstAddress As String
With sht.UsedRange
Set foundCells = .Resize(1, 1).Offset(.Rows.Count) ' fill foundCells with a "dummy" found one to avoid 'If Not foundCells Is Nothing' check before any 'Union()' method call
Set found = .Find(what:=val, lookat:=xlPart, LookIn:=xlValues)
If Not found Is Nothing Then
firstAddress = found.Address
Do
Set foundCells = Union(foundCells, found)
Set found = .FindNext(found)
Loop While found.Address <> firstAddress
End If
Set foundCells = Intersect(.Cells, foundCells) ' get rid of the "dummy" found cell
End With
GetCellsWithValue = Not foundCells Is Nothing
End Function
that you could use in your "main" sub as follows:
Option Explicit
Sub CellMarked()
Dim fndlist As Variant, val As Variant, sht As Worksheet
Dim foundCells As Range
fndlist = Array("Column1", "Column2")
For Each sht In ActiveWorkbook.Worksheets
With sht
For Each val In fndlist
If GetCellsWithValue(sht, val, foundCells) Then foundCells.Font.Color = 255
Next
End With
Next sht
End Sub
Find Text Apply Fill
Sub CellMarked()
Dim rngFind As Range, rngU As Range
Dim fndlist As Variant
Dim strFirst As String
Dim i As Integer, x As Integer
fndlist = Array("Column1", "Column2")
For i = 1 To Worksheets.Count
With Worksheets(i)
For x = 0 To UBound(fndlist)
' Check if worksheet has no values.
If Not .Cells.Find("*", .Cells(.Rows.Count, Columns.Count), -4163, 2, 1) _
Is Nothing Then
' Find string.
Set rngFind = .Cells.Find(fndlist(x), _
.Cells(.Rows.Count, Columns.Count))
If Not rngFind Is Nothing Then
If Not rngU Is Nothing Then
Set rngU = Union(rngU, rngFind) ' All other occurrences.
Else
Set rngU = rngFind ' First occurrence.
End If
strFirst = rngFind.Address
' Check for other occurrences.
Do
Set rngFind = .Cells.FindNext(rngFind)
If rngFind.Address <> strFirst Then
Set rngU = Union(rngU, rngFind)
Else
Exit Do
End If
Loop
End If
End If
Next
' Apply formatting.
If Not rngU Is Nothing Then
rngU.Interior.Color = 255
' rngU.Font.Color = 255
Set rngU = Nothing
End If
End With
Next
End Sub
Change "strToFind" and try:
Option Explicit
Sub test()
Dim strToFind As String
Dim rng As Range, cell As Range
Dim ws As Worksheet
'String to Find is "Test"
strToFind = "Test"
With ThisWorkbook
For Each ws In .Worksheets
With ws
Set rng = .UsedRange
For Each cell In rng
If cell.Value = strToFind Then
cell.Interior.Color = RGB(255, 0, 0)
End If
Next cell
End With
Next ws
End With
End Sub
Related
I have written a code, which includes the FindNext method. All the code works so far, only when it gets to the FindNext method it shows an error saying the FindNext Object cannot be assigned.
However, I don't see where the Range Object (in this case "cell") is changed in any way for the FindNext method to not be able to assign it. Has anybody got an idea?
Please ignore any chunky written code, I'm very new with VBA ;)
Sub Search()
Dim wks As Worksheet
Set wks = ActiveSheet
Dim cell As Range, sngCell As Range
Dim Count As Long
Dim firstCell As String
Dim rg As Range, lastColumn As Long
Set cell = wks.Cells.Find(what:="Planned Supply at BP|SL (EA)", LookIn:=xlValues, lookat:=xlWhole)
firstCell = cell.Address
Do
With wks
lastColumn = .Cells(cell.Row, .Columns.Count).End(xlToLeft).Column
Set rg = Range(cell, .Cells(cell.Row, lastColumn))
End With
For Each sngCell In rg
If IsNumeric(sngCell.Value) = True Then
If sngCell.Value > 0 Then
If sngCell.Offset(-2, 0).Value > sngCell.Value Then
Count = Count + 1
End If
If Count = 0 Then
Rows(sngCell.Row - 2).Delete
Rows(sngCell.Row - 1).Delete
Rows(sngCell.Row).Delete
End If
End If
End If
Next
Set cell = wks.Cells.FindNext(cell)
Loop While cell.Address <> firstCell
End Sub
Find() in a loop is complex enough that it's worth splitting it out into a separate function. Here's a slightly different approach which reduces the complexity in your main Sub and allows you to focus on the business rules instead of the nuances of using Find()
Sub Search()
Dim wks As Worksheet
Dim cell As Range, sngCell As Range
Dim firstCell As String
Dim rg As Range, lastColumn As Long, matches As Collection
Set wks = ActiveSheet
Set matches = FindAll(wks.Cells, "Planned Supply at BP|SL (EA)")
For Each cell In matches
Debug.Print "Found:", cell.Address
Set rg = wks.Range(cell, wks.Cells(cell.Row, Columns.Count).End(xlToLeft))
For Each sngCell In rg.Cells
If IsNumeric(sngCell.Value) Then 'no need for `= True`
If sngCell.Value > 0 Then
If sngCell.Offset(-2, 0).Value <= sngCell.Value Then
sngCell.Offset(-2, 0).Resize(3).EntireRow.Delete
Exit For 'stop checking...
End If
End If
End If
Next
Next cell
End Sub
'Find all matches for `val` in `rng` and return as a collection
Public Function FindAll(rng As Range, val As String) As Collection
Dim rv As New Collection, f As Range, addr As String
Set f = rng.Find(what:=val, after:=rng.Cells(rng.Cells.CountLarge), _
LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
If Not f Is Nothing Then addr = f.Address()
Do Until f Is Nothing
rv.Add f
Set f = rng.FindNext(after:=f)
If f.Address() = addr Then Exit Do
Loop
Set FindAll = rv
End Function
I would like to delete a certain range (3 rows & 19 columns) in excel that contains specific string (lns) on the top left of the range, repeatedly. They appear in different rows and columns, but the range size is always the same.
I have written a following code but nothing happens:
For Each vCell In ActiveSheet.UsedRange
If InStr(vCell.Value, "*lns*") Then
Range(Cells(vCell.Row, vCell.Column), Cells(vCell.Row + 2, vCell.Column + 18)).Delete shift:=xlShiftUp
End If
Next
It might be faster to locate the cells with Find
Option Explicit
Sub MyMacro()
Const ROW_SIZE = 3
Const COL_SIZE = 19
Const SEARCH = "lns"
Dim rng As Range, cel As Range
Dim n As Integer, s As Long
Set rng = ActiveSheet.UsedRange
Set cel = rng.Find(SEARCH, LookIn:=xlValues, lookat:=xlPart, _
searchdirection:=xlPrevious)
Do While Not cel Is Nothing
cel.Resize(ROW_SIZE, COL_SIZE).Delete shift:=xlShiftUp
n = n + 1
Set cel = rng.FindPrevious
If n > 1000 Then MsgBox "Code Error in Do Loop", vbCritical: Exit Sub
Loop
MsgBox n & " blocks deleted", vbInformation
End Sub
Delete Range 'Blocks'
Option Explicit
Sub DeleteBlocks()
Const rCount As Long = 3
Const cCount As Long = 19
Const Criteria As String = "lns"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim rg As Range: Set rg = ActiveSheet.UsedRange
Dim fCell As Range
Set fCell = rg.Find(Criteria, rg.Cells(rg.Rows.Count, rg.Columns.Count), _
xlFormulas, xlPart, xlByRows)
Dim drg As Range ' Delete Range
Dim brg As Range ' Block Range
Dim fCount As Long ' Found Count
Dim FirstAddress As String
If Not fCell Is Nothing Then
FirstAddress = fCell.Address
Do
Set brg = Nothing
On Error Resume Next ' if in last 2 rows or 18 last columns
Set brg = Intersect(rg, fCell.Resize(rCount, cCount))
On Error GoTo 0
If Not brg Is Nothing Then
fCount = fCount + 1
Set drg = GetCombinedRange(drg, brg)
Set fCell = rg.FindNext(fCell)
End If
Loop Until fCell.Address = FirstAddress
If Not drg Is Nothing Then
drg.Delete Shift:=xlShiftUp
End If
If fCount = 1 Then
MsgBox "1 block deleted.", vbInformation, "DeleteBlocks"
Else
MsgBox fCount & " blocks deleted", vbInformation, "DeleteBlocks"
End If
Else
MsgBox "No blocks found.", vbExclamation, "DeleteBlocks"
End If
End Sub
Function GetCombinedRange( _
ByVal BuiltRange As Range, _
ByVal AddRange As Range) _
As Range
If BuiltRange Is Nothing Then
Set GetCombinedRange = AddRange
Else
Set GetCombinedRange = Union(BuiltRange, AddRange)
End If
End Function
I am new to VBA... I am trying delete all columns from Sheet1:"Template" ROW1/headers file that doesn't match any of the cell values on varList:"ColumnsList" (that is in Sheet3).
How do I select the headers or how do I select the row 1 range to search into?
Also, I have a runtime error 5 in this line: invalid procedure call or argument.
If Intersect(rng.Cells(1, i).EntireColumn, rngF) Is Nothing Then
Any kind soul that help me with that please?
Also, I need to do the same but with rows from Sheet1:"Template". I need to delete any row that doesn't CONTAIN any cell value from varList:"Agents" (that is in Sheet2).
Could you please help me out?
Maaaany thanks in advance!!!
Option Compare Text
Sub ModifyTICBData()
Dim varList As Variant
Dim lngarrCounter As Long
Dim rngFound As Range, rngToDelete As Range
Dim strFirstAddress As String
'Application.ScreenUpdating = False
varList = VBA.Array("ColumnsList") 'I want to keep columns with these values, NOT DELETE THEM
For lngarrCounter = LBound(varList) To UBound(varList)
With Sheets("Template").UsedRange
Set rngFound = .Find( _
What:=varList(lngarrCounter), _
Lookat:=xlWhole, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=True)
If Not rngFound Is Nothing Then
strFirstAddress = rngFound.Address
If rngToDelete Is Nothing Then
Set rngToDelete = rngFound
Else
If Application.Intersect(rngToDelete, rngFound.EntireColumn) Is Nothing Then
Set rngToDelete = Application.Union(rngToDelete, rngFound)
End If
End If
Set rngFound = .FindNext(After:=rngFound)
Do Until rngFound.Address = strFirstAddress
If Application.Intersect(rngToDelete, rngFound.EntireColumn) Is Nothing Then
Set rngToDelete = Application.Union(rngToDelete, rngFound)
End If
Set rngFound = .FindNext(After:=rngFound)
Loop
End If
End With
Next lngarrCounter
Dim rngDel As Range
Set rngDel = NotIntersectRng(Sheets("Template").UsedRange, rngToDelete)
If Not rngDel Is Nothing Then rngDel.EntireColumn.delete
'Application.ScreenUpdating = True
End Sub
Private Function NotIntersectRng(rng As Range, rngF As Range) As Range
Dim rngNI As Range, i As Long, j As Long
For i = 1 To rng.Columns.Count
**If Intersect(rng.Cells(1, i).EntireColumn, rngF) Is Nothing Then**
If rngNI Is Nothing Then
Set rngNI = rng.Cells(1, i)
Else
Set rngNI = Union(rngNI, rng.Cells(1, i))
End If
End If
Next i
If Not rngNI Is Nothing Then Set NotIntersectRng = rngNI
End Function
Delete Columns, Then Rows
Description
Deletes columns that in the first row do not contain values from a list. Then deletes rows that in the first column do not contain values from another list.
The Flow
Writes the values from range A2 to the last cell in Sheet3 to the Cols Array.
Writes the values from range A2 to the last cell in Sheet2 to the Agents Array.
Using CurrentRegion defines the DataSet Range (rng).
Loops through the cells (cel) in first row starting from the 2nd column and compares their values to the values from the Cols Array. If not found adds the cells to the Delete Range(rngDel).
Finally deletes the entire columns of the cells 'collected'.
Loops through the cells (cel) in first column starting from the 2nd row and compares their values to the values from the Agents Array. If not found adds the cells to the Delete Range(rngDel).
Finally deletes the entire rows of the cells 'collected'.
Informs the user of success or no action.
The Code
Option Explicit
Sub ModifyTICBData()
' Define workbook ('wb').
Dim wb As Workbook
Set wb = ThisWorkbook
' Define Columns List ('Cols').
Dim ws As Worksheet
Set ws = wb.Worksheets("Sheet3")
Dim rng As Range
Set rng = ws.Cells(ws.Rows.Count, "A").End(xlUp)
Dim Cols As Variant
Cols = ws.Range("A2", rng).Value
' Define Agents List ('Agents').
Set ws = wb.Worksheets("Sheet2")
Set rng = ws.Cells(ws.Rows.Count, "A").End(xlUp)
Dim Agents As Variant
Agents = ws.Range("A2", rng).Value
' Define DataSet Range ('rng').
Set rng = wb.Worksheets("Template").Range("A1").CurrentRegion
Application.ScreenUpdating = False
' Define Delete Range ('rngDel') for Columns.
Dim rngDel As Range
Dim cel As Range
For Each cel In rng.Rows(1).Resize(, rng.Columns.Count - 1) _
.Offset(, 1).Cells
If IsError(Application.Match(cel.Value, Cols, 0)) Then
collectCells rngDel, cel
End If
Next cel
' Delete Columns.
Dim AlreadyDeleted As Boolean
If Not rngDel Is Nothing Then
rngDel.EntireColumn.Delete
Else
AlreadyDeleted = True
End If
' Define Delete Range ('rngDel') for Agents.
Set rngDel = Nothing
For Each cel In rng.Columns("A").Resize(rng.Rows.Count - 1) _
.Offset(1).Cells
If IsError(Application.Match(cel.Value, Agents, 0)) Then
collectCells rngDel, cel
End If
Next cel
' Delete Agents (Rows).
If Not rngDel Is Nothing Then
rngDel.EntireRow.Delete
AlreadyDeleted = False
End If
Application.ScreenUpdating = True
' Inform user
If Not AlreadyDeleted Then
MsgBox "The data was succesfully deleted.", vbInformation, "Success"
Else
MsgBox "The data had already been deleted.", vbExclamation, "No Action"
End If
End Sub
Sub collectCells(ByRef CollectRange As Range, CollectCell As Range)
If Not CollectCell Is Nothing Then
If Not CollectRange Is Nothing Then
Set CollectRange = Union(CollectRange, CollectCell)
Else
Set CollectRange = CollectCell
End If
End If
End Sub
I want to find a specific word in a range of cells then highlight it in red. To do so I created this code but it just worked on one line and highlighted all the cell text:
Sub Find_highlight()
Dim ws As Worksheet
Dim match As Range
Dim findMe As String
Set ws = ThisWorkbook.Sheets("MYSHEET")
findMe = "Background"
Set match = ws.Range("G3:G1362").Find(findMe)
match.Font.Color = RGB(255, 0, 0)
End Sub
Let's say your excel file looks like htis
To color specific word, you have to use the cell's .Characters property. You need to find where does the word start from and then color it.
Try this
Option Explicit
Sub Sample()
Dim sPos As Long, sLen As Long
Dim aCell As Range
Dim ws As Worksheet
Dim rng As Range
Dim findMe As String
Set ws = ThisWorkbook.Sheets("MYSHEET")
Set rng = ws.Range("G3:G1362")
findMe = "Background"
With rng
Set aCell = .Find(What:=findMe, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
sPos = InStr(1, aCell.Value, findMe)
sLen = Len(findMe)
aCell.Characters(Start:=sPos, Length:=sLen).Font.Color = RGB(255, 0, 0)
End If
End With
End Sub
OUTPUT
i made some change to be more general and accurate
Option Explicit
Sub HIGHLIGHTER()
Dim sPos As Long, sLen As Long
Dim rng As Range
Dim findMe As String
Dim i As Integer
Set rng = Application.InputBox(Prompt:= _
"Please Select a range", _
Title:="HIGHLIGHTER", Type:=8)
findMe = Application.InputBox(Prompt:= _
"FIND WHAT?(YOU CAN USE PATTERN USED IN LIKE OPERATOR ", _
Title:="HIGHLIGHTER", Type:=2)
For Each rng In rng
With rng
If rng.Value Like "*" & findMe & "*" Then
If Not rng Is Nothing Then
For i = 1 To Len(rng.Value)
sPos = InStr(i, rng.Value, findMe)
sLen = Len(findMe)
If (sPos <> 0) Then
rng.Characters(Start:=sPos, Length:=sLen).Font.Color = RGB(255, 0, 0)
i = sPos + Len(findMe) - 1
End If
Next i
End If
End If
End With
Next rng
End Sub
added an option to loop
Option Explicit
Sub Macro1()
Dim sPos As Long, sLen As Long
Dim aCell As Range
Dim ws As Worksheet
Dim rng As Range
Dim findMe As String
Set ws = ThisWorkbook.Sheets("Sheet2")
Set rng = ws.Range("A3:A322")
findMe = "find"
For Each rng In Selection
With rng
Set aCell = .Find(What:=findMe, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
sPos = InStr(1, aCell.Value, findMe)
sLen = Len(findMe)
aCell.Characters(Start:=sPos, Length:=sLen).Font.Color = RGB(0, 255, 0)
End If
End With
Next rng
End Sub
I too made some changes to allow for searching multiple words at the same time. I also took away the prompts and hard coded the search words. The only issue left is to make the search non-case sensitive...
Sub HIGHLIGHTER()
Dim sPos As Long, sLen As Long
Dim rng As Range
Dim findMe As String
Dim i As Integer
Dim t As Integer
Dim SearchArray
SearchArray = Array("WORD1", "WORD2")
For t = 0 To UBound(SearchArray)
Set rng = Range("N2:N10000")
findMe = SearchArray(t)
For Each rng In rng
With rng
If rng.Value Like "*" & findMe & "*" Then
If Not rng Is Nothing Then
For i = 1 To Len(rng.Value)
sPos = InStr(i, rng.Value, findMe)
sLen = Len(findMe)
If (sPos <> 0) Then
rng.Characters(Start:=sPos, Length:=sLen).Font.Color = RGB(255, 0, 0)
rng.Characters(Start:=sPos, Length:=sLen).Font.Bold = True
i = sPos + Len(findMe) - 1
End If
Next i
End If
End If
End With
Next rng
Next t
End Sub
I have a workbook with two sheets. On Sheet A, I have changed the interior color of some cells. I would like to find cells in Sheet B with matching text and set them to have the same interior color. However, when I get to hRow = Application..., I receive an error that The application does not support this object or property. I've been searching for similar functions, but I am not having any success finding a good way to match text without looping through each cell in a range.
Public Sub MatchHighlight()
Dim lRow As Integer
Dim i As Integer
Dim hRow As Integer
Dim LookUpRange As Range
Set LookUpRange = Worksheets("HR - Highlight").Range("C2:C104")
Dim compare As Range
Set compare = Worksheets("Full List").Range("C2:C277")
lRow = Worksheets("Full List").UsedRange.Rows.Count
For i = 2 To lRow
hRow = Application.Worksheets("Full List").WorksheetFunction.Match(compare.Range("C" & i).Text, LookUpRange, 0)
If Not IsNull(hRow) Then
compare.Range("C" & i).Interior.Color = LookUpRange.Range("C" & hRow).Interior.Color
End If
Next i
End Sub
Sub MatchHighlight()
Dim wsHighlight As Worksheet
Dim wsData As Worksheet
Dim rngColor As Range
Dim rngFound As Range
Dim KeywordCell As Range
Dim strFirst As String
Set wsHighlight = Sheets("HR - Highlight")
Set wsData = Sheets("Full List")
With wsData.Columns("C")
For Each KeywordCell In wsHighlight.Range("C2", wsHighlight.Cells(Rows.Count, "C").End(xlUp)).Cells
Set rngFound = .Find(KeywordCell.Text, .Cells(.Cells.Count), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
Set rngColor = rngFound
Do
Set rngColor = Union(rngColor, rngFound)
Set rngFound = .Find(KeywordCell.Text, rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
rngColor.Interior.Color = KeywordCell.Interior.Color
End If
Next KeywordCell
End With
End Sub
To get exactly what I wanted, I used #tigeravatar's code as a base and ended up with the following:
Sub MatchHighlight()
Dim wsHighlight As Worksheet
Dim wsData As Worksheet
Dim rngColor As Range
Dim rngFound As Range
Dim KeywordCell As Range
Dim strFirst As String
Dim rngPicked As Range
Set rngPicked = Application.InputBox("Select Cell", Type:=8)
Set wsHighlight = Sheets("HR - Highlight")
Set wsData = Sheets("Full List")
With wsData.Columns("C")
For Each KeywordCell In wsHighlight.Range("C2", wsHighlight.Cells(Rows.Count, "C").End(xlUp)).Cells
Set rngFound = .Find(KeywordCell.Text, .Cells(.Cells.Count), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
Set rngColor = rngFound
Do
Set rngColor = Union(rngColor, rngFound)
Set rngFound = .Find(KeywordCell.Text, rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
Set rngColor = rngColor.Offset(0, -2).Resize(1, 3)
If KeywordCell.Interior.Color = rngPicked.Interior.Color Then
rngColor.Interior.Color = KeywordCell.Interior.Color
End If
End If
Next KeywordCell
End With
End Sub
Only real differences are that I let the user pick the color of cells they're trying to match, I only change the interior color when it matches the color picked, and I change the color of the whole row.
This can be done much much faster with:
Option Explicit
Sub MatchHighlight()
Dim FullListCell As Range
Dim HighlightMasterCell As Range
Dim FullList As Range
Dim HighlightMaster As Range
Dim lastRow As Range
'find last row in FullList
Set lastRow = Range("C").End(xlDown)
Set HighlightMaster = ThisWorkbook.Sheets("kleuren_medewerkers").Range("A1:A100")
Set FullList = Range(Range("C2"), ActiveSheet.Cells(lastRow.Row, 3)) 'change the number 3 to include more columns but use the lastrow of column C
For Each HighlightMasterCell In HighlightMaster
For Each FullListCell In FullList
If FullListCell .Value = HighlightMasterCell.Value Then
FullListCell.Interior.Color= HighlightMasterCell.Interior.Color
End If
Next
Next
End Sub