VBA in Access: How does DoCmd.RunCommand acCmdSaveRecord work? - vba

I use
DoCmd.RunCommand acCmdSaveRecord
to save but Im not sure how this actually works. Does it save every unsaved change? Or does it save everything no matter if it was changed or not? Or does it only save the current form? Whats about releated unsaved changes in other forms? Or does it function in any other way? Is there any offical documentation to this function?

It saves the current record of the active form. It is the same as clicking on the record selector.
If the current record is not currently being edited (not "Dirty"), then nothing happens.
If you want better control, especially when dealing with subforms, I suggest to use the Form.Dirty property instead. With this you can explicitly address the form you want to save.
To save the current record in a form module (only save when needed):
If Me.Dirty Then
Me.Dirty = False
End If
To save the record in any form
With Forms!myForm
If .Dirty Then
.Dirty = False
End If
End With
Or a subform
Forms!mainForm!SubFormControl.Form.Dirty = False
This is much clearer and better IMO. I have stopped using DoCmd.RunCommand acCmdSaveRecord completely.

Related

Using checkbox to enable/disable other checkboxes not working in form

I feel like I'm going crazy here. I've tried everything I can think of and nothing seems to work. I have a checkbox CRShtnvsp and if checked, I have vba to enable additional checkboxes. This checkbox is actually enabled/disabled via another checkbox itself CRShtn. And that sequence works perfectly well. I just can't seem to get the vba for CRShtnvsp to enable additional checkboxes to work. Please help! VBA for both CRShtnvsp and CRShtn below.
This code works perfectly well:
Private Sub CRShtn_AfterUpdate()
If CRShtn = True Then
CRShtnvsp.Enabled = True
Else
CRShtnvsp.Enabled = False
End if
End Sub
This code does not work:
Private Sub CRShtnvsp_AfterUpdate()
If CRShtnvsp = True Then
CRShtn_vaso_phen.Enabled = True
Else
CRShtn_vaso_phen.Enabled = False
End if
End Sub
What's happening here?
All checkboxes are set to Enabled = No in the property sheet so that they are disabled until prior checkboxes or comboboxes on my form are changed using AfterUpdate() or Form_Current().
Sometimes Access seems to lose track of an event procedure. I don't know why that happens, but if that's your situation, you can remind Access the procedure exists.
Go to the Event tab on the property sheet for your CRShtnvsp checkbox. In the "After Update" property box, make sure "[Event Procedure]" is selected and then press the button labelled with 3 dots. You should be at your existing procedure. And I think that should be enough to remind Access it exists.
CRShtnvsp is not updating as it's not being used - I would try adding the code for enabling the next button under the original button you're using and see if that works. To clarify: Add the code for enabling CRShtn_vaso_phen under the CRShtn_AfterUpdate sub.

MS-Access: How to refresh a form after data was updated?

I have a query shown in a form as a table. Additionally I have a button which opens another form where you can manipulate saved data. As soon as this form closes I would like to have the query in the other form to be updated by a macro. I tried couple of macro commands. Nothing worked. I thought I could use the requery macro with my subform as parameter but even that didn't work. What can I do?
Data gets only updated when I hit 'refresh all' but this should happen automatically.
I have ran in to all sorts of strange edge cases with forms containing subforms. What happens when the current record is removed?
This may seem overkill but if you want to be sure that all forms are up to date then you can use this on the parent form:
'#Description("Refresh the data on the form and all subforms")
Public Sub RefreshForm(ByVal theForm As Form)
On Error GoTo ErrorHandling
Echo False
RequeryInPlace theForm
theForm.Refresh
MoveToValidRecord theForm
Dim childForm As Control
For Each childForm In theForm.Controls
If TypeOf childForm Is SubForm Then
RequeryInPlace childForm.Form
childForm.Form.Refresh
MoveToValidRecord childForm.Form
End If
Next
ErrorHandling:
' Fail fast, ensure echo is always left on!
Echo True
End Sub
'#Description("Requery an entire form without losing the currently selected record")
Public Sub RequeryInPlace(ByVal theForm As Form)
Dim whereIam As Variant
With theForm
whereIam = .Form.Bookmark
.Form.Requery
.Form.Bookmark = whereIam
End With
End Sub
'#Description("Move cursor to a valid record")
Public Sub MoveToValidRecord(ByVal theForm As Form)
With theForm.Recordset
If .EOF And .BOF Then Exit Sub
If .EOF Then
.MoveLast
ElseIf .BOF Then
.MoveFirst
Else
.MoveNext
.MovePrevious
End If
End With
End Sub
Ok, the way to do this?
If you just editing data? (not adding new rows) to that grid?
Your code to launch the 2nd form will look like this:
me.Refresh - optional but REQUIRED if you do allow editing in the gride.
docmd.OpenForm "frmDetailsEdit",,,"ID = " & Me!ID,,acDialog
me.refresh - this will show any data changes you made in the 2nd dialog form
Note carefull:
You don't need the first me.Refresh UNLESS you allow edits in the data/grid display.
The acDialog will cause the code to WAIT/HALT until the user is done editing in the 2nd form.
The final me.refresh will update any data changes, and it will also keep the reocrd pointer on the same current row.
However, if your 2nd dialog form launched ALSO allows adding of records, then a me.refresh will NOT show the newly added records.
If you allow adding? Then you need to do a me.Requery on that last line in place of me.refresh. This will re-load the form based on its query, but you will ALSO lose the current position. You can if you wish re-position the record pointer/location if you need to, but we don't know if you need this ability as of yet.
the query is not being updated in background unless you are opening the form which calls the Query to run , an alternative is to request the query to run again to update the form as follows
Form.Requery
if your form is having a sub form then you need to address the sub form as well

Error catching a form in Access using VBA

So i have a form that's updating a table in Microsoft Access. I'm using a BeforeUpdate event call to stop the form from updating if the 'first name' hasn't been filled in.
I basically only want the form to be saved once everything has been filled in, and then switch to see the updated table (the bit under If not cancel).
Currently the popup box to say 'You must enter a First Name' correctly fires, the next pop up box asking if the user wants to undo any changes fires. But then a pop up box saying 'No current record' appears and then the view changes to the table. I'm not sure why this is all happening.
Option Compare Database
Private Sub Form_BeforeUpdate(Cancel As Integer)
Cancel = False
' perform data validation
If IsNull(Me.customer_f_name) Then
MsgBox "You must enter a First Name.", vbCritical, "Data entry error..."
Me.customer_f_name.BorderColor = vbRed
DoCmd.GoToControl "customer_f_name"
Cancel = True
End If
If Not Cancel Then
DoCmd.SelectObject acTable, "ticket_tracker_table"
DoCmd.Requery
DoCmd.GoToRecord acDataTable, "ticket_tracker_table", acLast
End If
' if the save has been canceled or did not pass the validation , then ask to Undo changes
If Cancel Then
If MsgBox("Do you want to undo any changes?", vbYesNo, "Confirm") = vbYes Then
Me.Undo
End If
End If
End Sub
Review https://learn.microsoft.com/en-us/office/vba/api/Access.Form.BeforeUpdate(even)
Instead of GoToControl, use SetFocus.
I did a very simple test with the Undo question and it does work:
Private Sub Form_BeforeUpdate(Cancel As Integer)
If IsNull(Me.customer_f_name) Then
MsgBox "Must enter customer name."
Cancel = True
If MsgBox("Do you want to undo edits?", vbYesNo) = vbYes Then Me.Undo
Me.customer_f_name.SetFocus
End If
End Sub
Suggest you put table code in AfterUpdate event.
so to supplement J7, with some big picture view: "basically only want the form to be saved once everything has been filled in" …. that is swimming against the tide; which is why you are having to write a lot of code. The product enters data as you go fundamentally. that is it's core design.
it is very normal to do field checks as you go - typically in the AfterUpdate event, the check code is applied and if invalid - generate a message box and invoke an Undo plus reset the focus back to the field they were attempting to leave.
A totally different design, in some cases it may justify a temp local duplicate table just for the purposes of holding form input data - - this approach allows a very elaborate check(s) against data in the production database; where upon if valid an Append Query then writes the data into the permanent table.

Preventing close buttons from saving records in MS Access

In a Microsoft Access form, whenever the current record changes, any changes in bound controls are silently saved to the database tables. This is fine, but I don't want it to happen when a user closes a form, because it is the direct opposite of what many people would expect.
The best example is when you try to close an excel file with unsaved changes, it asks whether the changes should be discarded. This is exactly what I'm trying to achieve in Access, but can't find any way to trap the close button's event in VBA.
The form's Unload event is the first event that is triggered when someone clicks the close button, but by then the changes are already written to the database.
Is this at all possible, or do I have to create my own close buttons? I'm comfortable with writing large amounts of code for trivial things like this but I hate having to clutter the GUI.
You have to work with Form_BeforeUpdate event. Below is an example; however it does create a typical warning message: "You can't save this record at this time. Microsoft Access may have encountered an error while trying to save a record. ..." - depending on your database settings. You can use simple workaround below to avoid displaying of that message.
Private Sub Form_BeforeUpdate(Cancel As Integer)
Cancel = True
'Or even better you can check certain fields here (If Then...)
End Sub
Private Sub Form_Error(DataErr As Integer, Response As Integer)
If DataErr = 2169 Then
Response = True
End If
End Sub
Sean gave an almost correct answer but it leaves gaps.
In general, the FORM's BeforeUpdate is the most important form event. It is your LAST line of defense and ALWAYS runs prior to a record being saved regardless of what prompted the save (form close, new record, your own save button, clicking into a subform, etc.) Although I occasionally use the control's BeforeUpdate event just so the user gets the error message sooner, the bulk of the validation code I write runs in the Form_BeforeUpdate event. This is the event you MUST use if you want to ensure that certain controls are not empty. No control level event will do this reliably for all situations. Primarily because if the control never gets focus, no control level event ever fires. Form_BeforeUpdate is also the event you would use if your validation involves multiple fields. If you are using any other control or event level event, you are wasting your time. There is always away around your "trap" and your table almost certainly contains invalid data.
Regarding the OP's question. If you want to force people to use your own save button and prompt them if they don't then you need a form level variable as Sean's suggestion implied. The only difference, is that you need to set it to False, in the form's Current event NOT the Open event. You want the flag to be reset for EVERY new record, not just when the form opens. Then you set it to True in your save button click event, just before you force the record to save with DoCmd.RunCommand acCmdSaveRecord.
Then finally, in the Form_BeforeUpdate event, you check the value of the variable.
If bClose = False Then
If MsgBox("Do you want to save the changes?", vbYesNo) = vbNo Then
Cancel = True
If MsgBox("Do you want to discard the Changes?", vbYesNo) = vbYes Then
Me.Undo
End If
Exit Sub
End If
End If
this is code I have that checks to see if the form is being closed or saved.
Private Sub Form_BeforeUpdate(Cancel As Integer)
If Not UsingSaveButton Then
If MsgBox("Abandon Data?", vbInformation + vbYesNo) = vbNo Then
Cancel = True
Else
DoCmd.RunCommand acCmdUndo
End If
End If
End Sub
I have a Boolean Flag that is set to False on loading, and then when my Save button is used, I set it to true to allow the update to run through.
If the flag is not set, then they are leaving the record (either through going to a different record, or closing the form) so I ask if they actually want to save the changes.
The Cancel = True aborts the exit of the form or the move to a different record if any changes have been made.
The DoCmd.RunCommand acCmdUndo undoes any changes so they are not saved.
Actually, you cannot trap this, Access is working directly upon the table, every change is already being saved when the field looses the focus by moving to another field, record or a button.
Actually imho this is a great advantage compared to Excel
If you really want a behaviour similar to Excel you'd need to work on a copy of the table and some code for updating.
In the Form in the 'On Unload' event add the below code.
DoCmd.RunCommand acCmdUndo
DoCmd.Quit
Records no longer being saved when users close a form in any way.
** you can use exit button with this code**
Private Sub Button_Click()
If Me.Dirty = True Then
If MsgBox(" Save Change ", vbYesNo) = vbYes Then
Me.Dirty = False
Else
Me.Undo
End If
End If
DoCmd.Close acForm, "FormName"
End Sub

How to hide/Unhide a MS Access DB form using vb.net

If a form in Access DB is set as hidden. Then how to unhide it? so that we can manipulate the form programmentically using vb.net.
Thank you.
I cannot help with .net, in VBA:
Sub FormHidden()
Dim frm
For Each frm In CurrentProject.AllForms
SetHiddenAttribute acForm, frm.Name, False
Next
End Sub
The code Remou gave should work for unhiding forms whose properties have been changed to HIDDEN in the Access UI.
In VBA, to simplify Remou's example, that would be:
SetHiddenAttribute acForm, "MyHiddenForm", False
You may be required to automate Access from VB.NET in order to accomplish this, but SetHiddenAttribute is a method of the top-level Access application object, so ought to be fairly simple to use. The value of the VBA named constant acForm is 2, so you'd likely have to call that literally, something like this:
app.Application.SetHiddenAttribute 2, "MyHiddenForm", False
where the app object has been initialized as an Access application. Dunno how that's done in VB.NET, but in VBA it would be something like:
Set app = CreateObject("Access.Application")
I'm not sure if the correct syntax would be app.Application.SetHiddenAttribute or if it would be just app.SetHiddenAttribute, but you could easily try either one.
But keep in mind, it was Remou who gave the correct answer. I'm only speculating on how to make it work in a programming environment I don't even use!
Does the form have Visible property? You can set to true to make the form visible.
Do you mean the form is open but not visible, or that the form's meta properties are set to not visible? The later is something you shouldn't do, as items set with visible off will be deleted the next time the database is compacted.