I have a form with two subforms (on separate tab pages). It's an MDB project in Access
2003.
When it initially opens, Form_Current on the active subform fires once, as
it should.
But when you move to another record (ie. from the main form), it fires
Form_Current on the active subform 4 times. Then subsequent record-moves
result in Form_Current firing 2 times. This is a pain, because the subforms
have a lot of fields that get moved and/or hidden and so it jumps around for
every Form_Current, not to mention being slow.
I am opening the form with a filter via DoCmd.OpenForm (actually it sends
the filter in via OpenArgs). FilterOn is only set once, in Form_Open on the
main form, never in the subforms. Form_Current is not called explicitly
anywhere else in the code.
When I look at the call stack when Form_Current fires moving the first time,
it looks like:
my_subform.Form_Current
[<Debug Window>]
my_subform.Form_Current
So it seems like something in Form_Current is triggering another
Form_Current event. But only on the first record move.
The code in Form_Current is somewhat complex, involving custom classes and
event callbacks, but generally does not touch the table data. The only thing I
can think might be triggering a Form_Current is that it checks OldValue on
form controls - could this be causing it?
Or anything else come to mind?
Thanks.
Eric
As TheAceMan1 once noted, "The only thing you have to watch out for with the On Current event is recursion! That is ... code in the event that causes the event to retrigger. Like moving to another record within the code. Other than that ... the event can be as hefty as required." I have found Current to be horribly twitchy. That said, it is sometimes the only place to put your code.
You may be aware that events for sub-forms trigger before those for the parent form. This is certainly true for Load, and maybe for Current as well.
I can only suggest step-by-step trouble-shooting; comment out parts, check functioning, and work it out. Of course you can always post a heap more code for us to see.
Related
Below is my XML code
<toggleButton id="CloseBtn" label = "Close"
size="large"
imageMso="FrameDelete"
onAction= "=InvoiceRibbon_Close()"
getPressed="MyToggle"
keytip="C"
supertip= "Close current window."/>
By Function is Form code:
Public Function InvoiceRibbon_Close() As Boolean
It works on three computers. Two have Office 2016 Access and one is Access 2016 run time only. It does not work on the other two computers. Both are Access 2016 run time only. All three 2016 Run time computers are new computers which is Windows 10. Error message is attached.
The onAction function is inside Form VBA code. Below are some testing I did.
If I move InvoiceRibbon_Close to Module, it works.
On module OnAction function, I still cannot call function which is inside Form by Call Forms("FormName").InvoiceRibbonTesting_Close. Error message is still the same.
The function in Form works. I used it for a while.
I want to keep the function in Form VBA. I have hundreds Functions which is form related. It will be a huge tasks to move it to Module
Well, on the computers that fail, does ANY VBA code work? So, button code and what not - check if they work. If they don't work, then of course the Ribbon code will not work.
The other issue:
Check if you have a timer form running. Remember, the FANTASTIC idea you are using is to call functions in the form, and NOT USE horrible call backs which MUST be placed in standard code modules and thus are not attached to the given form in which 99% of the time we want code in that form to run and be used (say like any standard button code, but now you are using the ribbon. So, your setup is good and a high recommend approach.
Some things to check for this failure:
Does any VBA run at all? Check the trusted location settings.
Do you have a timer form running in the background. This is VERY but VERY VERY VERY important to check. What occurs with a timer form is that the form focus actually CHANGES to the the timer form while the timer code runs, and then returns back to whatever form currently has the focus. This is HUGE HUGE HUGE important since when you click on a ribbon button, it will FIRST TRY to run code in the current focused form's code module. This is good and great, since as you note that's were you have (and should) have that code that belongs to that form.
However, IF DURING THE TIME you click on the ribbon and the current forms focus changes (such as timer code, or other forms code running that MIGHT/MAYBE/COULD/PERHAPS/POSSBILE changes the forms focus to another?
Well, the Ribbon will look for and try to find that routine in the form with the CURRENT focus. A timer form running thus will change the form's focus, and if you click that button while the timer form code is running, then THAT FORM at THAT POINT in time actually has the focus, and thus the Ribbon will actually be attempting to find and run the function name in your ribbon in that timer form!!
You can quite quick test the above by removing for a test any timer forms you have running.
The other thing to look for is any kind of accidentally forms focus change. So, say a you recent added a sub form, or another navigation form or what not? Well, now the form you think that has the focus does not in fact have the focus, and thus the ribbon can't find the routine to run. This often happens when you say switch to a nice new computer, are using say the runtime. With a newer machine, often everything is running so much better and faster, that the FOCUS CHANGE you did not see much now occurs in a far more aggressive fashion.
The bottom line and thing you are looking for here is:
Does a un-expected form focus change occur. Say to some sub form that specifies a different ribbon, or some other small message or dialog or popup form that appeared un-expected. With any of these objects getting focus, then your main form with the code that the ribbon buttons call will not be found.
So, this is not a trusted location (ie: other VBA code works just fine), then next to check and test with is if any timer form is running (since as noted such forms VERY quick do get and rob the current forms focus away - and if you hit the ribbon at the same time while the timer code runs, then the focus change has also occurred, and thus the ribbon button will fail.
The above as a result ALSO means:
Don't have a timer form(s) if you can avoid as such.
The timer code if you have has to be VERY good and efficient code - it better get in, run VERY fast, and get out.
Don't have a aggressive timer interval. So, say some timer is to check and shut down access? Well, checking 10 times a second, or even once a second is OVER kill. Have it check say every 3 seconds.
Note that the above timer issue also often causes report print buttons to fail - you find that some blank form gets printed (and that blank form is in fact the timer form that temp had the focus when you hit your report print button.)
No. I assumed I could use the click event as opposed to the AfterUpdate event for a checkbox in a Read Only data source.
To test this I wrote the following
Sub chk_MyCheckbox
msgbox "Hello"
End Sub
And nothing happens. Obviously, this is because the checkbox is bound to a field in a read-only data source. I realize this is because it thinks I actually want to check the box and it can't. So I was hoping to only use the click event and NOT actually edit the checkbox. I do have a hyperlinked field from this same read only source where the click event works. So I was thinking I could use the click event of the checkbox.
I'd like the datasheet to respond as if it were actually allowed to be checked and I will programatically change the value behind the scenes and then refresh the sub form.
One idea I had is to populate a temp table with the data from the read only data source. Is there another way I could approach this?
You can't use the AfterUpdate event, as a read-only or locked checkbox will cannot update via the UI.
However, you can use the OnClick event as long as the checkbox is enabled.
I am developing an MS-Access application with the data sitting in a separate backend database (Also MS-Access). The main form contains a button to open a settings dialog, an exit button (both in the form header) and a navigation control with a number of tabs showing subforms in form details. The settings dialog is a popup. In the dialog the user can change the path to the backend database and all linked tables will be relinked automatically if the user saves settings. This works all very well.
However, after closing the dialog, requerying the active subform has no effect, only if I close and reopen the form (e.g., by clicking a different tab) would it show data from the new (relinked database).
And I think this has somehow nothing to do with my relinking vba code.
I tried to open my subform directly and used the Linked Table Manager, with the same result. Hitting the refresh button on the ribbon does nothing, only when I change to design view and back to form view, the data in the form would be updated.
I also tried different combinations of repaint, refresh and requery, but to no avail.
I use requery extensively in my code and it works well, but it seems not after a simple reconnect of linked tables. The different backend databases have the exact same structure, they are just duplicates with some records removed to have test cases.
What's going on here?
Regards Oliver
Thank you Jeffrey, Your answer pointed me in the right direction. I wasn't aware of the SourceObjectproperty. This is, however, a property of the subform control that holds the actual subform. In my special case of a navigation control, it is the navigation subform control that holds the active subform which is defined on the tab as NavigationTargetName.
So my code looks as below:
' ContentContainer is the NavigationSubform control
srcObjStr = Me.ContentContainer.SourceObject
Me.ContentContainer.SourceObject = ""
UpdatePathes 'Relinking
Me.ContentContainer.SourceObject = srcObjStr
There isn't even the need to requery. Very nice.
Regards Oliver
I think if you use the SourceObject property of the subform instead of binding them you could force an update without closing your forms.
Form_myForm.mySubForm1.SourceObject = ""
Form_myForm.mySubForm2.SourceObject = ""
run your relink routines
Form_myForm.mySubForm1.SourceObject = "mySubFormQuery1"
Form_myForm.mySubForm2.SourceObject = "mySubFormQuery2"
Form_myForm.mySubForm1.requery
Form_myForm.mySubForm2.requery
This is just speculation, I've never had a situation where I needed to relink at runtime, but I think it should work.
I'm using MS-Access 2013 to develop a computer-aided personal interview form. Given the length of the interview it seemed to make the most sense to use multiple pages (tabs) within a form to aid user navigation. The problem with that is that the data error checking (either out of range checking, or missing entry checking) doesn't take place until you try to close the form (which might mean that an error on page 1 won't be "flagged" until (e.g.) page 10. Is there some way to "force" Access to do error checking on the fields on a given page before allowing the user to proceed to the next page?
Hook the Unload event of the form, and call your validation routine there.
If you need to validate between tabs, hook the Tab.Change event.
Use The Before Update Event of each field to validate that field before changes are written. Record The Previous Tab And Next Tab With the On Click Event. Test for missing fields before setting next tab to previous tab as skipped fields will not fire the before update validataion.
As stated, this is an Access 2010 .accdb.
I have a main, navigation form: frmNav.
On frmNav I have a Navigation subform control: NavigationSubForm.
I use docmd browseto to move between tabs on the navigation control.
The first form to load in the NavigationSubForm control is frmInboundShipments.
frmInboundShipments contains a sub form control sfrmListInvoicesByShipment.
sfrmListInvoicesByShipment consists of a filtered datasheet of invoices associated with each shipment in frmInboundShipments.
frmItemInvoices contains the invoices referenced in sfrmListInvoicesByShipment.
The following methods of moving between frmInboundShipments and frmItemInvoices using the browseto command do trigger the UnLoad event on frmInboundShipments:
Click command button on frmInboundShipments to invoke browseto to frmItemInvoices
Click the navigation tab/button on the navigation control on frmNav to invoke browseto frmItemInvoices.
However, when I perform the following action(s), the UnLoad event on frmInboundShipments fails to trigger:
Double click on a field/datasheet row in sfrmListInvoicesByShipment to trigger the browseto command to show/move to frmItemInvoices by either:
a. Immediately executing docmd browseto to show frmItemInvoices
b. Set focus first on the parent form, then execute docmd browseto to show frmItemInvoices
i. e.g.: Me.Parent.sfrmListInvoicesByShipments.SetFocus
DoCmd.BrowseTo acBrowseToForm, "frmItemInvoices", "frmNav.NavigationSubform", ...criteria....
I can't understand for the life of me why the event doesn't trigger when the browseto command is issued from the subform, but is triggered in every other scenario when navigating between the two forms.
I've looked at the Access 2010 order of events and I don't see anything that explicitly states that the Unload event of the main form would not fire when moving to view another form from the subform.
The Deactivate event also does not fire in this circumstance.
Update, 10/18/13: further investigation reveals that the parent form's Close event does fire, but definitely not the deactivate nor unload events. Problem is that the value I want to capture is already gone once the form Close event occurs. Not sure what to do from here...
rudelerius,
I feel your pain. I have had long experience debugging Access Form events, and have come away with several rules:
Form events don't always fire when you expect them to.
Form events do not always fire in a particular order, despite what the documentation may say.
Finally, debugging by putting a Breakpoint in an event procedure can change the order or number of events that get fired.
So what can you do? My recommendations:
Form events are for handling Form-level actions. They are just plumbing, and you should not overload them with Application-level functions
Separate your Application or Business logic from your forms, and especially do not plug Business logic into any Form or Report Event. In other words, the code that supports what your application does should be separate from how it does it.
For debugging, use things like Debug.Print in the event procedure, and avoid Breakpoints.
So, how does this help you? My (necessarily broad) answer is put whatever information you want to preserve in an external module-level variable, or class, before you take your form action. Your main form can then use that information without relying on the state of the child.