5

I have a form in MS Access 2002 with the following snippet of code behind a command button.

'Requery subform to make sure total is calculated
Me.fsubUpdateShipments.Requery
DoEvents

'Confirm quantities have been entered
If Form_fsubUpdateShipments.txtTotalShipmentQty.Value <= 0 Then
    MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
    Exit Sub
End If

For months this has been working fine but today a colleague came to me and explained that the error message was showing even when they had entered quantities.

After doing a bit of digging I determined that the .Value <= 0 expression was being evaluated before the textbox in question had finished calculating its value: =Sum([QtyToShip]). This seems to only occur when the subform has a non-trivial number of records (around 10 or more) and is obviously a serious problem.

Adding a breakpoint on the line containing the If statement allows the code to run correctly but I obviously cannot use this method permanently.

Is there some way I can force the code to pause until the subform controls have finished recalculating their values?

Erik A
  • 31,639
  • 12
  • 42
  • 67
Aiken
  • 2,628
  • 2
  • 18
  • 25
  • 2
    I would say that [WAITING](http://stackoverflow.com/questions/1544526/how-to-pause-for-specific-amount-of-time) is the only *solution* here. –  Oct 13 '14 at 08:08
  • or if you decide to change your current solution to use an [`ADO.Recordset` then calling a `.Requery`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms680936(v=vs.85).aspx) on a recordset fires up [RecordsetChangeComplete](http://msdn.microsoft.com/en-us/library/windows/desktop/ms680919(v=vs.85).aspx) event which would allow you to proceed the IF statement as soon as the `RecorodsetChangeCompleted` fired. –  Oct 13 '14 at 08:12
  • @vba4all Correct me if I'm wrong but doesn't Application.Wait work only in Excel? – Aiken Oct 13 '14 at 08:15
  • have a look [here](http://www.fmsinc.com/microsoftaccess/modules/examples/AvoidDoEvents.asp) there is an access one or you can add references to Excel Object Model and use `application.wait` if you really wanted to –  Oct 13 '14 at 08:23
  • And what happens if you remove `Me.fsubUpdateShipments.Requery`? – Smandoli Oct 16 '14 at 21:25
  • Final note: Whatever the final choice, this is probably a good place to briefly set `Application.Echo False` – Smandoli Oct 17 '14 at 13:07
  • 1
    @Smandoli I still have the same issue if I remove the `.Requery`. I don't know why past me put it there and I can probably live without it, but I'm still running into the `If` statement being evaluated too early if the user clicks the command button on the main form while focus was on the subform. Seems like pteranodons's answer is the best I'm going to get. – Aiken Oct 17 '14 at 13:15
  • Open bounty and 2+ days remaining! WHO KNOWS WHAT WILL HAPPEN?! – Smandoli Oct 17 '14 at 14:00

2 Answers2

4

I would skip using the user interface altogether and look at the tables. If this is run on a main form command button, the subform has lost focus and saved its values. A requery is unnecessary. Just re-create the Parent-Child relationship in the criterion (third parameter) of a DSum:

If DSum("QtyToShip", "ShipmentDetails", "ShipmentID = " & Me!ShipmentID) <= 0 Then
    MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
    Exit Sub
End If
pteranodon
  • 2,037
  • 1
  • 13
  • 20
  • +1 for pointing out the subform data should be resolved. (Though why it seems otherwise is an important question.) This solution should work fine. As for 'poor performance', this doesn't seem a situation where the difference could be noticed. – Smandoli Oct 16 '14 at 21:17
  • I suggest you put the result of `DSum()` in an integer variable, then evaluate on that: `If intTotalQty < 1...` Use of a variable could reveal a flaw and perhaps even resolve a timing issue. It's a habit that will serve you well in any case. – Smandoli Oct 16 '14 at 21:36
1

There are two options, both already identified by others, but as their suggestions are either incomplete or untidy (slow), I'm making this post.

A. You could put in an enforced wait to delay the IF test:

Put this in a module:

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
...
Public Sub GoSleep(millisecondDelay as long)
    Sleep (millisecondDelay)
End Sub

And in your form:

'Requery subform to make sure total is calculated
Me.fsubUpdateShipments.Requery
DoEvents

GoSleep 1000 '1 second delay. Or 2000, whatever is required really

'Confirm quantities have been entered
If Form_fsubUpdateShipments.txtTotalShipmentQty.Value <= 0 Then
   MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
   Exit Sub
End If

B. As another answer says, you can recalculate it yourself, and test on the basis of this result.

However rather than using DSum as pteranodon suggests, I would suggest something faster than DSum is appropriate as DSum (DCount etc) is very slow.

I typically use ADO for data operations, though my code can be adapted for DAO readily enough. To use this code you'll need a reference to Microsoft activeX 2.8 if you don't have one already:

In a module:

Public Function GetDBValue(qry as string) as variant
    dim rst as new adodb.recordset
    rst.open qry, currentproject.connection, adOpenKeyset, adLockReadOnly
    if rst.eof then
        GetValue = null
    else
        GetValue = rst.fields(0)
    end if
end function

public Function IsNullSQL(basevalue as variant, replacementvalue as variant) as variant
    isNullSQL = iif(isnull(basevalue), replacementvalue, basevalue)
end function

in your form:

''Requery subform to make sure total is calculated
'Me.fsubUpdateShipments.Requery
'DoEvents

'Confirm quantities have been entered
If IsNullSQL(GetValue("SELECT Sum(QtyToShip) FROM tbl WHERE ..."), -1) < 0 Then
    MsgBox "Cannot create shipment, no quantities have been entered.", vbCritical, "No Quantities to Ship"
    Exit Sub
End If

'If Form_fsubUpdateShipments.txtTotalShipmentQty.Value <= 0 Then
'    MsgBox "Cannot create shipment, no quantities have been entered", vbCritical
'    Exit Sub
'End If
Smandoli
  • 6,919
  • 3
  • 49
  • 83
John Bingham
  • 1,996
  • 1
  • 12
  • 15
  • Using a sleep function could only be a last resort. Better to solve the problem (which I suspect is solvable). The second answer is maybe better supplied as a separate answer-post. I applaud the thorough coding, but (a) the `DSum()` is simpler and really no hazards, and (b) I can't support pressing someone toward `ADO` just for this. – Smandoli Oct 16 '14 at 21:22
  • I accept what you say about ADO - but it's just what I use. But DSum I can't agree. I can never remember the syntax of it, and where any significant amount of data is involved, it really can impact on performance. – John Bingham Oct 16 '14 at 22:35
  • I have to agree with @Smandoli here, I apologise if I wasn't clear in my question but the data set is _usually_ quite small, 3-4 records or so. Forcing an arbitrary wait time seems like it would cause more delay than necessary and with each user having different PC specs could be difficult to calculate in the first place. DSum might be a bit more of a performance hit, but it's definitely safer and more concise. – Aiken Oct 17 '14 at 07:53