Writing Flexible Code*
Flexible Code and System Variables *
Built-In Subprograms that Assist Flexible Coding *
Writing Code to Reference Objects by Internal ID *
Writing Code to Reference Objects Indirectly *
Sharing Objects and Code*
Inheriting Properties from Property Classes *
Grouping Related Objects for Reuse *
Reusing Objects from an Object Library *
Reusing PL/SQL code *
Managing Multiple-Form Applications*
Defining Multiple-Form Functionality *
Calling One Form from Another *
In this chapter, you will cover the following areas of forms programming:
As your applications become more complex, you need to start employing more sophisticated techniques to produce smooth-running, rich applications in a minimum of time. This chapter is here to help. It shows you how to leverage your coding effort by making your code more flexible and reusable, employing techniques such as indirect object referencing, property classes, object groups, object libraries, and PL/SQL code libraries. The chapter wraps up by showing you how to create and manage applications incorporating multiple form modules. Overall, the contents of this chapter comprise about 12 percent of OCP Exam 3 test content.
Writing Flexible Code
In this section, you will cover the following points related to writing flexible code:
The more flexible your code is, the more easily it can be reused, maintained, and expanded. This results in an overall reduction in the time and cost needed to complete a project, which makes you more valuable to your employer or clients. There are an infinite number of ways to use the techniques shown in this section, so instead of showing every possible usage, one or two simple examples of each technique are provided. That will be plenty for you to understand how the techniques work, and start to think about where you can apply them in your own applications.
Flexible Code and System Variables
Flexible code is code that can be used by a variety of objects, in a variety of modules, without requiring changes to the code. While this type of code often takes longer to write initially, the investment of additional time pays major dividends later, with the benefits increasing each time the code is reused. Code flexibility comes from the way the code is written, as well as from its availability for use in applications other than the one for which it was originally written.
One technique that makes code substantially more flexible is giving it the ability to refer to objects and values with generic names that are evaluated at run time. Form Builder comes with dozens of system variables to provide this ability; they are listed in Table 17-1.
|System Variable Name||Returns|
|$$DATE$$||Current operating system date|
|$$DATETIME$$||Current operating system date and time|
|$$DBDATE$$||Current database date from an Oracle database|
|$$DBDATETIME$$||Current database date and time from an Oracle database|
|$$DBTIME$$||Current database time from an Oracle database|
|$$TIME$$||Current operating system time|
|SYSTEM.BLOCK_STATUS||NEW if all records in current block are new, CHANGED if one or more records in block have been changed, QUERY if all records are unchanged since being retrieved|
|SYSTEM.COORDINATION_OPERATION||Type of event that is causing coordination between related master and detail blocks|
|SYSTEM.CURRENT_BLOCK||NULL if current navigation unit is a form; name of current block if current navigation unit is block, record, or item|
|SYSTEM.CURRENT_DATETIME||Current operating system date and time in DD-MON-YYYY HH24:MI:SS format|
|SYSTEM.CURRENT_FORM||Name of current form|
|SYSTEM.CURRENT_ITEM||NULL if current navigation unit is record, block, or form; name of current item (without block name) if current navigation unit is item|
|SYSTEM.CURRENT_VALUE||Value of the current item|
|SYSTEM.CURSOR_BLOCK||NULL if current navigation unit is form; name of block where cursor is located if current navigation unit is block, record, or item|
|SYSTEM.CURSOR_ITEM||Name of item that has input focus, in format block_name.item_name|
|SYSTEM.CURSOR_RECORD||Number representing current records physical order in a blocks records|
|SYSTEM.CURSOR_VALUE||Value of item where cursor is located|
|SYSTEM.DATE_THRESHOLD||Changeable variable; used with $$DBDATE$$, $$DBTIME$$, and $$DBDATETIME$$, specifies how many minutes must have passed since the last database date/time retrieval to make the Forms Runtime program retrieve the information from the database again, instead of just adding the amount of elapsed local time to the last retrieved date/time value|
|SYSTEM.EFFECTIVE_DATE||Changeable variable; allows you to set the effective database date|
|SYSTEM.EVENT_WINDOW||Name of last window that fired a window event trigger|
|SYSTEM.FORM_STATUS||NEW if all records in current form are new, CHANGED if one or more records in block have been changed, QUERY if all records are unchanged since being retrieved|
|SYSTEM.LAST_QUERY||Most recent select statement used to populate a block|
|SYSTEM.LAST_RECORD||TRUE if current record is last record in block; FALSE if not|
|SYSTEM.MASTER_BLOCK||Name of master data block involved in a firing of an ON-CLEAR-DETAILS trigger|
|SYSTEM.MESSAGE_LEVEL||Changeable variable; message severity level at or beneath which messages will not be displayed|
|SYSTEM.MODE||NORMAL during normal processing, QUERY when query is being processed, ENTER-QUERY when form is in Enter-Query mode.|
|SYSTEM.MOUSE_BUTTON_PRESSED||1 if left mouse button was pressed, 2 if right button (on 2-button mouse) or middle button (on 3-button mouse) was pressed|
|SYSTEM.MOUSE_BUTTON_SHIFT_STATE||Shift+ if shift key was held down when mouse button was clicked; Ctrl+ for control key; Alt+ for alt key; Shift-Control+ for shift-control keys|
|SYSTEM.MOUSE_CANVAS||NULL if mouse is not in a canvas; canvas name if it is|
|SYSTEM.MOUSE_FORM||Name of form mouse is in|
|SYSTEM.MOUSE_ITEM||NULL if mouse is not in an item; item name in block_name.item_name format if it is|
|SYSTEM.MOUSE_RECORD||0 if mouse is not in a record; number of record if it is|
|SYSTEM.MOUSE_RECORD_OFFSET||Used in multirecord blocks with more than one visible record, number representing offset from first visible record to record the mouse is in; 0 if mouse is not in a record|
|SYSTEM.MOUSE_X_POS||Horizontal position of mouse in relation to top-left corner of canvas, or top-left corner of items bounding box if mouse is in an item|
|SYSTEM.MOUSE_Y_POS||Vertical position of mouse in relation to top-left corner of canvas, or top-left corner of items bounding box if mouse is in an item|
|SYSTEM.RECORD_STATUS||NEW if current record is new, CHANGED if it has been changed since last validated, QUERY if its status is valid and it was retrieved from a database, or INSERT if it contains invalidated data and has not been saved to the database|
|SYSTEM.SUPPRESS_WORKING||Changeable variable; TRUE is "Working " messages are suppressed, FALSE if not|
|SYSTEM.TAB_NEW_PAGE||Name of destination of tab-page navigation|
|SYSTEM.TAB_PREVIOUS_PAGE||Name of source of tab-page navigation|
|SYSTEM.TRIGGER_BLOCK||Name of block that was current when trigger fired|
|SYSTEM.TRIGGER_ITEM||Name of item that was current when trigger fired|
|SYSTEM.TRIGGER_RECORD||Number of record that was current when trigger fired|
Table 1: System Variables
Built-In Subprograms that Assist Flexible Coding
Form Builder provides quite a few built-ins that are useful when you want to make your code more flexible. The bulk of these either read or set specific properties for a wide variety of objects. These property-manipulation built-ins have names that make very clear what they do. The built-ins that read object properties include the following:
The built-ins that set object properties are almost, but not quite, an exact mirror of the GET_ list; they include the following:
These built-ins require as parameters the name of the item you are interested in, the name of the property to read or setuse the property name from the Property Palette, with underscores to replace spaces between wordsand the value to place into the property, if it is a SET_ built-in. For example, the following code would make your sample applications SALARY item unavailable for editing:
set_item_property('EMPLOYEE_2.SALARY', enabled, PROPERTY_FALSE);
Some of the more useful properties you can control when working with text items include ITEM_NAME, ITEM_TYPE, DATATYPE, ENABLED, LABEL, PROMPT TEXT, and VISIBLE. There are also properties you can GET_ and SET_ that do not show up in the Property Palette. For the GET_ITEM_PROPERTY built-in, for example, additional properties include BLOCK_NAME and ITEM_IS_VALID.
In addition to the GET_ and SET_ built-ins, Form Builder offers a handful of other built-ins that are useful when you are writing flexible code. Table 17-2 lists these and describes what they do. Of the built-ins in the list, the most unusual is SHOW_LOV, because it has the combined task of displaying an object (an LOV), and also returning a Boolean value to the calling program indicating whether or not the user selected a value from the LOV.
|ADD_GROUP_COLUMN||Adds a column to a record group|
|ADD_GROUP_ROW||Adds a row to a record group|
|ADD_LIST_ELEMENT||Adds an element to a list|
|ADD_PARAMETER||Adds a parameter to a parameter list|
|COPY||Copies a value from a literal, text item, or global variable into a text item or global variable; used to write values into items referenced via the NAME_IN built-in|
|DEFAULT_VALUE||Copies a value to a variable if the variable is currently NULL; creates the variable if it is an undefined global variable|
|DISPLAY_ITEM||Assigns a display attribute to an item|
|NAME_IN||Returns a value from a named variable or object|
|SHOW_LOV||Displays a list of values at specified coordinates, returns TRUE if user selects value from list and FALSE if user dismisses list with the Cancel button|
Table 2: Additional Built-Ins for Flexible Coding
Writing Code to Reference Objects by Internal ID
Each built-in example you have seen so far has used an objects name as a parameter. It is also possible to use an internal ID to reference objects in many built-ins. The internal object ID is created by Form Builder when the object is created, and it is not visible to the user. Referring to objects by their ID rather than by name benefits you in two ways. First, it speeds up program execution if an object is referred to more than once. Form Builders native way of addressing objects is their object ID, and if you refer to an object by name, Form Builder has to go find that objects ID before processing can continue. Subsequent references to the object name will cause Form Builder to look up the object ID each time. Each of these lookups after the first one can be avoided by declaring a variable in your code to store the objects ID the first time it is looked up. After that, the ID is immediately available each time the object needs to be referenced: you just use the variable name in the referencing code statement. The second benefit is that it improves your codes flexibility and maintainability by storing the object name just once in a section of code, at the very beginning. If you wish to modify the object name in the code for maintenance reasons or so the routine can be used elsewhere, you need only change the object name once, because that is the only time the object is referred to by name.
You can obtain an objects ID by using the FIND_ built-in functions. Each object class has a matching FIND_ built-in; for instance, FIND_WINDOW, FIND_BLOCK, and FIND_LOV. Each of these built-ins returns its own unique VARCHAR2 value type; Table 17-3 shows each object types FIND_ function and return data type. For example, the following code would return the object ID of the AV_DATA_SOUND form and then move focus to that form:
v_form_id := find_form('AV_DATA_SOUND');
Once you have included code such as that just shown in a PL/SQL code block, you can refer to the object using the variable name (in the prior example, "v_form_id") instead of the object name. You can also use the FIND_ function directly as a parameter in another built-in, with a statement like go_form(find_form('AV_DATA_SOUND'));. However, because the forms ID does not get stored in a reusable variable, this approach provides no benefit over simply saying go_form('AV_DATA_SOUND');.
|Object Type||FIND_ Function||Return Data Type|
|List of Values||FIND_LOV||LOV|
|Record Group Column||FIND_COLUMN||GROUPCOLUMN|
Table 3: FIND_ Built-In Return Data Types
Writing Code to Reference Objects Indirectly
There are probably going to be times when you need to write code that refers to variables and objects in a way that is even more abstract than using object IDs. One reason to do this is to make your code even more flexible; a routine that receives a value, processes it, and places it somewhere else can achieve its maximum flexibility when it does not have the names of any source or destination objects hard-coded. Another reason is that you will soon be building applications that contain menu modules, library modules, and/or multiple form modules, and because each of these modules is compiled independently of the others, they cannot directly reference items or variables stored in a module other than their own. The solution in both of these situations is referencing objects indirectly.
The types of objects that cannot be referenced directly across modules are form items, system and global variables, and parameters. These are called form bind variables. There are two built-ins you can use to reference form bind variables: one to read values, and the other to write them. The built-in to read values is NAME_IN, and if you interpret its name somewhat loosely, it gets "the name that is in" a named object. The built-in to write values is COPY. The structure of each built-in follows:
You can nest NAME_IN inside the COPY built-in to give COPY the ability to place values into indirectly referenced locations. An example of this follows:
if :radio.choice = 1 then
GV_USER_TYPE := 'STANDARD';
GV_USER_TYPE := 'ADMIN';
copy(name_in('logon_screen.userid'), 'GLOBAL.' || GV_USER_TYPE || '_ID' );
The return value from a NAME_IN built-in is always a character string, even when it is retrieving data from a item with a data type of NUMBER or DATE. Use the TO_NUMBER and TO_DATE conversion functions to convert the character string to the appropriate type, as shown in the following code:
numeric_variable := to_number(name_in('block.numeric_item'));
date_variable := to_date(name_in('block.date_item'));
Sharing Objects and Code
In this section, you will cover the following points about sharing code and objects:
The allure of being able to reuse objects and code is powerful. It saves time and money. It reduces errors by eliminating re-creation of existing procedures and functions. It helps ensure uniform appearance and functionality with little effort. Form Builder offers sophisticated features to help you reuse objects and code: property classes, object groups, object libraries, and PL/SQL libraries.
Inheriting Properties from Property Classes
Every form module contains a node called Property Classes. A property class is an object that holds a collection of properties and property values that can be inherited by other objects. This allows you to create objects that automatically incorporate the properties you have taken the trouble to define previously, thereby cutting development time. It also assists in creating applications with a uniform look and behavior. An added benefit is that if you need to change a property later, you can change it in the property class and the change will automatically be propagated throughout your application, once again improving your productivity. A property class can hold any number of properties, including properties that apply to different object types. Once you have created a property class, you can use it as the basis for other objects, which will inherit the properties appropriate for their object types. In the objects based on the property class, you decide which properties are inherited by the object and which are overridden. A property class can also be subclassed into other modules.
Consider, for example, text items that display dollar values; the Salary field in your EMPLOYEE canvas is one example. By default the salary figures appear flush left in the field, without any commas or dollar signs, whereas numeric values are traditionally aligned to the right. You might also wish to have salary values of zero display with a red background to attract the users attention. These design features would also be useful in any item showing dollar values. You can create a property class containing the properties that produce these features, and then have each dollar-oriented item refer to that property class, thereby producing the appearance and behavior you want in each item without having to set the properties manually every time.
To make this example more concrete, open your sample application in Form Builder. Open the EMPLOYEE canvas in the Layout Editor, and open the Property Palette for the HIRE_DATE item. Select the following properties (remember that you can hold down the ctrl key to select multiple properties):
In the Property Palette toolbar, click on the Property Class button, shown here:
A dialog box will appear informing you that a property class was created; click on the OK button to dismiss it. Close the Layout Editor and open the Property Classes node in the Object Navigator. Click on the +to the left of your new property class to show its contents. Change the new property classs name to DATE_PROPERTIES. Change its Justification property to Right, and its Format Mask property to MM-DD-YYYY.
If you want to add additional properties to the property class, you can do so by clicking on the Add Property button in the Property Palette toolbar, shown here:
You can also delete unneeded properties by clicking on the Delete Property button in the Property Palette toolbar, shown next:
You now have a usable property class. To cause form items to inherit its properties, open your EMPLOYEE canvas once again in the Layout Editor and select the HIRE_DATE item. Open its Property Palette and select the Subclass Information property. Click on the More button that appears in the propertys value location, and you will see a dialog box similar to the one shown in Figure 17-1.
Figure 1: Subclass information dialog box
Click on the Property Class radio button, open the Property Class Name list, and select your DATE_PROPERTIES property class from the list. Then click on the OK button. When you do, you will see that some new symbols appear in the Property Palette to the left of the property names. The standard symbol is a small dot signifying that the propertys value is unchanged from the default, as follows:
Properties whose values have been changed manually are marked with a slightly larger square, which looks like this:
Properties displaying inherited values are marked with an arrow pointing to the right, as shown here:
Items whose values were inherited from a property class and then manually overridden are marked with an inheritance arrow whose point is replaced with a red "X", like following:
Grouping Related Objects for Reuse
An object group is a logical container you can create and then place into it pointers to other objects in a module. This allows you to group related objectseven objects of different typesso they can be easily copied to, or subclassed within, another module. Object groups are available within form and menu modules. They are very easy to create: you simply click on the Object Groups node in the Object Navigator and then click on the Create button. To add pointers to the object group, just select the items you want to group together and drag them onto the object groups name. To remove an item from an object group, select the item within the object group (not the original item!) and click on the Delete button or press the delete key.
Reusing Objects from an Object Library
While grouping objects into an object group is convenient, there are limitations inherent in this approach; for instance, subclassing the object group into another form module does not always work. A more powerful approach is to copy the original items into an object library, and subclass the object library items into each form module that needs them. Besides being a more robust approach to reusing objects, an object library lets you divide the objects visually onto tab pages of your own design so you can locate and select the ones you want more quickly.
To see this in action, you will create an object library to hold the objects necessary to save and load images to and from your AV_DATA table. Start by clicking on the Object Libraries node in Form Builder and then clicking on the Create button. Change the object librarys name to IMAGE_LIBRARY. Beneath its entry in the Object Navigator you will see a subnode named Library Tabs; double-click on the subnode to create your first tab, and then click on the Create button to create a second tab. Change the first tabs Name property to BLOCKS and its Label property to Blocks. Change the second tabs Name property to CANVASES and its Label property to Canvases. Now, double-click on the object librarys icon in the Object Navigator to open it. You will see a window similar to Figure 17-2. To place items into your new object library, open the IMAGE_MODULE form in Form Builder. Drag its AV_DATA_IMAGE data block into the object library. Click on the object librarys Canvases tab, and then drag the IMAGE_MODULEs AV_DATA_IMAGE canvas into the object library. Click on the object librarys Save button to save the object library to disk under the name image_library.olb. Then, close the IMAGE_MODULE form module so that it is no longer present in the Object Navigator.
Figure 2: Object Library window
Next, create a new form module in the Object Navigator. Then, return to the object library, click on the Blocks tab, and drag the AV_DATA_IMAGE data block from the object library onto the new form modules name. Form Builder will display a dialog box asking if you want to subclass the object library object or copy it. Select the Subclass option. Then, click on the object librarys Canvases tab and drag the AV_DATA_IMAGE canvas from the object library onto the new form module. When asked, select the Subclass option for this object as well. Then click on the Object Navigators Run button to run your new form. You will see that it runs exactly as the original IMAGE_MODULE form did. Bringing the functionality of the IMAGE_MODULE form into your new form module took only secondsa good demonstration of the productivity benefits you can enjoy from using object libraries.
Object libraries are a powerful way to enforce standards across an organizations applications. One common approach is to have two object libraries used in a project: an enterprise-wide object library containing objects applicable to any application created for the company, and a second object library containing objects for a particular application or group, if necessary. Developer/2000 comes with an object library named STANDARDS that contains premade alerts, input and display items, canvas layouts, visual attribute groups, and components that can speed creation of a menu, wizard, pick list, navigator, and calendar. You will find this object library in the stndrd20.olb file located in the <oracle_home>\tools\devdem20\demo\forms\ directory.
Reusing PL/SQL code
With all this talk about reusing objects, it is natural that the discussion turn to the subject of reusing code. You can copy and paste code just like any other object, of course, but Form Builder offers a far more elegant solution: PL/SQL libraries. A PL/SQL library allows you to store client-side program units and make them available to any form, menu, or library module. A PL/SQL library stores subprograms, including functions, procedures, and packages. Once attached to a module, a PL/SQL librarys program units can be called from your own routines, triggers, and menu item commands. You can attach multiple PL/SQL libraries to a single module, and many modules can attach to the same PL/SQL library. A library can even be attached to another library. A PL/SQL library is intelligent enough to only load its program units as an application needs them, thereby minimizing the demand for client-computer memory. This is called dynamic loading. PL/SQL libraries can also be handy for applications that need to be distributed in multiple languages: you can store different language versions of the display text in separate language-specific PL/SQL libraries, and then attach the appropriate library before the application is distributed to a specific region. PL/SQL libraries are created and modified under Form Builders PL/SQL Libraries node. To use a PL/SQL library in a module, you attach it under the Attached Libraries node within the appropriate module; the attached library is read-only. Developer/2000 comes with several PL/SQL libraries to simplify common tasks; look in the directories <oracle_home>\oca20\plsqllib, <oracle_home>\tools\devdem20demo\forms, and <oracle_home>\tools\open2k20\plsqllib.
Managing Multiple-Form Applications
In this section, you will cover the following points about multiple-form applications:
You have already seen how a single form module can contain different canvases. You can also integrate multiple form modules into a larger, coordinated application. Put on your seat belt!
Defining Multiple-Form Functionality
The Forms Runtime program can have more than one form module open simultaneously during a session. The application starts in the same familiar waywith a single formbut incorporates PL/SQL built-ins to invoke other forms. The additional forms can call other forms of their own, and so on. This can assist development of complicated applications by dividing functional groups into different forms. It also helps maximize the usability of client-computer memory because forms only consume memory when they are called, and their memory is released when they are closed. As an added bonus, using separate form modules lets you exercise a higher degree of control over what records are committed when the user performs a save procedure.
Form Builder gives you plenty of options related to running multiple forms. A newly opened form can run simultaneously with the one that called it, or the new form can replace the old one, causing the calling form to close. Multiple instances of the same form can be run. A form can be opened but kept in the background. And multiple forms can either share the same database connection or they can have multiple independent database connections, as if they were running on separate client computers. The next section provides all the dirty details.
Calling One Form from Another
Create a new form module in Form Builder, and name it START_SCREEN. Create a new data block within the module; change its name to BUTTON_BLOCK, and change its Database Data Block property to No. Create a new canvas manually by double-clicking on the Canvases node; name the new canvas BUTTON_CANVAS. Open the canvas in the Layout Editor and add four push buttons to it. Set the properties of the buttons as follows:
|Button Name||Button Label|
|RUN_SAMPLE_APP||Run Sample Application|
|RUN_SOUND_APP||Run Sound Application|
|RUN_IMAGE_APP||Run Image Application|
Your canvas should look similar to the one shown in Figure 17-3. Now create a WHEN-BUTTON-PRESSED trigger for the RUN_SAMPLE_APP button, and place inside it code similar to this:
Replace the sample_application_name with the name of your sample application as it appears on disknot as it appears in the Object Navigator. You do not need to include a file extension of .fmb or .fmx. If you have been storing the sample application somewhere other than Form Builders default path, you will need to specify that path in the OPEN_FORM built-in command (this is an excellent place to use a global variable in an application you are going to distribute). If your file specification includes spaces, you may need to use the old 8.3 filename format from DOS, which limits file and directory names to eight characters (excluding the three-character file extension) and no spaces. To convert a long file or directory name to this format, remove all spaces from the name, use the first six characters of what remains, and then add a tilde character ( ~ ) followed by a number indicating which number file or directory this is if more than one entry in this location could be matched by the first six characters you specified. It may take some experimenting with DIR and CD commands at a DOS prompt to find the right 8.3 format name. As an example, the modern filename of
P:\Data\OCP Dev2K\OCP Demo Form 1
converts into the following 8.3 format name:
Compile the button and close the PL/SQL Editor. Add similar triggers to the sound and image buttons to open their respective form modules. For the Exit button, create a trigger containing the command exit_form; to close the START_SCREEN module. Then, save your form and run it. In the Forms Runtime program, click on the first three buttons in your START_SCREEN application to load and open the other form modules. Execute a query with each module, and position them on your screen so their layout suits your taste. Your screen will end up looking similar to Figure 17-4. When you are done experimenting with your multiple forms, close them all and return to Form Builder.
Figure 3: Canvas with buttons to open other forms
Figure 4: Multiple forms running simultaneously
If you wish to invoke a form module without immediately passing control to it, you can do so by including the NO_ACTIVATE option in the OPEN_FORM built-in command, like this:
You can also elect to have a new form opened with its own database connection, separate from whatever database connection is already running on that client computer. The benefit of doing this is that each form will have independent commit processing. When multiple forms are opened without this optionand therefore are using the same database connectiona commit in any form causes data in all forms to be saved. If the forms do not have a functional relationshipfor instance, if one is a sales application and the other is a scheduleryou do not want records in one form to be saved just because records in another are. That is a situation where opening the forms with their database connections makes sense. To do this, include the SESSION option in the second forms OPEN_FORM built-in command, as shown:
For this to work, the Forms Runtime program must have its Session option set to TRUE. You can do that by starting the Forms Runtime program with the SESSION=YES option on its command line, like this:
f50run32 module=module_name userid=user/ID session=YES
Another option is invoking a form and having the calling form close automatically, thereby releasing the memory it consumed. The NEW_FORM built-in performs this task. An example of this built-in is as follows:
The NEW_FORM built-in has optional parameters enabling you to control what happens to unsaved changes in the parent form before it closes, whether to open the new form in query-only mode, whether to have the new form share library data with other forms, and what parameters will be passed from the calling form to the new form.
To navigate between open forms, the user can simply click on the desired form, or use the Forms Runtime programs Window command to select a form if it is not in view. You can also control navigation between forms programmatically. The GO_FORM built-in moves focus to a form identified either by name or by internal ID, using the syntax go_form('module_name');. If you know the order in which forms were loaded, you can also employ the built-ins NEXT_FORM; and PREVIOUS_FORM; to move to the next or prior forms in the form stack.
Table 17-4 offers a reference table of form-related built-ins.
|CALL_FORM||Runs a form; with options to select whether the calling form maintains the focus, whether the two forms share library data, and whether the called form is to be run only in query mode|
|CLEAR_FORM||Flushes records from current form|
|CLOSE_FORM||Closes indicated form|
|COMMIT_FORM||Saves current forms records to database|
|EXIT_FORM||Closes current form|
|FIND_FORM||Returns internal ID of form module with a given name|
|GO_FORM||Moves focus to the indicated form|
|NEW_FORM||Enters the indicated form, exiting and closing the current form|
|NEXT_FORM||Moves focus to next form|
|OPEN_FORM||Opens indicated form, with options to select whether the called form gets focus, shares library data with other forms, and gets its own database connection if required|
|PREVIOUS_FORM||Moves focus to prior form|
Table 4: Form Builder Built-Ins for Multiple-Form Applications
This chapter covered a lot of ground in the area of form programming. You read explanations and did exercises focusing on writing flexible code, sharing objects and code, and creating multiple-form applications. The subjects covered in this chapter represent about 12 percent of the material tested on OCP Exam 3.
You started by learning about writing flexible code, which is code that can be used by a variety of objects, in a variety of modules, without modifying the code. Code flexibility comes both from the way code is written and from its availability for use by applications other than the one for which it was originally written. One technique that makes code substantially more flexible is giving it the ability to refer to objects and values with generic names that are evaluated at run time. Form Builder comes with many system variables to provide this ability, such as SYSTEM.CURRENT_FORM, SYSTEM.CURRENT_BLOCK, SYSTEM.CURSOR_RECORD, and SYSTEM.CURRENT_ITEM. In addition, there are dozens of built-ins to assist in writing flexible code by reading and writing object properties during run time; their names follow a pattern of GET_objecttype_PROPERTY and SET_objecttype_PROPERTY. Form Builder also offers a handful of other built-ins that are useful when you are writing flexible code, including ADD_GROUP_COLUMN, ADD_GROUP_ROW, ADD_LIST_ELEMENT, ADD_PARAMETER, COPY, DEFAULT_VALUE, DISPLAY_ITEM, NAME_IN, and SHOW_LOV. The most unusual of these is SHOW_LOV, because it both displays an object (an LOV) and returns a Boolean value indicating whether or not the user selected a value from the object.
Another technique that can increase the flexibility of your code is referencing objects by their internal ID rather than by name. Using internal IDs minimizes the number of places where you will have to change hard-coded names, and it allows you to run multiple instances of a single form simultaneously. As an added benefit, it speeds up your code because object names only need to be correlated with their internal IDs once. You can obtain an objects ID by employing one of several FIND_ built-in functions. Each object class has a matching FIND_ built-in; for instance, FIND_WINDOW, FIND_BLOCK, and FIND_LOV. If you want your form module to be able to access values in form items, parameters, and system and global variables in other form, menu, or library modules, you can use the NAME_IN and COPY built-ins to read and write values, respectively, in other modules. These items are called form bind variables, and the technique is known as indirect referencing.
The next subject you delved into was sharing objects and code. Reusing objects and code saves time and money, reduces rewriting errors, and ensures uniform appearance and functionality with minimal effort. The Form Builder features that support reusing objects and code include property classes, object groups, object libraries, and PL/SQL libraries. A property class is an object that holds a collection of properties, property values, and triggers that can be inherited by other objects, thereby ensuring that new objects can quickly take on the characteristics you have deemed important. An added benefit is that if you need to change a property later, the change will automatically be propagated throughout your application. A property class can hold any number of properties, including properties that apply to different object types. Once you apply a property class to an object, you can still decide which property-class properties are inherited from the property class and which are overridden.
Performing a slightly different service are object groups. An object group is a logical container into which you place pointers to other objects in a module. This allows you to group related objectseven objects of different typesso they can be easily copied to, or subclassed within, another module. Providing a more powerful version of this functionality are object libraries, which are separate files you can create and then attach to a form as needed. An object library offers better subclassing features, and it lets you divide the objects visually into tab pages so you can locate the ones you want more quickly. Object libraries are a powerful way to make objects available to other applications, as well as to enforce standards across an organizations applications.
To facilitate reusing code in multiple modules, Form Builder offers PL/SQL libraries. These allow you to store client-side program units and make them available to any form, menu, or library module. A PL/SQL library stores subprograms, including functions, procedures, and packages. Once attached to a module, a PL/SQL librarys program units can be called from your own routines, triggers, and menu item commands. You can attach multiple PL/SQL libraries to a single module, and many modules can attach to the same PL/SQL library. Because a PL/SQL library only loads its program units as an application needs them, it minimizes the demand for client-computer memory. PL/SQL libraries are created and modified under Form Builders PL/SQL Libraries node. To use a PL/SQL library in a module, you attach it under the Attached Libraries node within the appropriate module; the attached library is read-only.
You then moved on to the subject of multiple-form applications. To create multiple-form applications, you write PL/SQL code incorporating built-ins that invoke other forms. The additional forms only consume client memory when they are called, so this is a good way to minimize your applications memory "footprint." Form Builder gives you plenty of options related to running multiple forms. A newly opened form can run simultaneously with the one that called it, or the new form can replace the old one, causing the calling form to close. Multiple instances of the same form can be run. A form can be opened but kept in the background. And multiple forms can either share the same database connection or they can have multiple independent database connections, as if they were running on separate client computers. The OPEN_FORM built-in opens a form and offers options to select whether the called form gets focus, shares library data with other forms, and gets its own database connection. The CALL_FORM built-in opens a form with options determining whether the calling form maintains the focus, whether the two forms share library data, and whether the called form is to be run only in query mode. The NEW_FORM built-in enters the called form and closes the calling form. The GO_FORM built-in moves focus to a specified form, and the EXIT_FORM built-in closes a form.