I'll start by saying the only VBA I know is trial and error from manipulating recorded macros. I am a CPA trying to learn VBA the hard way (and wishing I had gone to school for computer programming instead!).
I have large workbooks with multiple worksheets. Cells highlighted yellow in column G need to be formatted in a specific way in order for the file to import correctly to a web-based program. They need to remain highlighted yellow, be right/bottom aligned, and custom format of mm/dd/yyyy. I recorded a macro doing find/replace to try to replace all yellow highlighted cells within column G with highlighted yellow, bottom/right justified, custom format mm/dd/yyyy, but it's not limiting the replace to only column G. I also have no clue how to get the macro to loop through all the worksheets before finishing. Help?!
This is what I have from my basic macro recording:
Sub Macro2()
'
' Macro2 Macro
'
'
Columns("G:G").Select
Range("G:G").Activate
With Application.FindFormat.Interior
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
.PatternTintAndShade = 0
End With
Application.ReplaceFormat.Clear
Application.ReplaceFormat.NumberFormat = "mm/dd/yyyy"
With Application.ReplaceFormat
.HorizontalAlignment = xlRight
.VerticalAlignment = xlBottom
End With
With Application.ReplaceFormat.Font
.Subscript = False
.TintAndShade = 0
End With
With Application.ReplaceFormat.Interior
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
.PatternTintAndShade = 0
End With
Cells.Replace What:="", Replacement:="", LookAt:=xlPart, SearchOrder:= _
xlByRows, MatchCase:=False, SearchFormat:=True, ReplaceFormat:=True
End Sub
EDITED POST TO ADD: Please see screenshot of a typical sheet I am trying to reformat. Again, I only need to worry about changing the formatting on cells that are yellow highlighted, but I'm still having trouble limiting the find/replace to column G only... [1]: [https://i.stack.imgur.com/wRu30.jpg]
Here's some code that seems to do what you describe. I've placed a lot of .select statements in the code so that you can learn how it works by stepping through it, but you should remove all those once you understand. Also, I have some commented out code at the bottom which you can use to loop through multiple sheets. The animated gif shows the code running on an example I made up. Let me know if you have questions.
Sub reformat()
Dim sh As Worksheet, r As Range, cell As Range
Set sh = ActiveSheet
Set r = sh.Range("G1")
r.Select
If r.Offset(1, 0) <> "" Then Set r = sh.Range(r, r.End(xlDown))
r.Select
For Each cell In r
With cell
.Select
If .Interior.Color = 65535 Then
.HorizontalAlignment = xlRight
.VerticalAlignment = xlBottom
.NumberFormat = "mm/dd/yyyy"
End If
End With
Next
For Each sh In ThisWorkbook.Worksheets
'place the above code in this loop if you want
'to apply the above to all worksheets in the workbook
'also remove the set sh=ActiveSheet line
Next sh
End Sub
If you are trying to make sense of recorded code, the first thing to do is get rid of all the extraneous, verbose code that was added but doesn't do anything. Recorded code covers all aspects of an operation whether you require them or not.
This is a rewrite of your original using only what is required.
Sub yellowSpecialReplace()
Dim w As Long
Application.DisplayAlerts = False
With Application.FindFormat
.Clear
.Interior.Color = 65535
End With
With Application.ReplaceFormat
.Clear
.NumberFormat = "mm/dd/yyyy"
.HorizontalAlignment = xlRight
.VerticalAlignment = xlBottom
End With
With ActiveWorkbook
For w = 1 To .Worksheets.Count
With Worksheets(w).Columns("G:G")
.Cells.Replace What:=vbNullString, Replacement:=vbNullString, _
LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchFormat:=True, ReplaceFormat:=True
End With
Next w
End With
Application.DisplayAlerts = True
End Sub
Cycle through each worksheet in the active workbook then AutoFilter on the cell color then apply the changes to the visible cells..
sub yellowSpecial()
dim w as long
with activeworkbook
for w=1 to .worksheets.count
with worksheets(w)
if .autofiltermode then .autofiltermode = false
with .range(.cells(1, "G"), .cells(.rows.count, "G").end(xlup))
.autofilter field:=1, criteria1:=vbyellow, operator:=xlFilterCellColor
with .resize(.rows.count-1, .columns.count).offset(1,0)
if cbool(application.subtotal(103,.cells)) then
with .specialcells(xlcelltypevisible)
.HorizontalAlignment = xlRight
.VerticalAlignment = xlBottom
.numberformat = "mm/dd/yyyy"
end with
end if
end with
end with
if .autofiltermode then .autofiltermode = false
end with
next w
end with
end sub
Related
Hi, every day i have to update an excel file. This includes formatting column B. (see picture above). I haven't found VBA code yet, to geht this kind of formatting via VBA. in the picture you see a subset of formatting rules, there are more. But there is only those three colors, which I have the hex code.
yellow
#9C5700
red
#9C0006
green
#006100
' (1) Highlight defined good as green values
With Range("b:b").FormatConditions.Add(xlCellValue, xlEqual, "=2")
.Interior.ColorIndex = 6
.StopIfTrue = False
End With
With Range("b:b").FormatConditions.Add(xlCellValue, xlEqual, "in Anfrage")
.Interior.ColorIndex = 6
.StopIfTrue = False
End With
' (2) Highlight defined ok as yellow values
With Range("b:b").FormatConditions.Add(xlCellValue, xlEqual, "=1")
.Interior.ColorIndex = 4
.StopIfTrue = False
End With
With Range("b:b").FormatConditions.Add(xlCellValue, xlEqual, "ok")
.Interior.ColorIndex = 4
.StopIfTrue = False
End With
' (2) Highlight defined bad as red values
With Range("b:b").FormatConditions.Add(xlCellValue, xlEqual, "=3")
.Interior.ColorIndex = 3
.StopIfTrue = False
End With
With Range("b:b").FormatConditions.Add(xlCellValue, xlEqual, "kritisch")
.Interior.ColorIndex = 3
.StopIfTrue = False
End With
End Sub
I used this code, but i would like to use the hex colors. How do I use those?
Per this Article:
You can assign the color codes to any Color property of any object in either their decimal or hex representation. Precede the Hex value with the &H prefix
However for some reason VBA does swap the first two characters with the last two characters of a hex code, so your Yellow 9C7500 would go into VBA as 00759C
So, instead of .Interior.ColorIndex, use .Interior.Color and put in your hex codes with &H at the start.
Example:
' (1) Highlight defined good as green values
With Range("b:b").FormatConditions.Add(xlCellValue, xlEqual, "=2")
.Interior.Color = &H006100
.StopIfTrue = False
End With
You can use .Color instead of .ColorIndex and you can also use RGB() to more easily set the value
so change your code to
.Interior.Color = RGB(&H9C,&H57,&H00)
Please, try the next code. Formatting the whole column will consume a lot of Excel resources, slows down the process of formulas update and it useless. The above code format only the B:B column having data:
Sub SetFormatRngMultiple_Cond()
Dim ws As Worksheet, lastR As Long, rngF As Range
Set ws = ActiveSheet
lastR = ws.Range("B" & ws.rows.count).End(xlUp).row
Set rngF = ws.Range("B2:B" & lastR)
With rngF
'first condition:
With .FormatConditions
.Delete
.Add Type:=xlCellValue, Operator:=xlEqual, Formula1:="=2"
End With
With .FormatConditions(.FormatConditions.count)
.Font.Color = 1137094
.Interior.Color = vbYellow
.SetFirstPriority: .StopIfTrue = False
End With
'second condition:
With .FormatConditions
.Add Type:=xlCellValue, Operator:=xlEqual, Formula1:="=1"
End With
With .FormatConditions(.FormatConditions.count)
.Font.Color = 5287936
.Interior.Color = 11854022
.StopIfTrue = False
End With
'third condition:
With .FormatConditions
.Add Type:=xlTextString, String:="OK", TextOperator:=xlContains
End With
With .FormatConditions(.FormatConditions.count)
.Font.Color = 5287936
.Interior.Color = 11854022
.StopIfTrue = False
End With
'fourth condition:
With .FormatConditions
.Add Type:=xlTextString, String:="kritish", TextOperator:=xlContains
End With
With .FormatConditions(.FormatConditions.count)
.Font.Color = vbRed
.Interior.Color = 14083324
.StopIfTrue = False
End With
End With
End Sub
This code takes raw data and drops it into a report template where it is transformed using if then statements and conditional formatting. Data is downloaded from an online source. The imported file is moved into the workbook. The user then runs this macro to merge the imported file into the report template.
Before adding the ActiveWorkbook.Save line, this code would only run about half the time. Now it runs consistently, but its slow and goes into Excel "Not Responding" for several seconds before completing. Can someone help me make this code more efficient?
Sub Refresh()
' Refresh Macro
' Checks the import data for accurate column headings, then refreshes the Standup Report with the new import data. Keeps Board Status Entries
Dim ColumnOrder As Variant, ndx As Integer
Dim Found As Range, counter As Integer
Dim rTemplate As Worksheet, nImport As Worksheet
Set rTemplate = ThisWorkbook.Worksheets("Standup Report Template")
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlManual
On Error GoTo ErrHandler
'Move the "Standup Report Template" Worksheet to first position.
rTemplate.Move Before:=ActiveWorkbook.Sheets(1)
'Order Columns correctly
On Error Resume Next
Set nImport = ThisWorkbook.Worksheets(2)
nImport.Activate
ColumnOrder = Array("Formatted ID", "Name", "Schedule State", "Blocked", "Plan Estimate", "At Risk", "Added")
counter = 1
For ndx = LBound(ColumnOrder) To UBound(ColumnOrder)
Set Found = Rows("1:1").Find(ColumnOrder(ndx), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False)
If Not Found Is Nothing Then
If Found.Column <> counter Then
Found.EntireColumn.Cut
Columns(counter).Insert Shift:=xlToRight
Application.CutCopyMode = False
End If
counter = counter + 1
End If
Next ndx
'Check to make sure all columns are present
On Error GoTo ErrHandler
If Range("A1").Value = "Formatted ID" And Range("b1").Value = "Name" And Range("c1").Value = "Schedule State" And Range("d1").Value = "Blocked" And Range("e1").Value = "Plan Estimate" And Range("f1").Value = "At Risk" And Range("g1").Value = "Added" Then
'insert formula to retain the current board state into column H of the new import file.
Application.Calculation = xlAutomatic
Range("H2").Formula = "=IF(ISERROR(MATCH(A2,'Standup Report Template'!B:B,0)),""NEW"",IF(ISBLANK(INDEX('Standup Report Template'!$B$1:$L$200,MATCH(A2,'Standup Report Template'!B:B,0),11)),""-"",INDEX('Standup Report Template'!$B$1:$L$200,MATCH(A2,'Standup Report Template'!B:B,0),11)))"
With Sheets(2)
.Range("H2").AutoFill .Range("H2:H" & .Cells(.Rows.Count, "A").End(xlUp).Row)
End With
Application.Calculation = xlManual
'clear old data from report
rTemplate.Activate
Application.Goto Reference:="ClearEntries"
Selection.ClearContents
'Delete Header Row of New Import file
nImport.Rows("1:1").Delete Shift:=xlUp
'Assign (instead of copy paste) new import data to the report template
rTemplate.Range("B4:H104").Value = nImport.Range("A1:G100").Value
'Justify Text
With Columns("B:B")
.HorizontalAlignment = xlRight
.VerticalAlignment = xlCenter
End With
With Columns("C:C")
.HorizontalAlignment = xlLeft
.VerticalAlignment = xlCenter
End With
With Columns("D:H")
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
End With
With Range("B3:H3")
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
End With
'Copy Paste Revised Board State
nImport.Activate
ActiveSheet.UsedRange.Columns("H:H").Copy
rTemplate.Activate
Range("L4").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'Delete import file
nImport.Delete
rTemplate.Activate
Range("L4").Select
ActiveWindow.Zoom = 80
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
MsgBox "New data has been imported. Please update the Board State as needed to finalize the report."
Else:
Rows("1:1").Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 7765734
.TintAndShade = 0
.PatternTintAndShade = 0
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.Calculation = xlAutomatic
MsgBox "The columns in your import table must be ordered as follows:" & vbCrLf & vbCrLf & "Formatted ID" & vbCrLf & "Name" & vbCrLf & "Schedule State" & vbCrLf & "Blocked" & vbCrLf & "Plan Estimate" & vbCrLf & "At Risk" & vbCrLf & "Added" & vbCrLf & vbCrLf & "Please make the appropriate changes to your import table and try again."
End If
Exit Sub
ErrHandler:
MsgBox "The Stand Up Report can't find your data. Please move data into the workbook before trying again."
End Sub
Don't use select on a range, it's extremely costly, here is an example to avoid it:
Columns("B:B").Select
With Selection
.HorizontalAlignment = xlRight
.VerticalAlignment = xlCenter
End With
Becomes:
With Columns("B:B")
.HorizontalAlignment = xlRight
.VerticalAlignment = xlCenter
End With
Note remove the .Select and the Selection.
I would probably also turn off calculation at the start of the code and back on at the end.
If you decide to do that, then you will need to do a manual Calculate after entering a formula like here:
Range("H2").Formula = "=IF(ISERROR(MATCH(A2,'Standup Report Template'!B:B,0)),""NEW"",IF(ISBLANK(INDEX('Standup Report Template'!$B$1:$L$200,MATCH(A2,'Standup Report Template'!B:B,0),11)),""-"",INDEX('Standup Report Template'!$B$1:$L$200,MATCH(A2,'Standup Report Template'!B:B,0),11)))"
With Sheets(2)
.Range("H2").AutoFill .Range("H2:H" & .Cells(.Rows.Count, "A").End(xlUp).Row)
End With
Application.calculate
I'm working on a excel vba code to import and manipulate some data from CSV-file. Suddenly a part of my code didn't work any more though it had worked without problems before.
It is about range.select and afterward with selection.Interior.Pattern = xlSolid
I have tried to copy the same small part of the code to a different workbook and here it work just perfect.
Dim iPhase As Integer
iPhase = Application.WorksheetFunction.CountIf(Range("A:A"), "Phase")
Dim h As Integer
h = 1
Range("A6").Select
Do Until h > iPhase
Cells.Find(What:="Phase", after:=ActiveCell, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
ActiveSheet.Range(ActiveCell, ActiveCell.Offset(0, 16)).Select
With selection.Interior
.Pattern = xlSolid
.Interior.PatternColorIndex = xlAutomatic
.Interior.ThemeColor = xlThemeColorAccent6
.Interior.TintAndShade = 0
.Interior.PatternTintAndShade = 0
End With
With selection.Font
.Bold = True
End With
h = h + 1
Loop
I get a compile error: Expected function or variable #"selection.interior"
The comments already identify the issues with your code; but here is an alternative using Filter and SpecialCells to select the visible data. Comments are contained in the code.
Sub FliterWithConditionalFormatting()
Dim rng As Range
'properly defing and reference your workbook and worksheet, change as requiried
Set rng = ThisWorkbook.Sheets("Sheet1").Range("A1").CurrentRegion
'The WITH..END WITH statement allows you to shorten your code and avoid using SELECT and ACTIVATE
With rng
.AutoFilter Field:=1, Criteria1:="Phase", Operator:=xlAnd 'filter the rng
'set the range, to conditionally format only the visible data, skipping the header row
With .Range(Cells(2, 1), Cells(rng.Rows.Count, 17)).SpecialCells(xlCellTypeVisible)
With .Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = xlThemeColorAccent6
.TintAndShade = 0
.PatternTintAndShade = 0
End With
With .Font
.Bold = True
End With
End With
.AutoFilter 'Remove the filter
End With
End Sub
Hi there i have create the following code to format the Grand Total Row in excel spreadsheet. My problem is that I want to select dynamic the cells from Grand Total and right because I don’t have always 15 columns. I make a try with ActiveCell but it didnt work. Can anyone help me to change this code to fit my need?
Range("A1").Select
FindRow1 = Range("A:A").FIND(What:="Grand Total", LookIn:=xlValues, LookAt:=xlWhole).Activate
ActiveCell.Resize(, 15).Select
'Range(ActiveCell, Cells(, ActiveCell.End(xlToRight).Column)).Select
Selection.Font.Bold = True
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorAccent6
.TintAndShade = 0.399975585192419
.PatternTintAndShade = 0
End With
Selection.Font.Size = 12
[EDIT]: Here's a screenshot of my problem after trying suggested solutions:
You don't have to select the range. Just be sure of the address of the range you're using, and you're good.
It'd be better if you specify the worksheet you're working with, so
if you have multiple sheets in the workbook you'd still be working
on the right one.
Instead of activating the cell you found with Find, pass the row of that cell to a variable called myRow and use this variable in another function to define the range you need.
Once you have defined the range you need, pass it to a variable like myRange, and use it instead of using Selection in the rest of your code.
To make your range change its size dynamically (assuming you want your range to have one row and all the filled cells of that row), then you'll need to find the column of the last filled cell in your table, pass it to a variable lastCol and use it to define your range.
Sub formatRange()
Dim ws As Worksheet
Dim myRow As Long, lastCol As Integer, myRange As Range
Set ws = ThisWorkbook.ActiveSheet 'Change this to the name of the sheet you're working with
myRow = ws.Range("A:A").Find(What:="Grand Total", LookIn:=xlValues, LookAt:=xlWhole).Row 'The row that has "Grand Total"
lastCol = ws.Cells(myRow, Columns.Count).End(xlToLeft).Column 'The column of the last filled cell in `myRow`
Set myRange = ws.Range("A" & myRow).Resize(1, lastCol) 'The desired range has 1 row and (lastCol) columns
myRange.Font.Bold = True
With myRange.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorAccent6
.TintAndShade = 0.399975585192419
.PatternTintAndShade = 0
End With
myRange.Font.Size = 12
End Sub
Consider this case:
If the column of the last cell in the row myRow is NOT the same as the last column in the whole table (see screenshot), you have 2 choices to define your lastCol:
You can define lastCol as the last column of the row myRow (screenshot 1), and in that case you keep the code above as it is.
You can define it as the last column of the whole table (screenshot 2), and in that case you'd have to replace the lastCol line above with this:
'The column of the last filled cell in the whole table
lastCol = ws.Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
P.S, If the column of the last cell is the same in all your rows, you can ignore this last paragraph.
Another example as to how to avoid Select/Selection/Activate/ActiveXXX pattern and how to use nested With...End With structure:
With Range("A1", Range("A:A").Find(What:="Grand Total", LookIn:=xlValues, LookAt:=xlWhole).End(xlToRight))
With .Font
.Bold = True
.Size = 12
End With
With .Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorAccent6
.TintAndShade = 0.399975585192419
.PatternTintAndShade = 0
End With
End With
I am trying to select certain rows in a pivot table and highlight them using vba. I have been trying a few things, but I have only been able to highlight one cell. The below code isn't working, but maybe a few tweaks to it will fix it. I need to do this in vba and not use conditional formatting.
Edit: Improved code using suggestion from answer below and own knowledge. It is still not working correctly though.
Sub Highlight()
Dim fnd As Variant
fnds = Array("abc", "dfy", "zxc")
For i = 0 To UBound(fnds)
Cells.Find(What:=(fnds), After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False).Activate
With Selection.EntireRow.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
.PatternTintAndShade = 0
End With
Next
End Sub
by using .Find you are only finding the first instance of each element of the array fnds, is this what you are wanting? I think you are after every occurrence in which case we need to put a loop in there.
Also personal preference here but I prefer to create reference to the cells / rows / columns to be manipulated then only do the actual manipulation once. Not such a biggy when you are only shading but when making changes such as deletions and updates it can be a massive process time saver.
Sub Highlight()
Dim i As Long, DelRange As String
For i = 1 To Cells(Rows.Count, 1).End(xlUp).Row
If InStr(1, Cells(i, 1), "abc") <> 0 Or InStr(1, Cells(i, 1), "dfy") <> 0 Or InStr(1, Cells(i, 1), "zxc") <> 0 Then DelRange = DelRange & "," & i & ":" & i
Next i
With Range(Right(DelRange, Len(DelRange) - 1)).Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End Sub
The problem is the Rows collection you are making reference to. Instead you probably want EntireRow of the selected range.
With Selection.EntireRow.Interior
.Pattern = xlSolid
...