Building the user interface for the new ProDataSet
Now build a user interface for the new ProDataSet.
To build the user interface:
1. Create a new window procedure in the AppBuilder. Name it CodeWindow.w.
2. Make the window 10 rows by 130 columns.
3. Name the default frame CodeFrame and the window CodeWin.
4. Use the AppBuilder's temp-table utility to define a temp-table ttState LIKE the State database table.
5. Drop a browse called StateBrowse onto the design window and attach it the ttState table and its three fields.
Your window should look roughly like this when you place the StateBrowse to leave room for other objects. For example:
6. Add a statement to the Main Block to run a procedure where all the code will go to start up the window. For example:
MAIN-BLOCK:
DO ON ERROR UNDO MAIN-BLOCK, LEAVE MAIN-BLOCK
ON END-KEY UNDO MAIN-BLOCK, LEAVE MAIN-BLOCK:
RUN enable_UI.
RUN startupCodeWindow. IF NOT THIS-PROCEDURE:PERSISTENT THEN
WAIT-FOR CLOSE OF THIS-PROCEDURE.
END.
7. Add a line to the CLOSE trigger in the Main Block for a procedure to shut down the window support code. For example:
ON CLOSE OF THIS-PROCEDURE DO:
RUN shutdownCodeWindow.
RUN disable_UI.
END.
8. Add these variables to the Definitions section:
DEFINE VARIABLE hCodeSupport AS HANDLE NO-UNDO.
DEFINE VARIABLE hCodeSet AS HANDLE NO-UNDO.
DEFINE VARIABLE hRepBrowse AS HANDLE NO-UNDO.
DEFINE VARIABLE hRepQuery AS HANDLE NO-UNDO.
DEFINE VARIABLE hStateQuery AS HANDLE NO-UNDO.
9. Create the internal procedure startupCodeWindow. This starts the CodeSupport procedure and then asks it for a ProDataSet with just the ttSalesRep and ttState tables in it, as shown:
/*---------------------------------------------------------------------
Procedure: startupCodeWindow
Purpose: Fetch needed code tables from server.
Parameters: <none>
---------------------------------------------------------------------*/
RUN codeSupport.p PERSISTENT SET hCodeSupport.
RUN fetchCodeTables IN hCodeSupport
(INPUT "ttSalesRep,ttState", OUTPUT DATASET-HANDLE hCodeSet).
10. Create a dynamic query for the ttState table that comes back as part of the dynamic ProDataSet, attaches it to the StateBrowse, prepares it, and opens it. The variables you use in this procedure are the ones you added in the Definitions section:
CREATE QUERY hStateQuery.
hStateQuery:ADD-BUFFER(hCodeSet:GET-BUFFER-HANDLE("ttState")).
StateBrowse:QUERY IN FRAME CodeFrame = hStateQuery.
hStateQuery:QUERY-PREPARE("FOR EACH ttState").
hStateQuery:QUERY-OPEN().
Again, you might ask why you need a new dynamic query for this table. After all, you just defined a static temp-table ttState and a static browse StateBrowse against that temp-table.
Once again, the answer is that you are not really using that static temp-table. It only provides a definition to base the browse on. What comes back from fetchCodeTables is a separate dynamic temp-table that happens to have the same name and the same fields so that you can easily use it in place of the static temp-table you defined.
You cannot simply open the static query and use it. For example:
/* Can't do this:
OPEN QUERY StateBrowse FOR EACH ttState. */
If you do, you will not see any data in the browse.
11. To reinforce how to do this properly, you can create a dynamic query for the other table you are getting back, ttSalesRep:
Be sure to add the right buffer to the query, which is the one for the temp-table that comes back as part of the dynamic ProDataSet hCodeSet. If you had a local definition of ttSalesRep, its buffer would not do you any good for the same reason that your local definition of ttState cannot be used.
12. Finally, the procedure needs to prepare and open the dynamic query on ttSalesRep:
hRepQuery:QUERY-PREPARE("FOR EACH ttSalesRep").
hRepQuery:QUERY-OPEN().
13. Now, when you run the window, you see both the static browse and the dynamic browse. Both are, in fact, using dynamic temp-tables that came back as part of the dynamic ProDataSet hCodeSet, as shown:
14. Define the internal procedure shutdownCodeWindow to delete the supporting procedure instance:
/*---------------------------------------------------------------------
Procedure: shutdownCodeWindow
Purpose: Cleanup supporting procedure and any other objects when
deleting
the window.
---------------------------------------------------------------------*/
APPLY "CLOSE" TO hCodeSupport.
END PROCEDURE.
Rather than deleting it directly, applying the CLOSE event to it gives it a chance to clean up after itself. This is modeled on the standard code the AppBuilder generates to close a procedure by running disable_UI.
15. To handle the CLOSE event in CodeSupport.p, add the following trigger to its main block so that it can delete the other persistent procedure CodeSource.p that manages the Data-Sources, and then delete itself:
ON CLOSE OF THIS-PROCEDURE DO:
DYNAMIC-FUNCTION("detachDataSet" IN hSourceProc, INPUT hCodeSet).
DELETE PROCEDURE hSourceProc.
DELETE PROCEDURE THIS-PROCEDURE.
END.