I'm sure this is very simple, but I can't find it. In the close event of an Access Form, how can I cancel closing the form? I have a test that counts the records in a table. If that table has records, I want to ask the user if they want to close or go back and work with them. So how do I cancel the close event?
You can use the Unload event:
GlobalVar ButtonClicked
Private Sub Form_Open(Cancel As Integer)
ButtonClicked = False
End Sub
Private ClickMe_Click(Cancel As Integer)
ButtonClicked = True
End Sub
Private Sub Form_Unload(Cancel As Integer)
If Not ButtonClicked Then
Cancel = True
End if
End Sub
Order of events for database objects
Study and try this code, it worked for me. Replace necessary variable names with your chosen names. Paste the code in the form_unload Event of your form.
WARNING!!!: After you perform this operation you will find it difficult to access your form in design and layout view
Private Sub Form_Unload(Cancel As Integer)
userresponse = MsgBox("Are you sure you want close? All your work wouldn't be saved", vbYesNo, "Database Information")
Select Case userresponse
Case 6
Cancel = False
'this line opens another form in my own case
DoCmd.OpenForm "EngMenu"
Case 7
Cancel = True
'this line keeps my own form open in my own case
DoCmd.OpenForm "UpdateForm"
Case Else:
MsgBox "You are not allowed to perform this operation", vbInformation, "Database Information"
End Select
End Subenter code here
Use the "Form_BeforeUpdate(cancel as integer)" event and set cancel to True.
Notice that you simply will not be able to close at all unless you add some logic to actually allow updating the database.
Related
I have a textBox formatted as "Short Date". When I put invalid data in field, for example random "dfsdf", and try to change focus, form throws validation Error 2113.
My goal is to give an opportunity to user to close form by click on "Cancel" button without any problem, because no matter what he entered in Date textbox while form is canceled.
I can handle this error and disable message with Form_Error event, but focus stays set to date textBox anyway, when i try to click Cancel button, and button is never clicked.
I use a setup, probably similar to yours:
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Const InvalidDate As Long = 2113
Select Case DataErr
Case InvalidDate
MsgBox "Invalid date!"
Response = acDataErrContinue
' Cancel invalid entry.
Me.Undo
' DoCmd.Close
Case Else
Response = acDataErrDisplay
End Select
End Sub
Problem is, that this will fire before your button gets focus. This means, that the form doesn't even know what you clicked, only that it was somewhere outside the date textbox.
Thus, the DoCmd.Close would close the form for whatever was clicked.
You may turn it into an advantage, that the user is able to watch the removal of the faulty input ...
You should use the after update event to check if the user entered a valid date,
if not then clear or undo. Without knowing the controls of your form i can't tell if you allow users to change table values or if you are using transactions.
pseudo code:
Private Sub Text1_AfterUpdate()
If Check_If_Date Then
Execute_Code
Else
End If
End Sub
Private Function Check_If_Date() as Boolean
If IsDate(Text1) then
Check_If_Date = True
Else
Check_If_Date = False
Select Case msgbox("Not a valid date", vbOKCancel)
case vbOK
reset_value
case vbCancel
Close_Form
case else
End Select
End If
End Sub
Private Sub Reset_Value()
'need to clear value or undo
End Sub
Public Sub Execute_Code()
'Code to save values or allow Update to complete
End Sub
Private Sub Close_Form()
'Code For Closing Form and logging errors
End Sub
I split up repeatable actions for reuse in case the same thing can be repeated in another text box for example a second date or to get the same behavior for the other controls.
I had a piece of VBA running, and for some reason it has suddenly stopped working.
On my form unload event I have the code:
if isnull(me.field) then
ans=MsgBox("Warning you have not entered all the data. Do you want to exit the form", vbYesNo, "Error")
If ans=vbNo then
Cancel=True
end if
end if
This worked for a couple months, when the user exited the warning message would appear, and if they select no the form would not exit. Now when I click no I get an error:
Run time Error 3270. Property not Found
I changed the code to:
if isnull(me.field) then
ans=MsgBox("Warning you have not entered all the data. Do you want to exit the form", vbYesNo, "Error")
If ans=vbNo then
docmd.cancelevent
end if
end if
Now I get error:
Runtime Error '2001' You Canceled The Previous Operation
Which is what I want.
How do I get a messagebox to confirm that a user wants to exit a form?
Edit: I realize that the exit warning works when I exit the form by pressing x in the upper right, but when I exit using a button with the docmd.close I get the errors. Any way around that?
Simply use the button click event.
Where CommandButton14 is the name of your exit button.
EDIT for users comment.
Have your exit button call the UserForm_QueryClose event.
Private Sub CommandButton14_Click()
'UserForm_QueryClose 0, 0
Unload Me
End Sub
Ask your question in that event. If they say yes end the app or unload the form. If they say no, cancel.
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
Dim Response As Integer
' Displays a message box with the yes and no options.
Response = MsgBox("Warning you have not entered all the data. Do you want to exit the form?", vbYesNo)
' If statement to check if the yes button was selected.
If Response = vbYes Then
'Cancel = False
End
Else
Cancel = True
End If
End Sub
You can take everything out of your unload event.
I feel like I am missing something because the path you chose seems a little over complicated.
Firstly, with my database I always make sure to set all of my forms Close Button property to "No" this way you always have control of when the user closes the form.
So from that point you just need this code attached to your close button:
Private Sub btnClose_Click()
Dim blnClose As Boolean
Dim strResponse As String
'Default to true so it always closes unless one or more future checks fail
blnClose = True
If IsNull(Me.Field) Then
strResponse = MsgBox("Warning you have not entered all the data. Do you want to exit the form", vbYesNo, "Error")
'User wants to cancel close toggle Boolean to false
If strResponse = vbNo Then
blnClose = False
End If
End If
'If nothing has toggled to false then close the form
If blnClose = True Then
DoCmd.Close , ""
End If
End Sub
I have an unbound textbox control. The user enters some valid text and leaves the control. The user then returns to the control and enters some invalid text. I want to show the user a message then rollback the value of the control to its previous state and keep the focus in that control.
I've tried the following approaches, none of which gets me exactly what I am looking for:
OPTION A: SendKeys
Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
Cancel = DataIsInvalid(Me.MyTextBox.Value)
If Cancel Then SendKeys "{Esc}"
End Sub
This does exactly what I want, but I really want to avoid using SendKeys. There are many problems that come with using SendKeys: Vista+ compatibility, keys being sent to a different application, etc.
OPTION B: Undo method of control
Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
Cancel = DataIsInvalid(Me.MyTextBox.Value)
If Cancel Then Me.MyTextBox.Undo
End Sub
This is simply broken for unbound controls (at least as of MS Access 2002/XP). This method does not restore the value of MyTextBox to the valid input. However, it does allow the user to change the focus to a new control while leaving the invalid input in place in Me.MyTextBox! Unbelievable!!!
OPTION C: Undo method of form
Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
Cancel = DataIsInvalid(Me.MyTextBox.Value)
If Cancel Then Me.Form.Undo
End Sub
The Undo here does absolutely nothing. But at least it doesn't break the BeforeUpdate Cancel=True code and allow the invalid data to stand.
OPTION D: Explicitly restore old value in BeforeUpdate event
Private mPrevMyTextBoxValue As Variant
Private Sub MyTextBox_AfterUpdate()
mPrevMyTextBoxValue = Me.MyTextBox.Value
End Sub
Private Sub MyTextBox_BeforeUpdate(Cancel As Integer)
Cancel = DataIsInvalid(Me.MyTextBox.Value)
If Cancel Then Me.MyTextBox.Value = mPrevMyTextBoxValue
'If Cancel Then Me.MyTextBox.Text = mPrevMyTextBoxValue
End Sub
Attempts to assign the previous value to either the .Value or .Text property of the textbox result in the same error message:
The macro or function set to the BeforeUpdate or ValidationRule property for this field is preventing {Program Name} from saving the data in the field.
OPTION E: Explicitly restore old value in AfterUpdate event
Private mPrevMyTextBoxValue As Variant
Private Sub MyTextBox_AfterUpdate()
If DataIsInvalid(Me.MyTextBox.Value) Then
Me.MyTextBox.Value = mPrevMyTextBoxValue
Me.MyTextBox.SetFocus
Else
mPrevMyTextBoxValue = Me.MyTextBox.Value
End If
End Sub
This is really close to the desired behavior. Unfortunately, it's impossible to keep the focus on the control because the AfterUpdate event runs before the Tab/Enter keypress or mouse-click events are processed. So even if we try to force the focus to the proper control (via the .SetFocus statement above), the program will immediately shift the focus to the control the user selected.
It seems to me that the "right" way to do this is to use the .Undo method of the control. That does not work for unbound controls, though. This is an inexplicable shortcoming, especially given the fact that an Escape key-press performs this functionality for an unbound field.
Is there a better way to do this or should I just stick to using SendKeys?
After getting to page 3 on google I decided to just muck around and see what sort of ordering happens when trying your Option E. Below is the best workaround method I was able to use to get focus to "Stay" on the textbox in question.
Private mPrevMyTextBoxValue As Variant
Private Sub Text0_AfterUpdate()
If Me.Text0.Value = 0 Then
Me.Text0.Value = mPrevMyTextBoxValue
Me.Text12.SetFocus
Me.Text0.SetFocus
Else
mPrevMyTextBoxValue = Me.Text0.Value
End If
End Sub
0 simulates that it failed the validation. It seems like if you set focus to something else before setting it back to the textbox you are working with it will stay there. I don't have an answer as to why unfortunately.
Edit: I have decided to try to program my theory as to how this works.
Private blnGoToNextControl as Boolean
Private Function SetFocus(ctrl As control)
blnGoToNextControl = True
If ctrl.HasFocus = True Then
'Do nothing
Else
ctrl.HasFocus = True
blnGoToNextControl = False
End If
End Function
This is my idea of how the SetFocus function works. So then after the AfterUpdate event runs it would check to see if the flag to go to next control is and see that it is set to false and not go to the next control.
Rough coding obviously but hopefully this gets my theory across?
If it is unbound, why not use the AfterUpdate event:
Private Sub MyTextBox_AfterUpdate()
If DataIsInvalid(Me!MyTextBox.Value) Then
Me!MyTextBox.Value = Null
Me!MyTextBox.SetFocus
End If
End Sub
or modify your other solution:
Private Sub MyTextBox_AfterUpdate()
Static mPrevMyTextBoxValue As Variant
If IsEmpty(mPrevMyTextBoxValue) Then
mPrevMyTextBoxValue = Null
End If
If DataIsInvalid(Me!MyTextBox.Value) Then
Me!MyTextBox.Value = mPrevMyTextBoxValue
Me!MyTextBox.SetFocus
Else
mPrevMyTextBoxValue = Me!MyTextBox.Value
End If
End Sub
Try moving the focus off the textbox, then back:
Me!MyTextBox.Value = mPrevMyTextBoxValue
Me!SomeOtherControl.SetFocus
Me!MyTextBox.SetFocus
That someothercontrol can be a tiny nearly hidden textbox.
I have a bound pop up form with a Clear data button. It runs the Undo command.
The Form_Current event sets this button's enable property to False.
The Form_Dirty event sets this button's enable property to True.
The button's property is always set to False even after I enter data into the form. I think this is because my Form_Load event is populating two fields. One is passed from the main form as an OpenArgs, the other is the unique ID which is calculated based on the OpenArgs value.
Any suggestions as to how to get the Dirty event to activate under these circumstances? If not, then an alternative approach perhaps?
Thanks in advance.
Code below:
Private Sub cmdUndoChanges_Click()
CustID_temp = Me!CustID
FacNo_temp = Me!Unique_ID
DoCmd.RunCommand acCmdUndo
Me!CustID = CustID_temp
Me!Unique_ID = FacNo_temp
End Sub
Private Sub Form_Current()
Me!cmdUndoChanges.Enabled = False
End Sub
Private Sub Form_Dirty(Cancel As Integer)
Me!cmdUndoChanges.Enabled = True
End Sub
Private Sub Form_Load()
Dim test As Integer
Me!CustID = OpenArgs
test = DCount("Unique_ID", "tbl_Table2", "CustID = '" & Me!CustID & "'")
If IsNull(Me!UNIQUE_No) Then
test = test + 1
Me!Unique_ID = CustID & "-" & test
MsgBox "No Previous Data"
Else
' these will be turned back on when the user selects the
' modify data button or add new data button.
For Each ctl In Me.Controls
Select Case ctl.ControlType
Case acTextBox, acComboBox, acOptionGroup, acCheckBox
ctl.Locked = True
ctl.BackColor = 15066597
Box40.BackColor = 15066597
End Select
Next ctl
End If
MsgBox Me!Unique_ID
End Sub
Have you tried Form_BeforeInsert() instead of Form_Dirty()?
I found another case where the Form_Dirty event never fires, the hard way. I thought I'd add it here since this is the top search result for the "not firing" question.
If your Form_Load function does anything to change the value of an existing record, the form will be set to dirty immediately, apparently before the logic that fires the event begins to run. Because of that there will never be an event if other data on the form is changed.
We had a "smart" Save button that was disabled until Form_Dirty fired. After I added a fixup to Form_Load for old records with a junk field, editing those records no longer ever enabled the Save button.
Access 2007: I have one form with 100s of records displayed. I have a second form for editing or creating new records. When I return to the first form after adding a new record, I do On Activate: Me.Requery so the new record is added to the list, but I would like it to display on the screen with the record selector on the new record. Is there a way to do this? I am assuming that I save the ID in a global variable, but not sure what to do next. Thanks.
RESPONSE:
Thanks //t. Your answer got me going in the right direction. I will post my solution, which I think is more of a work-around. There has to be a better solution, but this seems to work.
Form 1 (list) -> Form 2 (edit/new) and create new record.
Private Sub Form_Current ()
glngID = Me.ID.Value
End Sub
Private Sub Form_Close
gstrLastForm = "Form2"
End Sub
When I close Form 2, Form 1 is active.
Private lngSelectedRecord as Long
Private Sub Form_Activate()
Me.Requery
FindSelectedRecord
If gstrLastForm = "Form2" Then
DoCmd.GoToRecord acDataForm, "Form1", acGoTo, lngSelectedRecord
End If
End Sub
Private Sub FindSelectedRecord()
...
Open recordset, move through records, increment counter, exit when ID found
...
lngSelectedRecord = intCounter
...
End Sub
This is usually done with a form opened in dialog mode. In most cases, you'd have a command button on your main form ADD NEW RECORD, that when clicked would run code like this:
DoCmd.OpenForm "MyAddForm", , , , acFormAdd, acDialog
This opens the form you use for adding a new record to a new, empty record, and pauses the code.
However, you need to know the PK of the record that was added, so you can't just close the form and let the code continue. So, the usual practice is to set the dialog form's Visible property to False, get the data out of it that you need, then close it and do what you want:
Dim lngPK As Long
DoCmd.OpenForm "MyAddForm", , , , acFormAdd, acDialog
If Forms!MyAddForm.Tag <> "Cancel" Then
lngPK = Forms!MyAddForm!PK
Application.Echo False
Me.Requery
With Me.RecordsetClone
.FindFirst "[PK]=" & lngPK
If Not .NoMatch Then
If Me.Dirty Then
Me.Dirty = False
End If
Me.Bookmark = .Bookmark
End If
End With
Application.Echo True
End If
DoCmd.Close acForm, "MyAddForm"
In the dialog form, you have to hide the default window controls so the user can't close it. Instead, have two command buttons SAVE and CANCEL. The SAVE button does this:
If Me.Dirty Then
Me.Dirty = False
End If.
Me.Visible = False
...and the CANCEL button does this:
Me.Undo
Me.Tag = "Cancel"
Me.Visible = False
The result is that you know that a record was not saved so you don't want to do anything to the calling form.
This is all standard Access user interface design, and it's by far the easiest and most bulletproof method for doing this kind of thing.
Use the on_close-method in your popup window to go to the inserted record,
something like:
private sub on_close()
'maby first check we're not undoing..
docmd.gotoRecord yourform,yournewid
me.close
(on osx currently, but I hope you get the concept...)