Formula in worksheet and VBA works differently - vba

Cell A1 contains the number 25, which is right-aligned, implying it's a number, not text.
D1 contains the formula:
="" & A1
The 25 in D1 is left-aligned, implying it's text. That is confirmed by the following formula, which returns 1:
=IF(D1="25",1,0)
The following VBA code puts 25 in F1 but the 25 is right-aligned and the IF test returns 0:
Range("F1") = "" & Range("A1")
Any explanation for the discrepancy?
E1 contains the following formula which gives a left-aligned 25 and the IF test returns 1:
TEXT(A1,"0")
However, the following code gives a right-aligned 25 and the IF test returns 0:
Range("F1") = Application.WorksheetFunction.Text(Range("A1"), "0")
Not that I have to use the TEXT function. I just wonder why it works differently than when in a worksheet.
Any rule that tells when or what worksheet functions won't work in VBA code, or more precisely, will give different results than when in worksheet?

When a data is written by vba into a cell, an internal type conversion function is called if required, that is if the data type is different from the cell's numberformat property.
You dont want that conversion function to be called.
To avoid this conversion function to be called, choose the proper Numberformat property for the cell before writing the data.
Range("b4").NumberFormat = "#"
Range("b4") = Application.WorksheetFunction.Text(Range("A1"), "0")

You simply get the wrong idea of what is a number in Excel.
in general ALL input is a string. Also writing "25" in a cell.
However: If possible, Excel will convert all inputs to a numerical value if possible. Also for dates and times.
To prevent this, you simply insert a ' in front of your "text" in the cell.
The confusing part for you is the different behavior for formulas.
A formula will always output a "result" AND the "data type".
So =1+1 will be numeric as the last action was math.
=Left(1+1,1) will be text as the last action was text-based.
For =A1 it will simply copy the type. If there is a formula, then this will be the same. But if there is a "direct input" it will always try to convert to numerical and only be text if it can't be converted or if it starts with a leading ' (A1 itself does this already).
As a result: If there is a plain 25 in the cell, it will always be "numerical" no matter "how" you input the 25.
For newer Excel there is only one exception: if the cell formatting is text prior to entering a number, it will be treated as text (no converting). This does not apply if you change the formatting later.
Simple test:
enter 25 in A1 (formatting general)
enter =ISNUMBER(A1) in A2 (will be TRUE)
set formatting for A1 to "text" (A2 will still be TRUE)
enter 25 in A1 (now A2 will become FALSE)
This may fail (Excel confuses itself sometimes here). Try it with a new sheet. ;)
Hopefully you understood the fault in your logic ;)

The cell alignment says nothing about the cell's contents. Forget about anything being "implied" by it. When you start on a virgin worksheet the format for all cells is "General" which means that Excel will decide the format of what you enter. If you enter a number the format will be "Number". If you enter what looks like a date to Excel the format will be "Date", and for most other things the format will be "Text".
So, if you enter " 25" in a cell formatted as "General" Excel will recognise this to be a number despite the leading spaces, read it is numeric, and format the cell to the right. This will happen regardless of whether you made the entry by hand or used VBA. You can then proceed to format the alignment as you wish.
However, if you enter the number 25 in a cell formatted as Text Excel will recognise the number as text and display it formatted to the left (unless you expressly formatted the horizontal alignment to the right).
The best way to deal with any problems you might encounter in this regard, set the NumberFormat and HorizontalAlignment properties for the cells that you want to write to. You can do that both manually or using VBA.

Worksheet function when used in the worksheet behaves / works the same way as when used in VBA. Consider below code:
Note: Range("B1") contains a numeric value 25
Dim r As Range, v As Variant
Dim wf As WorksheetFunction: Set wf = Application.WorksheetFunction
With Sheet1
Set r = .Range("B1")
v = r.Value2
v = wf.Text(r.Value2, "0")
End With
Now using the local window, let us check the data type of variant v.
SC1: All variables un-initialized
You can see, at the start that all variables have no value and the variant type v is empty.
SC2: Variables initialized and v assigned a value
After executing lines up to v = r.value2, all variable types were confirmed (e.g. Range/Range etc.) and variant v is now Variant/Double.
SC3: Re-assign a value on v but using worksheet function Text
Executing the last line which uses the worksheet function Text, variant v type becomes Variant/String. I think this confirms that the function Text works as expected converting the numeric 25 into a string type.
As for the behavior of passing VBA generated value to worksheet, it is covered by Docmarti's post above.

Related

UDF to return formatted number

Sometimes I like to display numbers along with text in the same cell. To do this, I custom format the cell to something like 0.00" test", e.g. a cell A1 with formula =PI(), but formatted with custom format 0.00" test" would return a displayed result of 3.14 test.
Crucially, the value of the cell is unchanged by the formatting - you can still do =A1 * 3 in another cell and get the result - since the value of A1 is still Pi, only it's display has changed.
For a UDF that returns a numerical value (in my case, Long, but it could be any number), is there a way of returning a cell such that it is displayed 1 way, but it's actual value (.Value2 in VBA I believe) is a number, not text.
I've tried Format( in VBA, but it returns a text string. I would just format my cell how I want it manually, but as you can see from the below code, the formatting is dependent on intermediate results (I'm trying to return the value formatted with the time the calculation took).
UDF_RESULT = Format(valueResult.Length, IIf(tElapsed < timeout, "0" & " """ & Round(tElapsed, 2) & "s""", "0"))
This would be easy to do with a Macro, but within a UDF it's harder. I could declare all of the intermediate values at a module level, then a Worksheet_Calculate() macro can access those values and apply the custom formatting, but a UDF-wrapped approach would be much better.
No.
You're confusing a cell's value with its number format. A UDF can compute a value, and the cell is free to format that value as needed.
So if a UDF returns a number, the cell's value is the result of that function - a number.
Just format the cell as needed. A function doesn't format anything.

Get value from cell without transform in vba macros

I have an issue with vba macros. Cell contains value 941144280284022000000. But when try to get this value in macros, variable is equal
9.41144280284022E+20. Is it possible to get "real" value?
Thanks.
Here is an example:
Sub dural()
MsgBox ActiveCell.Text
End Sub
This will insure that long strings of numerals are not converted to numbers.
EDIT#1:
This assumes that the cell actually displays the long string of numerals. If the cell displays ####, that is what the sub will pick-up. If the cell displays 9.41E+20, then that is what my sub will pick-up.
My sub will not necessarily pick-up the contents of the Formula Bar.
Try a simple :
Dim myVar as Variant
myVar = CDec(Range("A1").Value)
If you wish to store a value that looks like a number (or is a number) but has more than 15 digits of precision, then you must either format the cell as text before entering the value, or prepend the value with a single apostrophe to indicate to Excel that the value is to be treated as text and not a number.
If you don't do this, then as soon as the value is entered, there's a good chance it will be altered by Excel. The trailing zeros in the example value mean this does not happen in this specific case, but try changing that last 0 to 1 and you'll see what I mean.
You enter: 941144280284022000001
Excel converts this to: 941144280284022000000
More reading: https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel

(Excel VBA) - Draw from cell text in a macro

I'm trying to build a small macro that allows the user to format multiple different documents at once.
I would like for the user to be able to enter into a particular cell within the document containing the macro a particular piece of text.
I then want for this piece of text to be able to be drawn upon in the macro while affecting a different document.
For instance, a code to add another column might say
Worksheets(1).Range("A1").EntireColumn.Insert
Instead of specifying the column (A), I would like it to draw on a value in the host document. For instance, the user types "G" into the particular cell, and then clicks a button to run the macro, and the macro will dynamically know to affect column G in all excel documents it targets based off of the value in the host document.
I hope this makes sense.
Any suggestions for the sort of functions I should be looking at to make this work?
"Any suggestions on the sort of functions I should be looking at?"
Here's a few...
To get the value which is entered...
If the cell will always be in the same address, say A1:
' Define a string variable and set it equal to value in A1
Dim cellText as String
cellText = ThisWorkbook.ActiveSheet.Range("A1").Value
or instead of using Range you can also use Cells which takes a row and column number.
cellText = ThisWorkbook.ActiveSheet.Cells(1, 1).Value
If the cell changes then you may need to look into the Find function to look for a label/heading next to the input cell. Then you can use it (easily with Cells) to reference the input...
Once you have this variable, you can do what you like with it.
To put this value into cell B3 in another (open) workbook named "MyWorkbook", and a sheet named "MySheet" you can do:
Application.Workbooks("MyWorkbook").Sheets("MySheet").Range("B3").Value = cellText
To insert a column at cellText, do
Application.Workbooks("MyWorkbook").Sheets("MySheet").Range(cellText & "1").EntireColumn.Insert
Notably here, the & concatonates the strings together, so if
cellText="B"
then
cellText & "1" = "B1"
Further to your comment about moving values between sheets, see my first example here, but always refer to the same workbook. If you find yourself repeatedly typing something like
ThisWorkbook.Sheets("MySheet").<other stuff>
then you can use the With shorthand.
With ThisWorkbook.Sheets("MySheet")
' Starting anything with a dot "." now assumes the with statement first
.Range("A1").Value = .Range("A2").Value
.Range("B1").Value = .Range("B2").Value
End With
Important to note is that this code has no data validation to check the cell's value before using it! Simply trying to insert a column based on a value which could be anything is sure to make the macro crash within its first real world use!

VBA Inputbox strips decimal comma, converts double to string

I'm trying to create an Inputbox which has a number, with decimals, as the default value. I'm setting the Inputbox to the formula type, because the user might input a formula or reference a cell.
The problem is that the Inputbox seems to strip the comma and coerse the number to a string. I could fix this casting the number as a string with Format, and then going back to a number afterwards, but losing precision. And I'd like to understand what's going on.
The code is:
Sub test()
Dim Defolt As Double
Defolt = 1.1866701960364
Dim InputValue
InputValue = Application.InputBox("Value?", , Defolt, , , , , 0)
'for this example, the user just clicks OK to the default value
Debug.Print InputValue
End Sub
The results are these:
Thanks!
ps: the locale is Spanish. Excel version is Excel 2010 32bits.
Have a look here. The important part is right under the table:
You can use the sum of the allowable values for Type. For example, for an input box that can accept both text and numbers, set Type to 1 + 2.
and a little further down in the remarks:
If Type is 0, InputBox returns the formula in the form of text — for example, "=2*PI()/360". If there are any references in the formula, they are returned as A1-style references. (Use ConvertFormula to convert between reference styles.)
Try setting the type as 1 and see if you can still use a formula and number. The documentation leads me to think that you can (basically you get formula for free). Since you're setting the type to 0, you're getting back the default Text type.

Excel VBA - Interpret "N/A" Values

I am traversing a spreadsheet which contains a column of prices, in the form of double types. I am trying to locate a missing value which is shown on the spreadsheet as "n/a", but it is not letting me interpret this as a string type.
The cell containing "n/a" seems to be an integer type; how can I read this?
If all you want to do is to check for the error value then:
Application.WorksheetFunction.IsNA(rngToCheck.Value)
where rngToCheck is the cell which you want to check for the #N/A error value
(There's a list of the worksheet functions which can be called from Excel VBA here)
You could also examine rngToCheck.Text as this will contain the string "#N/A"
If instead, you want to read the formula in the cell which generated the #N/A then rngToCheck.Formula would do that
A cell containing #N/A is retrieved by VBA as a variant containing an error code
In general its usually best to assign Excel cells to Variants because a cell can contain a number(double), logical, string or error and you cannot tell in advance what the cell wil contain.
You can prepare the spreadsheet you like to check as described below and evaluate the special cells containing the IS Functions, it is easy to check them for True or False in VBA. Alternatively, you can write your own VBA function as shown below.
There are Excel functions which check cells for special values, for example:
=ISNA(C1)
(assumed that C1 is the cell to check). This will return True if the cell is #N/A, otherwise False.
If you want to show whether a range of cells (say "C1:C17") has any cell containing #N/A or not, it might look sensible to use:
=if(ISNA(C1:C17); "There are #N/A's in one of the cells"; "")
Sadly, this is not the case, it will not work as expected. You can only evaluate a single cell.
However, you can do it indirectly using:
=if(COUNTIF(E1:E17;TRUE)>0; "There are #N/A's in one of the cells"; "")
assuming that each of the cells E1 through E17 contains the ISNA formulas for each cell to check:
=ISNA(C1)
=ISNA(C2)
...
=ISNA(C17)
You can hide column E by right-clicking on the column and selecting Hide in Excel's context menu so the user of your spreadsheet cannot see this column. They can still be accessed and evaluated, even if they are hidden.
In VBA you can pass a range object as RANGE parameter and evaluate the values individually by using a FOR loop:
Public Function checkCells(Rg As Range) As Boolean
Dim result As Boolean
result = False
For Each r In Rg
If Application.WorksheetFunction.IsNA(r) Then
result = True
Exit For
End If
Next
checkCells = result
End Function
This function uses the IsNA() function internally. It must be placed inside a module, and can then be used inside a spreadsheet like:
=checkCells(A1:E5)
It returns True, if any cell is #N/A, otherwise False. You must save the workbook as macro-enabled workbook (extension XLSM), and ensure that macros are not disabled.
Excel provides more functions like the above:
ISERROR(), ISERR(), ISBLANK(), ISEVEN(), ISODD(), ISLOGICAL(),
ISNONTEXT(), ISNUMBER(), ISREF(), ISTEXT(), ISPMT()
For example, ISERR() checks for all cell errors except #N/A and is useful to detect calculation errors.
All of these functions are described in the built in help of Excel (press F1 and then enter "IS Functions" as search text for an explanation). Some of them can be used inside VBA, some can only be used as a cell macro function.