Premise: Performing comparisons between two textboxes, one in a subform, one in a parent form, during the subform textbox's AfterUpdate event. When making this comparison, illogical results are returned.
Objective: Return legitimate comparisons between the values contained within these two controls.
Progress:
The following code shows an example of code and apparent, illegitimate results outputted to the Immediate window.
Code:
Debug.Print IsNumeric(textBurnup) & IsNumeric(Parent!textBurnupLimit), _
textBurnup & ">" & Parent!textBurnupLimit & " = " & _
(textBurnup > Parent!textBurnupLimit)
textBurnup = Parent!textBurnupLimit - 100
Debug.Print IsNumeric(textBurnup) & IsNumeric(Parent!textBurnupLimit), _
textBurnup & ">" & Parent!textBurnupLimit & " = " & _
(textBurnup > Parent!textBurnupLimit)
Results:
TrueTrue 100>21500 = True
TrueTrue 21400>21500 = False`
Things I have tried:
Setting textbox format to 'general number'. Fixes problem but I dont want to have to do this.
Performing an arithmetic operation on either control. Fixes problem but I dont want to have to do this.
Using either controls' .Value property. Doesn't work.
Substituting a number in for the Parent!textbox. Works. Defeats the purpose of looking at the Parent's textbox.
A few other things I can't think of now because I am just too surprised that I managed to break logic.
The textboxes are both numbers and should be comparable. Why is this happening? Why does setting one of their formats to 'general number' fix it?
Related articles:
String Compare Logic (based on answers below)
Source Info:
Coding Horror (based on answers below)
In your example 100 > 21500 is True because it's a string ("ascii-betical") comparison, not a numeric one.
One wrinkle in that though...
? "100" > "21500" --> False!
? "100" >" 21500" --> True!
I'm guessing your observed result was because you had a leading space on the second value: if that wasn't the case there's no way it would evaluate to True...
IsNumeric() doesn't check to see if a variable value is a number, just if it could safely be converted to one, so
IsNumeric("100") = True
even though "100" is a string.
If you want values pulled from to behave as numbers then the correct approach is
1) use IsNumeric() to check they can safely be converted to a numeric type then
2) use CDbl(), CLng() etc to convert them as #simoco suggests.
"CDbl(textBurnup) > CDbl(Parent!textBurnupLimit)?" - simoco
"In your example 100>21500 is True because it's a string ("ascii-betical") comparison, not a numeric one. IsNumeric() doesn't check to see if a variable value is a number, so IsNumeric("100") = True even though "100" is a string. If you want values pulled from to behave as numbers then the correct approach is 1)Use IsNumeric() to check they can safely be converted to a numeric type then 2) use CDbl(), CLng() etc to convert them as #simoco suggests" - Tim Williams
Related
I have an If clause that was being completely ignored, until I discovered a workaround, but now I'd like to better understand what is happening under-the-hood.
We have a form with a ComboBox that gets populated, but a default value is not set on it. When I go to save the form, we need to test if the user selected a value. The code was:
If (Me.Combo78.Value = "") Then
mb "Please select a value"
End If
The code would never fire, until I changed the condition to read:
If ("" & Me.Combo78.Value = "") Then
mb "Please select a value"
End If
I'm guessing that the "" & forces the comparison to be a text comparison, and therefore can validly test against the empty string "", but what was the previous comparison actually doing, and is there a better, more intuitive way to manage this? The solution I have just feels gross.
-- 30 years experience with coding in Pascal, HTML, Javascript, but <1 year coding in VBA, after being handed a legacy application that needs debugging.
If the ComboBox has no value then Me.Combo78.Value will be null and consequently Me.Combo78.Value = "" will be null and will not validate the test expression for the if statement.
In your second code, concatenating an empty string with a null value will return an empty string, and so "" & Me.Combo78.Value returns an empty string, thus validating the test expression.
If you wish, you can verify this for yourself in the VBE Immediate Window (accessible using Ctrl+G):
?Null = ""
Null
?Null & "" = ""
True
A more readable solution might be:
If IsNull(Me.Combo78) Or Me.Combo78 = "" Then
mb "Please select a value"
End If
Alternatively, you could use the Nz function:
If Nz(Me.Combo78, "") = "" Then
mb "Please select a value"
End If
Since the Value property is the default member for this class, it may be safely omitted.
So I have a subform VBA function that looks like this:
Function GetVariable() As Double
' <control> = DLookup("<table var>","<table>", "<other table var> = <control>"
[someFunction] = DLookup("[Var]", "[tblExample]", "[tblExampleVar] = [subFormControl]")
' (return) = ( <control> - <otherControl>) / 12345.12
GetVariable = ([finalPosition] - [someFunction]) / 12345.12
End Function
And when I open the parent form (the form that contains this subform), I get the error, "Run-time error '2447' There is an invalid use of the . (dot) or ! operator or invalid parentheses."
What I gather from this is that Access is interpreting 12345.12 as an object and I do not understand why. When I run this subform on its own it works, but when it is a subform it does not. Has anyone dealt with this before?
Extra information: I have two subforms in this parent form that use the same calculation, both repeated in their form-specific VBA, and I do not think that they would conflict with one another because they do not share scope. So my conclusion remains that Access is trying to use 12345.12 as (object).member.
Thanks for reading.
Try to take care of Null values and to be more specific.
Also, a decimal value must be concatenated as a string with dot decimal separator:
Function GetVariable() As Double
If Not IsNull(Me![subFormControl].Value) Then
Me![someFunction].Value = DLookup("[Var]", "[tblExample]", "[tblExampleVar] = " & Str(Me![subFormControl].Value) & "")
If Not IsNull(Me![finalPosition].Value - Me![someFunction].Value) Then
GetVariable = (Me![finalPosition].Value - Me![someFunction].Value) / 12345.12#
End If
End If
End Function
References that work when a form opens independent won't necessarily work when that same form is used as a subform. That requires referencing to include the subform container name.
I don't see how running form as standalone could return correct data. I presume [subFormControl] is a field or control on form. This is a variable input. Variable must be concatenated in the DLookup() WHERE condition expression.
It seems these fields return names of controls or table. Do these names have spaces or punctuation/special characters (other than underscore). Really should not use in names nor use reserved words as names. If you do, need to delimit with [].
If you are referencing fields/controls on form to build a DLookup(), then all the inputs are variables and should be concatenated.
[someFunction] = DLookup("[" & [Var] & "]", "[" & [Table] & "]", "[" & [Condition] & "] = '" & [subformControl] & "'")
Whether or not delimiters are needed (and which ones) depends on the field type of the field that will be returned by the Condition input in the filter criteria.
I have an Excel sheet that can have the following string in one of its cells:
Valid Test for ≥ 30% ABCD?
When I capture this value in a variable and output it in the Immediate window, it looks like this:
?strVal
Valid Test for = 30% ABCD?
I want to check whether the cell contains this value, and currently I'm using:
Select Case strVal
Case "Valid Test for = 30% ABCD?"
doSomething
'... other Case statements
Case Else
doSomethingElse
End Select
This string match always fails, though, and I get the following results when I investigate in the Immediate window:
?strVal="Valid Test for = 30% ABCD?"
False
?asc(mid(strVal,16,1))
61
?asc("=")
61
So apparently what's happening here is that the "≥" symbol is being rendered in VBA output as "=" both in appearance and ASCII code, but somehow it's still not equivalent to "=" in string comparison. How can I get the string match that I'm looking for?
Excel and VBA use Unicode (UTF-16) for string/text values. Unfortunately, the VBA editor does not.
For characters that the VBA editor doesn't support, use ChrW. For Unicode Character 'GREATER-THAN OR EQUAL TO' (U+2265), use ChrW(&H2265) as in
Case "Valid Test for " & ChrW(&H2265) & " 30% ABCD?"
Note: Asc doesn't do what you think it does-It's not ASCII and it isn't a one-to-one conversion. In almost all cases, AscW is what you want; it gives the UTF-16 code unit value the "character", plain and simple as far as VBA is concerned. (Of course, text isn't simple, so Unicode and UTF-16 are correspondingly complex in some areas.)
Ok, I having been working on subtracting times in Access. It's a pain to say the least, however due to how the form is set up (someone set this up years before I started working at my job & it would be much too time consuming to fix), I cannot update a field in my table through an update query after a text box event is triggered on the form.
So I am however, able to do the calculations in VBA and place the values in the form's text box control to be updated into the table. I have searched online how to subtract the times and get the format displayed as I would like them to be...(hh:mm), however b doing this it converts the final product into a string and the table field linked to the text box is a number/double so I get a VBA error: "The value you entered isn't valid for this field"
Which I know is because it's now a string. I need help with getting the conversion the way I need it, and preserving a double type. CDbl(variable) is not working.
code:
apptTime = DLookup("[Appt Time]", "T_PKMS", "[Load] = """ & loadNum & """")
ArrvlTime = DLookup("[Arrival Time]", "T_PKMS", "[Load] = """ & loadNum & """")
hoursPassedSinceAppt = ([Forms]![F_DriverLog].[Time Out].Value - apptTime)
hoursPassedSinceArrvl = ([Forms]![F_DriverLog].[Time Out].Value - ArrvlTime)
Me.Text51.Value = Format(hoursPassedSinceAppt, "hh:nn")
Me.Text53.Value = Format(hoursPassedSinceArrvl, "hh:nn")
I have also tried:
Debug.Print CDbl(Format(hoursPassedSinceAppt, "hh:nn"))
Debug.Print CDbl(Format(hoursPassedSinceArrvl, "hh:nn"))
but I get a type mismatch error. Help I am pulling my hair out.
Obviously I have been staring at my code too long. I removed the "hh:nn" and changed the colon to a decimal and it allowed for cDbl() to change it from a string to a number.
I change:
Me.Text51.Value = Format(hoursPassedSinceAppt, "hh:nn")
To:
Me.Text51.Value = CDbl(Format(hoursPassedSinceAppt, "hh.nn"))
For example i want to count the number of "johns" appearing in the column A however column has both first name and last name and i want to count all the johns irrespective of their last name.
I am using the below code however doesn't seem to work:
Range("A199").Value = WorksheetFunction.CountIf(Range("A1", Range("A1").End(xlDown)), Cells.Find(What:="John "))
You don't need VBA. You can use a wildcard with COUNTIF/COUNTIFS - make your criteria "John*" (so, =COUNTIF(A1:A7, "John*")).
As #YowE3K commented, there is a reason why I use "=" & "John*"). It's a preparation for using a variable in the future instead of the hard-coded "John".
If you want to use a VBA solution , you can use:
Range("A199").Value = WorksheetFunction.CountIf(Range("A1", Range("A1").End(xlDown)), "=" & "John*")
Or, better yet:
Const NametoFind As String = "John"
Range("A199").Value = WorksheetFunction.CountIf(Range("A1", Range("A1").End(xlDown)), "=" & NametoFind & "*")
Note: Range("A1", Range("A1").End(xlDown)) will work for continous range only (not if you have blank cells in the middle of you range).
If you are allowed to use an extra column, make one right after you name column, using the following formula:
=ISNUMBER(SEARCH("JOHN"; A1)
The above example should be in cell B1. Then you will get a TRUE if the cell in column A contains the name "JOHN", otherwise you will get a FALSE. Then you simply make a count:
=COUNTIF(B:B; TRUE)
It might be a slight disadvantage that you have to use an extra column. But on the plus-side, it is very understandable (both for yourself and others whom might read your code).