Setting up an event handler for the OFF-END query event
Batching is supported by the OFF-END event on the query for the ttOrder temp-table. Whenever the client reaches the end of that query's rows, as the user scrolls down through the Order browse for example, the event allows your code to check whether there are more rows to retrieve.
To set up this event, add this SET-CALLBACK-PROCEDURE method to the LEAVE trigger for the SalesRep field, after getting the initial ProDataSet back from the support procedure:
BtnSave:SENSITIVE = FALSE.
/* Set up an OFF-END event handler for the Order buffer to do batching. */ QUERY OrderBrowse:SET-CALLBACK-PROCEDURE("OFF-END","OffEndOrder", THIS-PROCEDURE). OPEN QUERY OrderBrowse FOR EACH ttOrder.
Code the OffEndOrder internal procedure itself, also in PickOrderBatch.w. Like all event handlers, it receives the ProDataSet as an INPUT parameter by-reference, as shown:
/*-------------------------------------------------------------------------
Purpose: Procedure OffEndOrder handles the OFF-END event on the Order
query. It asks the support procedure for another batch of rows
unless the LAST-BATCH has already been returned.
Parameters: INPUT DATASET dsOrder
--------------------------------------------------------------------------*/
DEFINE INPUT PARAMETER DATASET FOR dsOrder.
This procedure needs to check to see whether all the Orders have been retrieved in the window procedure yet. The LAST-BATCH attribute on the ttOrder buffer provides this information. If the last batch of rows has not been returned, it passes the highest OrderNum value received so far to the same fetchOrderBatch routine that brought the first batch of rows over to the SalesRep LEAVE trigger.
Whatever rows come back are appended to what is already in the local ProDataSet, as shown:
DEFINE VARIABLE iOrderNum AS INTEGER NO-UNDO.
/* If LAST-BATCH flag doesn't indicate that all rows have been returned, pass
the current last OrderNum to tell where to start, and get another
batch. */
IF NOT BUFFER ttOrder:LAST-BATCH THEN DO:
FIND LAST ttOrder.
iOrderNum = ttOrder.OrderNum.
/* Field list (2nd param) only needs to be set once. */
RUN fetchOrderBatch IN hOrderProc (iOrderNum, "",
OUTPUT DATASET dsOrder APPEND).
After disabling the Save button until the user has actually retrieved and made changes to an Order's OrderLines, the procedure simply executes a RETURN NO-APPLY statement to cancel the default effects of the OFF-END event on the query. In the background, the AVM has automatically reopened the OrderBrowse query on the ttOrder table, and repositioned it to the first new row returned. In this way, the rest of the client application does not even know that the event occurred or that the end of the set of rows on the client was temporarily reached. For example:
BtnSave:SENSITIVE IN FRAME dsFrame = FALSE.
RETURN NO-APPLY. END. /* END DO IF NOT LAST-BATCH */
END PROCEDURE.
If the LAST-BATCH flag had already been set when the event occurred, none of the code would be executed and the procedure would return normally. Because there was no RETURN NO-APPLY statement to cancel the OFF-END, the QUERY-OFF-END event and any other related events such as OFF-END on the browse itself would occur.
To see the effects of the new event handler, save both procedures and rerun the window. Tab through the SalesRep field and see the first batch of 20 Orders come up in the Order browse. If you scroll down through those Orders, successive batches are transparently retrieved, added to the temp-table and its query, and therefore displayed in the browse. The browse's own OFF-END GUI event never occurs until the last batch of Orders is retrieved. (If you scroll patiently through all 4000+ Orders, you will see the scrolling eventually end. If you defined an OFF-END trigger for the browse, perhaps just to display a message, you would see that the message does not appear until you reach the end of all the Orders.) The same thing happens if the query's OFF-END event happens for any other reason, such as executing successive GET-NEXT methods on the query.