1

I know about conditional formatting, but it doesn't give me the options I'm looking for: namely, the possibility to manually change the cell fill color (in affected cells) based on how a color another cell, and with that, a standard fill color if I don't do anything. I have this VBA code for a single row (see below) and it works, though I have a feeling it's complicated in itself. Now, I want the same thing for another 149 rows, but the code obviously gets to complex. How can I achieve this? Is it wrong to put this in a SelectionChange?

Code:

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim Cell As Range
For Each Cell In Range("F7:PB7")
    If Cell.Value < Range("D8").Value Or Cell.Value > Range("E8").Value Then
        Cell.Offset(1, 0).Interior.ColorIndex = 0
    End If
    If Cell.Value >= Range("D8").Value And Cell.Value <= Range("E8").Value Then
        If Range("B8").Interior.ColorIndex < 0 Then
        Cell.Offset(1, 0).Interior.ColorIndex = 15
        Else
        If Range("B8").Interior.ColorIndex >= 0 Then
        Cell.Offset(1, 0).Interior.Color = Range("B8").Interior.Color
        End If
        End If
    End If

... et cetera next row ...

Next Cell
End Sub

Best regards!

12
  • 2
    Welcome to SO. So, first question; does it have to use SelectionChange? Could you put it in an event that's fired less often? Second; you mention the code would get complex for another 149 rows, is that because those rows need different logic / code? Commented Dec 2, 2021 at 13:11
  • I feel like you should add the criteria so this isn't triggering as you're moving data, etc., adding something like a If Not Intersect(Target, Range("F:PB")) Is Nothing Then Exit Sub would help with that. Commented Dec 2, 2021 at 13:36
  • Without understanding the relationship between rows 7 and 8, I don't know if we can extrapolate/interpolate modifying this for additional rows. If you would want to add criterion to remove every other row, or if you want to base the function on Target.Row, it may provide useful, which also allows you to eliminate even/odd rows from triggering the event. Commented Dec 2, 2021 at 13:39
  • It is unclear when this should run. If you want to run it manually or via a (command)button you need to put the code into a standard module. If not, you possibly need to cover a few scenarios, e.g. when a change happens in F7:PB7 or a change happens in columns D or E. Depending on if there are values or formulas in those locations, an appropriate solution could be created. There is no event that would get triggered if you change the color in column B. Try to additionally explain the logic and address the 'values or formulas issue', best in your answer which you can edit at any time. Commented Dec 2, 2021 at 18:11
  • It would be nice if I don't have to run a macro (click a button), so in practice, I'm looking for the kind of behavior we see with conditional formatting. Commented Dec 2, 2021 at 21:18

2 Answers 2

1

Try this out. I'm getting the default color for each row from ColA.

This is all in the worksheet code module:

Option Explicit

Const RW_DATES As Long = 7          'row with headers and dates
Const COL_NAME As Long = 2          'column with person's name
Const COL_START_DATE As Long = 4    'column with start date
Const COL_DATE1 As Long = 6         '1st date on header row
Const NUM_ROWS As Long = 150        'how many rows?


Private Sub Worksheet_Change(ByVal Target As Range)
    
    Dim c As Range, rng As Range, rngDates As Range, i As Long
    Dim startDate, endDate, rw As Range, arrDates, rngRowDates As Range
    Dim CheckAll As Boolean, hiliteColor As Long, hiliteName As String
    Dim cName As Range, selName, selColor As Long
    
    
    CheckAll = Target Is Nothing 'called from selection_change?
    
    If Not CheckAll Then
        'Was a cell changed? see if any start/end date cells were changed
        Set rng = Application.Intersect(Target, _
                       Me.Cells(RW_DATES + 1, COL_START_DATE).Resize(NUM_ROWS, 2))
        If rng Is Nothing Then Exit Sub   'nothing to do in this case
    Else
        'called from Selection_change: checking *all* rows
        Set rng = Me.Cells(RW_DATES + 1, COL_START_DATE).Resize(NUM_ROWS)
    End If
    Debug.Print "ran", "checkall=" & CheckAll
    
    'header range with dates
    Set rngDates = Me.Range(Me.Cells(RW_DATES, COL_DATE1), _
                            Me.Cells(RW_DATES, Columns.Count).End(xlToLeft))
    arrDates = rngDates.Value 'read dates to array
    
    Set cName = NameHiliteCell() 'see if there's a hilited name
    If Not cName Is Nothing Then
        selName = cName.Value
        selColor = cName.Interior.Color
    End If
    
    'loop over each changed row
    For Each rw In rng.EntireRow.Rows
        
        Set rngRowDates = rw.Cells(COL_DATE1).Resize(1, rngDates.Columns.Count)
        rngRowDates.Interior.ColorIndex = xlNone 'clear by default
        
        startDate = rw.Cells(COL_START_DATE).Value   'read the dates for this row
        endDate = rw.Cells(COL_START_DATE + 1).Value
        
        'determine what color the bar should be
        If Len(selName) > 0 And selName = rw.Cells(COL_NAME).Value Then
            hiliteColor = selColor
        Else
            hiliteColor = rw.Cells(1).Interior.Color
        End If
        
        If startDate > 0 And endDate > 0 Then
            i = 0
            For Each c In rngRowDates.Cells
                i = i + 1
                If arrDates(1, i) >= startDate And arrDates(1, i) <= endDate Then
                    c.Interior.Color = hiliteColor
                End If
            Next c
        End If
    Next rw
End Sub

'just calls Worksheet_Change; add some delay to reduce frequency of firing
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Static lastrun As Date
    If lastrun = 0 Then lastrun = Now
    If Now - lastrun > (1 / 86400) Then
        lastrun = Now
        Worksheet_Change Nothing
    End If
End Sub

'find the first name cell which has any fill and return it
Function NameHiliteCell() As Range
    Dim c As Range
    For Each c In Me.Cells(RW_DATES + 1, COL_NAME).Resize(NUM_ROWS)
        If Not c.Interior.ColorIndex = xlNone Then
            Set NameHiliteCell = c
            Exit Function
        End If
    Next c
End Function

My test range:

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

@TomWilliams, your code works flawlessly! Huge thanks! What a strong support a forum like this is. Thanks again! :)
You're welcome - was a fun mini project for me.
0

Would something like this be better? It will only fire when you change a value in the range F7:PB7.
It won't fire if the cell value is updated through a formula (for that you'd want to look at the cell that you changed to make the formula update).

Private Sub Worksheet_Change(ByVal Target As Range)
    If Target.Cells.Count = 1 Then 'Only fire if a single cell is changed.
        If Not Intersect(Target, Range("F7:PB154")) Is Nothing Then
            MsgBox Target.Address 'Test
            'Your code - looking at Target rather than each Cell in range.
        End If
    End If
End Sub

Edit: Updated the range so it looks at more than one row, but now thinking I should delete the answer due to the odd/even rows that @Cyril indicates, etc.... this isn't looking like a complete answer now.

3 Comments

The thing is, range F7:PB7 holds unique date values (F7: Dec 1 2021, G7: Dec 2 2021, H7: Dec 3 2021, etc.) and will never be changed. Columns D and E hold "start date" and "end date" values. If D8 is "Dec 1 2021" and E8 is "Dec 10 2021", range F8:O8 is filled with a "default color" (in my code ColorIndex 15, i.e. light grey). Now, if I fill B8 with a green color, I want range F8:O8 to change to green. If I change the fill color in B8, the color in range F8:O8 follows. If I delete the color in B8, the fill color in range F8:O8 gets back to "default color". Works for row 8, but I need more rows.
Also, if I delete values in D8 ("start date") and E8 ("end date"), I want the fill color in F8:O8 is also to be deleted.
Here is a layout and a visualization of what I'm trying to achieve: ibb.co/tPqNRCs

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.