Form management in an OPEN_FORM configuration requires special attention, especially when there is a chain of OPEN forms and each of these, in turn , has detail forms OPEN. For example, consider the multi-form situation described in the introduction to this chapter. It can be implemented by using OPEN_FORM to invoke multiple instances of ORDER form, with each instance displaying order information about each customer. The ORDER form might then invoke an ORDER DETAILS form that is particular to the current ORDER. Four critical issues must be taken care of:
Ensuring these will guarantee that the data, as well as the functionality, are in sync. I will discuss each of these in the following sections.
Proper Navigation Between Forms
You can navigate properly between forms by tracking the form_id. Consider the application cited earlier. Three forms are involved: CUSTOMER_SELECTION, CUSTOMER_ORDERS, and CUSTOMER_ORDER_DETS. The CUSTOMER_SELECTION form calls the CUSTOMER_ORDERS form using OPEN_FORM. It uses OPEN_FORM to enable multiple instances of the CUSTOMER_ORDERS form to be opened in order to compare the order information of two or more customers. The CUSTOMER_ORDERS form, in turn, uses OPEN_FORM to call the CUSTOMER_ORDER_DETS form, because the main CUSTOMER_SELECTION form is being called from a menu using CALL_FORM. No subsequent calls to CALL_FORM are allowed in the form chain, after using OPEN_FORM initially.
Tip
When opening multiple instances of the same form in OPEN_FORM configuration, especially in an OPEN_FORM chain, ensure correct navigation from the subform to its parent form. This can be done using GO_FORM(form_id) in the WHEN-WINDOW-ACTIVATED trigger of the calling form.
The desired functionality can be achieved in three steps:
DEFAULT_VALUE('','global.customer_order_dets_pressed'); WHEN-BUTTON-PRESSED trigger of CUSTOMER_ORDERS form. BEGIN :global.customer_order_dets_pressed := 'Y'; SET_ITEM_PROPERTY('BUTTON_PALETTE.PB_CUST_ORD_DETS', ENABLED, PROPERTY_FALSE); form_id := FIND_FORM('CUSTOMER_ORDERS'); :global.form_id := TO_CHAR(form_id.id); END;
The global variable is necessary to prevent pressing of the button to invoke CUSTOMER_ORDER_DETS a second time while it is already open. An alternative way is to follow the same tracking by form_id for this form, also, and control it. This is not necessary if a need exists to compare the order details of two orders, such as the order information of two customers.
DECLARE form_id FORMMODULE; BEGIN IF :SYSTEM.EVENT_WINDOW = 'WINDOW0'THEN IF :global.customer_order_dets_pressed = 'N'THEN IF :global.form_id IS NOT NULL THEN form_id.id := TO_NUMBER(:global.form_id); GO_FORM(form_id); :global.form_id := NULL; END IF; END IF; END IF; END; SET_ITEM_PROPERTY('BUTTON_PALETTE.PB_CUST_ORD_DETS', ENABLED, PROPERTY_TRUE);
The last line re-enables the button after correct navigation has taken place.
POST-FORM trigger of CUSTOMER_ORDER_DETS form :global.customer_order_dets_pressed := 'N';
Proper COMMIT
Proper COMMIT in the various forms is automatically ensured by forms, depending on whether the child forms are invoked in the same session or a different session. In such a case of multiple open forms,
Tip
Pending changes in the calling form invoke the called form in POST ONLY mode, which enables the form to only POST and NOT COMMIT, pending changes in the called form.
Proper Exiting Sequence
A proper exiting sequence makes sure that all child forms are exited before exiting the parent forms. (Do not exit parent forms before exiting child forms, unless otherwise specifically required.) This is done by writing a KEY-EXIT trigger for the CUSTOMER_ORDERS and CUSTOMER_SELECTION forms. If the CUSTOMER_ORDER_DETS form calls child forms, you must exit those forms properly, also. The following shows code for a KEY-EXIT for the CUSTOMER_ORDERS form. A similar KEY-EXIT can be written for the CUSTOMER_SELECTION form by replacing the module name .
KEY-EXIT for the CUSTOMER_ORDERS form DECLARE form_id FORMMODULE; v_msg VARCHAR2(1000); BEGIN form_id := FIND_FORM('CUSTOMER_ORDER_DETS'); IF NOT ID_NULL(form_id) THEN v_msg := 'There are instance(s) of the Customer Order Details Open.'; v_msg := v_msg'Please close them first!'; MESSAGE(v_msg);RAISE FORM_TRIGGER_FAILURE; END IF; IF FORM_SUCCESS THEN EXIT_FORM; END IF: END;
If the form is being exited by means of an Exit button or using the x box of the main window, use DO_KEY('EXIT_FORM') in the WHEN-BUTTON-PRESSED trigger of the Exit button or WHEN-WINDOW-CLOSED of the main window, respectively, to invoke the preceding code without repeating it.
Simulating a CLOSE ALL FORMS
When multiple forms are open in a single run-form session, exiting the application requires each open form to be exited individually. However, providing a single function to close all open forms in one shot gives greater flexibility to the end user . It calls for some amount of coding on the part of the developer. You can implement a trick of the trade to simulate a CLOSE ALL FORMS using the WHEN-WINDOW-ACTIVATED trigger. This section presents this technique, which is both elegant and efficient.
Tip
Set a global variable like global.close_all to 'Y'. Use CLOSE_FORM to close the currently activated form.
A WHEN-WINDOW-ACTIVATED trigger can be written for each and every form invoked, either by using CALL_FORM or by independently using OPEN_FORM , and its code is based on the following algorithm. Declare a global variable to initiate CLOSE ALL FORMS . Set its value to 'Y'. If the value of this global variable is 'Y', close the currently active form.
The algorithm is given below:
if = 'Y'then if then close form; end if; end if;
The WHEN-WINDOW-ACTIVATED trigger calls a procedure p_close_all_forms whose logic is based on the above algorithm. The code for this procedure follows this trigger code:
WHEN-WINDOW-ACTIVATED p_close_all_forms;
The code for the procedure p_close_all_forms is as follows:
PROCEDURE p_close_all_forms IS form_id FORMMODULE; BEGIN IF NAME_IN('global.close_all') = 'Y'THEN form_id := FIND_FORM(NAME_IN('SYSTEM.CURRENT_FORM'); IF NOT ID_NULL(form_id) THEN CLOSE_FORM(form_id); END IF; END IF; END;
The global global.close_all can be set in an iconic button on a toolbar, in a trigger for a hot key, or in a menu option common to all forms. The disadvantage of this method is that any nonactive forms are still left open until the user clicks on a particular form. To enhance the functionality to accommodate this feature, use :SYSTEM.MOUSE_FORM and the WHEN-MOUSE-ENTER trigger at the form level of each independent form. Then the user can just move the mouse around, and the CLOSE ALL FORMS magic follows. The corresponding procedure is named xclose_all_forms .
WHEN-MOUSE-ENTER xclose_all_forms;
The code for the procedure is as follows:
PROCEDURE xclose_all_forms IS form_id FORMMODULE; BEGIN IF NAME_IN('global.close_all') = 'Y'THEN form_id := FIND_FORM(NAME_IN('SYSTEM.MOUSE_FORM'); IF NOT ID_NULL(form_id) THEN CLOSE_FORM(form_id); END IF; END IF; END;
This method is more user friendly than the first because it saves the many extra clicks required in the former.
Tip
CLOSE_FORM is equivalent to EXIT_FORM(ASK_COMMIT). If other parameters such as NO_COMMIT, DO_COMMIT or NO_VALIDATE are required, use EXIT_FORM instead.
GUI Development
Advanced GUI Development: Developing Beyond GUI
Multi-form Applications
Advanced Forms Programming
Error-Message Handling
Object-oriented Methods in Forms
Intelligence in Forms
Additional Interesting Techniques
Working with Trees
Oracle 8 and 8i Features in Forms Developer