Incrementing textbox value up and down in vba does not cancel evenly - ms-access

I can't figure out why my code isn't maintaining significant digits when incrementing a textbox value.
I have a spin control (textbox plus two small command buttons to move textbox value up or down in value) on a form.
The textbox default value is zero.
The up arrow command button should increment the textbox value by + 0.1. Here is the code:
Private Sub cmdIndexSpinUp_Click()
If Me!txtIndexSpin >= 1.5 Then
MsgBox "The maximum Index adjustment has been reached."
Exit Sub
Else
Me!txtIndexSpin = Me!txtIndexSpin + 0.1
End If
End Sub
The down arrow command button should increment the textbox value by -0.1. Here is the code:
Private Sub cmdIndexSpinDown_Click()
If Me!txtIndexSpin <= -1.5 Then
MsgBox "The minimum Index adjustment has been reached."
Exit Sub
Else
Me!txtIndexSpin = Me!txtIndexSpin - 0.1
End If
End Sub
So I would expect that from the default value of 0, if I spin up once and down once, I should return to 0. That works fine. If I spin up twice and then down twice, my textbox value suddenly becomes 2.77555756156289E-17 instead of 0.
After more testing, it does not consistently happen based on the number of clicks, but it may be related to the time between clicks. The more rapid, the more prone to this error it seems.
How could this be happening?
I am going to code around it, since I see nothing wrong, but am curious what I am missing.

As #HansUp says floating points aren't precise. Source. An easy solution that you could use is to round the number before putting it in the text box.
Example :
Private Sub cmdIndexSpinDown_Click()
Dim value As Double
value = Me!txtIndexSpin
If value <= -1.5 Then
MsgBox "The minimum Index adjustment has been reached."
Exit Sub
Else
value = Round(value - 0.1, 1)
Me!txtIndexSpin = value
End If
End Sub
Private Sub cmdIndexSpinUp_Click()
Dim value As Double
value = Me!txtIndexSpin
If value >= 1.5 Then
MsgBox "The maximum Index adjustment has been reached."
Exit Sub
Else
value = Round(value + 0.1,1)
Me!txtIndexSpin = value
End If
End Sub

Round() is a reasonable solution for your floating point precision problem. And it may well be exactly what you want. However, be aware that you will be using "banker's rounding", sometimes called "round to even". So you might not get what you expect from rounding to 1 decimal place when the second decimal place is 5:
? Round(1.15, 1)
1.2
? Round(1.25, 1)
1.2
If that is not what you want, you could use a different rounding strategy. Or you could switch to integer math instead of floating point math ... and then the floating point precision challenge goes away. That might sound challenging, but it's actually simple to implement. Add a hidden text box to your form and use it like this ...
Private Sub cmdIndexSpinDown_Click()
With Me!txtHidden
If .Value <= -15 Then
MsgBox "The minimum Index adjustment has been reached."
Else
.Value = .Value - 1
Me!txtIndexSpin.Value = .Value / 10
End If
End With
End Sub
If txtIndexSpin is bound to a field in the form's Record Source, you can load txtHidden from the form's Current event:
Me!txtHidden.Value = Me!txtIndexSpin.Value * 10
And if you also allow the users to edit txtIndexSpin directly (not just via those command buttons), do that again from its After Update event.

Related

microsoft access Field must be blank or have a value that is a specific length

I have a field on a form and I need the user to leave it blank or enter a value that is a specific length (20 characters). Does anyone have code that may solve this need?
I have tried:
(Len([SIM / ENGRV]) = 20) or (isnull([SIM / ENGRV])) or ([SIM / ENGRV]="")
I assume the control (field) name in the form is Text1.
So you can use this code in the before update event .
Of course, the code can be much shorter,
But I think that's the clearest way to understand the logic.
Private Sub Text1_BeforeUpdate(Cancel As Integer)
Dim varTmp As Variant
' first check if [ENGRV] > 0 to avoid devision by zero error
If Not IsNumeric([ENGRV]) And [ENGRV] = 0 Then
Text1.Undo
Cancel = True
Else
' now avoid [SIM] is null error
If Nz([SIM], "") = "" Then
Text1.Undo
Cancel = True
Else
varTmp = [SIM] / [ENGRV]
' now we know that varTmp is somthing and not empty then check the length
If Len(Trim(str(varTmp))) <= 20 Then
Text1.Undo
Cancel = True
End If
End If
End If
End Sub
You can do that at the table level. Set the Validation Rule of the field to:
Len([SIM / ENGRV])=20 Or [SIM / ENGRV] Is Null

Access remove line break in string, program tells it's NULL

I have code that does something then you press enter in textfield, problem is when you use Ctrl+Enter, i can capture that event but access tells me in next line that that field is apparently NULL
Private Sub Text5_KeyPress(KeyAscii As Integer)
If KeyAscii = vbKeyReturn Or KeyAscii = 10 Then
If Len(Me.Text5) = 0 Then Exit Sub
If Val(Right(Me.Text5, 1)) > 2 Then Me.Text5 = Left(Me.Text5, Len(Me.Text5) - 1) & "0"
So 'Len' works fine, but the 'Right' function gives out 'Invalid use of null', when i hit debug and check the value it is NULL
I can't figure it out
I guess i need to remove new line characters but how to do that when the text box is null and every function for strings spits out that error
The problem with your check is that Len(Null) is not 0, it's Null.
There are a couple ways to get around this. First, as mentioned in the comments, you can simply add a check for IsNull:
If IsNull(Me.Text5) Or Len(Me.Text5) = 0 Then
The other way you can do this is force it to coalesce by concatenating vbNullString:
If Len(Me.Text5 & vbNullString) = 0 Then
Also you could use Nz and set a return value of your wish in case if the expression is null, in this example also vbNullString and check the result of this function:
If Nz(Me.Text5, vbNullString) = vbNullString Then
or
If Len(Nz(Me.Text5, vbNullString) = 0) Then
or
If Nz(Me.Text5, 0) = 0 Then
or
If Not Nz(Me.Text5, False) Then
For sure you can store the result in a variable first and then check and work with this later on.
Whatever fulfills your needs.
Well, i test it as much as i can and it's just that when you use Ctrl+Enter on a field and capture the key press, field will be null for some reason, i don't see possible way around this

Can I store the same option value for more than one toggle button in a control box?

I have a list of questions each with 4 possible answers that are displayed as toggle buttons on my form. What I want to do is if the user chooses either of the first two buttons, the Option Value stored is "1", if they choose either of the last two buttons, the Option Value stored should be "0". The option values must be different for each toggle button in a control group. Is there a way to recode the toggle buttons to store the desired response? (I work in psychology, thus the bait and switch of offering 4 choices to the user when really only two responses are recorded).
Here is what I have tried:
I tried thinking about a recode as jcarroll suggested, but this is a circular reference:
Private Sub Q1_Click()
If Me.Q1 = 1 Or 2 Then
Me.Q1 = 1
Else:
Me.Q1 = 0
End If
End Sub
I could recode into another variable but that is just as clunky as using a SQL statement on the data post-hoc, for instance:
NewVariable=Iif([Q1]=1,1,iif([Q1]=2,1,0)
Finally, I tried to code have both toggle buttons have the same Option Value (which causes both to look pressed if either is pressed) and recode the unpressed toggle button's back color. But while my code looks correct to me, this did not change the pressed color of the toggle button (which I think has to do with over riding toggle button design settings):
Private Sub Frame5_Click()
If Toggle8.Value = True Then
Toggle9.BackColor = &HFF00&
Else
Toggle9.BackColor = &H8000000F
End If
End Sub
I could not come up with any programming solutions on the form itself to solve this. The alternative is that I wrote a procedure to apply to the data after it is collected which will be stored as 1,2,3,4 to convert it to 0 or 1. This procedure also sums up the 1's. I have 50 variables/questions that will be passed through this procedure (as well as another like it that converts 3&4 to 1).
Public Function Recode1(ParamArray arg()) As Variant
Dim Size As Integer, skips As Integer, i As Integer, result As Variant
'Recodes first two toggle buttons as "1" for AQ assessment
Size = UBound(arg) + 1
For i = 0 To Size - 1
If IsNull(arg(i)) Or Not (IsNumeric(arg(i))) Or arg(i) = -99 Then
skips = skips + 1
Else
If arg(i) = 1 Or arg(i) = 2 Then
result = result + 1
Else:
result = result + 0
End If
End If
End sub

VBA Add combo box values together

I have 3 combo boxes that contain numbers:
Me.Authorized
Me.Officer
Me.Enlisted
What I'm trying to do is add the values of Me.Officer and Me.Enlisted and make sure it equals Me.Authorized. I have all the other statements/commands figured out, but I can't wrap my head around this one.
The combo box selected value is a string, even when that string contains only digits. You can use Val() to convert that string to a number.
So your required condition can be expressed as ...
Val(Me.Officer) + Val(Me.Enlisted) = Val(Me.Authorized)
You can enforce that requirement in the form's Before Update event ...
Private Sub Form_BeforeUpdate(Cancel As Integer)
If (Val(Me.Officer) + Val(Me.Enlisted) <> Val(Me.Authorized)) Then
MsgBox "Officer plus Enlisted must be equal to Authorized."
Cancel = True
End If
End Sub
That event procedure will abort a record save when your requirement is not satisfied.

MS Access runtime error 2115

In Ms Access, I have two unbound combo-boxes: StateBox and DVPCBox. StateBox is simply a list of U.S. states and DVPCBox contains Employee Names from a query based on the value of StateBox.
I'm trying to set the value of DVPCBox equal to the first item in its list. Since the list of Employees is based on the value of StateBox, I need the value of DVPCBox to update every time StateBox changes. I tried the following:
Private Sub StateBox_AfterUpdate()
Me.DVPCBox.Requery
If (Me.DVPCBox.ListCount = 1) Then
Me.DVPCBox.SetFocus
Me.DVPCBox.ListIndex = 0 //<-Error here
End If
End Sub
But I got runtime error 2115 - The macro or function set to the BeforeUpdate or ValidationRule property for this field is preventing Microsoft Office Access from saving the data in the field.
The strangest thing to me is that I'm not even using the BeforeUpdate Event or ValidationRule (as far as I'm aware.)
ItemData(0) is the first combo box value. So set the combo equal to that.
Private Sub StateBox_AfterUpdate()
Me.DVPCBox.Requery
If (Me.DVPCBox.ListCount >= 1) Then
Me.DVPCBox.SetFocus
'Me.DVPCBox.ListIndex = 0 //<-Error here
Me.DVPCBox = Me.DVPCBox.ItemData(0)
End If
End Sub
I also changed ListCount >= 1 because I assumed you wanted to do the same thing when the combo includes 2 or more rows.