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

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

Related

VBA Calling a subroutine in one user form from another user form

I am using Excel VBA. I have two user forms:
ClientInfo and ClientSearch
In ClientSearch, I search through an excel worksheet to list all clients with the same last name. From there, I pick the client I want (by highlighting the name in the list and clicking on command button cmdOpenClientInfo) and determine their ClientID (which is also in the worksheet).
I then want to pass this ClientID to the form ClientInfo in order to populate all text boxes on this form with the relevant data from the worksheet:
Coded in the ClientSearch form:
Private Sub cmdOpenClientInfo_Click()
Dim ClientID As Integer
ClientID = textSrchClientID.value
'user msgbox to check to make sure I get the correct ClientID ... and I do
msgbox(ClientID)
Me.Hide
frmClientInfo.show
Call frmClientInfo.PopulateClientInfo(ClientID) 'this is where it fails
End Sub
Coded in the ClientInfo form:
Sub PopulateClientInfo(ClientID As Integer)
'this is where I would populate the text field
End Sub
The routine always gets stuck at the
CALL frmClientInfo.PopulateClientInfo(ClientID)
In the case above, I get Run-time error '424' Object Required.
I have tried various solutions presented in this forum, but have not found a solution.
Your call to frmClientInfo.show is in Modal mode, so the next statement wont execute until the new form closes. You can try to make the call non-modal:
frmClientInfo.show Modal:=False
But this may be source of other problems. Better keep working in modal mode but pass the ClientID parameter to the form before it shows up.
1- Modify your PopulateClientInfo method of frmClientInfo like this:
Public Sub PopulateClientInfo(ClientID As Integer)
'....
'populate The fields, then:
'....
Me.Show ' <-- form shows itself after populating its fields
End Sub
2- Remove the call to frmClientInfo.show in the ClientSearch form.
You can't call a procedure in a form module from outside that module. Try this code (sorry, I didn't).
Private Sub cmdOpenClientInfo_Click()
Dim ClientID As Integer
Dim FrmInfo As frmClientInfo
ClientID = textSrchClientID.Value
'user msgbox to check to make sure I get the correct ClientID ... and I do
MsgBox (ClientID)
Me.Hide
Set FrmInfo = New frmClientInfo
With FrmInfo
.Tag = ClientID
.Show
' make this call in the FrmInfo Activate event procedure
' PopulateClientInfo(cint(Me.Tag)
End With
Unload FrmInfo
Set FrmInfo = Nothing
End Sub
I presume that you have a form which is named frmClientInfo. You can create an instance of that form with the command Set FrmInfo = New frmClientInfo. This object will not show until the Show method is invoked but you gain access to all its controls. To pass a variable to that form you can address any of them. Perhaps you have a Tbx which should show the ClientID. You can access that Tbx and set its value. The above code assigns the ClientID to the Tag property of the form itself.
The form's Activate event will occur when the Show method is invoked. That would be the moment to run the PopulateClientInfo procedure (from within the frmClientInfo module, of course), retrieving the ClientId from the Tag property.
Bear in mind that the code will continue running in the cmdOpenClientInfo_Click procedure when the ClientInfo form is closed. So, that is the time to remove that form from memory, on the one hand. On the other, the 'FrmInfo' object still exists and you could pick any information from it that you might want to use in the form which made the call. The syntax is very simple, like, FrmInfo.Textbox1.Value.

Row gets modified when form is opened in access vba

I need to prepare a screen that will work as a comments screen as used on Facebook. I have to prepare it on access form. Where User will enter the comments and the same should get stored in a table with PartNumber and ItemNumber. Also the recent Comments should be displayed on the same form.
I have prepared a form having Recordsource as my table where the comments needs to be saved. And written a click event where as user clicks the button the comments will get saved in table. But the prob is that without clicking that button if i type values in textbox and close the form still the table gets updated with value before click. Below is the code
Private Sub Post_cmnt_Click()
Me.RecordSource = "Part_GeneralPartComment"
Call FromForm_Add
End Sub
Access automatically commits pending changes when a bound form is closed. If you want to prevent that from happening you can add the following code as the On Close event handler for the form:
Private Sub Form_Close()
On Error GoTo Form_Close_error
DoCmd.RunCommand acCmdUndo
Exit Sub
Form_Close_error:
If Err.Number <> 2046 Then
' error was something other than "The command or action 'Undo' isn't available now."
Err.Raise Err.Number
End If
End Sub

Treeview behaving differently when tapped than when clicked

The situation: I have an Access 2010 db that is meant to be deployed on a Windows 8 tablet. The main form of the app contains a Treeview control. Selecting a node on the Treeview sets the visibility of one of several subforms that are used for viewing/editing details of the selected node item. I have a yes/no message box and some basic code on the BeforeUpdate event for each of the subforms. So when the record on the subform is dirty and the user clicks anywhere on the main form (including anywhere in the Treeview control) this code is triggered.
The problem: When the subform record is dirty and the user taps anywhere on the Treeview control, the message box pops up but cannot be interacted with because the app is busy. Doing what, I don't know, but it stays that way until Access is shut down via Task Manager. There is no code attached to the Treeview for anything but the Click event. This happens even when they touch white space in the Treeview below the existing nodes.
If the record is not dirty, everything works fine.
If the record is dirty and the user hits the "Save" button on the subform to trigger the BeforeUpdate event, everything works fine.
If the user taps a different control or in the empty space on the main form, the BeforeUpdate event is triggered and everything works fine.
If you plug a mouse into the tablet and do the same series of steps by clicking instead of tapping, everything works fine.
I've done a ton of searching and haven't been able to find anything relevant to this, so any suggestions or guidance to new places to look for suggestions would be deeply appreciated.
I've attached a sample of the BeforeUpdate code that exists on each of these subforms. It's pretty basic, but maybe there's something in it that tapping and Treeviews just don't like.
Private Sub Form_BeforeUpdate(Cancel As Integer)
'If the form data has changed a message is shown asking if
'the changes should be saved. If the answer is no then
'the changes are undone
On Error GoTo BeforeUpdate_Error
If Me.Dirty Then
'Add PropertyID, LPParentNodeID and TreeNodeID if Record is new
If Me.NewRecord Then
Me.PropertyID = Me.Parent!PropertyID
Me.LPParentNodeID = Me.Parent!txtCurrKey
Me.TreeNodeID = DateDiff("s", Date, Now())
End If
'Display prompt to save the record
If MsgBox("The record has changed - do you want to save it?", _
vbYesNo + vbQuestion, "Save Changes") = vbNo Then
Me.Undo
End If
End If
'If the record is still dirty, then record the change in the Audit table
If Me.Dirty Then
Call AuditTrail(Me, InstanceID, PropertyID)
End If
BeforeUpdate_Exit:
Exit Sub
BeforeUpdate_Error:
MsgBox Err.Description
Resume BeforeUpdate_Exit
End Sub
08/30/2013 Addition: I forgot to mention the debugging behavior in the original question. When I set a breakpoint on the BeforeUpdate Sub of the subform on any line from the actual Sub entry point down to the If statement with the message box, the code window comes up but the app again becomes busy, and I can't interact with either window. Just like before, this behavior is unique to tapping that accursed Treeview control.
What you could do is put a kind of edit/save structure into each of the subforms, whereby controls in the subform are locked until edit is clicked, and re-locked after save is clicked. So:
private sub bEdit()
editMode true
end sub
private sub bSave()
...save logic
editMode false
end sub
private sub editMode(isEdit as boolean)
dim ctl as control
for each ctl in me.controls
if ctl.controltype is actextbox or ctl.controltype is accombobox then
ctl.locked = (not isEdit)
end if
next
end sub
With this approach, it's then a small task to add editmode control for the parent form by adding
me.parent.editmode isEdit
to the end of of the editmode procedure.
In the parent form, editMode will need to be a PUBLIC sub.
In this sub, control whether the tree will do anything when clicked on:
public sub editMode(isEdit as boolean)
tree.enabled = (not isEdit)
end sub

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

VBA Listbox becomes unresponsive after first use

I have a VBA (Excel 2010) system which involves selecting an item from a listbox and then displaying it in another form. Here is a very simplified version of what happens.
' Part of frmForm1 code module
sub lstListbox_Click
dim MyEvent as string
dim i as integer
i=me.lstListbox.listindex
MyEvent=me.lstlistbox.list(i)
' Now show the item in the second form
Load frmForm2
me.hide
ThisWorkbook.LoadDataIntoForm2 (frmForm2, MyEvent)
frmForm2.show
unload frmForm2
me.show
end sub
The listbox accepts the click, and first the event (the event handler is giver above). Key parts of the event handler are:
Load the second form (to display the detail data)
Pass the second form as a UserForm parameter to a procedure (LoadDataIntoForm2)
Hide the host form (frmForm1) and show the second form (frmForm2)
When the second form processes an Exit click, the code looks like this:
' Part of frmForm2 code module
sub cmdExit_Click
me.hide
end sub
The first time round it works fine - but when I return to frmForm1 (in the tail end of the lstListBox_Click procedure), even though the rest of the form is operative, the listbox remains stubbornly unresponsive.
I've managed to abstract this down to a little demo system if that would help - the same behavior is seen there. (It's regular .xls file, but that seems not to be easily acceptable as an upload)
Has anyone seen this before? And does anyone have any ideas how I might get this to work the way I want it to?
Thanks,
Tony
The default for the .Show method is to make the form modal. Explicitly set it to modeless:
Sub lstListbox_Click
...
Me.Show vbModeless
End Sub