Passing data from one form to another is a common requirement in real projects. In this tutorial, you will learn practical, safe, and repeatable ways to Pass Values Between Multiple Forms in Oracle Forms using parameter lists, global variables, and a few supporting patterns. Clear, copy-pasteable examples are included so you can adapt them quickly.
Why you need to pass values across forms
Complex applications rarely live in a single form. You might open a Customer Details form from a Customer Search form, jump to a Line Items form from a Sales Order header, or return a selection from a picklist. Each of these flows requires you to share context—IDs, modes, dates, or flags—so the next screen can load the right records immediately and behave correctly.
The three dependable approaches
- Parameter lists (recommended): Create a
PARAMLIST, add typed parameters, and invoke the next form withCALL_FORM,NEW_FORM, orOPEN_FORM. The target form reads values via:PARAMETER.<name>orNAME_IN('<name>'). - GLOBAL variables (simple, but be disciplined): Assign values to
:GLOBAL.*before navigation and read them in the next form. Clear them when done to avoid stale data or name collisions. - Persisted state (advanced, selective use): Use a temporary table or a controlled server-side package/table when you need data to survive across sessions or long flows. This is heavier and should be applied sparingly.
Method 1 — Parameter lists (best practice)
Parameter lists are the most robust way to Pass Values Between Multiple Forms in Oracle Forms. They are explicit, typed, and scoped to a single navigation event.
Step 1: Define form parameters in the target form
In the target form, create form-level parameters (Object Navigator → Parameters), for example:
P_CUSTOMER_ID(Char)P_RUN_MODE(Char, e.g.,VIEWorEDIT)
You will reference these as :PARAMETER.P_CUSTOMER_ID and :PARAMETER.P_RUN_MODE.
Step 2: Create and populate a parameter list in the source form
Attach this to a button (e.g., “Open Details”) in your source form:
DECLARE
pl_id PARAMLIST;
BEGIN
-- Reuse if it exists
pl_id := GET_PARAMETER_LIST('PL_PASS');
IF NOT ID_NULL(pl_id) THEN
DELETE_PARAMETER_LIST(pl_id);
END IF;
-- Create fresh list and add typed parameters
pl_id := CREATE_PARAMETER_LIST('PL_PASS');
ADD_PARAMETER(pl_id, 'P_CUSTOMER_ID', TEXT_PARAMETER, :BLK_CUSTOMERS.CUSTOMER_ID);
ADD_PARAMETER(pl_id, 'P_RUN_MODE', TEXT_PARAMETER, 'EDIT');
-- Navigate: choose one of the following invocations
-- CALL_FORM keeps the calling form in memory and returns here when child closes
CALL_FORM('CUSTOMER_DETAIL', NO_HIDE, DO_REPLACE, pl_id);
-- Alternatively:
-- NEW_FORM('CUSTOMER_DETAIL', NO_HIDE, DO_REPLACE, pl_id); -- replaces the current form
-- OPEN_FORM('CUSTOMER_DETAIL', ACTIVATE, SESSION, pl_id); -- opens in a new session/window
END;
Key notes:
GET_PARAMETER_LIST/DELETE_PARAMETER_LISTavoid “already exists” errors.- Use
TEXT_PARAMETER,NUMBER_PARAMETER, orDATE_PARAMETERappropriately. - Pick
CALL_FORMif you need to return to the parent form once the child closes.
Step 3: Read parameters in the target form
In the target form, use WHEN-NEW-FORM-INSTANCE to consume the parameters and load data:
DECLARE
v_customer_id VARCHAR2(50);
v_mode VARCHAR2(10);
BEGIN
v_customer_id := :PARAMETER.P_CUSTOMER_ID; -- or NAME_IN('P_CUSTOMER_ID')
v_mode := :PARAMETER.P_RUN_MODE;
IF v_customer_id IS NOT NULL THEN
:BLK_DETAIL.CUSTOMER_ID := v_customer_id;
-- Optionally execute a query to fetch the record immediately
GO_BLOCK('BLK_DETAIL');
EXECUTE_QUERY;
END IF;
-- Honor the mode
IF v_mode = 'VIEW' THEN
SET_BLOCK_PROPERTY('BLK_DETAIL', UPDATE_ALLOWED, PROPERTY_FALSE);
SET_BLOCK_PROPERTY('BLK_DETAIL', INSERT_ALLOWED, PROPERTY_FALSE);
SET_BLOCK_PROPERTY('BLK_DETAIL', DELETE_ALLOWED, PROPERTY_FALSE);
END IF;
END;
Tips:
- Parameter names are case-sensitive; match exactly in both forms.
- Remember parameter values are character data when read via
:PARAMETER; convert explicitly if needed.
Choosing between CALL_FORM, NEW_FORM, and OPEN_FORM
- CALL_FORM: Opens the next form and returns control to the caller when it closes. The caller remains in memory (modal flow). Best for picklists or drill-down-and-return flows.
- NEW_FORM: Closes the current form and opens the next one (replaces memory). Best for linear flows (e.g., a wizard-like transition).
- OPEN_FORM: Opens another form instance in a separate session/window. Good for side-by-side work or dashboards.
Method 2 — GLOBAL variables (simple and effective for “return” flows)
GLOBAL variables are easy and useful when the child form must send a value back to the parent after a CALL_FORM.
Pass to child form
:GLOBAL.G_CUSTOMER_ID := :BLK_CUSTOMERS.CUSTOMER_ID;
CALL_FORM('CUSTOMER_DETAIL', HIDE, DO_REPLACE);
In the child form:
BEGIN
IF :GLOBAL.G_CUSTOMER_ID IS NOT NULL THEN
:BLK_DETAIL.CUSTOMER_ID := :GLOBAL.G_CUSTOMER_ID;
GO_BLOCK('BLK_DETAIL');
EXECUTE_QUERY;
END IF;
END;
Return a value from child to parent
In the child form (e.g., a Search/Select form), when user picks a row:
:GLOBAL.G_SELECTED_CUSTOMER := :BLK_SEARCH.CUSTOMER_ID; EXIT_FORM;
The code after CALL_FORM in the parent continues, so you can read the global:
IF :GLOBAL.G_SELECTED_CUSTOMER IS NOT NULL THEN
:BLK_CUSTOMERS.CUSTOMER_ID := :GLOBAL.G_SELECTED_CUSTOMER;
GO_BLOCK('BLK_CUSTOMERS');
EXECUTE_QUERY;
-- Clean up
ERASE('GLOBAL.G_SELECTED_CUSTOMER'); -- or set to NULL
END IF;
Best practices for GLOBALs:
- Use a clear naming convention (prefix with
G_). - Always clear them after use (
ERASE('GLOBAL.G_X')). - Avoid passing sensitive data.
- Prefer parameter lists for one-way input; prefer GLOBALs to return a result from a called form.
Validating and converting data safely
- Convert types explicitly:
:BLK_DETAIL.ORDER_ID := TO_NUMBER(:PARAMETER.P_ORDER_ID); - Guard against nulls:
IF NVL(:PARAMETER.P_CUSTOMER_ID, '') <> '' THEN … END IF; - Validate before navigation to avoid unnecessary round-trips:
IF :BLK_CUSTOMERS.CUSTOMER_ID IS NULL THEN MESSAGE('Select a customer first.'); RAISE FORM_TRIGGER_FAILURE; END IF;
A complete mini-scenario
Goal: From Customer Search form, open Customer Detail form in VIEW mode with the selected CUSTOMER_ID, then optionally return a picked value.
In Search form (button WHEN-BUTTON-PRESSED):
DECLARE
pl_id PARAMLIST;
BEGIN
IF :BLK_SEARCH.CUSTOMER_ID IS NULL THEN
MESSAGE('Please select a customer.');
RAISE FORM_TRIGGER_FAILURE;
END IF;
pl_id := GET_PARAMETER_LIST('PL_CUST');
IF NOT ID_NULL(pl_id) THEN
DELETE_PARAMETER_LIST(pl_id);
END IF;
pl_id := CREATE_PARAMETER_LIST('PL_CUST');
ADD_PARAMETER(pl_id, 'P_CUSTOMER_ID', TEXT_PARAMETER, :BLK_SEARCH.CUSTOMER_ID);
ADD_PARAMETER(pl_id, 'P_RUN_MODE', TEXT_PARAMETER, 'VIEW');
CALL_FORM('CUSTOMER_DETAIL', NO_HIDE, DO_REPLACE, pl_id);
-- If the detail form returned a choice (optional)
IF :GLOBAL.G_SELECTED_CUSTOMER IS NOT NULL THEN
:BLK_SEARCH.CUSTOMER_ID := :GLOBAL.G_SELECTED_CUSTOMER;
GO_BLOCK('BLK_SEARCH');
EXECUTE_QUERY;
ERASE('GLOBAL.G_SELECTED_CUSTOMER');
END IF;
END;
In Detail form (WHEN-NEW-FORM-INSTANCE):
DECLARE
v_id VARCHAR2(50) := :PARAMETER.P_CUSTOMER_ID;
v_mode VARCHAR2(10) := :PARAMETER.P_RUN_MODE;
BEGIN
IF v_id IS NOT NULL THEN
:BLK_DETAIL.CUSTOMER_ID := v_id;
GO_BLOCK('BLK_DETAIL');
EXECUTE_QUERY;
END IF;
IF v_mode = 'VIEW' THEN
SET_BLOCK_PROPERTY('BLK_DETAIL', UPDATE_ALLOWED, PROPERTY_FALSE);
SET_BLOCK_PROPERTY('BLK_DETAIL', INSERT_ALLOWED, PROPERTY_FALSE);
SET_BLOCK_PROPERTY('BLK_DETAIL', DELETE_ALLOWED, PROPERTY_FALSE);
END IF;
END;
In Detail form (optional “Choose” button):
:GLOBAL.G_SELECTED_CUSTOMER := :BLK_DETAIL.CUSTOMER_ID; EXIT_FORM;
This end-to-end pattern covers the most frequent business flows with clear control of direction (to child via PARAMLIST, back to parent via GLOBAL).
Troubleshooting checklist
- Parameter name mismatch: Ensure names and case are identical in
ADD_PARAMETERand:PARAMETER.<name>. - Reused list conflict: Always delete existing lists with the same name before
CREATE_PARAMETER_LIST. - Empty queries in target: Confirm you set the key item before
EXECUTE_QUERYand that the block has a properWHEREclause or key mapping. - Stale GLOBAL values: Clear them after use to prevent surprises on later navigations.
- Wrong invocation choice: Use
CALL_FORMwhen you must return to the caller; useNEW_FORMfor linear replacement; useOPEN_FORMfor parallel work.
Best practices (my opinionated shortlist)
- Prefer parameter lists for forward navigation—they are explicit, typed, and self-documenting.
- Use GLOBALs only to return a small value from a called form, and always clear them.
- Keep parameter names business-meaningful, like
P_CUSTOMER_ID,P_RUN_MODE,P_SOURCE_FORM. - Centralize constants (e.g., run modes) in a common package or in form-level constants to avoid typos.
- Validate early and fail fast with clear messages.
Conclusion
To Pass Values Between Multiple Forms in Oracle Forms reliably, make parameter lists your default, use GLOBALs sparingly for return flows, and choose the right navigation method (CALL_FORM, NEW_FORM, OPEN_FORM) for the user experience you want. With the patterns and code provided, you can wire multi-form navigation that is predictable, secure, and easy to maintain.

