Good day,
I'm trying to make an efficient conditional formatting work in Microsoft Word with an VBA, that will be very similar to the formatting known from Excel.
My current solution
I have a 4th column with an IF statement in a field that checks if the value in Column 2 is less equal or higher than in Column 3, combined with this VBA for conditional formatting:
Sub UBC()
color "No", wdRed
color "Yes", wdBrightGreen
End Sub
Function color(text As String, backgroundColor As WdColorIndex)
Dim r As Word.Range
Set r = ActiveDocument.Content
With r.Find
Do While .Execute(findText:=text, MatchWholeWord:=True, Forward:=True) = True
If r.Tables.Count > 0 Then
If r.Cells(1).ColumnIndex = 4 Then
r.Cells(1).Shading.BackgroundPatternColorIndex = backgroundColor
End If
End If
Loop
End With
End Function
The result I want to achieve
I want to eliminate the 4th column and use the VBA to do the check what's now handled by the IF statement. On top of that, I would also like to use RGB or HEX color codes instead of the wdColorIndex library.
Can somebody help me out modifying the current code?
Try this
Sub Tester()
Dim tbl As Table, rw As Row, v1, v2
Set tbl = ActiveDocument.Tables(1)
For Each rw In tbl.Rows
v1 = CellValue(rw.Cells(2))
v2 = CellValue(rw.Cells(3))
If IsNumeric(v1) And IsNumeric(v2) Then
v1 = CDbl(v1)
v2 = CDbl(v2)
Debug.Print v1, v2
rw.Cells(2).Shading.BackgroundPatternColor = _
IIf(v1 <= v2, RGB(100, 250, 100), RGB(250, 100, 100))
End If
Next rw
End Sub
Function CellValue(c As Cell)
Dim rv
rv = c.Range.Text
CellValue = Left(rv, Len(rv) - 2) 'remove "end of cell" marker
End Function
Related
Currently I'm having problems summing figures that are in black font. This is because I only need the Amt figures(in columns D,I,N,S) as shown in this IMAGE
This is the VBA Code I've found after searching online VBA Code Here
Here is the VBA Code itself:
Function SumByColor(rng As Range, FntClr As Range) As Double
Dim c As Range, TempSum As Double
Application.Volatile
clr = FntClr.Font.Color
TempSum = 0
On Error Resume Next
For Each c In rng.Cells
If c.Font.Color = clr Then
TempSum = TempSum + c.Value
End If
Next c
On Error GoTo 0
Set c = Nothing
SumByColor = TempSum
End Function
Right now because there is a C/S # which is a number itself, the current VBA Code i use will sum the C/S# (Black in font colour) itself into the Outstanding Amount which should not be the case.
Any idea how should do I select the column cells I need only? I have a feeling that =SumByColor(A1:S3,T7) is the code i have to change but how do I change it to only summing the columns in Column D,I,N,S?
BTW, Red Font = Payment made, Black Font = Outstanding Payment
You can check the header for each column in the input range:
Function SumByColor(rng As Range, FntClr As Range) As Double
Const HDR As String = "AMT"
Dim c As Range, TempSum As Double, clr As Long, col As Range
Application.Volatile
clr = FntClr.Font.Color
TempSum = 0
On Error Resume Next
'check each column in the input range
For Each col In rng.Columns
'Only check cells where the header is "AMT"
If col.EntireColumn.Cells(1).Value = HDR Then
For Each c In col.Cells
If c.Font.Color = clr Then
TempSum = TempSum + c.Value
End If
Next c
End If
Next col
On Error GoTo 0
Set c = Nothing
SumByColor = TempSum
End Function
To answer your question:
The way to only select the columns D,I,N,S out of the range A1:S3 is:
Intersect([A1:S3],[D:D,I:I,N:N,S:S])
If columns D,I,N,S are fixed, you should do this inside your vba function.
Otherwise, you might have to add an extra parameter to your function ...
I have a spreadsheet that is a data-entry tool for pulling equipment tags and line numbers from engineering drawings – it’s set up with a table that takes either 3-segment tags (columns A-C), 5 segment line numbers (columns A-E), or a list of complete tags (column F), with column G either concatenating the tag segments or pulling across the complete tag. I had this set up using a formula, but I’d rather avoid using complicated formulas in anything that anyone else is going to use and so I took a stab at converting the formula to VBA and putting in a Worksheet_Change procedure.
The code works fine... until you make a change to a cell on the last row of the table and then hit enter or use the down arrow key, at which point Excel crashes. Moving sideways or upwards is fine, and so is moving sideways off the changed cell before hitting enter. I tried converting the table to a regular range, and it still crashes at the last row of the data. I tried turning Application.EnableEvents to False, and that stops the crashing, but then the updating no longer triggers properly.
If the procedure is changed to Worksheet_SelectionChange, it doesn’t crash.
Just to make it more interesting, in both the Worksheet_Change and Worksheet_SelectionChange procedures, using the up/down arrow keys or the enter key fails to trigger a change, but in the Worksheet_SelectionChange procedure arrowing back down/up to the row off which I just moved triggers the update.
I’m sure there are a million ways to fix this, but I have no idea how to do it, and I haven’t had any luck finding an answer.
What I want is for the code to update column G whenever the active cell changes – regardless of whether I use the enter key, tab key, arrow keys, or the $!## mouse to change my cell selection.
I'm working on a Windows 10 machine, using Excel 2016. When I get to work tomorrow I'll see how it goes on Excel 2013.
Spreadsheet screencap, for reference: https://drive.google.com/file/d/0B_wa8YmM1J2ddjlkOWxERE5TM1k/view?usp=sharing
Any assistance would be hugely appreciated - especially if it comes with a thorough explanation about what is going on here.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim strDelim As String
Dim strConcatTag As String
Dim intActiveRow As Integer
Dim rngTagSegment As Range
Dim rngSingleTag As Range
Dim rng3SegmentTag As Range
Dim rng5SegmentTag As Range
Dim rngTagEntry As Range
Dim rngConcatTag As Range
Dim rngCheck As Range
strDelim = "-"
intActiveRow = ActiveCell.Row
Set rngSingleTag = Cells(intActiveRow, 6)
Set rng3SegmentTag = Range(Cells(intActiveRow, 1), Cells(intActiveRow, 3))
Set rng5SegmentTag = Range(Cells(intActiveRow, 1), Cells(intActiveRow, 5))
Set rngTagEntry = Range(Cells(intActiveRow, 1), Cells(intActiveRow, 6))
Set rngConcatTag = Cells(intActiveRow, 7)
If intActiveRow = 1 Then
Exit Sub
Else
Select Case True
Case WorksheetFunction.CountA(rngTagEntry) = 0
rngConcatTag = ""
Case WorksheetFunction.CountA(rng5SegmentTag) > 0 And WorksheetFunction.CountA(rngSingleTag) > 0
rngConcatTag = "Enter either a complete tag or the individual sections, not both"
Case WorksheetFunction.CountA(rng5SegmentTag) = 0 And WorksheetFunction.CountA(rngSingleTag) <> 0
rngConcatTag = UCase(Trim(rngSingleTag))
Case WorksheetFunction.CountA(rng3SegmentTag) = 3 And WorksheetFunction.CountA(rng5SegmentTag) = 3
For Each rngTagSegment In rng5SegmentTag
strConcatTag = IIf(rngTagSegment = "", Trim(strConcatTag) & "", IIf(strConcatTag = "", _
Trim(rngTagSegment.Text), Trim(strConcatTag) & strDelim & Trim(rngTagSegment.Text)))
Next
rngConcatTag = UCase(Trim(strConcatTag))
Case WorksheetFunction.CountA(rng3SegmentTag) = 3 And WorksheetFunction.CountA(rng5SegmentTag) = 5
For Each rngTagSegment In rng5SegmentTag
strConcatTag = IIf(rngTagSegment = "", Trim(strConcatTag) & "", IIf(strConcatTag = "", _
Trim(rngTagSegment.Text), Trim(strConcatTag) & strDelim & Trim(rngTagSegment.Text)))
Next
rngConcatTag = UCase(strConcatTag)
Case Else
rngConcatTag = "Incomplete Tag"
End Select
End If
End Sub
Something like this should work:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rw As Range, r As Range, dataRange As Range
Dim rngSingleTag As Range
Dim rng3SegmentTag As Range
Dim rng5SegmentTag As Range
Dim rngTagEntry As Range
Dim rngConcatTag As Range
'data entry area only (adjust to suit)...
Set dataRange = Application.Intersect(Target, Me.Range("A2:F10000"))
If dataRange Is Nothing Then Exit Sub 'nothing to do...
'process each changed row
For Each r In dataRange.Rows
Set rw = r.EntireRow
Set rngSingleTag = rw.Cells(6)
Set rng3SegmentTag = rw.Cells(1).Resize(1, 3)
Set rng5SegmentTag = rw.Cells(1).Resize(1, 5)
Set rngTagEntry = rw.Cells(1).Resize(1, 6)
Set rngConcatTag = rw.Cells(7)
Select Case True
Case filled(rngTagEntry) = 0
rngConcatTag = ""
Case filled(rng5SegmentTag) > 0 And filled(rngSingleTag) = 1
rngConcatTag = "Enter either a complete tag or the individual sections, not both"
Case filled(rng5SegmentTag) = 0 And filled(rngSingleTag) = 1
rngConcatTag = UCase(Trim(rngSingleTag))
Case filled(rng3SegmentTag) = 3 And filled(rng5SegmentTag) = 3
rngConcatTag = Tag(rng3SegmentTag)
Case filled(rng5SegmentTag) = 5
rngConcatTag = Tag(rng5SegmentTag)
Case Else
rngConcatTag = "Incomplete Tag"
End Select
Next r
End Sub
Function filled(rng)
filled = Application.CountA(rng)
End Function
Function Tag(rng) As String
Const DELIM As String = "-"
Dim c As Range, rv As String
For Each c In rng.Cells
rv = rv & IIf(Len(rv) > 0, DELIM, "") & Trim(c.Text)
Next c
Tag = rv
End Function
I'm writing a macro that basically draws a circle for every value in a column with a size based on that value, however some of the cells are blank and I just need to skip over them. I run into an error when I hit the first blank cell. Here's the code I have so far:
Sub plotCircles()
Set R = Range("D7:D205")
For Each Value In R
If Value = "" Then
Value = Value + 1
Else
Set shp = ActiveSheet.Shapes.AddShape(msoShapeOval, 10, 10, Value, Value)
End If
Next Value
End Sub
Try this:
Sub PlotCircles()
Dim r As Range, c As Range, shp As Shape
Set r = Sheet1.Range("D7:D205") 'change to suit
For Each c In r
With c
If .Value <> "" Then
Set shp = Sheet1.Shapes.AddShape _
(msoShapeOval, .Left, .Top, .Value, .Value)
Else
.Value = .Value + 1
End If
End With
Next
End Sub
I don't know why you hardcode your Left and Top argument for the AddShape method.
That will draw all the circles in the same location.
Above however draws the circles in the cell where you get your values from.
You can adjust that to suit your needs. HTH.
I'm trying to write a macro to change the colors of rows when the values in column B change. Column A will be my controlling column using 1's and 0's, i.e. column A will stay a 1 as long as column B stays the same; whenever B changes, A will flip to a 0, and so on.
I can get it to color the rows correctly when the values in column B change, but the problem arises when I filter the data. For example: let's say I have B2-B4 set to "test1", B5-B7 set to "test2", and B8-B10 set to "test3", then I filter column B to not include "test2". Originally, the rows would be colored differently where the column values changed, but rows B2-B4 and B8-B10 are set to the same color and now they're touching since the "test2" rows are hidden.
Here's the code I used to color the rows, but it doesn't work for filtering:
Sub ColorRows()
Dim This As Long
Dim Previous As Long
Dim LastRow As Long
Dim Color As Integer
Dim R As Long
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
RwColor = Array(15,0)
Color = 0
For R = 2 To LastRow
This = Cells(R, 1).Value
Previous = Cells(R - 1, 1).Value
If This <> Previous Then Color = 1 - Color
Range("A" & R & ":M" & R).Select
Selection.Interior.ColorIndex = RwColor(Color)
Next R
End Sub
How can I fix it so that even after filtering the rows are colored correctly when there is a change in column values?
Here's a way to do this:
1.) Insert the code below as a UDF in a code module.
2.) Then put the formula in A, as A2: =analyseVisible(B2).
This will compare B-cells to the next visible cell above and result in a 'rank'-counter in A.
Now that the counter in A in contiunous (even if rows are hidden), you can use MOD 2 to color it with conditional formatting:
3.) Add a conditional format (from A2 for the whole table): =MOD($A2,2)=1 and set the fill color.
If you use the filter now or change values in B, the rows are re-colored in realtime.
Public Function analyseVisible(r As Range) As Integer
Dim i As Long
If Application.Caller.Row <= 2 Or _
r.Row <> Application.Caller.Row Then
analyseVisible = 1
Exit Function
End If
i = r.Row - 1
While r.Worksheet.Rows(i).Hidden And i > 1
i = i - 1
Wend
If i = 1 Then
analyseVisible = 1
Else
analyseVisible = r.Worksheet.Cells(i, Application.Caller.Column).Value
If r.Worksheet.Cells(i, r.Column).Value <> _
r.Value Then analyseVisible = analyseVisible + 1
End If
End Function
The code below handles the issue by checking only the used & visible rows. It works pretty well, but I was unable to figure out how to fire it when the filter changes. It also does it's comparisons directly on the values that are changing.
Private Sub colorRows()
Dim this As Variant
Dim previous As Variant
Dim currentColor As Long
Dim rng As Range 'visible range
Dim c As Range ' cell
' pick a color to start with
currentColor = vbYellow
' rng = used and visible cells
Set rng = ActiveSheet.UsedRange.SpecialCells(xlCellTypeVisible)
For Each c In rng ' For each cell that is visible and used
If Not c.Row = 1 Then ' skip header row
this = c.Value
'some simple test logic to switch colors
If this <> previous Then
If currentColor = vbBlue Then
currentColor = vbYellow
ElseIf currentColor = vbYellow Then
currentColor = vbBlue
End If
End If
'set interior color
c.Interior.color = currentColor
previous = this
End If
Next c
End Sub
Then, in the module of the worksheet that you want to colorize, call the sub from the Worksheet_Activate() event. (In reality, you probably want a different event. I mostly work with Access, so I don't really know what's available to you. I'm just trying to point you in the right direction to what I'm sure is your next question if you stick with the method you started with.)
Private Sub Worksheet_Activate()
colorRows
End Sub
I am working on VBA EXCEL 2010.
I need to find the max value in a column and highlight its cell with a color.
Sub findMax_1()
Dim c As Range
Dim max As Double
Dim maxCell As String
max = 0
For Each c In Selection
If c.Value > max Then
max = c.Value
maxCell = c.Address
End If
Next c
ActiveSheet.Range("A10") = max
ActiveSheet.Range(maxCell).Color = vbBlue
End Sub
It does not work. Runtime error 438.
Any help would be appreciated.
As simco mentioned in the comments you would need to change the following line of code:
ActiveSheet.Range(maxCell).Color = vbBlue
To
ActiveSheet.Range(maxCell).Interior.Color = vbBlue
The problem with your current code is that if you have nothing selected you would end up with a 1004 error. One way of overcoming this is as simco mentioned to check if you have any cells selected. The method below is the method I preferr. Lets say you have your data in Column A:
Use the code below:
Sub findMax_1()
Dim c As Range
Dim flag As Boolean
Dim i As Integer
Dim max As Double
Dim maxCell As String
flag = True
i = 1
max = 0
While flag = True
If Cells(i, 1) <> "" Then
If Cells(i, 1) > max Then
max = Cells(i, 1)
maxCell = Range(Cells(i, 1), Cells(i, 1)).Address
End If
i = i + 1
Else
flag = False
End If
Wend
ActiveSheet.Range("A10") = max
ActiveSheet.Range(maxCell).Interior.Color = vbBlue
End Sub
Result:
Also you could look at this article on my blog for more information Excel VBA Formatting Cells and Range
Also as simco mentioned you could use conditional formatting, Select the column with the data:
From the Home Ribbon Select
Conditional Formatting>>Top/Bottom Rules >> Top 10 Items ...>>
Select "1" From the left text box and choose your color from the drop down list on the right: