Automatically Close Form After Certain Idle Time - ms-access

I am having a little bit problem on my access form.
I have 2 forms:
menu-form
input-form
After input-form has been idle for one minute, I want to close it and go back to the menu-form.
This is my code that isn't working.
Public ExpireTime As Date 'expiration time-date value
Sub ResetExpiration()
If ExpireTime <> 0 And ExpireTime > Now Then
Application.OnTime ExpireTime, "FormExpire", schedule:=False
End If
ExpireTime = Now + 1 / 1440#
Application.OnTime ExpireTime, "FormExpire", schedule:=True
End Sub
I also created a macro with this in it.
Sub FormExpire()
Unload input-form
End Sub

You need to set the form.Timer to 60000 (that is 1 minute) then use on OnTimer event to check if some properties have changed. Below I quickly wrote something to give you an idea, but it is not complete nor tested.
Option Compare Database
Option Explicit
Dim isIdle As Boolean
Private Sub Form_LostFocus()
'you can use this event also
End Sub
Private Sub Form_Timer()
Dim ctlName As String, wasDirty As Boolean
If ctlName = vbNullString Then ctlName = Me.ActiveControl.Name
If Me.ActiveControl <> ctlName Then isIdle = False
If wasDirty <> Me.Dirty Then isIdle = False
'more checks....
If isIdle Then DoCmd.Close acForm, Me.Name
End Sub

You've got a couple of potential issues here.
First and foremost, that's not how you close a form.
This:
Sub FormExpire()
Unload input-form
End Sub
Should look something more like this:
Sub FormExpire()
DoCmd.Close acform, Me.Name
DoCmd.OpenForm "menu-form"
End Sub
Your second issue is that I don't believe the Application.Ontime method is available from Access. MSDN says it's an Excel function, and I can't find it in the Access Documentation. I'd personally be interested in seeing you make your method work with the Access form Timer Event.
I'm not sure it matters, but I think this is what you were trying to do.
Public ExpireTime As Date 'expiration time-date value
Private Sub InputForm_Timer()
If ExpireTime <> 0 And ExpireTime > Now Then
Call FormExpired
Else
' reset timer
ExpireTime = Now + 1 / 1440#
End If
End Sub
I'm not sure this method would work without also including #iDevelop's answer. I just wanted to help clarify where you were going wrong.

I realize that this thread is aged but I recently experienced a need to close forms from a timer event and came across the answers here. My situation was slightly different as I had several forms to close so here is my solution.
Private Sub Form_Timer()
Dim daysRemaining As Integer
Dim endDate As Date
endDate = "4/15/2021"
daysRemaining = DateDiff("d", Now, endDate)
MsgBox ("This is an evaluation version of the application. You have " & Str(daysRemaining) & " days of use remaining")
If daysRemaining < 1 Then
Close_All
End If
End Sub
Private Sub Close_All()
For Each myForm In CurrentProject.AllForms
strFormName = myForm.name
DoCmd.Close acForm, strFormName
Next
End Sub

Related

Move to another record via combobox doesn't work in some circumstances

I have two forms: ‘frmClient’, (which has a subform that lists applicants), and ‘frmDisclosure’, which shows details of applicants. On frmClient there is a command button that opens a specified record in frmDisclosure. The procedure is Private Sub Command10_Click() - see below. This works.
The problem is that once in frmDisclosure via frmClient, it is not possible to move to another record. The procedure for opening another record in frmDiscloure is in a combobox control: Private Sub ComboFind_AfterUpdate().
This normally works, but it never works if frmDiscloure has been opened via frmClient. I have tried ‘requery’ and ‘refresh’ in various situations, and have tried closing frmClient once frmDisclosure is open. None of this works. If I want to get to a different record, the only solution I have at present is to close frmDisclosure and reopen it.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Private Sub Command10_Click()
If NumForms > 0 Then
DoCmd.OpenForm "frmDisclosure"
Forms!frmDisclosure.FilterOn = False
DoCmd.OpenForm "frmDisclosure", acNormal, "", "[DiscPK]=" & Me.DiscPK, , acNormal
Else
DisplayMessage ("No form ref for this application.")
Exit Sub
End If
End Sub
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Private Sub ComboFind_AfterUpdate()
Dim rs As Object
Set rs = Me.RecordsetClone
rs.FindFirst "[DiscPK] = " & Str(Nz(Me![ComboFind], 0))
If Not rs.EOF Then Me.Bookmark = rs.Bookmark
End Sub
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
frmDisclosure is opened to a single record, there are no other records to navigate. The RecordsetClone has only one record, so of course code won't find any others. Turn off the filter first:
Private Sub ComboFind_AfterUpdate()
Me.FilterOn = False
With Me.RecordsetClone
.FindFirst "[DiscPK] = " & Nz(Me.ComboFind, 0)
If Not .NoMatch Then Me.Bookmark = .Bookmark
End With
End Sub
As you can see, declaring and setting a recordset object variable is not required. .EOF would probably work just as well, I have just always used NoMatch. This will set focus to record, not filter the form.
If you prefer to display single record, then set the Filter property.
Private Sub ComboFind_AfterUpdate()
Me.Filter = "DiscPK=" & Nz(Me.ComboFind, 0)
End Sub

Timer Routine to Auto close database doesn't always kick off

I have a routine that works perfectly as long as the timer routine kicks off. If it doesn't start, nothing happens.
A Hidden form called frm_Invisible gets loaded when my main form opens. My main form is a typical main form with buttons that open other forms. It's not a switchboard. I used to call frm_Invisible in the On Load event and not sure if it has made any difference but right now I put the call in the On Open event.
The frmMain calls it like this:
Private Sub Form_Open(Cancel As Integer)
DoCmd.OpenForm "frm_Invisible", acNormal, , , , acHidden
End Sub
frm_Invisible has an On Timer event:
Private Sub Form_Timer()
Static OldControlName As String
Static OldFormName As String
Static ExpiredTime
Dim ActiveControlName As String
Dim ActiveFormName As String
Dim ExpiredMinutes
Dim CountDown
On Error Resume Next
ActiveControlName = Screen.ActiveControl.Name
ActiveFormName = Screen.ActiveForm.Name
Me.txtActiveForm = ActiveFormName
If (OldControlName = "") Or (OldFormName = "") _
Or (ActiveFormName <> OldFormName) _
Or (ActiveControlName <> OldControlName) Then
OldControlName = ActiveControlName
OldFormName = ActiveFormName
ExpiredTime = 0
Else
ExpiredTime = ExpiredTime + Me.TimerInterval
End If
'Timer interval is set to 1000 which is equal to 1 second
'for testing, you can remove the /60 and make ExpiredMinutes happen a lot faster
'otherwise, keep the /60 and it will truly be whatever you set at the end
ExpiredMinutes = (ExpiredTime / 1000) / 60
Me.txtIdleTime = ExpiredMinutes
Form_frmMain.txtExpiredMinutes = ExpiredMinutes
If ExpiredMinutes >= 3 Then ' Can change this to 3 if you remove the '/60
'Opening this form will trigger the final count down
DoCmd.OpenForm "frmAutoClose"
End If
End Sub
If the time runs out I open a 3rd form that counts down from 20, giving the user a chance to keep the database open.
It merely counts down from 20 and runs
DoCmd.quit
unless the user clicks a button before the count down finishes. That button just closes the 3rd form, preventing the database from closing.
To test the routine, I put a textbox on frmMain, so that I could monitor if the timer gets kicked off.
Form_frmMain.txtExpiredMinutes = ExpiredMinutes
Most of the time, it does and I can see the time counting. However, there are instances that I cannot account for why the timer doesn't start. So I haven't published this latest update for my users.
I can just give you some general advice for now:
You should kick out On Error Resume Next to see if there maybe occures any error.
Later on you should add 'correct' error handling.
Add a type to this variables: ExpiredTime and ExpiredMinutes. Maybe Long?
Variable CountDown isn't used at all.
To prevent overflows you could directly store seconds instead of milliseconds in your variable ExpiredTime.
Then see what happens.
Update:
Since it can happen in your scenario that no form and therefore no control can be active, I would create two procedures to retrieve that information.
Both just return an empty string in case error 2474/2475 occurs.
Public Function GetActiveFormName() As String
On Error GoTo Catch
GetActiveFormName = Screen.ActiveForm.Name
Done:
Exit Function
Catch:
If Err.Number <> 2475 Then
MsgBox Err.Number & ": " & Err.Description, vbExclamation, "GetActiveFormName()"
End If
Resume Done
End Function
Public Function GetActiveControlName() As String
On Error GoTo Catch
GetActiveControlName = Screen.ActiveControl.Name
Done:
Exit Function
Catch:
If Err.Number <> 2474 Then
MsgBox Err.Number & ": " & Err.Description, vbExclamation, "GetActiveFormName()"
End If
Resume Done
End Function

Looping through the entire record every time a form loads

The code is supposed to check if a filed "YearBigins" is empty and request an input then checks if the date value in "YearBigins" filed in older than one year and sets the value in the "counter" filed to 0. I have the following codes but does not work:
Private Sub Form_Load()
Me.Caption = Date
YearBegins_AfterUpdate
End Sub
Private Sub YearBegins_AfterUpdate()
Dim rst As Recordset
Do While Not rst.EOF
If IsNull(rst.Fields("YearBegins").Value) Then
YearBegins = InputBox("Please Enter the Beginning of counter Year Date as:
dd/mm/yyyy")
Else: YearBegins = YearBegins.Value
End If
rst.MoveNext
If (DateTime.Now - YearBegins.Value) < 365 Then
counter.Value = 0
End If
Loop
End Sub
You're missing the part where you actually update the recordset.
Private Sub Form_Load()
Me.Caption = Date
YearBegins_AfterUpdate
End Sub
Private Sub YearBegins_AfterUpdate()
Dim rst As Recordset
Do While Not rst.EOF
If IsNull(rst.Fields("YearBegins").Value) Then
YearBegins = InputBox("Please Enter the Beginning of counter Year Date as: dd/mm/yyyy")
rst.Edit '<---
rst("YearBegins") = cdate(YearBegins) '<---
rst.Update '<---
Else
YearBegins = YearBegins.Value
End If
rst.MoveNext
If (DateTime.Now - YearBegins.Value) < 365 Then
counter.Value = 0
End If
Loop
End Sub
With that said there are better ways to do this. You could open another form with only the data you want to update which will allow you to give your users some context. Just having a popup dialog with no data validation doesn't let them even really see what record they're updating. How do they know what value to put in?
It would be better if you could refactor your code in YearBegins_AfterUpdate to a new function and call that from Form_Load instead of calling an event handler like YearBegins_AfterUpdate.
I don't see where you're setting rst. You go from Dim rst As Recordset to using it but given the code you provided rst will always be null.

Access VBA - All checkboxes on form have been checked

I am relatively new to Access VBA and have a form that has around 30 checkboxes on it. When saving the form I want to ensure that all checkboxes have been ticked (set to true). The tickboxes have all got names SC1, SC2....SCN Is there a way to loop through each control and see if it has been set to true?
This is what I have tried but it doesnt seem to read the tickbox -
Private Sub Validate_Data(rstTop)
Dim n As Integer
Dim count As Integer
count = 0
For n = 1 To rstTop
If Form.Controls("SC" & n).Value = False Then
count = count + 1
End If
Next
If count <> 0 Then
MsgBox "Not all Questions have been ticked, please tick and add comments", vbInformation, _
"More information Required"
Else
End If
End Sub
Give this a try, it worked for me.
Option Compare Database
Option Explicit
Private Function Validate_Data() As Boolean
Dim ctl As Control
Validate_Data = True 'initialize
For Each ctl In Me.Form.Controls
If ctl.ControlType = acCheckBox Then
If (ctl.Name Like "SC*") And (ctl.Value = False) Then
MsgBox "Not all Questions have been ticked, please tick and add comments", vbInformation, _
"More information Required"
Validate_Data = False 'something isnt checked
Exit Function
End If
End If
Next ctl
End Function
Private Sub cmdGo_Click()
Validate_Data
End Sub

Why is my .setfocus ignored?

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