Variables in Conditional Formatting Formula1 - excel

I'm trying to use variables in the FormatCondition Formula1 property. The variables will be cell references. However, I can't get the syntax right. The two bits I'm having trouble with in the code below are: "=(C$3:J$10=""CM"")" and "=($C3:$J10=""RM"")".
The aim of this is to highlight a column with CM in a certain cell, and to highlight a row with RM in a certain cell. The number of columns and rows will increase and decrease, hence the use of variables.
Or if this isn't the right way or the best way, alternatives would be appreciated.
The code is:
Private Sub Workbook_Open()
Application.ScreenUpdating = False
'Rows
Dim iRowA As Integer, iRowB As Integer, iRowC As Integer
Dim iRowDataStart As Integer, iRowLast As Integer
'Columns
Dim iColX As Integer, iColY As Integer, iColZ As Integer
Dim iColDataStart As Integer, iColLast As Integer
'Ranges
Dim rAll As Range
Dim rRowB As Range, rColY As Range
Dim rRowMark As Range, rColMark As Range
'String
Dim sString As String
'Assign values, normally these would be variable values, not assigned
iRowA = 1: iRowB = 2: iRowC = 3
iRowDataStart = 4: iRowLast = 10
iColX = 1: iColY = 2: iColZ = 3
iColDataStart = 4: iColLast = 10
'Set ranges
Set rAll = Range(Cells(iRowA, iColX), Cells(iRowLast, iColLast))
Set rRowB = Range(Cells(iRowB, iColZ), Cells(iRowLast, iColLast))
Set rColY = Range(Cells(iRowC, iColY), Cells(iRowLast, iColLast))
Set rRowMark = Range(Cells(iRowC, iColZ), Cells(iRowLast, iColLast))
Set rColMark = Range(Cells(iRowC, iColZ), Cells(iRowLast, iColLast))
'Delete all CF currently in the worksheet
With rAll
.FormatConditions.Delete
End With
'Format column with Column Mark
sString = "=(C$3:J$10=""CM"")"
With rRowB
.FormatConditions.Add _
Type:=xlExpression, _
Formula1:=sString
.FormatConditions(.FormatConditions.Count).SetFirstPriority
With .FormatConditions(1)
.Interior.Color = RGB(196, 189, 151)
.StopIfTrue = False
End With
End With
'Format row with Row Mark
sString = "=($C3:$J10=""RM"")"
With rColY
.FormatConditions.Add _
Type:=xlExpression, _
Formula1:=sString
.FormatConditions(.FormatConditions.Count).SetFirstPriority
With .FormatConditions(1)
.Font.ColorIndex = 2
.Interior.Color = RGB(127, 127, 127)
.StopIfTrue = False
End With
End With
Range("A1").Select
Application.StatusBar = False
Application.CutCopyMode = False
End Sub

You just need to dynamically set your ranges by getting last row and column of your data where you can find many examples here like this one. Something like:
Dim r As Range
Dim lr As Long, lc As Long
Dim formula As String
With Sheet1 '~~> change to your actual sheet
lr = .Range("C" & .Rows.Count).End(xlUp).Row '~~> based on C, adjust to suit
lc = .Cells(3, .Columns.Count).End(xlToLeft).Column '~~> based on row 3
Set r = .Range(.Cells(3, 3), .Cells(lr, lc))
formula = "=(" & r.Address & "=""CM"")"
'~~> formatting code here
End With
Or you can try what I've posted here about Conditional Formatting which of course can be automated as I posted HERE and HERE. Something like:
formula = "=C3=""CM"""
[C3].FormatConditions.Add xlExpression, , formula
With [C3].FormatConditions(1)
.Interior.Color = RGB(196, 189, 151)
.ModifyAppliesToRange r
End With
HTH.

Related

Highlighting Values In Column to Column Comparison using VBA

I am attempting to compare two columns in two separate sheets, each column contains data that is a string. My issue is that there is data in one column that is identical to the other in separate rows; therefore I have to check the entire column for the data before moving to the next. I am very inexperienced with VBA and am trying to make one portion of my job easier rather than comparing the columns by hand. I have piece wised the following code from research and trial and error. I am able to get the entire Column searched in my first Sheet, but only one value is being highlighted on the second sheet and then it is returning a value of "True" in the first column. I am unsure where I have gone wrong, any help is greatly appreciated!
Sub Better_Work_This_Time()
Dim FindString As String
Dim Rng As Range
ActiveCell = Sheets("Last Week").Range("A2").Activate
FindString = ActiveCell
Dim County As Integer
Count = Cells.CurrentRegion.rows.Count
For i = 2 To County
If Trim(FindString) <> "" Then
With Sheets("Current Week").Range("A:A")
Set Rng = .Find(What:=FindString, After:=.Cells(.Cells.Count), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True)
If Not Rng Is Nothing Then
ActiveCell.Font.Color = vbBlue
End If
End With
End If
If IsEmpty(FindString) Then
FindString = False
End If
ActiveCell.Offset(1, 0).Select
i = i + 1
Next
End Sub
Without using ActiveCell and using Match instead of Find.
Option Explicit
Sub Does_Work_This_Time()
Dim wb As Workbook, wsLast As Worksheet, wsCurrent As Worksheet
Dim FindString As String, ar, v
Dim LastRow As Long, i As Long, n As Long
Set wb = ThisWorkbook
' put current week values into array
Set wsCurrent = wb.Sheets("Current Week")
With wsCurrent
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
ar = .Range("A2:A" & LastRow).Value2
End With
' scan last week matching current week
Set wsLast = wb.Sheets("Last Week")
With wsLast
.Columns(1).Interior.Color = xlNone
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To LastRow
FindString = Trim(.Cells(i, "A"))
If Len(FindString) > 0 Then
v = Application.Match(FindString, ar, 0)
If IsError(v) Then
'no match
ElseIf ar(v, 1) = FindString Then ' case match
.Cells(i, "A").Interior.Color = RGB(128, 255, 128) ' light green
n = n + 1
End If
End If
Next
End With
MsgBox n & " rows matched"
End Sub

Change the font color in a cell based on the value in another cell

I would like to change the color of certain text in the cells based on the values in another cells. I have tried using conditional formatting but it does not work since I only wanted to change the color of particular words in the cells. I have googled a few VBA codes as well but still could not find the right one. Is there any VBA Code to enable this?
As shown in the example below (see image), I want to highlight ONLY the dates in Column B and C that match the dates in Column G. The day should remain the same.
For information, the values in Column B and C are formatted as text and the values in G are formatted as date.
Before
and this is basically what I wish for.
After
I have modified code appropriately as per your requirement in the comment.
Sub Change_Text_Color()
Dim Find_Text, Cell, Cell_in_Col_G, LastCell_inColG As Range
Dim StartChar, CharLen, LastUsedRow_inRange, LastUsedRow_inColB, _
LastUsedRow_inColC As Integer
LastUsedRow_inColB = Sheet1.Cells(Rows.count, "B").End(xlUp).Row
LastUsedRow_inColC = Sheet1.Cells(Rows.count, "C").End(xlUp).Row
LastUsedRow_inRange = Application.WorksheetFunction. _
Max(LastUsedRow_inColB, LastUsedRow_inColC)
Set LastCell_inColG = Sheet1.Cells(Rows.count, "G").End(xlUp)
For Each Cell In Range(Sheet1.Cells(2, 2), Cells(LastUsedRow_inRange, 3))
For Each Cell_in_Col_G In Range(Sheet1.Cells(2, 7), LastCell_inColG)
CharLen = Len(Cell_in_Col_G.Text)
Set Find_Text = Cell.Find(what:=Cell_in_Col_G.Text)
If Not Find_Text Is Nothing Then
StartChar = InStr(Cell.Value, Cell_in_Col_G.Text)
With Cell.Characters(StartChar, CharLen)
.Font.Color = RGB(0, 255, 0)
End With
End If
Next
Next
End Sub
Please let me know your feedback on it.
Use Characters:
With Range("a1")
.Characters(Start:=1, Length:=4).Font.Color=0
.Characters(Start:=5, Length:=10.Font.Color=255
End With
colours the first four letters black and the next ten in red.
Ref:
https://learn.microsoft.com/en-us/office/vba/api/excel.characters
I find filtering works well in these scenarios. Assuming that the format of your sheet is as it is in your sample sheets, try the code below:
Sub MarkDatesInCells()
Dim oWS As Worksheet: Set oWS = ThisWorkbook.Worksheets("Sheet3") '<- Change to the sheet name
Dim iLRToHighlight As Long, iStartChar As Long, iC As Long, iLR As Long
Dim oHighlightRng As Range, oUpdateRng As Range, oRng As Range
Dim sColName As String
' Turn off updating
Application.ScreenUpdating = False
Application.EnableEvents = False
With oWS
' Clear autofilter if exists
If .AutoFilterMode Then .AutoFilterMode = False
' Loop through all values specified in column G
iLRToHighlight = .Range("G" & .Rows.Count).End(xlUp).Row
For Each oHighlightRng In .Range("G2:G" & iLRToHighlight)
' Loop through column B and C
For iC = 2 To 3
' Set autofilter based on the value in column G
.UsedRange.AutoFilter iC, "=*" & oHighlightRng.Value
' Loop through all visible rows
iLR = .Cells(.Rows.Count, iC).End(xlUp).Row
If iLR > 1 Then
sColName = Left(Replace(.Cells(1, iC).Address, "$", ""), 1)
Set oUpdateRng = .Range(sColName & "2:" & sColName & iLR).SpecialCells(xlCellTypeVisible)
' Update each cell text
For Each oRng In oUpdateRng
iStartChar = InStr(1, oRng.Value, "- ", vbTextCompare) + 2
oRng.Characters(Start:=iStartChar, Length:=Len(oHighlightRng.Value)).Font.Color = 255
Next
End If
.AutoFilterMode = False
Next
Next
End With
' Turn on updating
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
EDIT
Based on your requirement to have this solution for a sheet with a table connected to a database, try the below code. I don't have a database that I can test the below code on so you might have to tinker with it a bit to get it right (i.e. the text that is highlight)
Sub MarkDatesInCellsInATable()
Dim oWS As Worksheet: Set oWS = ThisWorkbook.Worksheets("Sheet4") '<- Change to the sheet name
Dim iLRToHighlight As Long, iStartChar As Long, iC As Long, iLR As Long
Dim oHighlightRng As Range, oUpdateRng As Range, oRng As Range
Dim sColName As String
Dim oTable As ListObject: Set oTable = oWS.ListObjects("Table_ExceptionDetails.accdb") '<- Change to the table name
Application.ScreenUpdating = False
Application.EnableEvents = False
With oWS
' Reset autofilter
oTable.Range.AutoFilter
' Loop through all values specified in column G
iLRToHighlight = .Range("G" & .Rows.Count).End(xlUp).Row
For Each oHighlightRng In .Range("G2:G" & iLRToHighlight)
' Loop through column B and C
For iC = 2 To 3
' Set autofilter based on the value in column G
oTable.Range.AutoFilter iC, "=*" & oHighlightRng.Value & "*"
' Loop through all visible rows
iLR = .Cells(.Rows.Count, iC).End(xlUp).Row
If iLR > 1 Then
sColName = Left(Replace(.Cells(1, iC).Address, "$", ""), 1)
Set oUpdateRng = .Range(sColName & "2:" & sColName & iLR).SpecialCells(xlCellTypeVisible)
' Update each cell text
For Each oRng In oUpdateRng
iStartChar = InStr(1, oRng.Value, "- ", vbTextCompare) + 2
oRng.Characters(Start:=iStartChar, Length:=Len(oHighlightRng.Value)).Font.Color = 255
Next
End If
oTable.Range.AutoFilter
Next
Next
End With
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub

Getting error in condition formatting using VBA

I am working on a project in which I am comparing column D with column C of sheet("Backend") and the difference is shown in column E (in %). I'd like to highlight the % difference (column E) in RED color, where the difference is less than -10.00% and greater than 10.00%. Then would like to copy those items from column B corresponding each highlighted cell and paste it in sheet("UPDATER") beneath cell A7.
Attached is the screenshot for your reference
Sub check_date()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim wsData As Worksheet, Datasht As Worksheet, lRow As Integer
Set wsData = Sheets("UPDATER")
Set Datasht = Sheets("Backend")
lRow = Datasht.Cells(Rows.Count, 13).End(xlUp).Row
wsData.Range("M8:M" & lRow).Interior.ColorIndex = xlNone
wsData.Range("M8:M" & lRow).FormatConditions.Add Type:=xlExpression, Formula1:="=AND(M8>=EOMONTH(TODAY(),-2)+1,M8<EOMONTH(TODAY(),-1))"
wsData.Range("M8:M" & lRow).FormatConditions(wsData.Range("M8:M" & lRow).FormatConditions.Count).SetFirstPriority
With wsData.Range("M8:M" & lRow).FormatConditions(1).Interior
.Color = RGB(255, 255, 0)
.TintAndShade = 0
End With
wsData.Range("M8:M" & lRow).FormatConditions(1).StopIfTrue = False
Range("M8").Select
End Sub
Here's what I got. It's a bit of a drastic change but I'm hoping this is actually what you're going for.
Sub formatcondition()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim wsData As Worksheet, Datasht As Worksheet, lRow As Integer, My_Range As Range, i As Integer, iRow As Integer, cell As Variant, RowNum As Long, lRowUpdater As Long
Set wsData = Sheets("UPDATER")
Set Datasht = Sheets("Backend")
lRow = Datasht.Cells(Rows.Count, 5).End(xlUp).Row
lRowUpdater = wsData.Cells(Rows.Count, 1).End(xlUp).Row
RowNum = 8 'setting the first row in the UPDATER sheet
Datasht.Range("E1:E" & lRow).Interior.ColorIndex = xlNone 'Reset the color before running
wsData.Range("A8:D" & lRowUpdater + 8).ClearContents 'clear your updater sheet. Remove if not needed.
For i = 1 To lRow
On Error GoTo Continue
If Datasht.Range("E" & i).Value < -0.1 Or Datasht.Range("E" & i).Value > 0.1 Then 'If greater than or less than
Datasht.Range("E" & i).Interior.ColorIndex = 6 'Change the color of affected cells if you need that
wsData.Range(wsData.Cells(RowNum, 1), wsData.Cells(RowNum, 4)).Value = _
Datasht.Range(Datasht.Cells(i, 2), Datasht.Cells(i, 5)).Value 'straight copy the values from the cells as it loops rather than using copy/paste
wsData.Range(wsData.Cells(RowNum, 2), wsData.Cells(RowNum, 4)).NumberFormat = "0.00%" 'change the number format of outputted cells to percentages (if needed)
RowNum = RowNum + 1 'move to the next row in the output
End If
Continue:
Resume Nexti
Nexti:
Next i
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
EDIT:
For the date to add a year my version would be just adding to what I gave earlier. Instead we now add an AND function to contain the OR, then checking if the YEAR in the cell is the current year. If you're only wanting this year then we can also forgo the IF statement which was checking that if the current month was January it would incorporate December. But if thats not needed then:
=AND(OR(MONTH(NOW())=MONTH(M8),MONTH(NOW())-1=MONTH(M8)),YEAR(M8)=YEAR(NOW()))
Or
=AND(MONTH(M8)>=MONTH(NOW())-1,MONTH(M8)<MONTH(NOW())+1,YEAR(M8)=YEAR(NOW()))
Both the same length and do the same thing just in different way.

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

Merge cells when cell value match (different column row value)

I would like to write a Excel vba to merge cells according to their values and a reference cell in another column. Like the picture attached.
I have over 18000 Lines, with many of variation.
All the values within the line are in order rank.
enter image description here
This is the code that I based my VBA
Sub MergeCells()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim rngMerge As Range, cell As Range
Set rngMerge = Range("B2:C10")
MergeAgain:
For Each cell In rngMerge
If cell.Value = cell.Offset(1, 0).Value And IsEmpty(cell) = False Then
Range(cell, cell.Offset(1, 0)).Merge
GoTo MergeAgain
End If
Next
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Edit Minor upgrade to allow merged ranges to be extended enabling merge updates.
Merge Vertically Adjacent Cells with Equal Values.
Save in a regular module.
Be sure the constants (Const) come before any other code in the module.
Consider adding a guard to ensure this only runs against the worksheet
it is intended for (see how to after the code).
Run the macro from the Alt-F8 Macro Dialogue.
NB Like most macros, this will wipe the Excel undo buffer.
It cannot be undone with a Ctrl-Z. (The only options are to revert to last saved
or manually edit to the way it was before.)
Copy/Paste
Private Const LastCol = 20
Private Const LastRow = 20
Public Sub Merge_Cells()
Dim r As Range
Dim s As Range
Dim l As Range
Dim c As Long
Dim v As Variant
For c = 1 To LastCol
Set s = Nothing
Set l = Nothing
For Each r In Range(Cells(1, c), Cells(LastRow, c))
v = r.MergeArea(1, 1).Value
If v = vbNullString Then
DoMerge s, l
Set s = Nothing
Set l = Nothing
ElseIf s Is Nothing Then
Set s = r
ElseIf s.Value <> v Then
DoMerge s, l
Set s = r
Set l = Nothing
Else
Set l = r
End If
Next r
DoMerge s, l
Next c
End Sub
Private Sub DoMerge(ByRef s As Range, ByRef l As Range)
If s Is Nothing Then Exit Sub
If l Is Nothing Then Set l = s
Application.DisplayAlerts = False
With Range(s, l)
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.Merge
End With
Application.DisplayAlerts = True
End Sub
Consider finding the last column and last row programmatically.
If the merge should start after row 1:
For Each r In Range(Cells(1, c), Cells(LastRow, c))
^
Change the 1 to the correct row number or replace with an added const variable.
To guard other worksheets, use the tab name (recommend renaming the tab first):
For Each r In Worksheets(TabName).Range(Cells(1, c), Cells(LastRow, c))
^^^^^^^^^^^^^^^^^^^^
Make this edit to the same line as the starting row edit.
And add Private Const TabName = "The Merge Tabs Name" ' Spaces ok
to the top of the Module with the other Const (constants).
Or place the name directly in the code: Worksheets("The Merge Tabs Name").
Add this into a module, select your range of data (excluding headers), run the macro and see if it works for you.
Public Sub MergeRange()
Dim rngData As Range, lngRow As Long, lngCol As Long, strTopCell As String
Dim strBottomCell As String, strThisValue As String, strNextValue As String
Dim strThisMergeArea As String, strNextMergeArea As String
Set rngData = Selection
Application.DisplayAlerts = False
Application.ScreenUpdating = False
With rngData
For lngCol = 1 To .Columns.Count
strTopCell = ""
For lngRow = 1 To .Rows.Count
If strTopCell = "" Then strTopCell = .Cells(lngRow, lngCol).Address
strThisValue = .Cells(lngRow, lngCol)
strNextValue = .Cells(lngRow + 1, lngCol)
If lngCol > 1 Then
strThisMergeArea = .Cells(lngRow, lngCol - 1).MergeArea.Address
strNextMergeArea = .Cells(lngRow + 1, lngCol - 1).MergeArea.Address
If strThisMergeArea <> strNextMergeArea Then strNextValue = strThisValue & "."
End If
If strNextValue <> strThisValue Or lngRow = .Rows.Count Then
strBottomCell = .Cells(lngRow, lngCol).Address
With rngData.Worksheet.Range(strTopCell & ":" & strBottomCell)
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.MergeCells = True
End With
strTopCell = .Cells(lngRow + 1, lngCol).Address
End If
Next
Next
End With
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
There's one trick to this which is able to be changed and that is that it will also group based off the prior column. You can see an example of what I'm talking about in cell C19 ...
... it has worked out that the previous column had a grouping that stopped at that point, therefore, the 1 isn't carried through and grouped to the next lot, it stops and is grouped there. I hope that makes sense and I hope it gives you what you need.
Another thing, this code here will attempt to demerge all of your previously merged data.
Public Sub DeMergeRange()
Dim rngData As Range, lngRow As Long, lngCol As Long, objCell As Range
Dim objMergeArea As Range, strMergeRange As String, strFirstCell As String
Dim strLastCell As String, objDestRange As Range
Set rngData = Selection
Application.DisplayAlerts = False
Application.ScreenUpdating = False
With rngData
For lngCol = 1 To .Columns.Count
For lngRow = 1 To .Rows.Count
Set objCell = .Cells(lngRow, lngCol)
If objCell.Areas(1).MergeArea.Cells.Count > 1 Then
strMergeRange = objCell.Areas(1).MergeArea.Address
objCell.MergeCells = False
strFirstCell = Split(strMergeRange, ":")(0)
strLastCell = Split(strMergeRange, ":")(1)
Set objDestRange = .Worksheet.Range(.Worksheet.Range(strFirstCell).Offset(1, 0).Address & ":" & strLastCell)
.Worksheet.Range(strFirstCell).Copy objDestRange
End If
Next
Next
End With
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
A note, my suggestion is to ensure you have the original source data saved to another workbook/sheet as a backup before running any code over the top of it.
If it stuffs with your data then it will be a right royal pain to undo manually.

Resources