Is there a simple way to do this, via macro or otherwise? By calculated field I mean a field that is computed from other fields, versus raw entered values. By highlight I mean colored differently. I need this to better understand a large spreadsheet from a client.
To do it manually, press the F5 key to bring up the GoTo dialog. Click the Special Cells button. On the next screen, select Formulas (it's an option on the right).
Excel will select all of the cells that match. Now it's just a matter of applying formatting.
I'm going to assume you're only talking about cell formulas rather than VBA calculations here, since you could set the cell colour in your VBA procedure if you're doing it that way.
The way to do this is to check the cell for a formula after you're done with it, and change it's colour at that point. The relevant event here is Change, and the cell's HasFormula property will tell you whether the cell is a literal value, or calculated from a formula:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.HasFormula Then
Target.Interior.Color = vbRed
Else
' remove background colour entirely (i.e. No Fill)
Target.Interior.ColorIndex = xlColorIndexNone
End If
End Sub
TLDR;
Use Conditional Formatting with a Formula to highlight all cells that contain a formula.
Details
In MS Office 365 Version: 5.0.4667.1002, the following works
Select a range of cells.
Case1: Use Ctrl + A to select all cells.
Case2: Select a specific range.
Go to the Home tab, Styles section, and choose Conditional Formatting > New Rule.
The "New Formatting Rule" dialog will open.
Choose "Use a formula to determine which cells to format"
In the textbox, add the following rule: =IsFormula(A1)
Case1: If you selected all cells, use A1 because it is the first cell.
Case2: If you selected a specific range, replace A1 with the first cell in your range.
Click Format...
The "Format Cells" dialog will open.
Choose the format you would like to apply. E.g. a yellow background.
Click OK.
All cells that have formulas will now have, for instance, a yellow background.
Screenshot
Excel has a built in feature of "Trace Dependents" (which shows arrows to show you the calculated cells)
Select the range containing your data.
Excel 2007 -> Formulas -> Trace Dependents
The code below should cycle through each sheet, highlighting every cells that starts with an '=' and colors it the desired color (currently colour 36 which is Light Yellow).
Sub HighLightFormulas()
Dim objSheet As Worksheet
Dim strOriginalSheet As String
Dim intMaxBlankCells As Integer
Dim intBlankColumns As Integer
Dim intBlankRows As Integer
Dim intCurrentColumn As Integer
Dim intCurrentRow As Long
intMaxBlankCells = 40
strOriginalSheet = ActiveSheet.Name
For Each objSheet In Worksheets
intBlankRows = 0
intCurrentRow = 1
intCurrentColumn = 1
Do While intCurrentRow <= 65536 And intBlankRows <= intMaxBlankCells
intBlankColumns = 0
intCurrentColumn = 1
Do While intCurrentColumn <= 256 And intBlankColumns <= intMaxBlankCells
If Left(objSheet.Cells(intCurrentRow, intCurrentColumn).Formula, 1) = '=' Then
objSheet.Cells(intCurrentRow, intCurrentColumn).Interior.ColorIndex = 36
End If
intCurrentColumn = intCurrentColumn + 1
Loop
If intCurrentColumn = intBlankColumns Then
intBlankRows = intBlankRows + 1
Else
intBlankRows = 0
End If
intCurrentRow = intCurrentRow + 1
Loop
Next objSheet
Worksheets(strOriginalSheet).Activate
Call MsgBox("The Highlighting process has completed", vbOKOnly, "Process Complete")
End Sub
It will also stop after 40 consecutive blank cells (to avoid processing all of a mostly blank sheet).
Hope this helps.
Simple solution:
Ctrl - ` (the key just above Tab)
You can use the Interior.ColorIndex property to change the active cell's background color:
ActiveCell.Interior.ColorIndex = 36
You may also apply it to a range:
Range("A1:A5").Interior.Color = RGB(200,160,35)
This applies to Excel 2003, I haven't used the latest version but I doubt this has changed.
You can usually record a macro and then look at the generated code to see how something is done.
I liked Craig's code here, because it keeps the layout of the existing worksheet and yet shows what is calculated and what is not 'at a glance', but I have reworked it a bit so it does a better job of working out the active area of sheets, and I added an 'UnhighlightFormulas' subroutine so one can easily undo the formatting (e.g. before printing). It has been tested in Excel 2007. Note that you will lose any other cell background colouring upon running this.
Option Explicit
Public Sub HighlightFormulas()
ColorFormulas (36) '36 is yellow
End Sub
Public Sub UnhighlightFormulas()
ColorFormulas (-4142) '-4142 is default
End Sub
Private Sub ColorFormulas(intColor As Integer)
Dim wshSheet As Worksheet
Dim rngRange As Range
Dim rngCell As Range
For Each wshSheet In Worksheets
Set rngRange = RangeInUse(wshSheet)
If Not rngRange Is Nothing Then
For Each rngCell In rngRange
If Left(rngCell.Formula, 1) = "=" Then
If rngCell.Interior.ColorIndex <> intColor Then rngCell.Interior.ColorIndex = intColor
Else
If rngCell.Interior.ColorIndex <> -4142 Then rngCell.Interior.ColorIndex = -4142 '-4142 is default
End If
Next
End If
Next
End Sub
Private Function RangeInUse(ws As Worksheet) As Range
Dim LastRow&, LastCol%
' adapted from http://www.beyondtechnology.com/geeks012.shtml
' Error-handling in case there is no data in worksheet
On Error Resume Next
With ws
LastRow& = .Cells.Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByRows).Row
LastCol% = .Cells.Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByColumns).Column
End With
Set RangeInUse = ws.Range("A1", Cells(LastRow&, LastCol%))
End Function
Related
First question: I want to run a macro automatically when a specific cell value changes, however when the cell value changes, it doesn't run. The only way it runs is when I go to the "Macros" section under the developer tab and manually run the macro.
Second Question: I have a cell that is formatted as text and displays "somenumber% / someothernumber%" and I want the negative values to be colored red and the positive values to colored green. The problem is it only registers the first value, so if it's positive then all of the cell values are green, and vice versa for negative. Here is the specific text formatting: = TEXT(AS4,"[>0]+#,###0.000%;[<0]-#,###0.000%")&" "&"/"&" "&TEXT(AS6,"[>0]+#,###0.000%;[<0]-#,###0.000%").
Here is my attempt at both solutions:
Sub TextColorChange()
Dim xWs As Worksheet
Set xWs = Sheets("Trading Statistics")
For Row = 10 To 13
vall = xWs.Cells(Row, 51).Value
CheckPlus = InStr(1, vall, "+")
CheckMinus = InStr(1, vall, "-")
CheckDash = InStr(1, vall, "/")
part = Len(vall) - CheckDash + 1
If CheckMinus <> 0 Then
xWs.Cells(Row, 51).Characters(Start:=CheckMinus, Length:=part).Font.ColorIndex = 3
End If
If CheckPlus <> 0 Then
xWs.Cells(Row, 51).Characters(Start:=CheckPlus, Length:=part).Font.ColorIndex = 10
End If
Next Row
End Sub
--------------------------------
Private Sub Worksheet_Calculate()
Dim Xrg As Range
Set Xrg = Me.Range("AY6")
If Not Intersect(Xrg, Me.Range("AY6")) Is Nothing Then
Call TextColorChange
End If
End Sub
I have an excel file that does this, the code I use to active my macro when a user paste data in a sheet is:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("A2")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
Call Sorting
Call Pic
End If
MsgBox "Data updated"
End Sub
Sorting and Pic are the macros being called and the above code has to be put in the actual sheet where the macro should trigger (i.e not in the module)
EDIT: this answers your first question. Please mark it as helpful if it was, then post your second question in a new post altogether.
Is it possible to write a vba macro that determines if there are any empty cells in a given range and returns the row number of that cell?
I'm new to vba and all that I managed to write after searching the internet was something that takes a range and colors every emty cell in it red:
Sub EmptyRed()
If TypeName(Selection) <> "Range" Then Exit Sub
For Each cell In Selection
If IsEmpty(cell.Value) Then cell.Interior.Color = RGB(255, 0, 0)
Next cell
End Sub
The macro does basically what I want, but instead of coloring the empty cell red I would like to know the row index of the empty cell.
A little background info: I have a very large file (about 80 000 rows) that contains many merged cells. I want to import it into R with readxl. Readxl splits merged cells, puts the value in the first split cell and NA into all others. But a completely empty cell would also be assigned NA, so I thought the best thing would be to find out which cells are empty with Excel, so that I know which NA indicate a merged cell or an empty cell. Any suggestions on how to solve this problem are very welcome, thanks!
Edit: To clarify: Ideally, I want to unmerge all cells in my document and fill each split cell with the content of the previously merged cell. But I found macros on the web that are supposed to do exactly that, but they didn't work on my file, so I thought I could just determine blank cells and then work on them in R. I usually don't work with Excel so I know very little about it, so sorry if my thought process is far too complicated.
To do exactly what you state in your title:
If IsEmpty(cell.Value) Then Debug.Print cell.Row
But there are also Excel methods to determine merged cells and act on them. So And I'm not sure exactly what you want to do with the information.
EDIT
Adding on what you say you want to do with the results, perhaps this VBA code might help:
Option Explicit
Sub EmptyRed()
Dim myMergedRange As Range, myCell As Range, myMergedCell As Range
Dim rngProcess As Range
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
Set rngProcess = Range("A1:B10")
For Each myCell In rngProcess
If myCell.MergeCells = True Then
Set myMergedRange = myCell.MergeArea
With myMergedRange
.MergeCells = False
.Value = myCell(1, 1)
End With
End If
Next myCell
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
End sub
Note that I explicitly declare all variables, and I hard coded the range to check. There are various ways of declaring the range to be checked; using 'Selection' is usually rarely preferred.
Before anything else: From the opposite end of the spectrum, you can use Range.MergeCells or Range.MergeArea to determine if a Cell is part of a Merged Area. But, I digress...
You can use Cell.Row to get the row number. How you return or display that is up to you - could be a Message Box, a delimited string, or an array, or even a multi-area range.
A Sub cannot return anything once called, so you may want a Function instead, e.g. Public Function EmptyRed() As String
(Also, I would recommend you get in the habit of explicitly declaring all of your variables, and perhaps using Option Explicit too, before you run into a typo-based error. Just add Dim cell As Range at the top of the sub for now)
Sub FF()
Dim r, wksOutput As Worksheet
Dim cell As Range, rng As Range, rngArea As Range
With Selection
.UnMerge
'// Get only blank cells
Set rng = .SpecialCells(xlCellTypeBlanks)
'// Make blank cells red
rng.Interior.Color = vbRed
End With
'// Create output worksheet
Set wksOutput = Sheets.Add()
With wksOutput
For Each rngArea In rng.Areas
For Each cell In rngArea
r = r + 1
'// Write down the row of blank cell
.Cells(r, 1) = cell.Row
Next
Next
'// Remove duplicates
.Range("A:A").RemoveDuplicates Array(1), xlNo
End With
End Sub
There are a couple ways:
Sub EmptyRed()
Dim rgn,targetrgn as range
Dim ads as string ‘ return rgn address
Set targetrgn= ‘ your selection
For Each rgn In Targetrgn
If IsEmpty(rgn.Value) Then
‘1. Use address function, and from there you can stripe out the column and row
Ads=application.worksheetfunction.addres(cell,1)’ the second input control the address format, w/o $
‘2. Range.row & range.column
Ads=“row:” & rgn.row & “, col: “ & rgn.column
End if
Next rgn
End Sub
Ps: I edited the code on my phone and will debug further when I have a computer. And I am just more used to use “range” rather than “cell”.
To clarify: Ideally, I want to unmerge all cells in my document and fill each split cell with the content of the previously merged cell.
Cycle through all cells in the worksheet's UsedRange
If merged, unmerge and fill the unmerged area with the value from the formerly merged area.
If not merged but blank, collect for address output.
Sub fillMerged()
Dim r As Range, br As Range, mr As Range
For Each r In ActiveSheet.UsedRange
If r.Address <> r.MergeArea.Address Then
'merged cells - unmerge and set value to all
Set mr = r.MergeArea
r.UnMerge
mr.Value = mr.Cells(1).Value
ElseIf IsEmpty(r) Then
'unmerged blank cell
If br Is Nothing Then
Set br = r
Else
Set br = Union(br, r)
End If
End If
Next r
Debug.Print "blank cells: " & br.Address(0, 0)
End Sub
For a Excel document that is becoming larger then the internet I am trying to get rid of automatic lay outs cause they are seriously slowing our excel to an extend where its becomes non usable.
I attempted to create a macro that colours the background of a cell based on the active cell value.
Sub find()
Dim CurValue As String
Dim ColorIndex As String
Dim Findr As Range
Dim MyRange As Range
Set MyRange = ActiveCell
CurValue = ActiveCell.Value
With ActiveCell
Set Findr = Range("A1:A10").find(CurValue)
If Not Findr Is Nothing Then
ColorIndex = Findr.Offset(ColumnOffset:=1).Value
MyRange.Interior.ColorIndex = ColorIndex
' rngStart.Select
End If
End With
End Sub
This sub works perfectly.
However for the problem:
Now i want to call it whenever a cell changes but if I call the macro whenever a cell changes in my Sheet.I tried using the sheet sourcecode for every change.
But then it uses the cell the user jumps to after the change rather then the previous edited cell.
How do i get this Macro to call for every changed cell rather then the new select cell?
Putting the following in the appropriate Worksheet object should work:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim CurValue As String
Dim ColorIndex As String
Dim Findr As Range
For Each AC In Target.Cells
CurValue = AC.Value
Set Findr = Range("A1:A10").find(CurValue)
If Not Findr Is Nothing Then
ColorIndex = Findr.Offset(ColumnOffset:=1).Value
AC.Interior.ColorIndex = ColorIndex
End If
Next
End Sub
Note - one of the A1:A10 needs to be blank, but the value in B next to the blank must have a ColorIndex value and cannot be blank. I'd suggest a zero to empty all colour out of the cell but I can't see what your sheet's 'empty' cells look like..
The For..Each loop is to handle where more than one cell is changed at once, it performs the colour-change on each changed cell.
Also, cells that 'change' due to the result of a formula changing, rather than an edit, will not change using this method.
I have values on Sheet 1 and I gave the background color using conditional formatting.
I want to copy only the color and paste it to the corresponding cell of sheet 2 without pasting the value.
Example if sheet 1 cell A1 has red color for specific value, transfer the color to sheet 2 A1.
I use two colors, red and white. Red is for higher value and white is for lower value.
Sub copycolor()
Dim intRow As Integer
Dim rngCopy As Range
Dim rngPaste As Range
For intRow = 1 To 20
Set rngCopy = Sheet1.Range("A" & intRow + 0)
Set rngPaste = Sheet2.Range("b" & intRow)
'Test to see if rows 500+ have a value
If rngCopy.Value <> "" Then
'Since it has a value, copy the value and color
rngPaste.Value = rngCopy.Value
rngPaste.Interior.Color = rngCopy.Interior.Color
End If
Next intRow
End Sub
rngPaste.Interior.Color = rngCopy.DisplayFormat.Interior.Color
Seems to work for me. Keep in mind that DisplayFormat is read-only and is not allowed to return value outside of the function it's used in. Also it is only available in Excel 2010 +
I was editing my answer to include the other stuff you mentioned and realized it was getting confusing to explain it all in separate chunks. Here's a recommended approach to achieve what you're saying.
Public Sub CopyColor()
Dim SourceSht As Worksheet
Dim TargetSht As Worksheet
Dim rngCopy As Range
Dim rngPaste As Range
Dim LastCopyRow As Long
Dim LastCopyColumn As Long
'Define what our source sheet and target sheet are
Set SourceSht = ThisWorkbook.Worksheets("Sheet1")
Set TargetSht = ThisWorkbook.Worksheets("Sheet2")
'Find our used space on the source sheet
LastCopyRow = SourceSht.Cells(Rows.Count, "A").End(xlUp).Row
LastCopyColumn = SourceSht.Cells(1, Columns.Count).End(xlToLeft).Column
'Setup our ranges so we can be sure we don't loop through unused space
Set rngCopy = SourceSht.Range("A1:" & SourceSht.Cells(LastCopyRow, LastCopyColumn).Address)
Set rngPaste = TargetSht.Range("A1:" & TargetSht.Cells(LastCopyRow, LastCopyColumn).Address)
'Loop through each row of each column.
' This will go through each cell in column 1, then move on to column 2
For Col = 1 To LastCopyColumn
For cel = 1 To LastCopyRow
' If the string value of our current cell is not empty.
If rngCopy.Cells(cel, Col).Value <> "" Then
'Copy the source cell displayed color and paste it in the target cell
rngPaste.Cells(cel, Col).Interior.Color = rngCopy.Cells(cel, Col).DisplayFormat.Interior.Color
End If
Next cel
Next Col
End Sub
Simplest would be to apply the same conditional formatting to Sheet2, but use the values from Sheet1 as your criteria. So if Sheet1 Cell A1 has the value that makes it red, add formatting to Sheet2 that turns Sheet2 Cell A1 red as well.
There's a good explanation of how to achieve this here.
.Interior.Color gets the actual colour of the cell rather than the conditionally formatted colour (the one you see). So you can't copy/paste this red colour in your example in this way.
I believe that the only way to get the conditionally formatted colour you see would be to recompute whatever formula you've used in your conditionally formatting criteria.
Excel 2007 conditional formatting - how to get cell color?
Edit
While #JeffK627 was giving an elegant solution, I was knocking up some rough vba code to recompute what I gather your conditional formatting does. I've done this over range A1:A20 on sheet 2. At the moment it colours the cell that contains the value itself, but only requires a little tweak to colour the equivalent cell on another sheet.
Sub ColouringIn()
Dim intColIndex As Integer
Dim dblMax As Double
Dim dblMin As Double
Dim rngCell As Range
'RGB(255, 255, 255) = white
'RGB(255, 0, 0) = red
'so need to extrapolate between
dblMax = Application.WorksheetFunction.Max(Sheet2.Range("A1:A20"))
dblMin = Application.WorksheetFunction.Min(Sheet2.Range("A1:A20"))
For Each rngCell In Sheet2.Range("A1:A20")
If IsNumeric(rngCell.Value) And rngCell.Value <> "" Then
intColIndex = (rngCell.Value - dblMin) / (dblMax - dblMin) * 255
rngCell.Interior.Color = RGB(255, intColIndex, intColIndex)
End If
Next rngCell
End Sub
Adding following example as alternative solution, as I needed something dynamic/active where color IS a required condition of data & not reliant on any other trigger.
Option1:
Dim rngPrev2Update As Range
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim cellbox As Range
Dim rngDest As Range
If Not rngPrev2Update Is Nothing Then
For Each cellbox In rngPrev2Update.Cells
Worksheets("Sheet2").Range(cellbox.Address).Interior.ColorIndex = cellbox.Interior.ColorIndex
Next cellbox
End If
Set rngPrev2Update = Target
End Sub
This will update destination cells when cursor is next moved to another cell.
Option2:
Private Sub Worksheet_Activate()
Dim cellbox As Range
Dim rngCells As Range
Set rngCells = Range("B1:B10")
For Each cellbox In rngCells.Cells
Range(cellbox.Address).Interior.ColorIndex = Worksheets("Sheet2").Range(cellbox.Address).Interior.ColorIndex
Next cellbox
End Sub
Will update relevant cells on sheet load.
Note: If you have very large data set you may want to put this into a macro button &/or filter this further for only the cells you need, otherwise this may slow your spreadsheet down.
Appreciating this was some time ago. I would like to do a similar thing however would like to append the Interior Color Reference ie. 255 to the cells value.
so if cell A1 has Hello in the cell and is Colored Red I'd want in the other worksheet cell A1: Hello | 255
Just used | as a delimiter but anything sensible...
I have a large data set that I'm working with in excel. About 1000+ columns and close to 1 million rows.
My issue is that many of my numbers are formatted as text. To resolve this, I've been using the copy paste > add technique, adding a blank cell.
My problem is that I'm trying to macro this functionality, but I can't figure out how to add a blank cell.
I tried to get crafty and have the macro create a new row, do the add, then delete that row. But, I can't seem to get that to work either.
Anyone have a solution?
Instead of selecting the entire range, you need to select only the cells with values in them. I would suggest the Special Cells function:
Highlight the cell with the #1 in it and COPY that cell
Highlight a column of cells to convert
Press F5 > Goto > Special > Constants (you may have to play with the options here to get only the cells you want)
OK (Now only the cells with values are selected)
Now select Paste Special > Multiply
Using VBA you can conditionally convert the target values to doubles (or another type of your choosing).
Tested example below assumes:
you are working with Sheet1 in ActiveWorkbook
numbers stored as text in column A (1)
converted values to appear in column B (2)
An aside: It's probably always a good idea save your work before running VBA. Cheers, and happy coding.
Option Explicit
Sub convert_to_dbl()
Dim r As Long
Dim ws As Worksheet
Set ws = ActiveWorkbook.Sheets(1)
For r = 1 To FindLastRow(ws)
With ws
If .Cells(r, 1).Value <> "" Then
.Cells(r, 2).Value = CDbl(.Cells(r, 1).Value)
End If
End With
Next r
End Sub
Function FindLastRow(ws As Worksheet)
Dim LastRow As Long
If WorksheetFunction.CountA(Cells) > 0 Then
LastRow = Cells.Find(What:="*", After:=[A1], _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
End If
FindLastRow = LastRow
End Function
The following code does what I was looking for.
Sub psAdd()
Dim x As Range 'Just a blank cell for variable
Dim z As Range 'Selection to work with
Set z = Cells
Set x = Range("A65536").End(xlUp).Offset(1)
If x <> "" Then
Exit Sub
Else
x.Copy
z.PasteSpecial Paste:=xlPasteAll, Operation:=xlAdd
Application.CutCopyMode = False 'Kill copy mode
End If
x.ClearContents 'Back to normal
End Sub