I have a keydown form event to allow the user to cursor down through the records. This works well.
I would also like them to be able to edit fields in that record. That does not work.
I had thought that the KeyPreview form property would allow the form to share any unused keys with the underlying control, but it seems to suck up all keys entered so they are not available to any control
Is there a way to achieve what I need?
Here is the code
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If (Shift And acAltMask) = 0 And (Shift And acShiftMask) = 0 And (Shift And acCtrlMask) = 0 Then
Select Case KeyCode
Case vbKeyDown
If CurrentRecord <> RecordsetClone.RecordCount Then
DoCmd.GoToRecord , , acNext
Else
'Do nothing
End If
Case vbKeyUp
If CurrentRecord <> 1 Then
DoCmd.GoToRecord , , acPrevious
Else
'Do nothing
End If
End Select
End If
KeyCode = 0
End Sub
Remove this line:
KeyCode = 0
to pass on the key stroke.
Related
This is for a project in MS Access 2016. I'd like to implement a radio button toggle across all records displaying in a continuous form. only one record can have the toggle "on" so when it's clicked it has to reset the previous record's flag to off. I'm only finding help on radio button usage to select from multiple values of a field for one record, usually on single form. Can this be done?
Radio button must be bound to a yes/no field then use UPDATE action SQL to make sure all records except current have field set to 0. Need a unique record identifier field such as autonumber.
Private Sub Option29_Click()
CurrentDb.Execute "UPDATE tablename SET fieldname = 0 WHERE ID <>" & Me.ID
End Sub
Be aware that in a multi-user database users can conflict with each other and another solution will be needed. Depends what you need to do with the selected record.
Use the RecordSetClone of the form:
Private Sub Active_AfterUpdate()
Dim Records As DAO.Recordset
Me.Dirty = False
If Me!Active.Value = True Then
Set Records = Me.RecordsetClone
Records.MoveFirst
While Not Records.EOF
If Records!Id.Value <> Me!Id.Value Then
If Records!Active.Value = True Then
Records.Edit
Records!Active.Value = False
Records.Update
End If
End If
Records.MoveNext
Wend
Records.Close
End If
End Sub
You can do it in easy way:
1- Add to your table a new Yes/No Field ( e.g named CurrentRec)
2- Put the Radio Button and link it to this field ( e.g named rdCurrRec)
3- For the radio button Set 'Enabled' property to False and 'Locked' property to 'True'. This will disable Click - Double Click and Hover response.
4- Put the following code
Dim PreBookmark(4) As Byte 'Save old bookmark
Private Sub Form_Current()
If Not Me.NewRecord Then 'Make sure not on new record
Me.rdCurrRec = True
If (PreBookmark(0) <> 0 Or PreBookmark(1) <> 0 Or PreBookmark(2) <> 0 Or PreBookmark(3) <> 0) Then
Me.RecordsetClone.Bookmark = PreBookmark
Me.RecordsetClone.Edit
Me.RecordsetClone.Fields("CurrentRec") = False
Me.RecordsetClone.Update
End If
PreBookmark(0) = Me.Bookmark(0)
PreBookmark(1) = Me.Bookmark(1)
PreBookmark(2) = Me.Bookmark(2)
PreBookmark(3) = Me.Bookmark(3)
Else 'If new record remove bullet from previous record
If (PreBookmark(0) <> 0 Or PreBookmark(1) <> 0 Or PreBookmark(2) <> 0 Or PreBookmark(3) <> 0) Then
Me.RecordsetClone.Bookmark = PreBookmark
Me.RecordsetClone.Edit
Me.RecordsetClone.Fields("CurrentRec") = False
Me.RecordsetClone.Update
End If
End If
End Sub
Private Sub Form_Close()
'Remove the mark before exiting to avoid it appear next time to open the form in two places
If Not Me.NewRecord Then
If (PreBookmark(0) <> 0 Or PreBookmark(1) <> 0 Or PreBookmark(2) <> 0 Or PreBookmark(3) <> 0) Then
Me.RecordsetClone.Bookmark = PreBookmark
Me.RecordsetClone.Edit
Me.RecordsetClone.Fields("CurrentRec") = False
Me.RecordsetClone.Update
End If
End If
End Sub
This will not iterate all the data so for large number of records it will not delay the response as it works only on two records
I've been trying a couple things to create a button that navigates back to the first record you are at the last record. I get the "can't go to specified record" error. Here are the two styles of code I have tried for this button:
Private Sub Command161_Click()
On Error Resume Next
If Me.CurrentRecord = acLast Then
DoCmd.GoToRecord , , acFirst
Else
DoCmd.GoToRecord , , acNext
End If
End Sub
Private Sub Command161_Click()
With Me.Recordset
If .AbsolutePosition = .RecordCount - 1 Then
DoCmd.GoToRecord , , acFirst
Else
DoCmd.GoToRecord , , acNext
End If
End With
End Sub
The goal is to loop back around to the first record without allowing the user to create a new record. I've tried this code with "allow additions" set to both yes and now, with the same result. Any help would be appreciated.
I would suggest defining a private predicate function within your form module which returns a boolean value depending on whether or not the active form is displaying the last record in its Record Source.
Such a function might be written:
Private Function LastRecordP() As Boolean
With Me.RecordsetClone
If Not .EOF Then
.MoveLast
.MoveFirst
LastRecordP = Me.CurrentRecord = .RecordCount
End If
End With
End Function
Your OnClick event handler for your button control could then be written more succinctly as:
Private Sub Command161_Click()
If LastRecordP Then
DoCmd.GoToRecord , , acFirst
Else
DoCmd.GoToRecord , , acNext
End If
End Sub
Alternatively, you could allow the function to accept a Form object as an argument and evaluate such function using the Me keyword, e.g.:
Private Function LastRecordP(Frm As Form) As Boolean
With Frm.RecordsetClone
If Not .EOF Then
.MoveLast
.MoveFirst
LastRecordP = Frm.CurrentRecord = .RecordCount
End If
End With
End Function
Private Sub Command20_Click()
If LastRecordP(Me) Then
DoCmd.GoToRecord , , acFirst
Else
DoCmd.GoToRecord , , acNext
End If
End Sub
The Goal: I've got an Access database with a continuous form and I want to add the functionality that you can go to the next or previous record by pressing the up- or down-arrow.
The Problem: I've got a multiline TextBox named txtProjekt and I want the database to check if the TextBox is filled with multi-lined text and only jump to the next record if the cursor is in the last line of the TextBox. Likewise I want it to only jump to the previous record if the cursor is in the first line of the TextBox.
I can only check the current cursor position with SelStart, but I can't find out in which line the cursor is.
Do you have any ideas?
Current code:
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
On Error GoTo err_Form_KeyDown
If Me.ActiveControl.Name = "txtProjekt" Then
If Not (Me.txtProjekt.SelStart = 0 And Me.txtProjekt.SelLength = Len(Me.txtProjekt.Text)) Then
GoTo exit_Form_KeyDown
End If
End If
If KeyCode = vbKeyUp Then
DoCmd.GoToRecord acActiveDataObject, Record:=acPrevious
KeyCode = 0
ElseIf KeyCode = vbKeyDown Then
DoCmd.GoToRecord acActiveDataObject, Record:=acNext
KeyCode = 0
End If
exit_Form_KeyDown:
Exit Sub
err_Form_KeyDown:
MsgBox Err.description
Resume exit_Form_KeyDown
End Sub
edit:
The Result (thanks to #Newd):
(be sure to active KeyPreview in your Form, otherwise it won't do anything)
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
On Error GoTo err_Form_KeyUp
If Shift = False Then
keyAction KeyCode, True
End If
exit_Form_KeyUp:
Exit Sub
err_Form_KeyUp:
MsgBox Err.description
Resume exit_Form_KeyUp
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
On Error GoTo err_Form_KeyDown
Dim curPos As Integer
If Shift = False Then
keyAction KeyCode, False
End If
exit_Form_KeyDown:
Exit Sub
err_Form_KeyDown:
MsgBox Err.description
Resume exit_Form_KeyDown
End Sub
Private Sub keyAction(KeyCode As Integer, KeyUp As Boolean)
On Error GoTo err_keyAction
Static curPos As Long
If KeyUp = False Then
If Me.ActiveControl.Name = "txtProjekt" Then
If Not (Me.txtProjekt.SelStart = 0 And Me.txtProjekt.SelLength = Len(Me.txtProjekt.Text)) Then
curPos = Me.txtProjekt.SelStart
GoTo exit_keyAction
End If
End If
Else
If Me.ActiveControl.Name = "txtProjekt" Then
If curPos >= 0 Then
If Me.txtProjekt.SelStart <> curPos Then
GoTo exit_keyAction
End If
curPos = -1
Else
GoTo exit_keyAction
End If
End If
End If
If KeyCode = vbKeyUp Then
DoCmd.GoToRecord acActiveDataObject, Record:=acPrevious
KeyCode = 0
ElseIf KeyCode = vbKeyDown Then
DoCmd.GoToRecord acActiveDataObject, Record:=acNext
KeyCode = 0
End If
exit_keyAction:
Exit Sub
err_keyAction:
MsgBox Err.description
Resume exit_keyAction
End Sub
(I know, all those GoTo Exit_keyAction is bad style, so don't copy too much from me)
I don't currently have the time to write this code out in full to incorporate your code as well. However I think if you were able to get to the point you are at right now you should be able to utilize it.
Basically it is just a way to tell if the user has hit the end or beginning of the Multi-line textbox.
Public intOnDown As Integer
Public intOnUp As Integer
'When the user presses key down
Private Sub txtProjekt_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyUp Or KeyCode = vbKeyDown Then
'Save the cursor position
intOnDown = txtProjekt.SelStart
End If
End Sub
'When the user lets go of the key
Private Sub txtProjekt_KeyUp(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyUp Or KeyCode = vbKeyDown Then
If intOnDown - txtProjekt.SelStart = 0 Then 'If the SelStart is the same
Debug.Print "Pointer hasn't moved so must be at the end or beginning"
End If
End If
End Sub
With the above code you listen for when the user has pressed the up or down key on Keydown then you listen again on KeyUp (When they let go of the key). Then you check to see if the SelStart has changed. If it hasn't then it must mean they are at the beginning or the end of the field and you can perform the record switching.
Note: Adjust accordingly if you have memo fields that are going to be over the max integer size by changing to a long and you probably want to have error handling for it regardless.
I have an Access form with a textbox that is meant to allow for repeatedly typing a number, hitting enter, and letting a script do stuff. For speed, the field should keep the focus after DoStuff() is done.
However, while I'm sure that DoStuff() is run, the focus always goes to the next field in the tab order. It's like Me.MyFld.SetFocus is being ignored.
How do I keep the focus on this field after DoStuff() is done?
Private Sub MyFld_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyReturn Then
DoStuff
Me.MyFld.SetFocus
End If
End Sub
If you look at the order of events for a keypress that would change focus, you can see that it always follows this pattern:
KeyDown → BeforeUpdate → AfterUpdate → Exit → LostFocus
You can re-set the focus anywhere in there and it will still keep following the pattern. So we need to tell it to stop following the pattern. Replace your Me.MyFld.SetFocus with DoCmd.CancelEvent and it should fix your problem. Basically, this just kicks you out of the above pattern, so the Exit and LostFocus events never fire...
A workaround is moving the focus to another control and then back to the first control. Like this:
Private Sub MyFld_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyReturn Then
DoStuff
Me.anotherControl.SetFocus
Me.MyFld.SetFocus
End If
End Sub
click on access options
select Advanced
select Don't move from Move after enter
click ok
It will work 100%
Try removing the whole line for variable_name.SetFocus and simply add:
Cancel = True
Private Sub MyFld_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyReturn Then
DoStuff
Cancel = True
End If
End Sub
Another solution to the problem that I use in Excel.
Let there exist UserForm1 with the TextBox1 and CommandButton1 controls.
Code in the form module:
Option Explicit
Private Sub CommandButton1_Click()
Unload Me
End Sub
Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = vbKeyReturn Then
'Call DoStuff
Application.OnTime Now, "'Control_SetFocus """ & Me.Name & """, """ & Me.ActiveControl.Name & """ '"
' The concatenation returns a string: 'Control_SetFocus "UserForm1", "TextBox1"'
End If
End Sub
And code in the standard module:
Option Explicit
Sub Control_SetFocus(FormName As String, ControlName As String)
Dim oUserForm As Object
Set oUserForm = GetFormByName(FormName)
If Not oUserForm Is Nothing Then
oUserForm.Controls(ControlName).SetFocus
End If
End Sub
Function GetFormByName(FormName As String) As Object
Dim oUserForm As Object
On Error GoTo ErrHandle
For Each oUserForm In VBA.UserForms
If StrComp(oUserForm.Name, FormName, vbTextCompare) = 0 Then
Exit For
End If
Next oUserForm
If oUserForm Is Nothing Then
Set oUserForm = UserForms.Add(FormName)
End If
Set GetFormByName = oUserForm
Exit Function
ErrHandle:
Select Case Err.Number
Case 424:
MsgBox "Userform " & FormName & " not exists.", vbExclamation, "Get userform by name"
Case Else:
MsgBox Err.Number & ": " & Err.Description, vbCritical, "Get userform by name"
End Select
End Function
Artik
An easy solution that works in Excel is to set the KeyCode to 0. If DoStuff steals the focus then you should also set the focus back:
Private Sub MyFld_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyReturn Then
DoStuff
KeyCode = 0
Me.MyFld.SetFocus
End If
End Sub
I added the following code in the AfterUpdate event of a textbox in an MS Access form:
Private Sub txtComments_AfterUpdate()
With Me!txtComments
.SetFocus
If Len(.Value) > 0 Then
DoCmd.SetWarnings False
.SelStart = 1
.SelLength = Len(.Value)
DoCmd.RunCommand acCmdSpelling
.SelLength = 0
DoCmd.SetWarnings True
End If
End With
End Sub
This runs a spell check when the user exits the field. It partially works. It opens the spell check dialogue, and locates the first error. The problem is, when you click Ignore, Change, etc to handle/repair the spelling error, the code fails and the following error box appears:
"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."
I tried adding record-saving code before the spell check code:
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
DoCmd.DoMenuItem acFormBar, acRecordsMenu, 5, , acMenuVer70
but this didn't solve it.
This code works as the On Exit event (instead of After Update).
Private Sub txtComments_Exit(Cancel As Integer)
With Me!txtComments
.SetFocus
If Len(.value) > 0 Then
.SelStart = 1
.SelLength = Len(.value)
DoCmd.RunCommand acCmdSpelling
.SelLength = 0
End If
End With
End Sub
Using an update event associated with the control is not going to work, because each change triggers the event again. You need a button, or such like:
Private Sub Spell_Click()
With Me!txtComments
.SetFocus
.SelStart = 0
.SelLength = Len(Me!txtComments)
End With
DoCmd.RunCommand acCmdSpelling
End Sub
It would be possible to avoid some of the problems with the On Exit event by the addition of a line:
If Me.txtComments.Value <> Me.txtComments.OldValue Then
With Me!txtComments
.SetFocus
.SelStart = 0
.SelLength = Len(Me!txtComments)
End With
<...>
At least this will only run when you pass through the control until the record is saved, not every time, whether txtComments is changed or not.