Halt code execution until requery is finished.

A

Al Camp

Is there some code, or technique that I can use that will force a subform
requery to finish completely, before subsequent code runs?

Sequence of code (aircode)
First button click = increment a qty by +1
Make a decision about (qty +1)
Requery subform to reflect new qty

Second Button click = increment a qty by +1
**Make a decision about (qty + 1) (**Problem: the subform has not finished
the requery,
so my decision coding returns erroneous values from the requeried subform.)

Tried disabling/enabling the button, but it enables the button before the
Requery is complete.

Any suggestions?

Thanks in advance,
Al Camp
 
M

Marshall Barton

Al said:
Is there some code, or technique that I can use that will force a subform
requery to finish completely, before subsequent code runs?

Sequence of code (aircode)
First button click = increment a qty by +1
Make a decision about (qty +1)
Requery subform to reflect new qty

Second Button click = increment a qty by +1
**Make a decision about (qty + 1) (**Problem: the subform has not finished
the requery,
so my decision coding returns erroneous values from the requeried subform.)

Tried disabling/enabling the button, but it enables the button before the
Requery is complete.


You can do a MoveLast on the subform's RecordsetClone to
force the subform's record source to completely load.
However, that may not force all controls on the subform to
complete their recalculation. So a total using an aggregate
function may still be out of sync.
 
A

Al Camp

Marshall Barton said:
You can do a MoveLast on the subform's RecordsetClone to
force the subform's record source to completely load.
However, that may not force all controls on the subform to
complete their recalculation. So a total using an aggregate
function may still be out of sync.

Marshall,
Thanks for taking a shot at this problem. A real poser. I tried...
Forms!frmParts!frmPartsDetail.Form.RecordSetClone.MoveLast
after the Requery, but it still allows the button to overrun the requery.
I tried the requery before the "decision" code and after it, with the
same results. I think I'll try to find another event to hang that Requery
on... rather than have it in this pseudo loop.
Thanks again,
Al Camp
 
M

Marshall Barton

Al said:
Thanks for taking a shot at this problem. A real poser. I tried...
Forms!frmParts!frmPartsDetail.Form.RecordSetClone.MoveLast
after the Requery, but it still allows the button to overrun the requery.
I tried the requery before the "decision" code and after it, with the
same results. I think I'll try to find another event to hang that Requery
on... rather than have it in this pseudo loop.


What is this qty that you are incrementing? It looks like a
field in the subform's record source, but which record?

I don't know, I'm just guessing, but maybe there is some
detail in your real code that's important??
 
A

Al Camp

Marshall Barton said:
What is this qty that you are incrementing? It looks like a
field in the subform's record source, but which record?

I don't know, I'm just guessing, but maybe there is some
detail in your real code that's important??

I posted this question on 2/7 on .forms, but got responses, so I tried again
here,
but, with much less detail.
Here's more details...
I have two subforms (single form Sub1 and continuous Sub2) on one main
form.
There is a Qty field on Sub1 that uses a Sum value (TotalOnHand) from the
footer of Sub2 to make a certain decision. (actually On Hand Qty)

Sub1 (a Disbursement form) is used in a production enviornment, so I've
set up
the form to be filled out without the need for keyboard entry. Since most
transactions
are small quantities, the form can be completely "mouse" entered 90% of the
time.

I have a [+] button that increments the Disbursement Qty field on Sub1 by
1 each time.
and each time I increment the Qty, I check the TotalOnHand from Sub2 to
determine if I've
met a certain criteria. (OnHand = MinOrderPoint or OnHand = 1 or OnHand
=0). After
this criteria checking, I requery Sub2 to reflect the newly incremented Sub1
Qty.

This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...

Hope that helps Marshall. I've tried everything I can think of...

Thanks,
Al Camp
 
K

Ken Snell \(MVP\)

Not an elegant solution, but have your code disable the button until it's
done, then reenable the button. That will avoid the possibility of
reclicking it before the code is done.

Also, your earlier code said you tried to do a MoveLast on the
RecordsetClone. I think you would need to do that on the Recordset, as the
RecordsetClone may not force the reloading of the recordset? I haven't done
any testing with this, so I may be off-track.

--

Ken Snell
<MS ACCESS MVP>


Al Camp said:
Marshall Barton said:
What is this qty that you are incrementing? It looks like a
field in the subform's record source, but which record?

I don't know, I'm just guessing, but maybe there is some
detail in your real code that's important??

I posted this question on 2/7 on .forms, but got responses, so I tried
again here,
but, with much less detail.
Here's more details...
I have two subforms (single form Sub1 and continuous Sub2) on one main
form.
There is a Qty field on Sub1 that uses a Sum value (TotalOnHand) from
the
footer of Sub2 to make a certain decision. (actually On Hand Qty)

Sub1 (a Disbursement form) is used in a production enviornment, so I've
set up
the form to be filled out without the need for keyboard entry. Since most
transactions
are small quantities, the form can be completely "mouse" entered 90% of
the time.

I have a [+] button that increments the Disbursement Qty field on Sub1
by 1 each time.
and each time I increment the Qty, I check the TotalOnHand from Sub2 to
determine if I've
met a certain criteria. (OnHand = MinOrderPoint or OnHand = 1 or OnHand
=0). After
this criteria checking, I requery Sub2 to reflect the newly incremented
Sub1 Qty.

This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...

Hope that helps Marshall. I've tried everything I can think of...

Thanks,
Al Camp
 
A

Al Camp

Ken,
That was my first thought too...
Tried disabling/enabling the button, but it enables the button before the
Requery is complete.
I just re-tested that again to be sure.
As long as I hit the + button slowly, everything works just fine.

I tried this... regarding the MoveLast...
Forms!frmParts!frmPartsDetail.Requery
Forms!frmParts!frmPartsDetail.Form.Recordset.MoveLast
and...
Forms!frmParts!frmPartsDetail.Requery
Forms!frmParts!frmPartsDetail.Recordset.MoveLast
but get "Object doesn't support this property or method."

Thanks for your help,
Al Camp


Ken Snell (MVP) said:
Not an elegant solution, but have your code disable the button until it's
done, then reenable the button. That will avoid the possibility of
reclicking it before the code is done.

Also, your earlier code said you tried to do a MoveLast on the
RecordsetClone. I think you would need to do that on the Recordset, as the
RecordsetClone may not force the reloading of the recordset? I haven't
done any testing with this, so I may be off-track.

--

Ken Snell
<MS ACCESS MVP>


Al Camp said:
Marshall Barton said:
Al Camp wrote:
Al Camp wrote:

Is there some code, or technique that I can use that will force a
subform
requery to finish completely, before subsequent code runs?

Sequence of code (aircode)
First button click = increment a qty by +1
Make a decision about (qty +1)
Requery subform to reflect new qty

Second Button click = increment a qty by +1
**Make a decision about (qty + 1) (**Problem: the subform has not
finished
the requery,
so my decision coding returns erroneous values from the requeried
subform.)

Tried disabling/enabling the button, but it enables the button before
the
Requery is complete.


You can do a MoveLast on the subform's RecordsetClone to
force the subform's record source to completely load.
However, that may not force all controls on the subform to
complete their recalculation. So a total using an aggregate
function may still be out of sync.


Thanks for taking a shot at this problem. A real poser. I tried...
Forms!frmParts!frmPartsDetail.Form.RecordSetClone.MoveLast
after the Requery, but it still allows the button to overrun the
requery.
I tried the requery before the "decision" code and after it, with
the
same results. I think I'll try to find another event to hang that
Requery
on... rather than have it in this pseudo loop.


What is this qty that you are incrementing? It looks like a
field in the subform's record source, but which record?

I don't know, I'm just guessing, but maybe there is some
detail in your real code that's important??

I posted this question on 2/7 on .forms, but got responses, so I tried
again here,
but, with much less detail.
Here's more details...
I have two subforms (single form Sub1 and continuous Sub2) on one main
form.
There is a Qty field on Sub1 that uses a Sum value (TotalOnHand) from
the
footer of Sub2 to make a certain decision. (actually On Hand Qty)

Sub1 (a Disbursement form) is used in a production enviornment, so I've
set up
the form to be filled out without the need for keyboard entry. Since
most transactions
are small quantities, the form can be completely "mouse" entered 90% of
the time.

I have a [+] button that increments the Disbursement Qty field on Sub1
by 1 each time.
and each time I increment the Qty, I check the TotalOnHand from Sub2 to
determine if I've
met a certain criteria. (OnHand = MinOrderPoint or OnHand = 1 or OnHand
=0). After
this criteria checking, I requery Sub2 to reflect the newly incremented
Sub1 Qty.

This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to
the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...

Hope that helps Marshall. I've tried everything I can think of...

Thanks,
Al Camp
 
M

Marshall Barton

Al said:
What is this qty that you are incrementing? It looks like a
field in the subform's record source, but which record?

I don't know, I'm just guessing, but maybe there is some
detail in your real code that's important??

I posted this question on 2/7 on .forms, but got responses, so I tried again
here,
but, with much less detail.
Here's more details...
I have two subforms (single form Sub1 and continuous Sub2) on one main
form.
There is a Qty field on Sub1 that uses a Sum value (TotalOnHand) from the
footer of Sub2 to make a certain decision. (actually On Hand Qty)

Sub1 (a Disbursement form) is used in a production enviornment, so I've
set up
the form to be filled out without the need for keyboard entry. Since most
transactions
are small quantities, the form can be completely "mouse" entered 90% of the
time.

I have a [+] button that increments the Disbursement Qty field on Sub1 by
1 each time.
and each time I increment the Qty, I check the TotalOnHand from Sub2 to
determine if I've
met a certain criteria. (OnHand = MinOrderPoint or OnHand = 1 or OnHand
=0). After
this criteria checking, I requery Sub2 to reflect the newly incremented Sub1
Qty.

This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...


That's what I was afraid of. Your code is trying to use a
calculated value. Unfortunately, the calculation task runs
at a lower priority than the VBA code execution task so your
code and user interactions can easily get ahead of control
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out. I didn't fully
understand it's convoluted contortions, but he did say it
seemed to work in his situation.

My approach to situations like this has always been to make
sure the tables were up to date with whatever saves needed
to be done and then running a query to calculate the Sum.
 
K

Ken Snell \(MVP\)

Marshall Barton said:
This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...


That's what I was afraid of. Your code is trying to use a
calculated value. Unfortunately, the calculation task runs
at a lower priority than the VBA code execution task so your
code and user interactions can easily get ahead of control
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out. I didn't fully
understand it's convoluted contortions, but he did say it
seemed to work in his situation.


I did figure out a way to have the code "count" the number of
"recalculation" firings that occur when a calculated control in a continuous
forms view is being recalculated via a form requery. I have found that it
doesn't always work if there is just one record in the form, but if there
are three (two seems to work, too) or more, it seems to be pretty reliable.
"Convoluted" is one word for it -- it involves using a global variable and a
public function.

If you're interested in trying it, Al, let me know and I'll post it for you
to try.
 
K

Ken Snell \(MVP\)

This would be the correct syntax to use:
Forms!frmParts!frmPartsDetail.Form.Recordset.MoveLast

I've found that ACCESS may not recognize the existence of objects in a
subform or form while it's being requeried or updated in some way, so your
result is not a surprise.

This is one of the most frustrating issues to handle in ACCESS VBA, I've
found. See other post elsethread for one other possibility, if you're game
to try it.
--

Ken Snell
<MS ACCESS MVP>


Al Camp said:
Ken,
That was my first thought too...
Tried disabling/enabling the button, but it enables the button before the
Requery is complete.
I just re-tested that again to be sure.
As long as I hit the + button slowly, everything works just fine.

I tried this... regarding the MoveLast...
Forms!frmParts!frmPartsDetail.Requery
Forms!frmParts!frmPartsDetail.Form.Recordset.MoveLast
and...
Forms!frmParts!frmPartsDetail.Requery
Forms!frmParts!frmPartsDetail.Recordset.MoveLast
but get "Object doesn't support this property or method."

Thanks for your help,
Al Camp


Ken Snell (MVP) said:
Not an elegant solution, but have your code disable the button until it's
done, then reenable the button. That will avoid the possibility of
reclicking it before the code is done.

Also, your earlier code said you tried to do a MoveLast on the
RecordsetClone. I think you would need to do that on the Recordset, as
the RecordsetClone may not force the reloading of the recordset? I
haven't done any testing with this, so I may be off-track.

--

Ken Snell
<MS ACCESS MVP>


Al Camp said:
Al Camp wrote:
Al Camp wrote:

Is there some code, or technique that I can use that will force a
subform
requery to finish completely, before subsequent code runs?

Sequence of code (aircode)
First button click = increment a qty by +1
Make a decision about (qty +1)
Requery subform to reflect new qty

Second Button click = increment a qty by +1
**Make a decision about (qty + 1) (**Problem: the subform has not
finished
the requery,
so my decision coding returns erroneous values from the requeried
subform.)

Tried disabling/enabling the button, but it enables the button before
the
Requery is complete.


You can do a MoveLast on the subform's RecordsetClone to
force the subform's record source to completely load.
However, that may not force all controls on the subform to
complete their recalculation. So a total using an aggregate
function may still be out of sync.


Thanks for taking a shot at this problem. A real poser. I tried...
Forms!frmParts!frmPartsDetail.Form.RecordSetClone.MoveLast
after the Requery, but it still allows the button to overrun the
requery.
I tried the requery before the "decision" code and after it, with
the
same results. I think I'll try to find another event to hang that
Requery
on... rather than have it in this pseudo loop.


What is this qty that you are incrementing? It looks like a
field in the subform's record source, but which record?

I don't know, I'm just guessing, but maybe there is some
detail in your real code that's important??

--
Marsh
MVP [MS Access]

I posted this question on 2/7 on .forms, but got responses, so I tried
again here,
but, with much less detail.
Here's more details...
I have two subforms (single form Sub1 and continuous Sub2) on one main
form.
There is a Qty field on Sub1 that uses a Sum value (TotalOnHand) from
the
footer of Sub2 to make a certain decision. (actually On Hand Qty)

Sub1 (a Disbursement form) is used in a production enviornment, so
I've set up
the form to be filled out without the need for keyboard entry. Since
most transactions
are small quantities, the form can be completely "mouse" entered 90% of
the time.

I have a [+] button that increments the Disbursement Qty field on Sub1
by 1 each time.
and each time I increment the Qty, I check the TotalOnHand from Sub2 to
determine if I've
met a certain criteria. (OnHand = MinOrderPoint or OnHand = 1 or OnHand
=0). After
this criteria checking, I requery Sub2 to reflect the newly incremented
Sub1 Qty.

This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to
the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...

Hope that helps Marshall. I've tried everything I can think of...

Thanks,
Al Camp
 
A

Al Camp

I posted this question on 2/7 on .forms, but got responses, so I tried
again
here,
but, with much less detail.
Here's more details...
I have two subforms (single form Sub1 and continuous Sub2) on one main
form.
There is a Qty field on Sub1 that uses a Sum value (TotalOnHand) from
the
footer of Sub2 to make a certain decision. (actually On Hand Qty)

Sub1 (a Disbursement form) is used in a production enviornment, so I've
set up
the form to be filled out without the need for keyboard entry. Since most
transactions
are small quantities, the form can be completely "mouse" entered 90% of
the
time.

I have a [+] button that increments the Disbursement Qty field on Sub1
by
1 each time.
and each time I increment the Qty, I check the TotalOnHand from Sub2 to
determine if I've
met a certain criteria. (OnHand = MinOrderPoint or OnHand = 1 or OnHand
=0). After
this criteria checking, I requery Sub2 to reflect the newly incremented
Sub1
Qty.

This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...


That's what I was afraid of. Your code is trying to use a
calculated value. Unfortunately, the calculation task runs
at a lower priority than the VBA code execution task so your
code and user interactions can easily get ahead of control
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out. I didn't fully
understand it's convoluted contortions, but he did say it
seemed to work in his situation.

My approach to situations like this has always been to make
sure the tables were up to date with whatever saves needed
to be done and then running a query to calculate the Sum.

--
Marsh
MVP [MS Access] ---------------------------------------------------------------------
Marsh,
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out.
Are you refering Ken's to the disable/enable the button suggestion? That
was one of the first things I tried, but it doesn't seem to work, for the
same reason... overrun.
sure the tables were up to date with whatever saves needed
to be done and then running a query to calculate the Sum.
Yes, but no matter how I make my criteria decision about the OnHandQty (via
query, calculation, or etc...), I still need to requery the Sub2 "detail"
form to keep it in synch with the entries in Sub1.
Sub1 is really a single record from the same table as the basis for Sub2.
Sub2 shows Details of all present and past Disbursements against a partno.

Are you saying that a "totals" query would execute completely before
allowing further code to run, whereas the calculated value in Sub2 doesn't?
I'll give that a go... I am determined to find some way around this... ugly
or not!

(I may even try a "timer delay" today along with the disable/enable scheme.)

I know this is a toughie to solve... particularly "remotely" via email.
Thanks for hanging in on this. All help appreciated...
Al Camp
 
A

Al Camp

Ken Snell (MVP) said:
Marshall Barton said:
This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to
the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...


That's what I was afraid of. Your code is trying to use a
calculated value. Unfortunately, the calculation task runs
at a lower priority than the VBA code execution task so your
code and user interactions can easily get ahead of control
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out. I didn't fully
understand it's convoluted contortions, but he did say it
seemed to work in his situation.


I did figure out a way to have the code "count" the number of
"recalculation" firings that occur when a calculated control in a
continuous forms view is being recalculated via a form requery. I have
found that it doesn't always work if there is just one record in the form,
but if there are three (two seems to work, too) or more, it seems to be
pretty reliable. "Convoluted" is one word for it -- it involves using a
global variable and a public function.

If you're interested in trying it, Al, let me know and I'll post it for
you to try.
---------------------------------------
Please do Ken... I'm willing to give anything a go.
As I told Marsh, I may try a "timer" delay (.3 secs or so with our
disable/enable scheme) this morning. Seems to me the timer must run to
completion before the button will be re-enabled.
Al Camp
 
K

Ken Snell \(MVP\)

OK - you asked < g >. I've also included the "rationale" (more of empirical
observations!) for how/why this works (when it does work) so that it might
provide more background for the idea. Note: I have found that this method
can "hang" (endlessly loop) if there is just one record in the recordset,
but sometimes it will work there too. However, you may need to trap for this
and test yourself in your own situation. Let me know if you have questions!
-----

I think I've come up with a way to be reasonably foolproof for how to detect
when a calculated textbox has a value that can be read after you requery the
form. Unfortunately, I didn't find an event that I can use....but, I did
find a test that seems to work with 150 records in the recordsets. Why, you
ask, is this "number of records" important? Well, stay tuned for my
explanation..... and, sorry, it's a long explanation to get to the final
method, but it'll help everyone see why this seems to work.

--------

My brainstorm was the idea of combining a global variable with a function
that is called from the calculated control. Stay with me, as this may get a
bit confusing to explain.

My thought was to wrap the calculation in the textbox's control source with
a simple function that would use the calculation as an argument, so that the
calculation would have to be done before the function could be called. For
example, let's say that my calculation expression in the textbox were this:
=Sum([Field1]*[Field2])

My "new" expression would be this:
=IsTheCalculationDone(Sum([Field1]*[Field2]))

The IsTheCalculationDone function is very simple:

Public Function IsTheCalculationDone(varValue As Variant) As Variant
IsTheCalculationDone = varValue
End Function


Simple, eh? And then I figured that I would set the value of a global
variable in this function to tell me when the function had run:

Public gblnCalcIsDone As Boolean
Public Function IsTheCalculationDone(varValue As Variant) As Variant
IsTheCalculationDone = varValue
gblnCalcIsDone = True
End Function

And my code that forced the requery then would be this:

gblnCalcIsDone = False
Forms("Name").Controls("Subform").Requery
Do
DoEvents
Loop While gblnCalcIsDone = False

Aha, said I. This is too easy! And it was... because it didn't work quite as
planned. For you see, when a form is "queried" and there is a calculation
expression in a control source, it turns out that the calculation is
actually "done" *for each record in the form's recordset*. And in fact, the
calculation is performed with Null records, then once for each record in the
recordset. Therefore, if the form's recordset contains 5 records, the
calculation is actually called 6 times.

However, the calculated control actually doesn't need to go through all
these calculations in order to get its value. In my testing, the value
appears in the textbox after the calculation is done for the first record
(the second calculation) if the recordset is small, or after the calculation
is done for the second record (the third calculation). With very large
recordsets, it might take more than that, but I found that the third
calculation call usually was enough time to then be able to read the
textbox.

I'm sure you're all seeing what the problem is. The first call, when there
are "null" records in the calculation, to the function IsTheCalculationDone
sets the global variable to True, and my code that reads the variable
merrily goes on its way, thinking that all is well, when in fact the
situation that I was trying to avoid is still present. Namely, there is no
valid value in the textbox yet to be read.

So, I decided to use the multiple calculations as a way to count the calls,
essentially using a numeric variable as the global variable, and then to
test for the value of that global variable to see if it had exceeded a
minimum value (because of my testing above, I settled on the minimum between
2 and the number of records in the form's recordset).

So, I changed my public variable and function to this:

Public glngCalcIsDone As Long
Public Function IsTheCalculationDone(varValue As _
Variant) As Variant
IsTheCalculationDone = varValue
glngCalcIsDone = glngCalcIsDone + 1
End Function

And I changed my code that forced the requery to this:

glngCalcIsDone = 0
Forms("Name").Controls("Subform").Requery
Do
DoEvents
Loop While glngCalcIsDone <= _
VariantMin(2, Forms("Name").Controls("Subform"). _
Form.RecordsetClone.RecordCount)

VariantMin is a function that I use to return the minimum of two values
passed as the arguments:

Public Function VariantMin(ByVal var1 As Variant, _
ByVal var2 As Variant) As Variant
On Error Resume Next
VariantMin = var1
If var1 > var2 Then VariantMin = var2
Err.Clear
End Function

Voila! I seem to have completely eliminated the problem of trying to decide
when the calculated textbox contains a valid value that can be read and
used.

P.S. One additional observation I noted while doing this experimentation.

If you have a main form in continuous forms view, the calculation is not run
for every record that is in the recordset. ACCESS doesn't "fill" the
recordset completely unless you force the form to display all the records.
So, although the calculation in the calculated textbox is correct, the
calculation is called only for the records that are displayed on the form
(what the user can see).

If you scroll down the form, as each "additional" record comes into view,
the calculation is again called -- and this is where it got tricky to figure
out what is happening.

It seems that, when you scroll down by clicking in the "grey" space below
the scroll bar (causing the form to jump down *one page*, the calculation is
triggered again from the "null" records and then is done again for each
record from the first to the last one you can then see on the form. Thus, if
you have 23 records in the recordset, and if you can see 16 records at a
time on the screen, the initial screen "load" will cause the calculation to
run 17 times. If you then "page down", and expose the remaining 7 records of
the entire 23 records, the total number of times that the calculation is run
is up to 41 times (1 for null, 16 for initial records visible, 1 for null
again, and 23 for all records). (It's even more complicated to predict the
number of calculations if you would have to do multiple "page downs" to
scroll through the entire recordset, but it grows quickly.)

However, if you scroll down by clicking the down arrow for the scroll bar
(causing the form to jump down *one record*, the calculation is triggered
again only for the newly "visible" record. So, using the above example, this
method of scrolling would cause the calculation to be run 24 times total.

This is why I decided to use a "minimum" number of calculations to decide
that the calculated textbox's value was correct. I had tried to test for
when the count equaled the number of records plus 1 and quickly ran into
"unending DoEvent loops".


--------------------------------------

Thus, here are the steps to take when you want to implement this capability:
----------
1. Declare a global Long variable in a regular module (e.g.,
glngCalcIsDone).

2. Create a public function in a regular module:

Public Function IsTheCalculationDone(varValue As _
Variant) As Variant
IsTheCalculationDone = varValue
glngCalcIsDone = glngCalcIsDone + 1
End Function

3. For the control source of a calculated textbox that you'll want
to read, wrap the calculation with this public function:

=IsTheCalculationDone(...)

4. In the Open event of the form that contains this calculated textbox,
put this code step in order to "reset" the global variable (note
that the public function is called when the form loads, so this is
to
avoid the (possibly unlikely) scenario of the upper numeric limit
of the Long variable being exceeded during normal operations):

glngCalcIsDone = 0

5. When you do a requery and need to read the value from the
calculated textbox, use these code steps (where VariantMin
is a function similar to the one I posted above, and
MaxCalcsNeeded is a number that you would supply as the
minimum number of calculations to have done before you
read the value -- in my case, I was using 2):

Dim lngCountComparison As Long
glngCalcIsDone = 0
Forms("Name").Controls("Subform").Requery
DoEvents
lngCountComparison = VariantMin(MaxCalcsNeeded,
Forms("Name").Form. _
RecordsetClone.RecordCount)
Do
DoEvents
Loop While glngCalcIsDone <= lngCountComparison

----------

--

Ken Snell
<MS ACCESS MVP>



Al Camp said:
Ken Snell (MVP) said:
Marshall Barton said:
This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to
the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...


That's what I was afraid of. Your code is trying to use a
calculated value. Unfortunately, the calculation task runs
at a lower priority than the VBA code execution task so your
code and user interactions can easily get ahead of control
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out. I didn't fully
understand it's convoluted contortions, but he did say it
seemed to work in his situation.


I did figure out a way to have the code "count" the number of
"recalculation" firings that occur when a calculated control in a
continuous forms view is being recalculated via a form requery. I have
found that it doesn't always work if there is just one record in the
form, but if there are three (two seems to work, too) or more, it seems
to be pretty reliable. "Convoluted" is one word for it -- it involves
using a global variable and a public function.

If you're interested in trying it, Al, let me know and I'll post it for
you to try.
---------------------------------------
Please do Ken... I'm willing to give anything a go.
As I told Marsh, I may try a "timer" delay (.3 secs or so with our
disable/enable scheme) this morning. Seems to me the timer must run to
completion before the button will be re-enabled.
Al Camp
 
A

Al Camp

Ken,
Well, I'm a bit "awed" by your solution. I think I see what your doing,
but it will take a while for me to "cogitate" on exactly what the code is
doing.
It may take me a bit to try to implement this, but I will definitely give
it a try.
And... I will reply, yea or nay.

I take it you've had to deal with this issue yourself. I was beginning
to question my design, when I butted up against this problem, but evidently
it's an issue that does arise.

I take it you mean by "regular" module... a module external to the form
modules?

Also, to be clear, the value I'm examining is a Sum calculation in the
footer of "detail" Sub2, not in each of the individual "detail" records. Is
that OK?

Ken, it was very kind of you to to take the time to "finger-bone" in all
that information for me.
Thanks very much!
Al Camp

Ken Snell (MVP) said:
OK - you asked < g >. I've also included the "rationale" (more of
empirical observations!) for how/why this works (when it does work) so
that it might provide more background for the idea. Note: I have found
that this method can "hang" (endlessly loop) if there is just one record
in the recordset, but sometimes it will work there too. However, you may
need to trap for this and test yourself in your own situation. Let me know
if you have questions!
-----

I think I've come up with a way to be reasonably foolproof for how to
detect when a calculated textbox has a value that can be read after you
requery the form. Unfortunately, I didn't find an event that I can
use....but, I did find a test that seems to work with 150 records in the
recordsets. Why, you ask, is this "number of records" important? Well,
stay tuned for my explanation..... and, sorry, it's a long explanation
to get to the final method, but it'll help everyone see why this seems to
work.

--------

My brainstorm was the idea of combining a global variable with a function
that is called from the calculated control. Stay with me, as this may get
a bit confusing to explain.

My thought was to wrap the calculation in the textbox's control source
with a simple function that would use the calculation as an argument, so
that the calculation would have to be done before the function could be
called. For example, let's say that my calculation expression in the
textbox were this:
=Sum([Field1]*[Field2])

My "new" expression would be this:
=IsTheCalculationDone(Sum([Field1]*[Field2]))

The IsTheCalculationDone function is very simple:

Public Function IsTheCalculationDone(varValue As Variant) As Variant
IsTheCalculationDone = varValue
End Function


Simple, eh? And then I figured that I would set the value of a global
variable in this function to tell me when the function had run:

Public gblnCalcIsDone As Boolean
Public Function IsTheCalculationDone(varValue As Variant) As Variant
IsTheCalculationDone = varValue
gblnCalcIsDone = True
End Function

And my code that forced the requery then would be this:

gblnCalcIsDone = False
Forms("Name").Controls("Subform").Requery
Do
DoEvents
Loop While gblnCalcIsDone = False

Aha, said I. This is too easy! And it was... because it didn't work quite
as planned. For you see, when a form is "queried" and there is a
calculation expression in a control source, it turns out that the
calculation is actually "done" *for each record in the form's recordset*.
And in fact, the calculation is performed with Null records, then once for
each record in the recordset. Therefore, if the form's recordset contains
5 records, the calculation is actually called 6 times.

However, the calculated control actually doesn't need to go through all
these calculations in order to get its value. In my testing, the value
appears in the textbox after the calculation is done for the first record
(the second calculation) if the recordset is small, or after the
calculation is done for the second record (the third calculation). With
very large recordsets, it might take more than that, but I found that the
third calculation call usually was enough time to then be able to read the
textbox.

I'm sure you're all seeing what the problem is. The first call, when there
are "null" records in the calculation, to the function
IsTheCalculationDone sets the global variable to True, and my code that
reads the variable merrily goes on its way, thinking that all is well,
when in fact the situation that I was trying to avoid is still present.
Namely, there is no valid value in the textbox yet to be read.

So, I decided to use the multiple calculations as a way to count the
calls, essentially using a numeric variable as the global variable, and
then to test for the value of that global variable to see if it had
exceeded a minimum value (because of my testing above, I settled on the
minimum between 2 and the number of records in the form's recordset).

So, I changed my public variable and function to this:

Public glngCalcIsDone As Long
Public Function IsTheCalculationDone(varValue As _
Variant) As Variant
IsTheCalculationDone = varValue
glngCalcIsDone = glngCalcIsDone + 1
End Function

And I changed my code that forced the requery to this:

glngCalcIsDone = 0
Forms("Name").Controls("Subform").Requery
Do
DoEvents
Loop While glngCalcIsDone <= _
VariantMin(2, Forms("Name").Controls("Subform"). _
Form.RecordsetClone.RecordCount)

VariantMin is a function that I use to return the minimum of two values
passed as the arguments:

Public Function VariantMin(ByVal var1 As Variant, _
ByVal var2 As Variant) As Variant
On Error Resume Next
VariantMin = var1
If var1 > var2 Then VariantMin = var2
Err.Clear
End Function

Voila! I seem to have completely eliminated the problem of trying to
decide when the calculated textbox contains a valid value that can be read
and used.

P.S. One additional observation I noted while doing this experimentation.

If you have a main form in continuous forms view, the calculation is not
run for every record that is in the recordset. ACCESS doesn't "fill" the
recordset completely unless you force the form to display all the records.
So, although the calculation in the calculated textbox is correct, the
calculation is called only for the records that are displayed on the form
(what the user can see).

If you scroll down the form, as each "additional" record comes into view,
the calculation is again called -- and this is where it got tricky to
figure out what is happening.

It seems that, when you scroll down by clicking in the "grey" space below
the scroll bar (causing the form to jump down *one page*, the calculation
is triggered again from the "null" records and then is done again for each
record from the first to the last one you can then see on the form. Thus,
if you have 23 records in the recordset, and if you can see 16 records at
a time on the screen, the initial screen "load" will cause the calculation
to run 17 times. If you then "page down", and expose the remaining 7
records of the entire 23 records, the total number of times that the
calculation is run is up to 41 times (1 for null, 16 for initial records
visible, 1 for null again, and 23 for all records). (It's even more
complicated to predict the number of calculations if you would have to do
multiple "page downs" to scroll through the entire recordset, but it grows
quickly.)

However, if you scroll down by clicking the down arrow for the scroll bar
(causing the form to jump down *one record*, the calculation is triggered
again only for the newly "visible" record. So, using the above example,
this method of scrolling would cause the calculation to be run 24 times
total.

This is why I decided to use a "minimum" number of calculations to decide
that the calculated textbox's value was correct. I had tried to test for
when the count equaled the number of records plus 1 and quickly ran into
"unending DoEvent loops".


--------------------------------------

Thus, here are the steps to take when you want to implement this
capability:
----------
1. Declare a global Long variable in a regular module (e.g.,
glngCalcIsDone).

2. Create a public function in a regular module:

Public Function IsTheCalculationDone(varValue As _
Variant) As Variant
IsTheCalculationDone = varValue
glngCalcIsDone = glngCalcIsDone + 1
End Function

3. For the control source of a calculated textbox that you'll want
to read, wrap the calculation with this public function:

=IsTheCalculationDone(...)

4. In the Open event of the form that contains this calculated textbox,
put this code step in order to "reset" the global variable (note
that the public function is called when the form loads, so this is
to
avoid the (possibly unlikely) scenario of the upper numeric limit
of the Long variable being exceeded during normal operations):

glngCalcIsDone = 0

5. When you do a requery and need to read the value from the
calculated textbox, use these code steps (where VariantMin
is a function similar to the one I posted above, and
MaxCalcsNeeded is a number that you would supply as the
minimum number of calculations to have done before you
read the value -- in my case, I was using 2):

Dim lngCountComparison As Long
glngCalcIsDone = 0
Forms("Name").Controls("Subform").Requery
DoEvents
lngCountComparison = VariantMin(MaxCalcsNeeded,
Forms("Name").Form. _
RecordsetClone.RecordCount)
Do
DoEvents
Loop While glngCalcIsDone <= lngCountComparison

----------

--

Ken Snell
<MS ACCESS MVP>



Al Camp said:
Ken Snell (MVP) said:
This works fine, but, if I re-click the [+] button too quickly, I
can
outpace the Sub2 Requery, and receive erroneous messages pertaining to
the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...


That's what I was afraid of. Your code is trying to use a
calculated value. Unfortunately, the calculation task runs
at a lower priority than the VBA code execution task so your
code and user interactions can easily get ahead of control
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out. I didn't fully
understand it's convoluted contortions, but he did say it
seemed to work in his situation.


I did figure out a way to have the code "count" the number of
"recalculation" firings that occur when a calculated control in a
continuous forms view is being recalculated via a form requery. I have
found that it doesn't always work if there is just one record in the
form, but if there are three (two seems to work, too) or more, it seems
to be pretty reliable. "Convoluted" is one word for it -- it involves
using a global variable and a public function.

If you're interested in trying it, Al, let me know and I'll post it for
you to try.
---------------------------------------
Please do Ken... I'm willing to give anything a go.
As I told Marsh, I may try a "timer" delay (.3 secs or so with our
disable/enable scheme) this morning. Seems to me the timer must run to
completion before the button will be re-enabled.
Al Camp
 
M

Marshall Barton

Al said:
Are you refering Ken's to the disable/enable the button suggestion? That
was one of the first things I tried, but it doesn't seem to work, for the
same reason... overrun.

No, see his later post.

Yes, but no matter how I make my criteria decision about the OnHandQty (via
query, calculation, or etc...), I still need to requery the Sub2 "detail"
form to keep it in synch with the entries in Sub1.
Sub1 is really a single record from the same table as the basis for Sub2.
Sub2 shows Details of all present and past Disbursements against a partno.

Are you saying that a "totals" query would execute completely before
allowing further code to run, whereas the calculated value in Sub2 doesn't?
I'll give that a go... I am determined to find some way around this... ugly
or not!

Yes, That's pretty much what I'm saying. If you open a
recordset on a totals query, it will use the latest saved
values, regardless of the state of the calculations in the
form. The fundamental premis is that if you need a value in
a vba procedure, then you need to use VBA code to calculate
the value, i.e. eliminate all cross dependencies between
Access's various tasks.

(I may even try a "timer delay" today along with the disable/enable scheme.)

I know this is a toughie to solve... particularly "remotely" via email.
Thanks for hanging in on this. All help appreciated...

Don't go there. Read Ken's latest post for all his
observations on getting the cross task approach to
synchronize
 
A

Al Camp

Marsh,
I'm still experimenting, and Ken's given me something to try.
That implementation will keep me busy for a while...
Thanks,
Al Camp

Marshall Barton said:
Al said:
Al Camp wrote:
Al Camp wrote:

Is there some code, or technique that I can use that will force a
subform
requery to finish completely, before subsequent code runs?

Sequence of code (aircode)
First button click = increment a qty by +1
Make a decision about (qty +1)
Requery subform to reflect new qty

Second Button click = increment a qty by +1
**Make a decision about (qty + 1) (**Problem: the subform has not
finished
the requery,
so my decision coding returns erroneous values from the requeried
subform.)

Tried disabling/enabling the button, but it enables the button before
the
Requery is complete.


You can do a MoveLast on the subform's RecordsetClone to
force the subform's record source to completely load.
However, that may not force all controls on the subform to
complete their recalculation. So a total using an aggregate
function may still be out of sync.


Thanks for taking a shot at this problem. A real poser. I tried...
Forms!frmParts!frmPartsDetail.Form.RecordSetClone.MoveLast
after the Requery, but it still allows the button to overrun the
requery.
I tried the requery before the "decision" code and after it, with
the
same results. I think I'll try to find another event to hang that
Requery
on... rather than have it in this pseudo loop.


What is this qty that you are incrementing? It looks like a
field in the subform's record source, but which record?

I don't know, I'm just guessing, but maybe there is some
detail in your real code that's important??

I posted this question on 2/7 on .forms, but got responses, so I tried
again
here,
but, with much less detail.
Here's more details...
I have two subforms (single form Sub1 and continuous Sub2) on one main
form.
There is a Qty field on Sub1 that uses a Sum value (TotalOnHand) from
the
footer of Sub2 to make a certain decision. (actually On Hand Qty)

Sub1 (a Disbursement form) is used in a production enviornment, so I've
set up
the form to be filled out without the need for keyboard entry. Since most
transactions
are small quantities, the form can be completely "mouse" entered 90% of
the
time.

I have a [+] button that increments the Disbursement Qty field on Sub1
by
1 each time.
and each time I increment the Qty, I check the TotalOnHand from Sub2 to
determine if I've
met a certain criteria. (OnHand = MinOrderPoint or OnHand = 1 or OnHand
=0). After
this criteria checking, I requery Sub2 to reflect the newly incremented
Sub1
Qty.

This works fine, but, if I re-click the [+] button too quickly, I can
outpace the Sub2 Requery, and receive erroneous messages pertaining to the
TotalOnHand "criteria" logic. (thinks TotalOnHand = 0)

Of course, normal manual data entry for Qty works fine...


That's what I was afraid of. Your code is trying to use a
calculated value. Unfortunately, the calculation task runs
at a lower priority than the VBA code execution task so your
code and user interactions can easily get ahead of control
calculations. I used to say that this is impossible to
synchronize, but Ken figured something out. I didn't fully
understand it's convoluted contortions, but he did say it
seemed to work in his situation.

My approach to situations like this has always been to make
sure the tables were up to date with whatever saves needed
to be done and then running a query to calculate the Sum.
 
K

Ken Snell \(MVP\)

When I worked out this "solution", I was doing it for a textbox in the
subform's Footer section, just as you are trying to use.

Yes, a "regular" module is one that is seen in the Modules section of the
database window, one that is external to any other objects.

I hope that this may work for you. The situation that you face is a common
one, and my "solution" has not been useful in all situations. In those
situations, I have had to resort to the code recalculating the result
directly from the tables. I hope you won't need to do that! < g >

--

Ken Snell
<MS ACCESS MVP>



Al Camp said:
Ken,
Well, I'm a bit "awed" by your solution. I think I see what your doing,
but it will take a while for me to "cogitate" on exactly what the code is
doing.
It may take me a bit to try to implement this, but I will definitely
give it a try.
And... I will reply, yea or nay.

I take it you've had to deal with this issue yourself. I was beginning
to question my design, when I butted up against this problem, but
evidently it's an issue that does arise.

I take it you mean by "regular" module... a module external to the form
modules?

Also, to be clear, the value I'm examining is a Sum calculation in the
footer of "detail" Sub2, not in each of the individual "detail" records.
Is that OK?

Ken, it was very kind of you to to take the time to "finger-bone" in all
that information for me.
Thanks very much!
Al Camp

< snipped >
 
K

Ken Snell \(MVP\)

..
Yes, That's pretty much what I'm saying. If you open a
recordset on a totals query, it will use the latest saved
values, regardless of the state of the calculations in the
form. The fundamental premis is that if you need a value in
a vba procedure, then you need to use VBA code to calculate
the value, i.e. eliminate all cross dependencies between
Access's various tasks.


I also have had to use this approach that Marsh mentions -- my other
solution is not always 100% successful in all situations.

:-(
 
A

Al Camp

Ken and Marshall,
Well, I decided to stop trying to "fight" Access over this one.

After + incrementing the DisburseQty, I refresh the record, and then do a
DSum against the table. I use that value to make my ONHand decision, and
post messages as needed.
One nice thing about this solution is that I can click as fast as I want,
and no overrun... just a bit of time lag to the Sub2 OnHand calculation.
Also, if I hit a criteria value while clicking, the message posts, and
it stops right there. In other words, any clicks past my warning points are
"cleared", which is goodness. Say I have 8 on hand and the MinOrderPoint is
set at 5... I can click 5 times quickly, get the warning on the 3rd click,
and upon responding to the message box with OK, the two other clicks are
cleared... Qty, doesn't reduce by 2 more afterwards.

Marshall, I think this is what you were getting at... not sure you meant
a DSum per se, but that's what I'm going with for now. Response time is
very good... even with 16,000 records right now. Also, this is not a high
volume entry form. Usually one user entering several transactions from time
to time.

Ken, you wrote...
I have had to resort to the code recalculating the result directly from the
tables. *I hope you won't need to do that! *
Is my solution what you were warning me about? I think that's what I've
done... but it appears to be work just fine.

"Any old port in a storm.", as they say...

Thank you both for your assistance,
Al Camp
Candia Computer Consulting - Candia NH
http://home.comcast.net/~cccsolutions
 
K

Ken Snell \(MVP\)

Your solution is a typical result for that situation. I wasn't warning you
from using it; just hoping that you would be able to avoid the duplicative
code..... glad that you have a solution! As I noted elsethread, I have used
the duplicative calculation approach many times as well.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top