VBA script that format celle with different color, slow spreadsheet - excel

I'm brand new to VBA and this is my first VBA script, it seems good enough, but it's made my spreadsheet really slow, I can do something to optimize it.
The script runs through some defined columns and checks for content "A" "S" and so on, and if the content matches, the script must color the cell a specific color and also the cell on the right
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim My_Range As Range
Set My_Range = Worksheets("Sæson").Range("J10:J40,Q10:Q39,X10:X40,AE10:AE39,AL10:AL40,AS10:AS40,AZ10:AZ39,BG10:BG40,BN10:BN39,BU10:BU40,CB10:CB40,CI10:CI38,CP10:CP40")
For Each cell In My_Range
If cell.Value = "S" Then
cell.Interior.Color = RGB(0, 255, 255)
cell.Offset(0, 1).Interior.Color = RGB(0, 255, 255)
ElseIf cell.Value = "FE" Then
cell.Interior.Color = RGB(255, 192, 0)
cell.Offset(0, 1).Interior.Color = RGB(255, 192, 0)
ElseIf cell.Value = "SF" Then
cell.Interior.Color = RGB(255, 192, 0)
cell.Offset(0, 1).Interior.Color = RGB(255, 192, 0)
ElseIf cell.Value = "T" Then
cell.Interior.Color = RGB(49, 255, 33)
cell.Offset(0, 1).Interior.Color = RGB(49, 255, 33)
ElseIf cell.Value = "TK" Then
cell.Interior.Color = RGB(0, 176, 240)
cell.Offset(0, 1).Interior.Color = RGB(0, 176, 240)
ElseIf cell.Value = "TH" Then
cell.Interior.Color = RGB(255, 153, 204)
cell.Offset(0, 1).Interior.Color = RGB(255, 153, 204)
ElseIf cell.Value = "SY" Then
cell.Interior.Color = RGB(255, 0, 0)
cell.Offset(0, 1).Interior.Color = RGB(255, 0, 0)
Else
cell.Interior.Color = xlNone
cell.Offset(0, 1).Interior.Color = xlNone
End If
Next
End Sub

As first option, you can also try to disable the ScreenUpdating option during the macro execution.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'variable declarations
'disable screen updating
Application.ScreenUpdating = False
'...
'code
'...
're-enable screen updating
Application.ScreenUpdating = True
End Sub

Apply Criteria Colors
It is assumed that the cells of the source range contain formulas. This will reapply colors to the whole range automatically (event-driven) each time the worksheet recalculates not necessarily meaning that a value in the range has changed (not quite efficient). It should be very fast on this small range though.
If the cells contain values then you can manually run applyCriteriaColors to get the desired result. Also, a solution would then be a different code written for the Worksheet_Change event (you cannot use this one).
Copy the codes to the appropriate modules.
Adjust the values in the constants section.
Standard Module e.g. Module1
Option Explicit
Sub applyCriteriaColors()
Const wsName As String = "Sheet1"
' The number of columns to apply the color to.
Const ColCount As Long = 2
' "cRangesList" has to contain a list of addresses of ONE-column ranges.
Const cRangesList As String = "J10:J40,Q10:Q39,X10:X40,AE10:AE39," _
& "AL10:AL40,AS10:AS40,AZ10:AZ39,BG10:BG40,BN10:BN39,BU10:BU40," _
& "CB10:CB40,CI10:CI38,CP10:CP40"
' "CriteriaList" and "CellColors" have to have the same number of elements.
' Note that "Ranges" has the same number of elements (ranges) as well.
Const CriteriaList As String = "S,FE,SF,T,TK,TH,SY"
Dim CellColors As Variant: CellColors = VBA.Array( _
16776960, 49407, 49407, 2228017, 15773696, 13408767, 255)
' Write values from Criteria List to Criteria Array.
Dim Criteria() As String: Criteria = Split(CriteriaList, ",")
' Calculate Ranges Count (not to be confused with "aCount").
Dim rCount As Long: rCount = UBound(Criteria) + 1
' Define Ranges Array.
Dim Ranges() As Range: ReDim Ranges(1 To rCount)
Dim wb As Workbook: Set wb = ThisWorkbook
' Define Source Range.
Dim srg As Range: Set srg = wb.Worksheets(wsName).Range(cRangesList)
' Calculate Source Range Areas Count, the number of elements in Data Array.
Dim aCount As Long: aCount = srg.Areas.Count
' Define Data Array (of Arrays).
Dim Data As Variant: ReDim Data(1 To aCount)
' Define One-Cell Array.
Dim OneCell As Variant: ReDim OneCell(1 To 1, 1 To 1)
Dim arg As Range ' Source Range Current Area
Dim cValue As Variant ' Current Value
Dim cMatch As Variant ' Current Match
Dim n As Long ' Source Range Areas Counter, Ranges Array Ranges Counter
Dim i As Long ' Current Array (of Data Array) Rows Counter
For n = 1 To aCount
' Write values from current area ('srg.Areas(n)') of Source Range
' ('srg') to current array ('Data(n)') of Data Array ('Data').
Set arg = srg.Areas(n) '.Columns(1) ' ... ONE-column ranges
If arg.Rows.Count > 1 Then
Data(n) = arg.Value
Else
Data(n) = OneCell: Data(1, 1) = arg.Value
End If
For i = 1 To UBound(Data(n), 1)
cValue = Data(n)(i, 1)
If Not IsError(cValue) Then
If Len(cValue) > 0 Then
' Attempt to find a match in Criteria Array.
cMatch = Application.Match(cValue, Criteria, 0)
If IsNumeric(cMatch) Then
' Combine matched cell resized by "ColCount"
' with 'associated' range in Ranges Array.
If Ranges(cMatch) Is Nothing Then
Set Ranges(cMatch) _
= arg.Cells(i).Resize(, ColCount)
Else
Set Ranges(cMatch) = Union(Ranges(cMatch), _
arg.Cells(i).Resize(, ColCount))
End If
End If
End If
End If
Next i
Next n
Application.ScreenUpdating = False
' Reset colors. Note that "Resize" doesn't work with multi-area ranges.
For n = 1 To aCount
srg.Areas(n).Resize(, ColCount).Interior.Color = xlNone
Next n
' Apply colors to the 'combined' ranges.
For n = 1 To rCount
If Not Ranges(n) Is Nothing Then
Ranges(n).Interior.Color = CellColors(n - 1)
End If
Next n
Application.ScreenUpdating = True
End Sub
Sheet Module e.g. Sheet1
Option Explicit
Private Sub Worksheet_Calculate()
applyCriteriaColors
End Sub

Related

Excel VBA - Change cell color based on value

In the table in the indicated range, I would like to mark the cell with the value in white, and the other cells with no value in gray. I have code but it doesn't produce any result. There is no error either. What to change to make it work
For Each cell In wbMe.Sheets("page3").Range("B76:K89")
If cell.Value = "Yes" Then cell.Interior.ColorIndex = 10
If cell.Value = "No" Then cell.Interior.ColorIndex = 3
Next cell
Please, run the next code. It will automatically place conditional formatting in the range you need:
Sub makeCondFormatting()
Dim sh As Worksheet, rng As Range, cond1 As FormatCondition, cond2 As FormatCondition
Set sh = ActiveSheet
Set rng = sh.Range("B76:K89")
With rng
.FormatConditions.Delete
Set cond1 = .FormatConditions.Add(xlExpression, Formula1:="=" & rng.cells(1, 1).Address(0, 0) & " <> """"")
Set cond2 = .FormatConditions.Add(xlExpression, Formula1:="=" & rng.cells(1, 1).Address(0, 0) & " = """"")
End With
With cond1
.Interior.color = RGB(255, 255, 255)
End With
With cond2
.Interior.color = RGB(197, 198, 198)
End With
End Sub
It will make the range cells change their interior color automatically when the cell is empty, or not.
try this code
Sub SetColor()
Dim r As Range
Set r = ThisWorkbook.ActiveSheet.Range("B2:B7")
Dim white As Long
white = RGB(255, 255, 255)
Dim grey As Long
grey = RGB(200, 200, 200)
Dim c As Range
For Each c In r
If c.Value2 = 1 Then c.Interior.Color = white
If c.Value2 = 0 Then c.Interior.Color = grey
Next
End Sub
As Ike mentions for Empty values you can use this
Sub SetColor()
Dim r As Range
Set r = ThisWorkbook.ActiveSheet.Range("B2:B7")
Dim white As Long
white = RGB(255, 255, 255)
Dim grey As Long
grey = RGB(200, 200, 200)
Dim c As Range
For Each c In r
If IsEmpty(c.Value2) Then
c.Interior.Color = white
'OR
'c.Interior.Pattern = xlNone
Else
c.Interior.Color = grey
End If
Next
End Sub

Macro not working when I "Call" it from another macro, but does work when I select it individually

I have a formatting macro below:
Sub Colour_whole_sheet()
Dim lastRow As Long
Dim lastColumn As Long
lastRow = Range("A1").End(xlDown).Row
lastColumn = Range("A3").End(xlToRight).Column
'Colour alternate rows purple / white
For Each cell In Range(Cells(1, 1), Cells(lastRow, lastColumn))
If cell.Row Mod 2 = 1 Then
cell.Interior.Color = RGB(242, 230, 255)
Else
cell.Interior.Color = RGB(255, 255, 255)
End If
Next cell
End Sub
It doesn't run when I call it from another macro, which is just:
Sub Run_macros()
[A bunch of other subs]
Call Colour_whole_sheet
[A bunch of other subs]
End Sub
It doesn't come up with an error - it just doesn't do anything. But when I select it specifically on its own, from View > Macros > View Macros > Run, it works fine.
Do you know why this might be?
EDIT:
Sub Colour_whole_sheet()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Calendar")
Dim lastRow As Long
Dim lastColumn As Long
lastRow = ws.Range("A1").End(xlDown).Row
lastColumn = ws.Range("A3").End(xlToRight).Column
'Colour alternate rows purple / white
For Each cell In ws.Range(ws.Cells(1, 1), ws.Cells(lastRow, lastColumn))
If cell.Row Mod 2 = 1 Then
cell.Interior.Color = RGB(242, 230, 255)
Else
cell.Interior.Color = RGB(255, 255, 255)
End If
Next cell
End Sub
you might be after this revision of your code
Sub Colour_whole_sheet(Optional sht As Variant)
If IsMissing(sht) Then Set sht = ActiveSheet ' if no argument is passed assume ActiveSheet
Dim lastRow As Long
Dim lastColumn As Long
Dim i As Long
With sht ' reference passed/assumed sheet object
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row ' best way to get a column last used cell row index
lastColumn = .Cells(3, .Columns.Count).End(xlToLeft).Column ' best way to get a row last used cell column index
'Colour alternate rows purple / white
With .Range("A1", Cells(lastRow, lastColumn)) ' reference all your range
.Interior.Color = vbWhite ' color it white
For i = 1 To .Rows.Count Step 2 ' loop through referenced range uneven rows
.Rows(i).Interior.Color = RGB(242, 230, 255) ' color them with purple
Next
End With
End With
End Sub
as you can see:
it always references some sheet(be it passed through sub argument or be it the active one)
it doesn't loop through all cells, but just through uneven rows
Here Range("A1") is not specified in which worksheet this range is. Always specify a worksheet for all your Range(), Cells(), Rows() and Columns() objects.
Otherwise it is very likely that your code runs on the wrong worksheet. Note that this is applicable to all your macros (not just this one). Check if you have specified a worksheet everywhere, or your code might randomly work or fail.
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1") 'your sheet name here
Then adjust the following lines:
lastRow = ws.Range("A1").End(xlDown).Row
lastColumn = ws.Range("A3").End(xlToRight).Column
For Each cell In ws.Range(ws.Cells(1, 1), ws.Cells(lastRow, lastColumn))
Also note that you can format an Excel table to get rows alternated colored.
Additional notes:
The method you used is not reliable in finding the last used row/column. Better do it the other way round. Start in the very last row and go xlUp.
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row 'Last used row
lastColumn = ws.Cells(3, ws.Columns.Count).End(xlToLeft).Column 'last used column in row 3
Also you don't need to go through all cells. Looping throug rows would do.
Dim i As Long
For i = 1 To lastRow
If i Mod 2 = 1 Then
ws.Rows(i).Interior.Color = RGB(242, 230, 255)
Else
ws.Rows(i)..Interior.Color = RGB(255, 255, 255)
End If
Next i
or if you don't want to color the whole row but only up to the last used column
ws.Cells(i, lastColumn).Interior.Color
Note that coloring each row on on its own can slow down a lot if there are many rows. Therefore I suggest to collect all even/uneven rows in a reference and color it at once.
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row 'Last used row
lastColumn = ws.Cells(3, ws.Columns.Count).End(xlToLeft).Column 'last used
Dim EvenRows As Range
Dim OddRows As Range
Dim i As Long
For i = 1 To lastRow
If i Mod 2 = 1 Then
If OddRows Is Nothing Then
Set OddRows = ws.Rows(i)
Else
Set OddRows = Union(OddROws, ws.Rows(i))
End If
Else
If EvenRows Is Nothing Then
Set EvenRows = ws.Rows(i)
Else
Set EvenRows = Union(EvenRows, ws.Rows(i))
End If
End If
Next i
If Not OddRows Is Nothing Then OddRows.Interior.Color = RGB(242, 230, 255)
If Not EvenRows Is Nothing Then EvenRows.Interior.Color = RGB(255, 255, 255)

Excel Duplicate Finder in Seperate Columns

I've found this piece of code online and tried to modify it to do what i want. It works in a very basic way, where you assign it as a macro to a Form button, to find duplicates in 6 seperate columns, but is there a way of tidying up the code, and possibly automating it, so i don't have to press a form button to run the macro every time ?
Sub Check_Dups()
'Declaring variables
Dim Cell As Variant
Dim Source As Range
Dim Source2 As Range
Dim Source3 As Range
Dim Source4 As Range
Dim Source5 As Range
Dim Source6 As Range
'Initializing source range
Set Source = Range("E8:E105")
Set Source2 = Range("F8:F105")
Set Source3 = Range("G8:G105")
Set Source4 = Range("H8:H105")
Set Source5 = Range("I8:I105")
Set Source6 = Range("J8:J105")
'Removing any previous formatting from the source
Source.Interior.Color = RGB(255, 255, 255)
Source2.Interior.Color = RGB(255, 255, 255)
Source3.Interior.Color = RGB(255, 255, 255)
Source4.Interior.Color = RGB(255, 255, 255)
Source5.Interior.Color = RGB(255, 255, 255)
Source6.Interior.Color = RGB(255, 255, 255)
'Looping through each cell in the source range
For Each Cell In Source
'Checking whether value in cell already exist in the source range
If Application.WorksheetFunction.CountIf(Source, Cell) > 1 Then
'Highlighting duplicate values in red color
Cell.Interior.Color = RGB(255, 0, 0)
End If
Next
'Looping through each cell in the source range
For Each Cell In Source2
'Checking whether value in cell already exist in the source range
If Application.WorksheetFunction.CountIf(Source2, Cell) > 1 Then
'Highlighting duplicate values in red color
Cell.Interior.Color = RGB(255, 0, 0)
End If
Next
'Looping through each cell in the source range
For Each Cell In Source3
'Checking whether value in cell already exist in the source range
If Application.WorksheetFunction.CountIf(Source3, Cell) > 1 Then
'Highlighting duplicate values in red color
Cell.Interior.Color = RGB(255, 0, 0)
End If
Next
'Looping through each cell in the source range
For Each Cell In Source4
'Checking whether value in cell already exist in the source range
If Application.WorksheetFunction.CountIf(Source4, Cell) > 1 Then
'Highlighting duplicate values in red color
Cell.Interior.Color = RGB(255, 0, 0)
End If
Next
'Looping through each cell in the source range
For Each Cell In Source5
'Checking whether value in cell already exist in the source range
If Application.WorksheetFunction.CountIf(Source5, Cell) > 1 Then
'Highlighting duplicate values in red color
Cell.Interior.Color = RGB(255, 0, 0)
End If
Next
'Looping through each cell in the source range
For Each Cell In Source6
'Checking whether value in cell already exist in the source range
If Application.WorksheetFunction.CountIf(Source6, Cell) > 1 Then
'Highlighting duplicate values in red color
Cell.Interior.Color = RGB(255, 0, 0)
End If
Next
End Sub
Some other ways to automate a macro are WorkSheet_Change and Workbook_Open events, but I would stick with Button_Click to initiate your macro.
The below code can be use to loop through the range in each column and color duplicate values using AutoFilter. If you want to color each group a different color, you can use the randomized RGB line of code.
Sub ColorDuplicates_wRGB()
'This macro loops thru each cell, if the cell has duplicates in the range, it filters the range using the cell value,
'then colors the visible cells in the range Red or with a unique color using RGB Colors.
'xlNone in the If statement, skips previous colored cells.
'Works with both sorted and unsorted data.
Dim ws As Worksheet, rng As Range, cel As Range, colr As String, i As Long 'Define your variables
Application.ScreenUpdating = False 'I hate to see the screen flickering
Set ws = ThisWorkbook.Sheets("Sheet1") 'identify the worksheet variable; you will need to change the sheet reference
For i = 5 To 9 'To loop through each column
Set rng = ws.Range(ws.Cells(8, i), ws.Cells(105, i))
rng.Interior.ColorIndex = xlNone 'clear interior color for all cells in range
For Each cel In rng 'Loop
If WorksheetFunction.CountIf(rng, cel.Value) > 1 And cel.Interior.ColorIndex = xlNone Then
'Filter using cel.Value
rng.AutoFilter field:=1, Criteria1:=cel.Value
colr = RGB(255, 0, 0)
'If you want different colors for each duplicate group use the next line
'colr = RGB(Int((255 - 1 + 1) * Rnd() + 1), Int((255 - 1 + 1) * Rnd() + 1), Int((255 - 1 + 1) * Rnd() + 1))
'Select the visible cells in range and color, the -1 removes the blank row at the end caused by Offset
rng.Offset(1).Resize(rng.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Interior.Color = colr
rng.AutoFilter 'reset filter
End If
Next cel
Next i
Application.ScreenUpdating = True
End Sub

In Excel how to replace cell interior color with two conditions

In my Excel sheet, First condition is to Highlight the intersected cell with BLUE based on text matching of row and column.
Second condition: The cell values which are highlighted in Blue must Change to red if the cell value(date Format) is less than today's date.
I am able to fulfill first condition but failing to satisfy second condition.
The Excel data Looks like below:
First Condition:
Second Condition:Problem I am facing to get red interior
I am trying with a VBA Code as below:
Sub RunCompare()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim cols As Range, rws As Range
Dim lastRow As Integer: lastRow = ws.UsedRange.Rows.Count
Dim lastColumn As Integer: lastColumn = ws.UsedRange.Columns.Count
For Each cols In ws.Range(ws.Cells(4, 1), ws.Cells(4, lastColumn))
If cols.Value <> vbNullString Then
For Each rws In ws.Range("A1:A" & lastRow)
'first condition statement
If (rws.Value = cols.Value) Then
ws.Cells(rws.Row, cols.Column).Interior.Color = RGB(15, 219, 241)
End If
'second condition statement
If (rws.Value = cols.Value) < Date Then
ws.Cells(rws.Row, cols.Column).Interior.Color = RGB(255, 0, 0)
End If
Next
End If
Next
End Sub
This can easily be done with conditional formatting.
Add two rules based on these formulas:
RED: =AND($A3=B$1,B3<>"",B3<TODAY()).
BLUE: =AND($A3=B$1,B3<>"")
If you really want to keep your current VBA, you could change
If (rws.Value = cols.Value) < Date Then
to
If (rws.Value = cols.Value) And (ws.Cells(rws.Row, cols.Column).Value < Date) Then
Or you could simplify further, by moving the RED condition inside the existing BLUE condition check (rws.Value = cols.Value must be true for both red and blue.)
If rws.Value = cols.Value Then
With ws.Cells(rws.Row, cols.Column)
If .Value < Date Then
.Interior.Color = RGB(255, 0, 0) ' RED
Else
.Interior.Color = RGB(15, 219, 241) ' BLUE
End If
End With
End If
Is this solution OK for you?
Dim ws As Worksheet
Dim col As Integer
Dim row As Integer
Dim lastRow As Integer
Dim lastCol As Integer
Dim OK As Boolean
Set ws = ActiveSheet
lastRow = ws.UsedRange.Rows.Count
lastCol = ws.UsedRange.Columns.Count
For col = 1 To lastCol
For row = 2 To lastRow
If ws.Cells(row, 1).Value = ws.Cells(1, col).Value Then
If ws.Cells(row, col) < Date Then
ws.Cells(row, col).Interior.Color = RGB(255, 0, 0)
Else
ws.Cells(row, col).Interior.Color = RGB(15, 219, 241)
End If
End If
Next
Next

Conditional formatting cell error

The code I have below is checking two worksheets in order to see if the values inserted in the specific column are similar. For example, it looks to see if the values inserted in column A from sheet1 are the same as the values inserted in sheet2 column B. If yes, then the cells in sheet1 column A remain 'white' otherwise, they turn 'red'. The code works without any problems and really fast.
My problem is the following. Lets say:
I need to insert a value in sheet1 - Column A, cell A2 to A5 that match the ones from sheet2 Column B.
sheet2 column B has the following values: car, house, garden, city, country.
If in A2 I write car, A3 I leave empty, A4 country and A5 car, then A2, A4 and A5 will remain 'white' because those values are in sheet2 - Column B. However, A3 turns red even though the cell is empty - this my problem. How can I make the code to not take into consideration if that cell is empty? It should not turn red because I left the cell empty and it is not comparing anything...
I hope I explain myself somehow. Thanks for your help!
Private Sub CommandButton1_Click()
Set wb = Excel.ActiveWorkbook
Set aRec = wb.Worksheets(1)
Set bRec = wb.Worksheets(2)
Application.ScreenUpdating = False
For a = 2 To aRec.Cells(Rows.Count, "A").End(xlUp).Row
match = Application.match(aRec.Cells(c, 1).Value, bRec.Columns(2), 0)
If IsError(match) Then
aRec.Cells(c, 1).Interior.Color = RGB(255, 0, 0)
Else
aRec.Cells(c, 1).Interior.Color = RGB(255, 255, 255)
End If
Next c
End Sub
Like?
Private Sub CommandButton1_Click()
Set wb = Excel.ActiveWorkbook
Set aRec = wb.Worksheets(1)
Set bRec = wb.Worksheets(2)
Application.ScreenUpdating = False
For a = 2 To aRec.Cells(Rows.Count, "A").End(xlUp).Row
Match = Application.Match(aRec.Cells(a, 1).Value, bRec.Columns(2), 0)
If IsError(Match) And Not IsEmpty(aRec.Cells(a, 1)) Then
aRec.Cells(a, 1).Interior.Color = RGB(255, 0, 0)
Else
aRec.Cells(a, 1).Interior.Color = RGB(255, 255, 255)
End If
Next a
End Sub
With correct loop variable, Option Explicit, type declarations and switching screenupdating back on
Option Explicit
Private Sub CommandButton1_Click()
Dim wb As Workbook
Dim aRec As Worksheet
Dim bRec As Worksheet
Dim a As Long
Dim Match As Variant
Set wb = ActiveWorkbook
Set aRec = wb.Worksheets(1)
Set bRec = wb.Worksheets(2)
Application.ScreenUpdating = False
For a = 2 To aRec.Cells(Rows.Count, "A").End(xlUp).Row
Match = Application.Match(aRec.Cells(a, 1).Value, bRec.Columns(2), 0)
If IsError(Match) And Not IsEmpty(aRec.Cells(a, 1)) Then
aRec.Cells(a, 1).Interior.Color = RGB(255, 0, 0)
Else
aRec.Cells(a, 1).Interior.Color = RGB(255, 255, 255)
End If
Next a
Application.ScreenUpdating = True
End Sub

Resources