Excel VBA Add formula based on row - excel

So I have that table above, I use Excel VBA to add new prices then add the formula to Decision column.
As you can see, cell B2 formula should be =IF($A2>50000,"Ignore","Buy") and cell B3 formula should be =IF($A3>50000,"Ignore","Buy") so the formula in B2 refers to the value in A2, this is the same for B3 to A3 and so on. I use the VBA below to add the same formula to blank cells. Yes, there will be blank decision cells and they need formula. I must NOT use autofill from top to bottom. I tried using below (LastRow is the usedrange.row):
Sheet1.Range("B2:B" & LastRow).SpecialCells(xlCellTypeBlanks). _
Formula = "=IF($A2>50000,""Ignore"",""Buy"")"
The problem with that VBA is even in cell B5 the formula is =IF($A2>50000,""Ignore"",""Buy"") when it should be =IF($A5>50000,""Ignore"",""Buy"") (should be $A5 instead of $A2). What am I doing wrong?

With SpecialCells(xlCellTypeBlanks) you will probably get a non continuous range. With this the auto fill process will not work with A1 formulas. But with R1C1formulas it will.
Use:
.Range("B2:B" & lastrow).SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=IF(RC1>50000,""Ignore"",""Buy"")"
RC1 means the Row you are currently in but always fix Column 1.
For R1C1 references see https://support.office.com/en-us/article/Overview-of-formulas-7abfda78-eff3-4cc6-b4a7-6350d512d2dc?CorrelationId=2bedf5ef-a3b7-4a82-9b12-6ee86b494ae9&ui=en-US&rs=en-US&ad=US#bmusing_references_in_formulas. Scroll down to The R1C1 reference style.

You can paste the formula in all cells, considering you have the formula in cell "B2":
Range("B2").Copy
Range("B2:B" & LastRow).PasteSpecial xlPasteFormulas

edit for more detail: You can use the R1C1 reference style, more importantly, R[1]C[1] notation. There is a caveat for different languages though, see the very end of the post. Examples:
R2C4 'row 2, column 4 so it's the cell D2 in A1-notation
R[2]C[4] 'the cell 2 to the right and 4 down from the current cell (where this reference is located)
R[2]C4 'the cell 2 to the right from the current cell in column 4 (D)
R[-2]C[-4] 'you can also give negative arguments, this is the cell 2 to the left and 4 up
R[2]C 'the same as R[2]C[0]
RC[4] 'the same as R[0]C[4]
R2C 'the same as R2C[0]
RC4 'the same as R[0]C4
R2 'row 2
C4 'column 4 (the same as D:D)
As you can see from the last three examples, the notations can't be mixed.
Now for your case:
If you want to have the following in cell Bx (replace x by any number)
"=IF($Ax>50000,""Ignore"",""Buy"")"
This would be the R1C1 formula
"=IF(RC1>50000,""Ignore"",""Buy"")"
or if it is more important that it is the column to the left:
"=IF(RC[-1]>50000,""Ignore"",""Buy"")"
The latter would be the like dropping the $ from the original formula.
Your second formula was
"=IFERROR(VLOOKUP(RC3,Database!$A:$F,3,FALSE),""Missing"")"
and Axel's answer
"=IFERROR(VLOOKUP(RC3,Database!C1:C6,3,FALSE),""Missing"")"
should be clear now.
If you don't want or can't use the formulaR1C1 property but still use the R1C1 style reference for a single cell, you can use the INDIRECT worksheet function. INDIRECT("R1C1",FALSE) is a reference to R1C1. The FALSE tells it to use R1C1 instead of A1 notation. It might behave slightly different than a simple reference if there is something other than numbers in the referenced cell.
I personally like the R1C1 notation better than the A1 notation mostly because it is easier to reference cells relative to the current position but also because it is easier to read for high column numbers and it's closer to the Cells(rowIndex,columnIndex) syntax.
One last thing: In other language versions of excel, R1C1 might be named differently. That doesn't affect the formula when you enter it via VBA (I think) but if you want to enter it from the worksheet, you need to keep that in mind. In German it's Z1S1 for example. This can also cause problems when opening the file with a different language version. If you used INDIRECT("R1C1",FALSE) in a formula, the INDIRECT and FALSE will be translated but the string will not so it will not work :( (The last part is from memory)

Related

How to dynamically access column properties in a With Range statement for conditional formatting

I am trying to write a script to take an easy look into my data. The data is structured as follows:
Row 1: Parameter Name
Row 2: Lower Limit
Row 3: Upper Limit
Row 4: Unit
Row 5 and below: data (can go up to a couple thousands lines and couple hundred columns).
The thing I want to achieve is a script which formats each cell from row 5 down, to color green if it is in between the limits, and to color red if it is not. Each cell should look to it's own column row 2 and 3 for the limits.
I have tried going cell per cell, or column by column. Both worked fine on smaller datasets, but showed problems (excel freezing and eventually closing) on bigger datasets.
I am now trying to format a complete range (because excel has no problems when I do a big range in one piece by hand whatsoever) at once, but I can't access the individual column properties.
The code I am using:
With formatRange
.FormatConditions.Delete
.FormatConditions.Add Type:=xlCellValue, Operator:=xlBetween, Formula1:="=" & Cells(2, formatRange.Column).Address, Formula2:="=" & Cells(3, formatRange.Column).Address
.FormatConditions(1).Interior.Color = RGB(0, 249, 49)
End With
Now say my range is from A5:B10.
formatRange will be A5:B10.
I would expect every cell from A5:A10 to compare their values against A2 and A3. This is indeed the case
But the cells of B5:B10 also compare their values against A2 and A3.
So my question is, is there a scalable way I can make range B5:B10 look at B2 and B3 instead?
Edit
The answer of #Ryan B. is an easy and correct way of doing it by hand.
The problem in vba turned out to be the following:
Formula1:="=" & Cells(2, formatRange.Column).Address would ultimately result in Formula1:="=$A$2"
As suggested by the accepted answer, this needed to change to Formula1:="=A$2" to work.
The solution I found was to create a function which cut of the first character, and create the correct formula this way.
This can be done by tricking Excel's absolute and relative referencing within conditional formulas. No VBA is going to be required. Here is a quick mock of how I understand your data:
Select the first cell of your 'Row 5' range -- where you're going to apply the conditional formulas.
Open the conditional formatting dialog from the Styles button group in the Home Ribbon (this is B5 in my mock-up),
Create a 'New Rule'
Choose 'Use a formula to determine which cells to format' rule type
Begin typing the following rule. You will have to be quite careful and avoid all use of the arrow keys. If you need to get to a different point in the formula, use your mouse to move the insertion point. Make the proper adjustment in your formula if that initial column isn't column 'B' in your worksheet
=AND( B5 >= B$2, B5 < B$3)
Pay special notice to the Dollar Signs. There are NO absolute references used for the target cell, B5. There are absolute references (that's the dollar sign) in front of the row numbers for Lower and Upper, but not on the columns.
Set you're desired look for the "in-bounds" formatting and select OK.
Repeat the steps for your "out-of-bounds" formatting. Use the formula
=OR( B5 < B$2, B5 >= B$3)
Finally, to apply the formulas to your entire range:
Select the cell with the prepared formulas (B5 in this example) and hit [ctrl] + [c] to put excel into cut/copy mode
Select the entire target range
Right click and take 'Paste Special' from the context menu
Paste as formats
And your formatting should propagate through the worksheet.
Hope it helps. Always experiment on a copy of your workbook :)

Conditional formatting using the INDIRECT function fails with boolean AND or OR or with cells containing formulas

I have the following function for checking whether column L contains the word "completed" and I use INDIRECT to be able to color the whole row with Conditional Formatting:
=INDIRECT("l"&ROW())="completed"
This function works. However, I need to extend this, I want to use Conditional Formatting based on an extra cell as well, so I tried this:
=AND(INDIRECT("l"&ROW())="completed";INDIRECT("m"&ROW())="duplicate")
When I use this second function inside the Excel worksheet they give the proper TRUE or FALSE.
Furthermore, I needed a Custom Formatting on the result of a formula in a cell. I tried the following:
=INDIRECT("n"&ROW())=123456
This only worked if I removed the formula in the cell with the result itself as a number. Again, the function worked when pasted in an worksheet cell.
Is there a way to make this work inside Excel or is there a limit to what Conditional Formatting functions can do?
In case you ask: AND(1;1) works and makes everything yellow, AND(INDIRECT("n"&ROW())=123456;1) does not work, nor does replacing AND with OR.
The semicolon is because I am in the Dutch locale. Replace it with a comma if you are in an English locale.
Not sure why this wouldn't work in Conditional Formatting. But you can simply replace the AND function with * such as:
=(INDIRECT("l"&ROW())="completed")*(INDIRECT("m"&ROW())="duplicate")
You have to think in terms of xlR1C1 formulas to understand CFRs. A CFR based on a formula thinks of it as =RC12="completed" or more completely =AND(RC12="completed", RC13="duplicate").
The xlR1C1 formula does not change no matter what cell you paste it to; it is in this way that CFRs can be applied to a wide range of cells without expending calculation cycles to update the formula for each individual cell. RC12 means 'the cell in column L on the row you are on'. It does not change if filled down, filled right or copied to any other location.
Now unless you are actually working in xlR1C1 (File, Options, Formulas, Working with Formulas, R1C1 reference style) you have to convert the xlR1C1 to xlA1 style. If you are applying the CFR to a number of rows starting with the first row then the R becomes 1 and the C12 becomes $L.
'xlR1C1
=AND(RC12="completed", RC13="duplicate")
'xlA1
=AND($L1="completed", $M1="duplicate")
If you were applying the CFR to a range starting in row 2 change the $L1 to $L2 and the $M1 to $M2.
Among other reasons for not putting the xlR1C1 style formula directly into the CFR creation dialog when working in xlA1 style is that there actually is a RC12 cell in xlA1.

Calling the same cell of different worksheets in EXCEL

I have a drop down list (with name of sheets) and based on that value, let's say that I select the Sheet4 as in the image, I want to bring to another sheet the value of that selection, let's say on the cell B8.
I know that this works:
=IF(B1="Sheet1", Sheet1!B8, IF(B1="Sheet2", Sheet2!B8, Sheet3!B8))
That's for just 3 sheets but is there a nicer or more efficient way to do this?
This is in general how all the sheets look like:
Use INDIRECT to construct a valid worksheet and cell reference from text-that-looks-like-a-worksheet-and-cell-reference 1
The indirect takes a string and turns it into a valid reference.
=INDIRECT("'" & B1 & "'!B8")
So in the case above it would create a string "'Sheet4!B8". Then the Indirect will turn it into a valid cell reference.
As Jeeped also pointed out in the comments The B8 reference since it is literal text it will not change if the formula is copied or dragged to another cell.
The B1 which is a cell reference and is relative will change as it is copied or dragged to different cells.
1 as per #Jeeped comment

Simple VB ststment deconstruction

Could somebody please explain this simple vba statment to me? I just want to know what each part is referring to, and basically what this statement is accomplishing within my workbook. Thank you
ActiveCell.FormulaR1C1 = "='Bren Template'!R[-3]C[-6]"
This is a cell reference. In the ActiveCell (the one chosen), it will put the formula ='Bren Template'!R[-3]C[-6]. The formula breakdown is "Bren Template" is a reference to a sheet with that name. The R[-3] refers to three rows ABOVE the active cell. The c[-6] refers to three columns to the LEFT of the active cell.
So, if the active cell is H5 that formula will read ='Bren Template'!B2
If your active cell is I6, then the formula will read ='Bren Template'!C3
Note: The r[-3] and c[-6] will be "translated" from R1C1 style (i.e. Row 1 Column 1) when the formula is actually set in the cell.

Dynamic Excel Subtraction

Say I've got cells A1-A10 populated with numbers. My initial formula in cell B1 is =A1-A6. However, I'd like to strike-out cell A3 (keeping the contents visible underneath the strike-out if possible), and I'd like the formula in B1 to recognise that change, and then automatically adjust itself to =A1-A7 (the idea being that I'd like A1 subtracted by the number in the cell 5 "non-struck out" cells below it). And then if I strike out cell A5 I'd like the formula to adjust itself to =A1-A8 and so on. Does anyone know how to do this?
(EDIT#1: misread the input, sorry)
A bit straightforward, but will do the job: type =A1-INDIRECT("A"&SMALL(IF(A:A<>"",ROW(A:A),""),6)) and press CTRL+SHIFT+ENTER instead of usual ENTER - this will define an ARRAY formula and will result in {} brackets around it (but do NOT type them manually!).
To speed up calculation you may replace A:A to any limited range.
Sample file (resulting formula is yellow-marked): https://www.dropbox.com/s/sy7zkg71xtfgib9/Subtract5th.xlsx
(EDIT#2: misread the "strike-out", sorry)
Font styles (as well as similar cell properties) may NOT be read by default Excel functions, that's why you need to add UDF called StrikeOut:
Press ALT-F11 - thiss will open VBA editor.
Insert new module: Insert > Module.
Paste the code to added module:
Function StrikeOut(R As Range) As Long
Dim c As Range
StrikeOut = 0
For Each c In R.Cells
If c.Font.Strikethrough = True Then StrikeOut = StrikeOut + 1
Next
End Function
Add the formula to B1: =A1-INDIRECT("A"&(6+StrikeOut(A2:A10)))
Set strikethrough font to any cells in A1:A10.
Unfortunately, cell format change does NOT trigger any change event, so you need either press F9 or change any cell value on the sheet to recalculate and therefore update result in B1.
Sample file is shared: https://www.dropbox.com/s/n9o7tn3ks3x8nza/StrikeOut.xlsm
P.S. at least for me that was extremely useful)))

Resources