Access VBA bit masking in KeyDown Event - ms-access

I'm hoping to use the Access key events but need to better understand how the code works. In the below code I don't understand how "(Shift And acShiftMask) > 0 " is working. I Would appreciate it if someone could help me understand bit masks and how the below code works?
Private Sub KeyHandler_KeyDown(KeyCode As Integer, _
Shift As Integer)
Dim intShiftDown As Integer, intAltDown As Integer
Dim intCtrlDown As Integer
' Use bit masks to determine which key was pressed.
intShiftDown = (Shift And acShiftMask) > 0
intAltDown = (Shift And acAltMask) > 0
intCtrlDown = (Shift And acCtrlMask) > 0
' Display message telling user which key was pressed.
If intShiftDown Then MsgBox "You pressed the SHIFT key."
If intAltDown Then MsgBox "You pressed the ALT key."
If intCtrlDown Then MsgBox "You pressed the CTRL key."
End Sub

The Shift argument contains certain bits that encode the status of the SHIFT, CTRL and ALT keys when the KeyDown event is fired. The buit-in constants acShiftMask (=1), acCtrlMask (=2) and acAltMask (=4) contain ony the bit that encodes ("masks") one of these special keys. Bitwise, this looks like this:
acShiftMask: 0000000000000001
acCtrlMask : 0000000000000010
acAltMask : 0000000000000100
Now, if the user presses a key together with the SHIFT and CTRL keys, the Shift argument will be as follows:
Shift : 0000000000000011
To check whether the SHIFT key is pressed, the code must test the bit of the shift mask, which is done with the bitwise AND operator:
0000000000000011 AND 0000000000000001 = 0000000000000001 (<>0)
which is <>0 (in fact the comparison should look like this). To be exact, the comparison should be (Shift And acShiftMask) = acShiftMask, and the variable holding the result could be of type Boolean.
Anyway, if the comparison holds, the result isn't zero, so that special key is pressed. If the result is zero, as with the ALT key in this example, the key is not pressed:
0000000000000011 AND 0000000000000100 = 0000000000000000 (=0)

Related

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

Simple Function Errors when Running in Access

I am very new to program and VBA. I am trying to play around with VBA in Access so I wrote this little thing to test my understanding. However, when I git run, it pops up empty Macro dialog/window instead of a Message box says the things in the code ( I thought it would). Could anyone take 5 secs to let me know what did I miss please. Thank you very much
Public Function AddOne(value As Integer) As Integer
AddOne = value + 1
End Function
MsgBox "Adding 1 to 5 gives:" & AddOne(5)
It's not possible to Run a macro that takes any arguments/parameters from the "Run" dialog.
So if you press Run button, or F5, you'll see the Dialog box because that is Excel asking you "Which procedure do you want to run".
It will display any available procedures. Procedures which take any argument(s) will not be visible, since the arguments would not be supplied.
A few other points:
Your MsgBox statement is outside of the function. It should be inside the function
Function AddOne(val As Integer)
Dim ret As Integer
ret = val + 1
'Display msgBox:
MsgBox "Adding 1 to " & val & " gives:" & ret
'return to caller:
AddOne = ret
End Function
Since you can't run from the dialog, you need to invoke this manually from the Immediate window:
Or, you can simply print the result to immediate window using the following in the Immediate pane:
?AddOne(5)
Which will print "6" in the Immmediate.

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.

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.

moving focus from subform to main form

i have main form MAINF and two subforms SUBONE and SUBTWO
I want to be able to move focus (cursor) between them.
From MAINF to SUBONE or SUBTWO, I can call Me![SUBONE].SetFocus or Me![SUBTWO].SetFocus. This seems to work.
BUT:
1) From SUBONE to SUBTWO, I have no idea. what is the correct way of programatically moving focus?
2) From SUBONE to one of MAINF's control say "Customer ID", how do i do it?
Edit: Here's the code that rtochip provided.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case vbKeyF5
Me.Parent![Item - Order Subform].SetFocus
DoCmd.GoToControl "Supplier ID NUM"
Case vbKeyF6
Me.Parent.[Item ID].SetFocus
End Select
End Sub
If SUBTWO is a child of SUBONE, then it's the same way. However, if they are siblings thenyou have to reference it as an object on the parent first.
There are two ways to reference objects on your parent:
You can reference the parent
Me.Parent.[Customer ID].SetFocus
(btw, change than control's name to Customer_ID - it makes it easier to use, and you won't require the []'s)
You can reference it directly
Forms!MAINF.[Customer ID].SetFocus
Update: The KeyDown event is probably being caught later on the main form. You could always clear it out before you finish with moving focus.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case vbKeyF5
Me.Parent![Item - Order Subform].SetFocus
DoCmd.GoToControl "Supplier ID NUM"
KeyCode = 0 'Trap F5
Case vbKeyF6
Me.Parent.[Item ID].SetFocus
keyCode = 0 'Trap F6
End Select
'keyCode = 0 'Note: you can't do it here because it will trap ALL your
'KeyCodes. Not just F5 and F6.
End Sub