4 Working with Data Binding

This chapter describes how to create Swing containers and components that are bound to data objects from ADF Business Components. It describes the easiest way to create databound containers using the ADF Swing wizards.

This chapter contains the following sections:

4.1 About Working With Data Binding

Data binding in ADF Swing is the ability to create Swing containers and components that are bound to data in back-end business services. To enable data binding, ADF Swing provides a small API that works with the Oracle ADF model layer. The API is exposed in the application source code through a combination of ADF Swing bootstrap code:

  • Call loadCpx() -- load the application metadata (specified in the DataBindings.cpx file), which specifies a connection to the business service implementation instance (for example, an ADF Business Components application module instance) using the ADF data control for the instance, as well as the ADF binding context.

  • Call setBindingContext() -- make the ADF binding context available to the frame or panel.

  • Call createPanelBinding() -- to create an object that will access the business service's contained data collections through Swing component models.

  • Call bindUIControl() on the panel binding to set the ADF model for the individual components of the ADF Swing form or panel.

To work with a data binding in your Swing application, each container (a frame or panel) must either create a panel binding object or get one from the source from which it originated. The frame that creates the first panel binding also contains the ADF Swing bootstrap code, where the connection to the business services is created. Subsequent containers that your application creates either chain from the original panel binding or they create their panel binding in order to display unrelated data. How you want to partition the data views of your application determines whether a container sets a new panel binding or whether it gets an existing one:

  • If you want to create independent branches of the business services views, then your application should open a frame that sets a new panel binding.

  • If you want to maintain the same view along a continuous branch of your application (say a master and detail branch for example), then secondary containers all share the panel binding object created by the initial frame.

4.1.1 ADF Swing Containers

The easiest way to create databound containers is to use the ADF Swing wizards (see the ADF Swing folder in the New Gallery). Specifically, if you use these two ADF Swing wizards, then the source code will contain the bootstrap code and constructors needed to create the panel binding:

  • Use the Create ADF Swing Empty Form dialog to generate an empty frame that creates an ADF Swing panel binding with a connection to the business service used by your application, for example ADF Business Components.

  • Use the Create ADF Swing Empty Panel dialog to generate an empty panel with constructors to create a new panel binding or to share one from its parent frame.

An additional benefit to using these two wizards is their support for easy drag-and-drop UI design within JDeveloper. Because they are generated with the bootstrap code for a specific data control object (which contains the business service's collections, structured objects, attributes, and methods), all of the Swing components that you insert from the Data Controls panel in JDeveloper will have access to any business service that the data control object contains.

4.1.2 Standard Java Containers

If you start with a standard frame or panel (one generated without using the ADF Swing wizards) that you want to enable an ADF Swing data binding for, you can add the appropriate ADF Swing bootstrap code to the main frame and then handle the panel binding in your secondary windows this way:

  • If you want to share the panel binding with the parent frame:

    BusinessCompViewName(getPanelBinding()); frame.setVisible(true);

  • If you want the new frame to define its own panel binding:

    BusinessCompViewName(new JUPanelBinding(getPanelBinding().getApplicationName(),null));frame.setVisible(true);

The first creates the frame object and set the panel binding. The second call makes the frame visible.

4.2 Navigating the UI Using ADF Swing Controls

When you create a default master-detail form using ADF Swing, it will create and place a navigation bar on both the master and the detail panel, which permits users to scroll through the data in each panel independently. Or, you can create a single navigation bar which responds to the panel which has current focus.

4.2.1 How to Navigate Using the Navigation Bar

In the ADF Swing form, you need to move the code for the navigation bar from the individual panels to the layout panel where the navigation event will affect all the child panels. For example, you can move the code from the department and employees data panel to the layout panel.

The code that needs to be moved will be similar to Example 4-1.

Example 4-1 Navigation Bar Code

// The declaration of the navigation bar
private JUNavigationBar navBar = new JUNavigationBar();
 
// The code that binds the navigation bar to the individual panel.
navBar.setModel(JUNavigationBar.getModelInstance(getPanelBinding(), "DepartmentsView", null, "DepartmentsViewIter")); 
 
//Add the navigation bar to the panel
add(navBar, BorderLayout.NORTH);

Once you have moved the navigation bar code you need to add the control binding to the layout panel which contains both the master and the detail panels. Example 4-2 shows the code that you will add to the layout panel to bind the model for the navigation bar.

Example 4-2 Binding Model for Navigation Bar to Panel

//The declaration of the navigation bar
private JUNavigationBar navBar = new JUNavigationBar(); 
 
//Bind the model for the navigation bar to the panel
navBar.setModel(JUNavigationBar.getModelInstance(getPanelBinding(),navBar));  
 
//Add the navigation bar to the panel
add(navBar, BorderLayout.SOUTH); 
add(masterScroller, BorderLayout.NORTH); 
add(detailViewPanel, BorderLayout.CENTER);

4.2.2 How to Navigate Using Tree Navigation

When you add a tree control to your panel you create node-populating rules using the property editor for the ADF Swing node model. The property editor does not allow you to handle node selection. If you want to handle the node-selection event in order to populate controls in a panel, you can use JUTreeDefaultMouseListener to synchronize master and detail panels on the selected node. Example 4-3 shows how to add the listener to the tree control.

Example 4-3 Adding Listener to Tree Control

myTreeControl.addMouseListener(new JUTreeDefaultMouseListener
  ( panelBindingVar, new String [][] {
    { "NodeType1" , "DepartmentViewIter" }, "NodeType2" , "EmployeeViewIter" } }
  )
);

4.3 What You May Need to Know About the ADF Swing Data Context

The ADF SwingPanel interface implemented by ADF SwingFrame or JPanel permits your ADF Swing application to:

  • Maintain a consistent data context between the databound panels (also known as chaining between data panels)

  • Access data through databound Swing controls

During design time, each data browsing panel that you add to the ADF Swing application gets its context for marshaling interactions between the UI controls and the business service's row set iterator from the panel binding object created in the frame or containing panel (such as the master-detail layout panel). The capability in ADF Swing to chain data browsing panels is provided without the need to write additional code. For example, the data browsing panels generated by the wizard, PanelDeptView and PanelEmpView1, share the same data context through an instance of a panel binding (JUPanelBinding) when each JPanel implements the setPanelBinding() and getPanelBinding() methods of the ADF SwingPanel interface.

Once you have a frame or panel that creates this panel binding, ADF Swing permits you to assemble the application by adding new data browsing panels that either share the existing panel binding object or create a new one.

Then you can use the Data Controls panel in JDeveloper to add databound controls one by one to the data panel. At the level of the Swing component, this sets the data binding by specifying an ADF Swing control model on the control's document or model property. At runtime, each control in the data panel becomes databound through the panel binding object as an argument to the control's setModel() or setDocument() method.

4.4 What Happens at Runtime: How Panel Bindings Function

To understand how the panel binding is created and used by the databound panels, consider what happens when you run the application, starting with the ADF Swing frame, and the following ADF Swing code is executed:

  1. The main() method bootstraps the application. It starts a binding context and loads the ADF data control, based on entries in the DataBindings.cpx file. Then it passes the binding context with initialized ADF model objects to the panel binding to create the ADF data bindings.

    For more information, see Section 4.5, "What You May Need to Know About the ADF Swing Bootstrap Code."

  2. The frame is initialized (FrameDeptViewEmpView1, in the example above) through a constructor that takes an application object. Initialization of the frame results in a panel binding object (JUPanelBinding), based on an ADF model definition that may have components that are bound to data from more than one data control. The creation of the panel binding is an important part of the ADF Swing functionality, which enables data binding for Swing components and chaining of data panels.

  3. The frame or applet class initializes a layout panel(MDPanelDeptViewEmpView1, in the example above) and sets the panel binding on the new layout panel, using the setBindingContext() method.

    For more information, see Section 2.3, "What Happens When You Create an ADF Swing Form."

  4. In the layout panel's jbInit() method, the data browsing (children) panels are created. For this, ADF Swing uses the shared binding context for binding the child data panels (PanelDeptView and PanelEmpView1, in the example above).

  5. A control-to-attribute data binding occurs using the control's specified ADF Swing model. (This binding information is stored in the binding container XML metadata.)

  6. The control binding handles events to populate and update data for the UI control

    For more information, see Section 4.9, "What Happens At Runtime: How Control Bindings Function."

4.5 What You May Need to Know About the ADF Swing Bootstrap Code

When you select the frame class in the navigator and choose Run, the main() method “bootstraps” the application. It starts a binding context and loads data controls, based on entries in the DataBindings.cpx file. Then it passes the binding context with initialized data controls to the panel binding to create the ADF data bindings.

Example 4-4 shows the bootstrap code created by the Create ADF Swing Form wizard, using selected columns from the Employees and Departments tables from the HR schema.

Example 4-4 Bootstrap Code Created by Create Form Wizard

// bootstrap application
JUMetaObjectManager.setBaseErrorHandler(new JUErrorHandlerDlg()); 
 
// Lookup the *.cpx file and create all data controls listed in this file. 
JUMetaObjectManager mgr = JUMetaObjectManager.getJUMom(); 
 
// Use the definition classes provided by ADF Swing. Change only if you do not want to use custom DefClasses.
mgr.setJClientDefFactory(null); 
 
// Create a new binding context that extends java.util.Hashtable.
BindingContext ctx = new BindingContext(); 
 
// Get user connection information if available. If not, display logon dialog. 
ctx.put(DataControlFactory.APP_PARAM_ENV_INFO, new JUEnvInfoProvider()); 
 
// Set locale to the default locale of the JVM.
ctx.setLocaleContext(new DefLocaleContext(null)); 
 
// Load data binding container data binding file.
HashMap map = new HashMap(4); map.put(DataControlFactory.APP_PARAMS_BINDING_CONTEXT, ctx);
mgr.loadCpx("mypackage.DataBindings.cpx", map); 
 
// Get handle to the ADF Business Components application module. The code lines
// below are added only when using the ADF Swing Form wizard. Declaratively creating
// the frame, starting with an empty form wizard does not add the following lines.
DCDataControl app = (DCDataControl)ctx.get("model_AppModuleDataControl");
app.setClientApp(DCDataControl.JCLIENT); 
 
// Despite the following line of code, attribute sets and fetches are normally 
// performed in one batch operation. This requires only one network round 
// trip. Attributes that aren't needed are not loaded to the client. The code 
// line below is added only when using the ADF Swing Form wizard. Declaratively creating
// the frame, starting with an empty form wizard does not add the following lines.
app.getApplicationModule().fetchAttributeProperties(new String[] {"DepartmentsView1", "EmployeesView3"}, new String[][] {{"DepartmentId", "DepartmentName" }, {"EmployeeId", "FirstName", "LastName" "DepartmentId" }}, null); 
 
// Initialize application root class.
FormDepartmentsView1EmployeesView3 frame = new FormDepartmentsView1EmployeesView3();
 
 
// Set binding context to the frame.
frame.setBindingContext(ctx);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();

The frame is initialized by its constructor, which does not expect any arguments by default. The binding context of the application is passed to the setBindingContext() method of the frame.

Initialization of the frame results in a panel binding object (JUPanelBinding) based on an Oracle ADF model definition that may have components that are bound to data from more than one data control. The creation of the panel binding is an important part of the ADF Swing functionality, which enables data binding for Swing components and chaining of data panels.

After you lay out the data panel or form, you can improve the performance of your ADF Swing application by defining the fetchAttributeProperties() method in your form. This will ensure that your form performs in batch mode to fetch attribute values. For more information, see Section 10.4, "How to Limit Fetching of ADF Business Components Attributes in ADF Swing."

4.6 How to Display Object Attributes in a Databound Text Field

When using databound text fields to display the attribute values of an object, such as those defined by an Address object, the fields will not display the attribute values (they will display instead some static text).

If you bind a text field to an object attribute, you can ensure the value is correctly displayed in the ADF Swing panel by forcing the query to reexecute on the iterator binding of the bound object.

To force executeQuery() on the object's iterator binding, add this method call after jbInit() is done in the panel, where Street is replaced by the name of your object attribute binding:

panelBinding.findControlBinding("Street").getIteratorBinding().
executeQuery();

It is only necessary to call executeQuery() on one of the object domain attributes (like Street) to force the iterator binding to refetch all attribute values of the same object.

4.7 How to Create a New Row in a Databound Table or Tree Control

If your business services supports create operation on the data collection, you can use the operation in an ADF Swing panel to display a new row in your databound table or tree control. The new row will appear when the user clicks a Create button that has been bound to a create-and-insert action binding. Because the operation creates and inserts the row in a single step, this operation is ideal for in-place editing of the component by the user.

Note:

Although the Data Controls panel displays this operation as Create, the action binding editor will be set to CreateInsert. This behavior differs in the case of web applications.

To create an ADF Swing bound control that uses a create operation to insert a row:

  1. Open the data panel in the Java visual editor.

    For more information, see Section 2.10, "How to Create an Empty ADF Swing Panel."

  2. In the Data Controls panel, select the collection node that contains the attributes you want your UI component to display. This is the collection to which the control will add the row. In the dropdown list, select the desired UI component to display the new row.

    Note:

    The data collection you select must not be a detail collection, represented in the data control hierarchy as a child of another collection node. This will ensure that the iterator binding does not perform navigation.

  3. Drag the collection into the open ADF Swing panel.

    For more information, see Section 3.3, "How to Insert UI Components into ADF Swing Panels."

    Tip:

    Optionally, your panel can display navigation buttons to the allow the user to browse the collection. For more information, see Section 7.8, "How to Use the JUNavigationBar Control."

  4. To insert the operation to create the row, select Create in the operations folder for the previously selected collection and select Button from the dropdown list.

  5. Insert the button into the open ADF Swing panel.

    Note:

    When you drag Create from the Data Controls panel, the button's action binding is set to CreateInsert. This behavior differs in the case of web applications.

4.8 How to Sort Columns in a Databound Table

When you use the ADF iterator binding to create a databound Swing table, you can specify the sort criteria (ascending or descending) in which the data of the table columns display. You can use one or more columns as the sort criteria, and specify the sort priority of each of those columns. For example, to sort an employee table you could choose last name as the first sort criteria and first name as the second sort criteria. In this example, the second sort criteria becomes useful when two or more employees have the same last name thus requiring sorting, in the specified order, by first names.

When sorting the columns in a databound table, you use the iterator binding for the table.

To sort the columns in a table bound to an ADF iterator binding:

  1. In the Java visual editor, right-click the form or panel that contains the table you want to sort and choose Go to Page Definition.

  2. In the overview for the page definition editor, double-click the iterator binding in the Executables list.

  3. In the Edit Iterator Binding dialog, click the Sort Criteria tab.

  4. Select an attribute and choose whether the sort should be performed in ascending or descending order.

    If the attribute is not a sort criteria for the table, leave the No Sort selection assigned.

  5. Use the up and down arrow buttons to change the sort priority of an attribute.

    Moving the sort criteria attribute higher in the list, yields a higher sort priority, which means the attribute will be sorted before the sort criteria attributes that appear lower in the list.

4.9 What Happens At Runtime: How Control Bindings Function

After data browsing panels are initialized, the layout panel calls executeIfNeeded() on the panel binding to execute the query on the ADF Business Components data source.

4.9.1 Populating Controls with Data

The executeIfNeeded() method determines whether the query has executed on the view object. If not, the method calls executeQuery() on it. This executed query brings data from the database into the cache and causes the ADF Business Components row set listener events to fire. The first among these is the RowSetListener.rangeRefreshed event. This event is captured by the iterator binding (because it implements RowSetListener and has registered itself as a listener). It retrieves the rows of the range and calls updateValuesFromRows() on the control binding. The control binding takes the data out from the rows and assigns them to the controls using the Swing API. As a result, the Swing API updates the panel UI with the data.

4.9.2 Updating Data through Controls

The user's interaction with an ADF Swing-bound control may cause ADF Business Components to update the data. For example, in the case of the text field (textFieldDname), if the user edits the text field's content and leaves the control (generating focusLost event), ADF Swing is notified of the event. As a result, ADF Swing will retrieve the updated data from the control and call setAttribute() on the row.