Link to home
Start Free TrialLog in
Avatar of Terrygordon
Terrygordon

asked on

detect a color change in a cell

I have a spreadsheet where the users enter different color codes in cells that all mean different things. This is currently achieved by a userform with colored buttons that simply changes the color of the cell and then offsets the interior.colorindex value to another cell to be counted. An example of the code for this is:

Sub ColorYellow()
'
' CellColor Macro
' Macro recorded 15/12/2005 by GordonT
'

'
    With Selection.Interior
        .ColorIndex = 6
        Selection.Font.ColorIndex = 1
        Selection.Offset(0, 80).Value = 6
    End With
    Selection.Borders.ColorIndex = 15
    End Sub

My problem is that sometimes people copy and paste cells without using the user interface or manually change the color of the cells. As a result, the colorindex value doesn't get written to the offset cell. As counting these offset values is crucial to the functionality of the spreadsheet I wondered if there was any way (perhaps in a worsheet change event) of automatically detecting that a cell's color has been changed and writing the new colorindex value to the offset cell?

I don't want to use a macro that simply scans the whole range and updates all the offset cell values accordingly - the spreadsheet covers the activities of 40 people for a whole year and a macro to do this takes 30 seconds to run, so, as you can imagine, doing this every time a cell is changed would be counterproductive. I literally want it to recognise that a single cell has changed and write the interior.colorindex value of that cell to the offset location.

Hope someone can help.

Regards

Terry
Avatar of Rory Archibald
Rory Archibald
Flag of United Kingdom of Great Britain and Northern Ireland image

No, there is no event for that, so you would have to scan the whole range. You would be better off using code to colour the data based on a value they enter rather than trying to do this the other way round. Colour is not data, and should not be used as such. ;)
Avatar of Terrygordon
Terrygordon

ASKER

Unfortunately the values they enter in the cells are quite random but, again, essential to the functionality of the spreadsheet so that is not really an option.
Then your options are to scan the whole range, or use volatile UDFs to return the colorindex of the range in question (though they will only be recalculated when the workbook calculates, not when the colour is actually changed.
Not sure if this is any use to you, but here is a UDF that sums the values of cells based on the background colour/font colour. You might be able to adapt it to your purpose.
Function SumByColor(InRange As Range, WhatColorIndex As Integer, _
    Optional OfText As Boolean = False) As Double
'
' This function return the SUM of the values of cells in
' InRange with a background color, or if OfText is True a
' font color, equal to WhatColorIndex.
'
' You can call this function from a worksheet cell with a formula like:
' =SUMBYCOLOR(A1:A10,3,FALSE)

Dim rng As Range
Dim OK As Boolean

Application.Volatile True
For Each rng In InRange.Cells
    If OfText = True Then
        OK = (rng.Font.ColorIndex = WhatColorIndex)
    Else
        OK = (rng.Interior.ColorIndex = WhatColorIndex)
    End If
    If OK And IsNumeric(rng.Value) Then
        SumByColor = SumByColor + rng.Value
    End If
Next rng

End Function

Open in new window

I think I may have worked out a solution for you.

This processes on every worksheet_selectionChange() event.  It works on one cell (haven't tested copy/paste, but SHOULD be able to be adapted) - let's see if this is helping, and I can assist further, if needed.

On a selection change (clicking or arrowing anywhere in the worksheet), the Worksheet_SelectionChange() event is fired.  The routine does the following:

1 - turns events off - because its going to do an UNDO and we don't want a recursive loop
2 - after the undo, the activecell is now where it WAS before the selection change, and the color of the cell is what it WAS before the change, if any.
3 - it captures the priorCell address, and gets the priorColor
4 - then UNDO is done again - this is effectively a REDO
5 - now, we can get the most recent color - currentColor from that range priorCell
6 - now we need to clear the UNDO stack, so we can go about our business and move forward with spreadsheet work!
7 - so, then we reposition the cursor back to the cell where the user was maneouvering - the Target - Target.select gets us there
8 - finally, we check to see if currentColor is different from priorColor, and if so, you can put your offset logic there (it currently does a msgbox popup letting you know it trapped the color change event!)
9 - once you've made the offset change (see code below), then need to enable events back to true for normal processing.
 
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim priorColor As Long
Dim currentColor As Long
Dim myPriorCell As Range


Application.EnableEvents = False
On Error GoTo endcheck 'nothing in the stack, so nothing to process

    'find last cell selected and color before change
    Application.Undo 'now we're back at the last active cell, and the before color is present
    Set myPriorCell = ActiveCell
    priorColor = myPriorCell.Interior.Color
    
    'revert back and get color from last selected cell after change, if any
    Application.Undo 'now the active cell is back where it was, and the color of the prior active cell is what it was most recently changed to
    currentColor = myPriorCell.Interior.Color
    
    'clear undo stack
    Range("A1").Copy Range("A1")

    'now get cursor back to where it was headed
    Target.Select
    
    If currentColor <> priorColor Then
        'PUT OFFSET CODE LOGIC HERE - priorCell is the range of the changed color, priorColor it its old color, currentColor is its new color
        MsgBox "colors were changed, need to set offset logic", vbOKOnly
    End If

endcheck:
Application.EnableEvents = True

End Sub

Open in new window


I hope this helps!

Demo file is attached.

Dave

ColorChange-Event-r1.xlsm
Does that not assume they only change one cell at a time?
Hi Dave

I am trying your code, which works fine on the sample sheet you provided. But in my workbook, it kept on going back to a cell on the last sheet I was on. I think this was a minor glitch because when I closed and reopened the workbook (on the correct sheet) it worked fine. I can put some code in the workbook open event to ensure it always opens on this sheet.

Incidentally, it seems to work ok when a cell is copy/pasted as well.

The only problem is that it does only work on one cell at a time and my users will often copy ranges of cells so is there any way that it can cope with a range of cells being changed?

Regards

Terry
@rorya and Terry - yes, I stated that in my post, this works on one cell at a time.  I wasn't going to over work it unless it was directionally what Terry was looking for...

I'll take a look now at managing range changes...

Dave
FWIW, I'd use functions to get the colorindex rather than event code because this will prevent your users undoing anything, and they tend not to be happy about that sort of thing.
@rorya - read the fine print... the UNDO is not permanent, the function REDOes the change, lol.  However, the rest of the stack is busted, as would any stack be busted once a macro performs any changes in the worksheet.  And unless something can be made of the sumofcolor suggestion or other alternative, this should suit as the best alternative (did i just say if there is no better alternative then this is the best? lol!)

Here's the adapted function.  It handles single cell changes, multiple cell changes, or mass copy/paste changes.  To demonstrate how it works with a range of input as a result of a change, I also had it color the cells in the same row of column U as an example of what you might be trying to do.

As far as the starting sheet is concerned, I've circumvented that, but I bust the undo stack doing that.  So if you're in another sheet, even at the start, then go to this sheet with the color event, the UNDO stack is busted (so it doesn't revert back on an UNDO trying to see if there were changes).

Enjoy!

Dave
ColorChange-Event-r2.xlsm
As the macro does change values in the workbook, the UNDO stack is still purged - again, this happens on ANY macro that makes sheet changes, so if you have those running around, or you're happy with this solution, then you'll just need to explain it to your users...

I've given thought as well to the sumofcolor routine and it may also work for you depending on how your sheet is  structured.  If you MUST make updates 80 columns to the right of their change, then this event will work for you.  I'm a bit boggled to understand how sumofcolor would work in that instance, but then I haven't thought THAT much about it - seems like you'd have to have prescience to understand exactly where to land that function!

I modified this code to do exactly that - wherever you change the color, the color will also be duplicated at 80 columns to the right (no error checking going out of bounds to the right :)

Cheers,

Dave
ColorChange-Event-r4.xlsm
Hi Dave

This seems to be doing the trick but I think I'm confused about the variable that needs to be offset. Basically,

myPriorCell.Offset(0, 30).Value = currentColor

doesn't seem to work (it did in the single cell version)

So how do I offset the colorindex numbers for the changed range so that if, for example, the colorindex values of the cells changed from 3 to 6, 6 would appear in the offset cell?

Thanks in advance (and for a great job that supposedly couldn't be done).

Regards

Terry
ASKER CERTIFIED SOLUTION
Avatar of dlmille
dlmille
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Dave

Thanks mate. Pure genius. I changed the offset line to:

myColorInfo(i).rng.Offset(0, 30).Value = myColorInfo(i).rng.Interior.ColorIndex

and it works like a dream.

Thanks again.

Regards

Terry
Just proves that 'impossible' is a very subjective concept :-)