What started as a simple validation code converted in something very paradoxical in my eyes.
The following code returns "Good work!" when I input a negative number in the InputBox popup
Dim myvar As String
myvar = InputBox("input a positive number, please")
If IsNumeric(myvar) Then
myvar = CDbl(myvar)
Select Case myvar
Case Is < 0
MsgBox "I need a positive number"
Exit Sub
Case Is > 0
MsgBox "Good work!"
[MyField] = myvar
RunCommand acCmdSaveRecord
Me.Requery
Exit Sub
Case Else
MsgBox "You entered '" & myvars & "'. I don't know what to do about"
End Select
Else
MsgBox "A Number, please"
End If
Is this really the best way to validate an InputBox?
Since myvar is a String, CDbl(myvar) will get implicitly converted back to a string. Create a temporary numeric variable for the Select Case.
I agree with some of the other answers. Think about this code re-write (notice the second variable declared):
Dim myvar as String
Dim myDbl as Double
myvar = inputBox ("Input a positive number, please")
if isnumeric(myvar) then
myDbl = cDbl(myvar)
else
msgbox "Enter a number please"
exit sub
end if
if mydbl <=0 then
msgbox "I need a positive number"
else 'if mydbl > 0 then
MsgBox "Good work!"
[MyField] = myvar
RunCommand acCmdSaveRecord
Me.Requery
end if
That would solve you problems by accounting for zero and declaring a separate variable as a double there. The if statement instead of the case is just preference I suppose. But now think about the two variables you have.
debug.print "MyVar is a string containing: " & myvar
debug.print cstr(cdbl(myvar)*2)
'This forces myvar into a double(a number-type) to do math on it and then print it out
However, if you were to re-run the first print, you would see that myvar is still a string and can be used like a string. In VBA, unlike some other languages, variables are only what you declare them as. If you want to use them as other things, you have to declare another variable of the needed type.
TL;DR Think of them as different containers. Strings are circle boxes and doubles are square boxes. They might be able to hold similar stuff but the functionality of the containers is limited by their shape. In VBA you don't have a way to force a circle into a square so you have to make a whole second container and transfer the stuff over.
Related
This is my code:
Private Sub Form_Current()
Set rs = CurrentDb.OpenRecordset("Sites", dbOpenDynaset, dbSeeChanges)
rs.Requery
rs.MoveFirst
If Nz(Me.Site_ID.Value) > 0 Then
Me.H2OBillingIDlbl.Caption = DLookup("H2OBillingIDNum", "Sites", "H2OBillingIDNum = " & Me.txtHotelID)
Else
Me.H2OBillingIDlbl.Caption = ""
End If
End Sub
The DLookup line is throwing the error.
Me.txtHotelID box is a text entry box on the form and is used to enter numbers only.
The H2OBillingIDNum field in the recordset is Long.
I have tried putting brackets around H2OBillingIDNum; .Value at the end of H2OBillingIDNum and Me.txtHotelID alternatively and combined; entering the data as a string in which case I get data mismatch error.
I don't believe I can use a SQL query because it is a text entry field, but if I'm wrong, I'll happily take the information as I've never heard of a SQL query like that and it's a faster and more accurate method of pulling the data.
I'm out of ideas. Any suggestions? Is it the NZ? Is there a better way of writing that? Should that not be included at all? If it helps, this is a DAO db.
The error must be that Me.txtHotelID is empty, therefore your DLookup call is incomplete
DLookup("foo", "bar", "myValue = ")
gives Runtime error 3075: Syntax error (missing operator) in 'myValue = '
Here is a guide on how to debug problems like this.
Take the code apart (one command per line), and use intermediate variables. Their values can be seen in break mode by hovering the mouse on the variable name, or in the Watch window.
Recommended reading: Debugging VBA Code
If you use DLookup, there is no need at all for a recordset, so I have removed it.
Run (or step through) this code, and the error will become clear:
Private Sub Form_Current()
Dim SiteID As Long
Dim HotelID As Long
Dim strCaption As String
SiteID = Nz(Me.Site_ID.Value, 0)
If SiteID > 0 Then
' Intentionally without Nz(), will throw error
HotelID = Me.txtHotelID
' Nz() will be needed here too!
strCaption = DLookup("H2OBillingIDNum", "Sites", "H2OBillingIDNum = " & HotelID)
Else
strCaption = ""
End If
Me.H2OBillingIDlbl.Caption = strCaption
End Sub
You can reduce it to:
Private Sub Form_Current()
Dim rs As DAO.Recordset
Dim SQL As String
Dim Caption As String
If Nz(Me!Site_ID.Value, 0) > 0 Then
SQL = "Select Top 1 H2OBillingIDNum From Sites Where H2OBillingIDNum = " & Nz(Me!txtHotelID.Value, 0) & ""
Set rs = CurrentDb.OpenRecordset("SQL", dbOpenDynaset, dbSeeChanges)
Caption = rs!Fields(0).Value
End If
Me!H2OBillingIDlbl.Caption = Caption
Set rs = Nothing
End Sub
As you can see, it doesn't make much sense, as you look up H2OBillingIDNum which you already have as Me!txtHotelID.Value, so it probably should read:
SQL = "Select Top 1 SomeTextField From Sites Where H2OBillingIDNum = ...
Sorry misunderstod you
I don't really see the purpose, but one thing is sure - if your Dlookup returns a Null you get ant error. You cannot load the caption with null.
An NZ around the Dlookup is needed. But i don't know if it is possible that the Dlookup doesn't find anything
I have a subroutine as below
Public Sub updateStagesTable(sName As String, percentageValue As Double)
stageName = "'" & sName & "'"
sSQL = "INSERT INTO StagesT ([Stage Name], [Stage Value In Percentage]) VALUES (" & stageName & "," & percentageValue & ");"
DoCmd.SetWarnings False
DoCmd.RunSQL sSQL
End Sub
and I call it from another subroutine as below
economy = 3.53
updateStagesTable ("Economy", economy)
But I get this compile error
Compile Error: expected: =
I don't understand what I am doing wrong here. Please help.
updateStagesTable ("Economy", economy)
should be
updateStagesTable "Economy", economy
with no parentheses
See related: Unexpected results from typename
I have never liked this peculiarity of VB so I always use the alternate CALL syntax, in your case this would be:
Call updateStagesTable("Economy", economy)
which does allow the parentheses that all other languages expect
Apparently this is an area of confusion!
For one thing, wrapping the input argument in parentheses seems to work for some subroutine calls. And, furthermore, the tooltips in the VBA Editor mirror the statement defining the procedure, which includes parentheses!
So what is behind the confusion? Below is some basic code to explore this.
' Simple subroutine with two input arguments.
Sub twoInputs(in1, in2)
Debug.Print in1 & " eats "; in2 & "!"
End Sub
' Simple subroutine with one input argument.
Sub oneInput(in1)
Debug.Print in1 & " eats pizza!"
End Sub
' Routine to test various syntaxes for calling subroutines.
Sub subCallingTest()
'twoInputs("Cat", "fish") 'FAILS. Parentheses cannot be ignored here.
twoInputs "Cat", "fish" 'Works.
twoInputs ("Cat"), ("fish") 'Works, but only because parentheses can be ignored here!
Call twoInputs("Cat", "fish") 'Works.
'Call twoInputs "Cat", "fish" 'FAILS. Parentheses are required here.
oneInput "Daughter" 'Works.
oneInput ("Daughter") 'Works, but only because parentheses can be ignored here!
Call oneInput("Daughter") 'Works.
'Call oneInput "Daughter" 'FAILS. Parentheses are required here.
End Sub
It turns out that the parentheses are tolerated for a single input argument if the procedure/code only cares about the value of the inputs (see counter-example at the end), because they can be ignored in the same way as they can be when they are wrapped around individual numbers — but not when they are wrapped around several numbers.
debug.print 1 + 2 * 10 ' Answer is 21.
debug.print (1) + (2) * 10 ' Answer is 21.
debug.print (1 + 2) * 10 ' Answer is 30.
There is one very subtle clue as to when the parentheses are expected, which is in the spacing. Notice that the Editor inserts a space after the procedure's name in oneInput ("Daughter"), but not in Call oneInput("Daughter").
—DIV
P.S. If a procedure has no arguments at all, then parentheses are not used to invoke it (neither with nor without the call statement).
P.P.S. A subroutine can be guaranteed to only care about the values of the input arguments if the inputs are explicitly marked as ByVal in the defining Sub statement. In my examples above there is no explicit designation of the input arguments, so the arguments have defaulted to ByRef; however, it is apparent from the above examples that the performance will nevertheless be unaffected. A simple counter-example would be as follows.
Sub oneInputMod(in1)
in1 = UCase(in1)
Debug.Print in1 & " eats pizza!"
End Sub
Sub subCallingTestMod()
person = "everyone" ' Set a local variable's value.
oneInputMod person ' Works.
Debug.Print person ' Output is "EVERYONE", as nominally intended.
person = "everyone" ' Reset a local variable's value.
oneInputMod (person) ' Seems to work, judging by immediately visible output (which was not affected by the parentheses).
Debug.Print person ' Output is "everyone", which is nominally unintended.
person = "everyone" ' Reset a local variable's value.
Call oneInputMod(person) ' Works.
Debug.Print person ' Output is "EVERYONE", as nominally intended.
End Sub
So this code is meant to serve as a simple search system to go to certain records in a recordset. I originally had it so they had to click the btnGoToID button in order to perform the search. I decided to just make it a little more user friendly and make it so the search field listened for the Enter button and that would perform the search as well.
The issue that I am running into when the code gets to strID = Trim(Nz(Me.txtSearch.Value, "")) the value will randomly come back as an empty string even though visually I can see that there is a value in the textbox.
I haven't been able to narrow down any pattern for when this issue occurs. At this point I don't really even know how to troubleshoot this problem, nor the words to search for that could yield any results in Google. All I can say is that it SEEMS like changing between records will affect whether or not the search goes through.
This has always worked up until I put in the txtSearch_KeyPress sub procedure.
'============================================================================
' txtSearch_KeyPress
'============================================================================
Private Sub txtSearch_KeyPress(KeyAscii As Integer)
'If user pressed enter
If KeyAscii = 13 Then
Call btnGoToID_Click
End If
End Sub
'============================================================================
' btnGoToID_Click
'============================================================================
' <<Purpose>>
' Allow the user to search for a specific ID
'============================================================================
Private Sub btnGoToID_Click()
On Error GoTo Err_Handler
Dim rs As Recordset
Dim strID As String
Set rs = Me.RecordsetClone
strID = Trim(Nz(Me.txtSearch.Value, ""))
If (strID <> "") Then
'Go to the ID
rs.FindFirst "ID = '" & strID & "'"
If rs.NoMatch Then
MsgBox "ID does not exist"
Else
'If we have a match, set the record as the current record
Me.Bookmark = rs.Bookmark
End If
Else
MsgBox "Please enter a valid ID.", vbOKOnly, "Invalid ID"
End If
Exit_Handler:
On Error Resume Next
Me.txtSearch.Value = ""
rs.Close
Set rs = Nothing
Exit Sub
Err_Handler:
Call LogError(Err.Number, Err.Description, "txtSearch on " & Me.Name)
Resume Exit_Handler
End Sub
After the conversation in the comments I have narrowed down this question to be a whole lot simpler. This yields an "Invalid use of null" error message even after there are several characters in the text field. Why would that happen, and what can I do to make it pick up the values in the textbox?
'============================================================================
' txtUnitNoToSearch_KeyPress
'============================================================================
Private Sub txtUnitNoToSearch_KeyPress(KeyAscii As Integer)
MsgBox Me.txtUnitNoToSearch
End Sub
I think your problem is related to the fact that a text box has 2 properties, .Value and .Text, which appear similar but behave differently.
While you have an edit in progress in the text box, the changing content is available via the .Text property. However, the content of the .Value property has not yet been updated to match.
After the text box's After Update event, .Value will contain the new content. And if focus has moved away from the text box, its .Text property will no longer even be available.
Sorry, I couldn't think how to explain that better. But I think the situation will be clearer with this KeyPress event procedure:
Private Sub txtUnitNoToSearch_KeyPress(KeyAscii As Integer)
Debug.Print "Text: '" & Me.txtUnitNoToSearch.Text & "'"
Debug.Print "Value: '" & Me.txtUnitNoToSearch.Value & "'"
End Sub
Watch the output from Debug.Print in the Immediate window as you make changes to the context of the text box. (Ctrl+g will open the Immediate window.)
One final point is that just Me.txtUnitNoToSearch gets you its default property which is .Value. Keep that in mind as you work through the rest of your code.
im trying to check if a number is a text or number in a banking system. If the value is incorrect it should erase and send an error message, but my code isnt working at all... can someone help me? here is the code:
Private Sub TextBox1_Change()
name = TextBox1
If Application.IsText(name) = True Then
IsText = True
Else
MsgBox "Insert only words"
Selection.name.Delete
End If
End Sub
I think you should be using name = TextBox1.Text instead. It's probably giving you an error on that line.
Also, you may want to wait until the user is finished typing the response, I believe the changed event will run each time a character is pressed. The AfterUpdate will run after you tab or click away from the textbox.
And really, instead of deleting the response (which I don't think will work), you should simply set the textbox blank back to an empty string.
Private Sub TextBox1_AfterUpdate()
If Not MyIsANumber(TextBox1.Text) Then
MsgBox ("It's Text")
Else
TextBox1.Text = ""
End If
End Sub
Function MyIsANumber(str As String) As Boolean
For i = 1 To Len(str)
If IsNumeric(Mid(str, i, 1)) Then
MyIsANumber = True
Exit Function
End If
Next
MyIsANumber = False
End Function
I'm sure the code could be structured better, but there's something along the lines of what you need. In this case, it just wipes the textbox if there is a number entered. Obviously, things will need to be the opposite for your textbox that you want to contain a number.
Have you considered the vartype() function? https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/vartype-function
Simple scenario: a form and one text box (unbound), Text1.
If "" <> Text1 Then
MsgBox "Not Empty"
End If
The above code works. The expression ""<> Text1 evaluates to True if the text box contain characters.
The opposite doesn't work, regardless of the text box is empty or not:
If "" = Text1 Then ' or alternatively, False = ("" <> Text1)
MsgBox "Empty!"
End If
Can you clarify this issue?
The textbox is usually null if it does not contain anything, this is not the same as a zero length string (""). It can often be best to use:
If Trim(Text1 & "") = vbNullString
'Empty
This will be true if the textbox contains spaces, a zero length string or null.
Alternatively, I've got a small function that I find useful for checking that sort of thing when dealing with various variable types and I just want to know whether it's blank.
'-----------------------------------------------------------------------------'
' True if the argument is Nothing, Null, Empty, Missing or an empty string. '
'-----------------------------------------------------------------------------'
Public Function IsBlank(arg As Variant) As Boolean
Select Case VarType(arg)
Case vbEmpty
IsBlank = True
Case vbNull
IsBlank = True
Case vbString
IsBlank = (arg = vbNullString)
Case vbObject
IsBlank = (arg Is Nothing)
Case Else
IsBlank = IsMissing(arg)
End Select
End Function
Just Made a blog post today about it too.
I always use the function Nz (variable, valueIfNull) to check those kind of things. For example:
if (Len(Nz(Me.txt1, "")) > 0) then
'Me.txt1 does not contain a value
end if
Or if I want to save a textbox's value to a recordset:
rst!Name = Nz(Me.txtName, "No name given")