How do I get the format of a cell in VBA - excel

When iterating through cells in a worksheet, how can I get what the format setting on the cell is? Because based on this, I would like to build a SQL statement to either add the single ticks or not to the value retreived

Sounds like you need the VarType() function. Vartype(Range("A1"))
OK, so you don't want to know the format setting for the cell, but whether the value is numeric.
Can you just call IsNumeric(Range("A1")) and quote it if False?
Based on your comment that some numbers are stored as text in the DB, you are not going to solve this by a simple formula. Can't you just quote the values as you build your SQL statement?

Try using the following in VBA:
Range("A1").NumberFormat = "0.00" 'Sets cell formatting to numeric with 2 decimals.
Range("A1").Formula = "=Text(6, " & """0.00""" & ")" 'Looks like a number _
' but is really text.
Debug.Print WorksheetFunction.IsNumber(Range("A1")) 'Prints False
Range("A1").Value = 6 'Puts number into the cell, which also looks like 6.00
Debug.Print WorksheetFunction.IsNumber(Range("A1")) 'Prints True
This should tell you if the value is really text or really a number, regardless of the cell's formatting properties.
The key is that the intrinsic Excel IsNumber() function works better for this purpose than the VBA function IsNumeric. IsNumber() tells you whether the cell's value is a number, whereas IsNumeric only tells you if the cell is formatted for numeric values.

I don't think there's any property of a cell that indicates whether the cell actually contains a numeric value, although VarType() might help, it gets tricky because Excel will allow a number-formatted cell to contain string, and a text formatted cell to contain numeric values, without overriding the NumberFormat property.
In any case you likely need some independent test to figure out whether a cell IsNumeric (or other criteria) AND whether its NumberFormat is among an enumerated list which you can define.
Sub numFormat()
Dim cl As Range
Dim numFormat As String
Dim isNumber As Boolean
For Each cl In Range("A1")
numFormat = cl.NumberFormat
isNumber = IsNumeric(Trim(cl.Value))
Select Case numFormat
Case "General", "0", "0.0", "0.00" ' <--- modify as needed to account for other formats, etc.
If isNumber Then
Debug.Print cl.Address & " formatted as " & numFormat
End If
Case Else
'ignore all other cases
End Select
Next
End Sub

I don't think the format of the cell is the important thing. Rather, it's the data type of the field in your database. If you have the string 'foobar' in a cell and you create an INSERT INTO sql statement that attempts to put that into a Long Integer field, it's going to fail regardless of tickmarks.
Conversely, if a cell contains a numeric value (like 100) that needs to go into a VARCHAR field, it will need tickmarks (like '100').
If you're using ADO, check the Type property of the Field object to determine the data type. Use this list http://support.microsoft.com/kb/193947 to see what the types are. Then set up the SQL statement according to the field type.

Related

Insert string into cell with only integers while keeping fonts- VBA

I am trying to append text into an excel cell with VBA. Currently I am using :
Range("A1").Characters(Len(Range("A1")) + 1).Insert ("text")
This works fine for cells with text, but if the cell contains only numbers (integers) it throws an error:
Run-time error '1004' Insert method of Characters class failed
Parts of the text in the cell are in different fonts or bolded and I need to keep them that way.
I have also tried to change the NumberFormat the cell to general, but it does not help:
Range("A1").NumberFormat = "#"
If someone knows a way to append text to a cell containing only integers with different fonts please answer.
Unlike Strings, Cells that contain actual Numbers (but not strings that look like numbers) cannot have format applied individual characters.
Therefore split your processing based on data type1
Dim cell as Range
Dim AppendText as String
AppendText = "text"
Set cell = Range("A1")
If TypeName(cell.Value2) <> "String" Then
cell.Value2 = cell.Value2 & AppendText
Else
cell.Characters(Len(cell.Value2) + 1).Insert AppendText
End If
After this has run, cells thar were numeric will now be strings, and can have individual characters formmated.
Notes:
You might want to consider other data types too: boolean, date, ...

Code to find cells that contain a constant preceded by = sign

Any suggestions for Excel vba code that will identify cells that contain values but no function. The catch being that the values I'm searching for are preceded by an "=" sign.
I often have to review other people's financial spreadsheets and knowing which cells contain the actual inputs (assumptions) is key. I can easily find cells with just a constant input with a simple:
Selection.SpecialCells(xlCellTypeConstants, 1).Select
The problem is some users have a habit of inputing numbers preceded by an = sign and these cells are treated by excel as containing a function. I know I can search for cells with functions too, but I only want to find the ones that don't actually have a function, but are input starting with an = sign.
In other words, instead of inputing 1000 in a cell the user inputs =1000. It is only these types of cells I am trying to highlight.
Any suggestions on how I can identify these cells?
Exactly this "The problem is some users have a habit of inputing numbers preceded by an = sign" is your problem. Stop people having this habit!
First it is a stupid habit because it slows everything down because Excel has to evaluate those cells that contain something like =1000 to find out the value and
second those cells actually contain a formula and not a value. So there is no built in way to distinguish them.
Those people are just using Excel wrong.
There is only one workaround I can imagine and that is looping through all cells that contain a formula, remove the = sign and check if the rest of the "formula" is numeric IsNumeric(). If so replace the formula by its value.
So for example if the cell is =1000 then 1000 is numeric and it will replace it by the value 1000. If the cell is =1000*2 then 1000*2 is not numeric because it contains a * sign and it will keep the formula.
Note that this workaround is just fighting symptoms but not fixing the root cause. The root cause is those people using Excel wrong! And you should always fix the causes not fight the symptoms.
Option Explicit
Public Sub ReplaceFormluasWithValueIfNoCalculation()
Dim AllCellsWithFormulas As Range
On Error Resume Next ' next line errors if no formulas are found, so hide errors
Set AllCellsWithFormulas = ActiveSheet.Cells.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0 ' re-enable error reporting
If Not AllCellsWithFormulas Is Nothing Then
Dim Cell As Variant
For Each Cell In AllCellsWithFormulas.Cells ' loop through all formulas
Dim ExtractedValue As String ' extract formula right of the `=` sign
ExtractedValue = Right$(Cell.Formula, Len(Cell.Formula) - 1)
' check if extracted formula is only a value, if so replace formula with value
If IsNumeric(ExtractedValue) Then
Cell.Value = ExtractedValue
End If
Next Cell
End If
End Sub
Even the above can be done, I recommend fixing the root cause.
Assuming those inputs only contain number you could use a regex for that and ensure the value only contain "=" and numbers you'll have a code like this :
Dim RegEx As Object, MyString As String
Set RegEx = CreateObject("VBScript.RegExp")
MyString = cell.formulas
With RegEx
.Pattern = "=*[0-9., ]+"
End With
RegEx.test(MyString) 'True if MyString is a number false if it's a function
You'll need to activate "Microsoft VBScript Regular Expression" though.

Excel VBA inserting date into cell produces incorrect format

Hoping someone can help with a funny issue I'm having. Using the following Excel VBA code to update a cell with a date in a sheet. The cell is formatted to "DD/MM"YYYY" my local PC is set to this also.
Dim SelectedDate As String
SelectedDate = "05/02/2018"
Sheets("CONTROL").Range("F36").NumberFormat = "dd/mm/yyyy"
Sheets("CONTROL").Range("F36").Value = Format(SelectedDate,"dd/mm/yyyy")
MsgBox Format(SelectedDate,"dd/mm/yyyy") ' Returns 05/02/2018 - Correct
MsgBox Sheets("CONTROL").Range("F36").Value ' Returns 02/05/2018 - Incorrect
Any help would be greatly apprecaited.
Easy to get confused with Excel, VBA and date formats. Even easier when you are using strings instead of dates in VBA and/or on the worksheet. And it will be easier to figure out what is going on if you format your output to unambiguous dates eg: dd-mmm-yyyy.
You are entering a string into F36. VBA is US Centric and thinks the string represents May 2, 2018
To have the string interpreted the same as your windows short date format, you can use the Datevalue function and treat your entries as dates and not as strings.
So one alternative to your code would be:
Sub marine()
Dim SelectedDate As Date
SelectedDate = DateValue("05/02/2018") 'will convert to 5-Feb-2018
Sheets("CONTROL").Range("F36").NumberFormat = "dd/mm/yyyy"
Sheets("CONTROL").Range("F36").Value = SelectedDate
MsgBox Format(SelectedDate, "dd/mm/yyyy")
MsgBox format(Sheets("CONTROL").Range("F36").Value,"dd/mm/yyyy")
End Sub
The NumberFormat properties just define the way the underlying value is visually represented, but it doesn't change the underlying value itself. Instead of printing the Value property, use Text (which holds the formatted representation of the underlying value):
MsgBox Sheets("CONTROL").Range("F36").Text
Alternatively, if you want to stick on the Value property of the cell, you are forced to print it as follows:
MsgBox Format(Sheets("CONTROL").Range("F36").Value, "dd/mm/yyyy")

Compare number formatting of two cells in excel using vba

My problem:
Cell A1: value =12 and the number property is general.
Cell A2: value =12 and the number property is number with 2 decimal points, so the number is shown as "12.00"
How can I validate if both cells have the same number property, either in VBA using macros or in any other way?
You can compare their NumberFormat property:
If Range("A1").NumberFormat = Range("A2").NumberFormat Then
However, There are a number of different formats, including custom formats, that can be applied to cells that would make them look the same, without them having exactly the same NumberFormat value.
This means that comparing formats will never be as reliable as comparing the values. (It is also unusual to need to compare formats.)
You can use the NumberFormat method.
Using this code, with 12 in A1 (formatted as general) and 12 in B1 (formatted as 12.00)
Option Explicit
Sub Stuff()
MsgBox Range("A1").NumberFormat
MsgBox Range("B1").NumberFormat
End Sub
Will return messages of "General" and "0.00" respectively.
So using
If Range("A1").NumberFormat = Range("B1").NumberFormat Then
-- do stuff here
End If
Will tell you if they are equal to one another or not.
It sounds like you are really concerned with the values of the cells. The .NumberFormat property simply masks how the value appears, visually, to the user of the spreadsheet.
So you can easily do an equivalence test on the two values, like:
Sub Test()
MsgBox [A1].Value = [A2].Value
End Sub
Or you could do this in the worksheet formula:
=A1=A2
That formula should return either True or False, and should ignore the cell's formatting.

What is the difference between .text, .value, and .value2?

What is the difference between .text, .value, and .value2? Such as when should target.text, target.value, and target.value2 be used?
.Text gives you a string representing what is displayed on the screen for the cell. Using .Text is usually a bad idea because you could get ####
.Value2 gives you the underlying value of the cell (could be empty, string, error, number (double) or boolean)
.Value gives you the same as .Value2 except if the cell was formatted as currency or date it gives you a VBA currency (which may truncate decimal places) or VBA date.
Using .Value or .Text is usually a bad idea because you may not get the real value from the cell, and they are slower than .Value2
For a more extensive discussion see my Text vs Value vs Value2
In addition to the answer from Bathsheba and the MSDN information for:
.Value
.Value2
.Text
you could analyze the following tables for better understanding of differences between these three properties.
target.Value will give you a Variant type
target.Value2 will give you a Variant type as well but a Date is coerced to a Double
target.Text attempts to coerce to a String and will fail if the underlying Variant is not coercable to a String type
The safest thing to do is something like
Dim v As Variant
v = target.Value 'but if you don't want to handle date types use Value2
And check the type of the variant using VBA.VarType(v) before you attempt an explicit coercion.
Regarding conventions in C#. Let's say you're reading a cell that contains a date, e.g. 2014-10-22.
When using:
.Text, you'll get the formatted representation of the date, as seen in the workbook on-screen:
2014-10-22. This property's type is always string but may not always return a satisfactory result.
.Value, the compiler attempts to convert the date into a DateTime object: {2014-10-22 00:00:00} Most probably only useful when reading dates.
.Value2, gives you the real, underlying value of the cell. In the case for dates, it's a date serial: 41934. This property can have a different type depending on the contents of the cell. For date serials though, the type is double.
So you can retrieve and store the value of a cell in either dynamic, var or object but note that the value will always have some sort of innate type that you will have to act upon.
dynamic x = ws.get_Range("A1").Value2;
object y = ws.get_Range("A1").Value2;
var z = ws.get_Range("A1").Value2;
double d = ws.get_Range("A1").Value2; // Value of a serial is always a double
.Text is the formatted cell's displayed value; .Value is the value of the cell possibly augmented with date or currency indicators; .Value2 is the raw underlying value stripped of any extraneous information.
range("A1") = Date
range("A1").numberformat = "yyyy-mm-dd"
debug.print range("A1").text
debug.print range("A1").value
debug.print range("A1").value2
'results from Immediate window
2018-06-14
6/14/2018
43265
range("A1") = "abc"
range("A1").numberformat = "_(_(_(#"
debug.print range("A1").text
debug.print range("A1").value
debug.print range("A1").value2
'results from Immediate window
abc
abc
abc
range("A1") = 12
range("A1").numberformat = "0 \m\m"
debug.print range("A1").text
debug.print range("A1").value
debug.print range("A1").value2
'results from Immediate window
12 mm
12
12
If you are processing the cell's value then reading the raw .Value2 is marginally faster than .Value or .Text. If you are locating errors then .Text will return something like #N/A as text and can be compared to a string while .Value and .Value2 will choke comparing their returned value to a string. If you have some custom cell formatting applied to your data then .Text may be the better choice when building a report.
Out of curiosity, I wanted to see how Value performed against Value2. After about 12 trials of similar processes, I could not see any significant differences in speed so I would always recommend using Value. I used the below code to run some tests with various ranges.
If anyone sees anything contrary regarding performance, please post.
Sub Trial_RUN()
For t = 0 To 5
TestValueMethod (True)
TestValueMethod (False)
Next t
End Sub
Sub TestValueMethod(useValue2 As Boolean)
Dim beginTime As Date, aCell As Range, rngAddress As String, ResultsColumn As Long
ResultsColumn = 5
'have some values in your RngAddress. in my case i put =Rand() in the cells, and then set to values
rngAddress = "A2:A399999" 'I changed this around on my sets.
With ThisWorkbook.Sheets(1)
.Range(rngAddress).Offset(0, 1).ClearContents
beginTime = Now
For Each aCell In .Range(rngAddress).Cells
If useValue2 Then
aCell.Offset(0, 1).Value2 = aCell.Value2 + aCell.Offset(-1, 1).Value2
Else
aCell.Offset(0, 1).Value = aCell.Value + aCell.Offset(-1, 1).Value
End If
Next aCell
Dim Answer As String
If useValue2 Then Answer = " using Value2"
.Cells(Rows.Count, ResultsColumn).End(xlUp).Offset(1, 0) = DateDiff("S", beginTime, Now) & _
" seconds. For " & .Range(rngAddress).Cells.Count & " cells, at " & Now & Answer
End With
End Sub
Value2 is almost always the best choice to read from or write to an Excel cell or a range... from VBA.
Range.Value2 '<------Best way
Each of the following can be used to read from a range:
v = [a1]
v = [a1].Value
v = [a1].Value2
v = [a1].Text
v = [a1].Formula
v = [a1].FormulaR1C1
Each of the following can be used to write to a range:
[a1] = v
[a1].Value = v
[a1].Value2 = v
[a1].Formula = v
[a1].FormulaR1C1 = v
To read many values from a large range, or to write many values, it can be orders of magnitude faster to do the entire operation in one go instead of cell by cell:
arr = [a1:z999].Value2
If arr is a variable of type Variant, the above line actually creates an OLE SAFEARRAY structure of variants 26 columns wide and 999 rows tall and points the Variant arr at the SAFEARRAY structure in memory.
[a1].Resize(UBound(arr), UBound(arr, 2).Value2 = arr
The above line writes the entire array to the worksheet in one go no matter how big the array is (as long as it will fit in the worksheet).
The default property of the range object is the Value property. So if no property is specified for the range, the Value property is silently referenced by default.
However, Value2 is the quickest property to access range values and when reading it returns the true underlying cell value. It ignores Number Formats, Dates, Times, and Currency and returns numbers as the VBA Double data type, always. Since Value2 attempts to do less work, it executes slightly more quickly than does Value.
The Value property, on the other hand, checks if a cell value has a Number Format of Date or Time and will return a value of the VBA Date data type in these cases. If your VBA code will be working with the Date data type, it may make sense to retrieve them with the Value property. And writing a VBA Date data type to a cell will automatically format the cell with the corresponding date or time number format. And
writing a VBA Currency data type to a cell will automatically apply the currency Number Format to the appropriate cells.
Similarly, Value checks for cell currency formatting and then
returns values of the VBA Currency data type. This can lead to
loss of precision as the VBA Currency data type only recognizes
four decimal places (because the VBA Currency data type is really just a 64-bit Integer scaled by 10000) and so values are rounded to four places,
at most. And strangely, that precision is cut to just two decimal
places when using Value to write a VBA Currency variable to a worksheet range.
The read-only Text property always returns a VBA String data type. The value returned by Range.Text is a textual representation of what is displayed in each cell, inclusive of Number Formats, Dates, Times, Currency, and Error text. This is not an efficient way to get numerical values into VBA as implicit or explicit coercion is required. Text will return ####### when columns are too thin and it will slow down even more when some row heights are adjusted. Text is always VERY slow compared to Value and Value2. However, since Text retains the formatted appearance of cell values, Text may be useful, especially for populating userform controls with properly formatted text values.
Similarly, both Formula and FormulaR1C1 always return values as a VBA String data type. If the cell contains a formula then Formula returns its A1-style representation and FormulaR1C1 returns its R1C1 representation. If a cell has a hard value instead of a formula then both Formula and FormulaR1C1 ignore all formatting and return the true underlying cell value exactly like Value2 does... and then take a further step to convert that value to a string. Again, this is not an efficient way to get numerical values into VBA as implicit or explicit coercion is required. However, Formula and FormulaR1C1 must be used to read cell
formulas. And they should be used to write formulas to cells.
If cell A1 contains the numeric value of 100.25 with a currency number formatting
of $#,##0.00_);($#,##0.00) consider the following:
MsgBox [a1].Value 'Displays: 100.25
MsgBox TypeName([a1].Value) 'Displays: Currency
MsgBox [a1].Value2 'Displays: 100.25
MsgBox TypeName([a1].Value2) 'Displays: Double
MsgBox [a1].Text 'Displays: $ 100.25
MsgBox TypeName([a1].Text) 'Displays: String
MsgBox [a1].Formula 'Displays: 100.25
MsgBox TypeName([a1].Formula) 'Displays: String
MsgBox [a1].FormulaR1C1 'Displays: 100.25
MsgBox TypeName([a1].FormulaR1C1) 'Displays: String

Resources