VBA Add combo box values together - ms-access

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.

Related

MS Access VBA, efficient way to enable a button only after all required textboxes contains valid data

My code is working, but I just want to know if there is a more efficient way to achieve the same effect.
I have this layout in a form:
In my effort to foolproof the record creation process, I would like to have the "Save and Clear fields" button enabled only after all but the 'Comment' textbox/combobox contains some valid data.
The text/combo boxes are called txtBatteryID, cmbModelNumber, cmbChemistryType, txtSpecVoltage, txtSpecCapacity.
My code is as follow
Private Sub EnableSaveBtnCheck()
'this checks if the required fields contains valid data, if so, enables the save button.
If Me.btnSaveAndCLear.Enabled = False Then
If IsNull(txtBatteryID) = False And IsNull(cmbModelNumber) = False And IsNull(cmbChemistryType) = False And IsNull(txtSpecVoltage) = False And IsNull(txtSpecCapacity) = False Then
Me.btnSaveAndCLear.Enabled = True
End If
End If
End Sub
As you can see, I did the most straightforward way of using AND to combine all must-have conditions in an IF statement. This sub is called in After_Update() event of each text/combo box. Like this:
Private Sub cmbChemistryType_AfterUpdate()
Call EnableSaveBtnCheck
End Sub
My question, in the end, is: Is there a more efficient way to setup the condition "all text/combo box need to have something valid in them"? And is there a more elaborate way to check if the condition is met (something like a event on the form itself)?
Add the values of those 5 fields. If any of them is Null, the sum will be Null. So you only need call IsNull() once.
If IsNull(txtBatteryID + cmbModelNumber + cmbChemistryType + txtSpecVoltage + txtSpecCapacity) = False Then

Change the value of the Checkbox

I add a new line of data into the table through the form. On the form I also have checkbox which is also integrated into the table. Ticking the checkbox leads to getting a value of -1. Is it possible to change -1 into a user defined value such as 'x'?
Simple answer: No.
More complex answer: You can use a hidden bound field, an unbound checkbox, VBA and default values to make a checkbox that behaves just like a bound checkbox that returns a different value.
Consider two fields, myUnboundCheckbox and myBoundTextfield. myBoundTextfield holds "X" for True, "Y" for False.
myUnboundCheckbox is an unbound checkbox, myBoundTextfield is a hidden bound text field
Then you can use the following:
Private Sub myUnboundCheckbox_AfterUpdate()
If myUnboundCheckbox Then
myBoundTextfield = "X"
Else
myBoundTextfield = "Y"
End If
End Sub
Private Sub Form_Current()
myUnboundCheckbox = myBoundTextfield = "X"
End Sub

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

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.

Assiging a 'User Stamp' when saving a record

So the first thing is that every table has 4 fields. Created_By, Created_Date, Last_Updated_By and Last_Updated_Date. This will allow me to keep a track of who saved what records and when. I want a public sub (or function?) to update these fields when any record is saved.
Ideally I don't want to have to copy/call my code from every form, but I will if that's what is necessary. I imagine there is a way of doing this globally.
I currently have the following:
Public gLoggedIn As Integer
Public Function get_global(Global_name As String) As Variant
Select Case Global_name
Case "Employee_ID"
get_global = gLoggedIn
End Select
End Function
My next thing is to add vba code to fill the 2 of the 4 fields. The following is something I have in one of my forms, but like I say, I want to able to achieve this globally on every save.
Private Sub Form_BeforeUpdate(Cancel As Integer)
If Me.NewRecord = True Then
Me.[Created_By] = gLoggedIn
Me.[Created_Date] = Now()
Else
Me.[Last_Updated_By] = gLoggedIn
Me.[Last_Updated_Date] = Now()
End If
End Sub
How can I do this globally?
I'm not sure if 'me' is the correct reference to us if this is global?
Exactly for your demand, you could use a VBA modul with following sub
Public Sub setTimestamp(objForm As Form)
If objForm.NewRecord = True Then
objForm![Created_By] = gLoggedIn
objForm![Created_Date] = DateTime.Now
Else
objForm![Last_Updated_By] = gLoggedIn
objForm![Last_Updated_Date] = DateTime.Now
End If
End sub
You need to call this method in every form when the Timestamp Update should be fired (BTW: I would recommend an event when a save button is clicked or field update, because an edit in the dataset of the form at form update event will probably cause another form update event and could lead to an endless loop).
call setTimestamp(Me)
But you should alternatively consider using a macro like #E Mett said in comment.
Another hint: If you set the variable gLoggedIn to public, you can refer directly without using a function. (You did this even in your example) A function makes sense when you want to hide the variable inside a modul as private. This is called encapsulation, if you want to learn more about it.
EDIT
Changed code by andre451 ' s suggestion.

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.