cekendricks
asked on
Excel VBA conditional formatting
I am very new to VBA. I have a spreadsheet which is exported from Access 2007 to Excel 2007. The spreadsheet lists products (one product SKU number per record) and different manufacturing costs for each month in the columns. I am trying to highlight (display in red) cells for a specific product if the manufacturing cost changes from the previous month. So basically if the number in one cell is not the same as the number in the cell to its left, then display in red. I need to accomplish this programmatically from Access VBA module as this is where the spreadsheet originates before it is exported.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
If you want to compare let's say Column C with Column B then add this to your code.
Sid
With xlWs.Columns("C:C")
.FormatConditions.Add Type:=xlExpression, Formula1:="=OR(C1>B1,C1<B1)"
.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
With .FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
.FormatConditions(1).StopIfTrue = False
End With
Sid
ASKER
Thanks SiddharthRout, but I am still having a problem...I am now getting a 'Run-time error 9 Subscript out of range' error with the line .FormatConditions(Selectio n.FormatCo nditions.C ount).SetF irstPriori ty highlighted. Here is how I tried to adapt your suggestion to my situation:
Set xlRng = xlWs.Cells.SpecialCells(xl CellTypeLa stCell)
z = xlRng.Column
x = xlRng.Row
Set xlRng = xlWs.Range(xlWs.Cells(2, 1), xlWs.Cells(x, 9))
xlRng.Interior.Color = RGB(255, 255, 204)
' Format worksheet columns
'xlWs.Columns.AutoFit
'Set xlRng = xlWs.Columns("N:AC")
Set xlRng = xlWs.Range(xlWs.Cells(2, 14), xlWs.Cells(x, z))
xlRng.NumberFormat = "###,###,###,##0.00;[Red]( ###,###,## #,##0.00"
xlRng.ColumnWidth = 13.71
Set xlRng = xlWs.Columns("J")
xlRng.NumberFormat = "###,###,###,##0.000;[Red] (###,###,# ##,##0.000 )"
'######################### ########## ########## ########## ########## ########## ####
Set xlRng = xlWs.Range(xlWs.Cells(2, 14), xlWs.Cells(x, 18))
With xlRng
For w = 2 To x
For y = 15 To 18
.FormatConditions.Add Type:=xlExpression, _
Formula1:="=OR(xlWs.Cells( w,y)>xlWs. Cells(w,y- 1),xlWs.Ce lls(w,y)<x lWs.Cells( w,y-1))"
.FormatConditions(Selectio n.FormatCo nditions.C ount).SetF irstPriori ty
With .FormatConditions(1).Inter ior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
.FormatConditions(1).StopI fTrue = False
Next y
Next w
End With
What am I still missing??
Set xlRng = xlWs.Cells.SpecialCells(xl
z = xlRng.Column
x = xlRng.Row
Set xlRng = xlWs.Range(xlWs.Cells(2, 1), xlWs.Cells(x, 9))
xlRng.Interior.Color = RGB(255, 255, 204)
' Format worksheet columns
'xlWs.Columns.AutoFit
'Set xlRng = xlWs.Columns("N:AC")
Set xlRng = xlWs.Range(xlWs.Cells(2, 14), xlWs.Cells(x, z))
xlRng.NumberFormat = "###,###,###,##0.00;[Red](
xlRng.ColumnWidth = 13.71
Set xlRng = xlWs.Columns("J")
xlRng.NumberFormat = "###,###,###,##0.000;[Red]
'#########################
Set xlRng = xlWs.Range(xlWs.Cells(2, 14), xlWs.Cells(x, 18))
With xlRng
For w = 2 To x
For y = 15 To 18
.FormatConditions.Add Type:=xlExpression, _
Formula1:="=OR(xlWs.Cells(
.FormatConditions(Selectio
With .FormatConditions(1).Inter
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
.FormatConditions(1).StopI
Next y
Next w
End With
What am I still missing??
Oops a typo...
Delete that "Selection" so the line
.FormatConditions(Selectio n.FormatCo nditions.C ount).SetF irstPriori ty
becomes
.FormatConditions(.FormatC onditions. Count).Set FirstPrior ity
Sid
Delete that "Selection" so the line
.FormatConditions(Selectio
becomes
.FormatConditions(.FormatC
Sid
ASKER
Well, i am still having problems getting the code to work. It is running OK. I verified by placing a Debug.Print line inside the loop and watching the counter increment. However I am getting no change in the font or interior color of the cells that should be affected by the formatting, namely those that have a value which is not the same as the cell to its left.
Quick Question.
Why are you using loops 'w' and 'y'? Why not simply select the range and directly apply the formatting?
Sid
Why are you using loops 'w' and 'y'? Why not simply select the range and directly apply the formatting?
Sid
ASKER
This is the method that was recommended to me by a colleague. As I mentioned, I am a virtual novice when it comes to VBA. How would you solve this problem...can I just remove the loop structure?
Yes. Directly apply the formatting to the range as I did in ID: 34826432
Sid
Sid
ASKER
I cut and pasted the code you provided only changing the columns I wanted to compare and removing the word Selection that we discovered yesterday, but I am still having no success. I don't understand it, there is no formatting taking place after the code we inserted, and it runs and exports the spreadsheet with no errors, but every value in colunm O that I would expect to be formatted with red font is still black. Her is the code:
With xlWs.Columns("O:O")
.FormatConditions.Add Type:=xlExpression, Formula1:="=OR(O1>N1,O1<N1 )"
.FormatConditions(.FormatC onditions. Count).Set FirstPrior ity
With .FormatConditions(1).Inter ior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
.FormatConditions(1).StopI fTrue = False
End With
With xlWs.Columns("O:O")
.FormatConditions.Add Type:=xlExpression, Formula1:="=OR(O1>N1,O1<N1
.FormatConditions(.FormatC
With .FormatConditions(1).Inter
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
.FormatConditions(1).StopI
End With
Can you upload the excel workbook which is created?
Sid
Sid
ASKER
This is the spreadsheet that I am creating within Access and exporting to Excel. What I want is to have certain columns compared with their neighboring column to the left (which will hold the previous months data) and have that column's text displayed in red is it has changed from the previous month. This is only the data for January and the formatting will only need to take place on the Trend file actual costs worksheet. Case-Costs-2011.xlsx
Ok Let me test it for you...
Paste the code that you are using at the moment.
Sid
Paste the code that you are using at the moment.
Sid
I just notice that you need to declare this at the top of the Access Code.
Const xlExpression as Long = 2
Sid
Const xlExpression as Long = 2
Sid
ASKER
I inserted the declaration for the constant xlExpression in the general declaration area of the module in which the procedure exists and ran it again, and still column O is all black font.
Yeah I noticed in the excel file that in the column it is putting the formula but it is referring to a different range. I just need to see the complete code that you are using so that I can test it from my side.
Sid
Sid
ASKER
OK here you go...and I really appreciate all the time you're spending on this. I've attached a text file with the three procedures I use to create the spreadsheet. The last one is the one that creats the Trends sheet. ExportCaseCosts.txt
Gang,
Setting Conditional Formatting formula-based rules that use relative references is one of the very few instances where you actually DO have to select a range.
For example, I adapted Sid's code from http:#a34826432, and ran it in a case where the ActiveCell was A1:
When I did that, I did not get the results I expected. Indeed, when I went to check out the CF that did get applied, the formula for C1 was =E1<>D1.
However, when I first issued a Select on C1, I got the result I expected:
Patrick
Setting Conditional Formatting formula-based rules that use relative references is one of the very few instances where you actually DO have to select a range.
For example, I adapted Sid's code from http:#a34826432, and ran it in a case where the ActiveCell was A1:
Sub test()
With ActiveSheet.Columns("C:C")
.FormatConditions.Add Type:=xlExpression, Formula1:="=C1<>B1"
.FormatConditions(.FormatConditions.Count).SetFirstPriority
With .FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
.FormatConditions(1).StopIfTrue = False
End With
End Sub
When I did that, I did not get the results I expected. Indeed, when I went to check out the CF that did get applied, the formula for C1 was =E1<>D1.
However, when I first issued a Select on C1, I got the result I expected:
Sub test()
[c1].Select
With ActiveSheet.Columns("C:C")
.FormatConditions.Add Type:=xlExpression, Formula1:="=C1<>B1"
.FormatConditions(.FormatConditions.Count).SetFirstPriority
With .FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 255
.TintAndShade = 0
End With
.FormatConditions(1).StopIfTrue = False
End With
End Sub
Patrick
Few things that I noticed...
I will type it as I read through your code so that I don't forget later.
1) Don't use a Function. Use a Sub. Function is used when you want to return something.
2) I see that you are using DAO. DAO is now obsolete. Use ADO instead.
3) Dim w, x, y, z As Integer
Please note that in VBA w,x,y will be declared as variant and only z will be declared as Integer. To declare all of them as integer declare it as
Dim w As Integer, x As Integer, y As Integer, z As Integer
4) I also noticed above that you want to change the font and not the cell color? If that is what you want then the code will be slightly different.
5) BTW, I tested the code by removing all the code referring to the database and ran this code. It just worked fine...
Try putting this piece of code at the end of your code just before you are closing the Excel File
Sid
I will type it as I read through your code so that I don't forget later.
1) Don't use a Function. Use a Sub. Function is used when you want to return something.
2) I see that you are using DAO. DAO is now obsolete. Use ADO instead.
3) Dim w, x, y, z As Integer
Please note that in VBA w,x,y will be declared as variant and only z will be declared as Integer. To declare all of them as integer declare it as
Dim w As Integer, x As Integer, y As Integer, z As Integer
4) I also noticed above that you want to change the font and not the cell color? If that is what you want then the code will be slightly different.
5) BTW, I tested the code by removing all the code referring to the database and ran this code. It just worked fine...
Sub ExportCaseCost()
Dim xlWs As Worksheet
Set xlWs = Sheets("Trend file actual costs")
With xlWs.Columns("O:O")
.FormatConditions.Add Type:=xlExpression, Formula1:="=OR(O1>N1,O1<N1)"
.FormatConditions(.FormatConditions.Count).SetFirstPriority
With .FormatConditions(1).Font
.Color = -16776961
.TintAndShade = 0
End With
.FormatConditions(1).StopIfTrue = False
End With
End Sub
Try putting this piece of code at the end of your code just before you are closing the Excel File
Sid
Good Point Patrick :)
Sid
Sid
ASKER
OK, now I do see the formatting formula, but as Pactric points out it is on the wrong column, In fact its on column O, but refers to columns AC & AB, which are the last two columns on the other worksheet. What is the syntax to Select the correct range from my access code before applying the conditional formatting?
Try this
Sid
Sub ExportCaseCost()
Dim xlWs As Worksheet
Set xlWs = Sheets("Trend file actual costs")
xlWs.Range("O1").Select
With xlWs.Columns("O:O")
.FormatConditions.Add Type:=xlExpression, Formula1:="=OR(O1>N1,O1<N1)"
.FormatConditions(.FormatConditions.Count).SetFirstPriority
With .FormatConditions(1).Font
.Color = -16776961
.TintAndShade = 0
End With
.FormatConditions(1).StopIfTrue = False
End With
End Sub
Sid
ASKER
Getting very close...it worked, but only for the first row...Only the column header for column O turned red...I want each consecutive pair of values in each row, for all rows to be compared. And then eventually it will have to compare each consecutive pair over a range of 12 columns, but I'm sure I'll be able to figure that out once I get over this first hurdle.
Ok try this
Repalce
xlWs.Range("O1").Select
by
xlWs.Range("O:O").Select
Now try.
Sid
Repalce
xlWs.Range("O1").Select
by
xlWs.Range("O:O").Select
Now try.
Sid
ASKER
Ooops my mistake, it is working fine for the whole column..I mistakenly populated the same month twice, so there were no differences in the data....so can I just string together more columns in the formula in order to include more columns in the comparison?
Yes, but chnage the code accordingly specially .Select Part as Patrick suggested.
Sid
Sid
ASKER
I think I've got the hang of it now...Thank you so much Sid and Patrick...you quite possibly saved the whole project. I am very appreciative.
Glad to be of help :)
Sid
Sid
cekendricks: I believe you did an incorrect allocation of points. The points should have been equally split between me and Patrick. :)
Sid
Sid
ASKER
I realize that Patrick deserves a lot of credit also, but didn't know how to award both of you. And now my employers want another field to be highlighted in a different way. In other words they've changed the requirements. I posted a new question.
Don't worry :) I have already requested a mod :)
Looking at your other question now.
Sid
Looking at your other question now.
Sid
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
My problem was completely resolved
ASKER
Public Function expTrends(strMonth, strYear As String)
Dim xlApp As Excel.Application
Dim xlWb As Excel.Workbook
Dim xlWs As Excel.Worksheet
Dim xlRng As Excel.Range
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim fld As DAO.Field
Dim x, y, z As Integer
Dim sqltxt As String
Dim aryHeader(29) As String
Dim wsCnt As Integer
Dim strFile As String
Dim mkTblQry As String
Dim tblToMake As String
Dim strFileName As String
Dim strMoveMonth As String
strFileName = CCFILENAME & strYear & ".xlsx"
'strFileName = SaveLocation()
'strFile = SaveLocation()
'Debug.Print strFile
'If strFile = "" Then
' MsgBox ("Exiting Export")
' GoTo ExitHere
'End If
Set db = Access.CurrentDb
aryHeader(1) = "Material"
aryHeader(2) = "Description"
aryHeader(3) = "Product" & vbCrLf & "Hierarchy"
aryHeader(4) = "Base" & vbCrLf & "Hierarchy"
aryHeader(5) = "Base" & vbCrLf & "Description"
aryHeader(6) = "Sub" & vbCrLf & "Hierarchy"
aryHeader(7) = "Sub" & vbCrLf & "Description"
aryHeader(8) = "Profit" & vbCrLf & "Center"
aryHeader(9) = "Division"
aryHeader(10) = "Ea to Case"
aryHeader(11) = "BUOM"
aryHeader(12) = "Globe Std Cost"
aryHeader(13) = "Calculated" & vbCrLf & "BUOM Std"
aryHeader(14) = "Quantity total BI" & vbCrLf & "@ Act"
aryHeader(15) = "Quantity total Acq" & vbCrLf & "@ Act"
aryHeader(16) = "Quantity total" & vbCrLf & "Sales @ Act"
aryHeader(17) = "Quantity total EI" & vbCrLf & "@ Act"
aryHeader(18) = "Value total BI" & vbCrLf & "@ Act"
aryHeader(19) = "Value total Acq" & vbCrLf & "@ Act"
aryHeader(20) = "Value total" & vbCrLf & "Sales @ Act"
aryHeader(21) = "Value total EI" & vbCrLf & "@ Act"
aryHeader(22) = "Base qty BI"
aryHeader(23) = "Base qty Acq"
aryHeader(24) = "Base qty Sales"
aryHeader(25) = "Base qty EI"
aryHeader(26) = "Unit cost BI"
aryHeader(27) = "Unit cost Acq"
aryHeader(28) = "Unit cost Sales"
aryHeader(29) = "Unit cost EI"
If strMonth = "Jan" Then
Set xlApp = New Excel.Application
Set xlWb = xlApp.Workbooks.Add
Else
Set xlApp = New Excel.Application
Set xlWb = xlApp.Workbooks.Open(strFi
End If
xlApp.Application.DisplayA
xlApp.Visible = True
Set xlWs = xlWb.Worksheets.Add
xlWs.Name = strMonth & " " & strYear
'Set xlWs.Name = "Jan"
Select Case strMonth
Case "Feb"
strMoveMonth = "Jan"
Case "Mar"
strMoveMonth = "Feb"
Case "Apr"
strMoveMonth = "Mar"
Case "May"
strMoveMonth = "Apr"
Case "Jun"
strMoveMonth = "May"
Case "Jul"
strMoveMonth = "Jun"
Case "Aug"
strMoveMonth = "Jul"
Case "Sep"
strMoveMonth = "Aug"
Case "Oct"
strMoveMonth = "Sep"
Case "Nov"
strMoveMonth = "Oct"
Case "Dec"
strMoveMonth = "Nov"
End Select
Set rs = db.OpenRecordset("qryTrend
Set xlWs = xlWb.ActiveSheet
x = 1
For Each fld In rs.Fields
With xlWs.Cells(1, x)
.Value = aryHeader(x)
.BorderAround xlContinuous, xlThin, 5
.RowHeight = 48
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
If x < 13 Then
.Interior.Color = RGB(192, 192, 192)
End If
If x = 13 Then
.Interior.Color = RGB(204, 255, 204)
End If
If x > 13 And x < 18 Then
.Interior.Color = RGB(255, 204, 153)
End If
If x > 17 And x < 22 Then
.Interior.Color = RGB(192, 192, 192)
End If
If x > 21 And x < 26 Then
.Interior.Color = RGB(204, 255, 255)
End If
If x > 25 Then
.Interior.Color = RGB(204, 255, 204)
End If
End With
x = x + 1
Next fld
Set xlRng = xlWs.Cells(2, 1)
xlRng.CopyFromRecordset rs
xlWs.Columns.AutoFit
xlWs.Columns("D").ColumnWi
Set xlRng = xlWs.Cells.SpecialCells(xl
z = xlRng.Column
x = xlRng.Row
'For y = 6 To z
' Set xlRng = xlWs.Cells(x, y)
' If y = 6 Then
' xlRng.Value = "TOTALS"
' xlRng.Font.Bold = True
' End If
' If Not y = 6 Then
' xlRng.FormulaR1C1 = "=Subtotal(9,R2C:R[-2]C)"
' xlRng.Font.Bold = True
' End If
'Next y
' Shade the first 10 columns yellow (below the headers)
'x = x - 2
Set xlRng = xlWs.Range(xlWs.Cells(2, 1), xlWs.Cells(x, 9))
xlRng.Interior.Color = RGB(255, 255, 204)
' Format worksheet columns
'xlWs.Columns.AutoFit
'Set xlRng = xlWs.Columns("N:AC")
Set xlRng = xlWs.Range(xlWs.Cells(2, 14), xlWs.Cells(x, z))
xlRng.NumberFormat = "###,###,###,##0.00;[Red](
Set xlRng = xlWs.Columns("J")
xlRng.NumberFormat = "###,###,###,##0.000;[Red]
xlWs.Range("A1").Select
With xlApp.ActiveWindow
.SplitColumn = 0
.SplitRow = 1
End With
xlApp.ActiveWindow.FreezeP
If strMonth = "Jan" Then
xlWb.Sheets("Sheet3").Dele
xlWb.Sheets("Sheet2").Dele
xlWb.Sheets("Sheet1").Dele
End If
Select Case strMonth
Case "Jan"
x = 1
Case "Feb"
x = 2
Case "Mar"
x = 3
Case "Apr"
x = 4
Case "May"
x = 5
Case "Jun"
x = 6
Case "Jul"
x = 7
Case "Aug"
x = 8
Case "Sep"
x = 9
Case "Oct"
x = 10
Case "Nov"
x = 11
Case "Dec"
x = 12
End Select
If strMonth <> "Jan" Then
xlWb.Sheets(strMonth & " " & strYear).Move After:=xlWb.Sheets(strMove
End If
If strMonth = "Jan" Then
xlWb.SaveAs strFileName
End If
xlApp.Application.DisplayA
rs.Close
xlApp.Quit
Set fld = Nothing
Set rs = Nothing
Set qry = Nothing
Set db = Nothing
Set xlRng = Nothing
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
DoCmd.SetWarnings WarningsOn
Call DefineArray(strMonth, strYear)
End Function