Many of the controls on the JBCL page of the component palette are data-aware. For example, the ChoiceControl
, ListControl
,
and TextFieldControl
components can display and edit data in a column
of a data set, while the GridControl
can display and edit the data
in all rows and columns of a data set.
You can also use the JBCL controls
with data that doesn't come from a data set. For example, your application
can supply a list of items to display in a ListControl
. Because
the data isn't obtained from a data set, the list control is not being
used as a data-aware component.
This section describes how to make a component data aware. These are the topics covered:
ListControl
component is made data aware.Every data-aware control must have a way to identify the data set it links with,
and the column it accesses. So each data-aware control needs
a dataSet
property and most need a columnName
property. Grids
are examples of controls that won't need columnName
, as they display
all columns in a data set. Data-aware status bars also don't need columnName
; they don't display the data, but report on the status of data operations.
For controls that need only a dataSet
property, implement the DataSetAware
interface, which contains just the getter and setter methods for a single dataSet
property. For controls that need both a dataSet
and a columnName
property, implement the ColumnAware
interface. ColumnAware
contains just the getter and setter methods for a columnName
property, but it extends the DataSetAware
interface, so your data-aware control must implement the dataSet
property, too.
Data-aware components can implement four additional data-access interfaces: AccessListener
,
DataChangeListener
, NavigationListener
, and StatusListener
. Each interface
has just one method to implement. See the following table:
Data-access interfaces
|
||
Interface | Method | Purpose |
|
||
AccessListener |
accessChange() |
Responds to AccessEvent occurrences, such as opening, posting
to, and closing a data set. |
DataChangeListener |
dataChanged() |
Responds to DataChangeEvent occurrences, such as a change in
a data value or the data being sorted. |
NavigationListener |
navigated() |
Responds to NavigationEvent occurrences, such as the user moving
to a new row in the control. |
StatusListener |
statusMessage() |
Responds to StatusEvent occurrences, such as loading the data, throwing exceptions, and so on. It is used primarily by data-aware status display controls, such as JBCL's StatusBar . |
|
ListControl
component has an openDataSet()
method that opens the data set and calls another method, bindDataSet()
,
which fetches the data and ultimately displays it in the list control.
ListControl
also has a setItems()
method, an accessor method
for the items
property, which holds the list of strings in the list
control.
ColumnAware
or DataSetAware
interface to provide a dataSet
and columnName
property, or just a dataSet
property.AccessListener
interfaceDataChangeListener
interfaceNavigationListener
interfaceListControl
component found on the Controls page of the component
palette is a data-aware control. The Writing
a composite component section introduced you to the some of the inner
workings of ListControl
. This section again uses ListControl
and shows you what makes it a data-aware control. Find the source code
for ListControl
in the controls
package and refer to it as you read through the remainder
of this chapter.
ListControl
is defined as extending the ListView
class and as implementing several
interfaces, including the three data-access interfaces: AccessListener
,
DataChangeListener
, and NavigationListener
. Here is the beginning
of the ListControl
declaration:
public class ListControl extends ListView implements NavigationListener, DataChangeListener, AccessListener, VectorSubfocusListener, WritableVectorModel, BlackBox, ColumnAware { . . .Note that
ListControl
implements the ColumnAware
interface, so ListControl
must have a dataSet
and a columnName
property.
ListControl
has a dataSet
property:
public DataSet getDataSet() { return dataSet; } public void setDataSet(DataSet newDataSet) { if (dataSet != null) { dataSet.removeAccessListener(this); dataSet.removeNavigationListener(this); dataSet.removeDataChangeListener(this); } openDataSet(newDataSet); if (dataSet != null) { dataSet.addAccessListener(this); dataSet.addNavigationListener(this); dataSet.addDataChangeListener(this); } }The
setDataSet()
method opens the specified data set and the control registers itself as a listener for access, navigation, and data change events in the data set.
ListControl
displays data from a column of the specified data set. Which column it displays depends on
the setting of the columnName
property:
private String columnName; public String getColumnName() { return columnName; } public void setColumnName(String newColumnName) { columnName = newColumnName; if (addNotifyCalled) openDataSet(dataSet); }
setColumnName()
specifies the column to use in the data set and
calls the openDataSet()
method, passing to it the value of the dataSet
property.
openDataSet()
method makes the data set passed to it the current dataSet
property value. If a data set is specified an attempt is made to open the data set. If the data set is now open, the control binds with the data in the data set.
Here is the code for openDataSet()
in its entirety:
private void openDataSet(DataSet newDataSet) { dataSet = newDataSet; if (dataSet == null) { buildStringList(null); return; } else if (addNotifyCalled && !dataSet.isOpen()) { try { dataSet.open(); } catch (DataSetException ex) { DataSetException.handleException(dataSet, this, ex); return; } } if (dataSet.isOpen()) { bindDataSet(); } }As you can see, once the
dataSet
property has a new value, openDataSet()
checks to see if the dataSet
value is null. If it isn't and
the ListControl
is initialized, openDataSet()
attempts to call the
open()
method of the data set. Finally openDataSet()
calls the bindDataSet()
method.
So far the data set is open and the ListControl
is registered
as a listener of critical, data-access events, but data from the data set
has not yet been retrieved. This is the task of bindDataSet()
.
In Understanding model-view component architecture,
you learned how a model-view component fetches the data from the model
and passes it to a view manager, which selects a painter to paint the item
in the view. In the Writing a composite component
section, you saw how ListControl
set the model
property
value using the BasicVectorContainer
class. You also saw that ListControl
specified the BasicViewManager
as the view manager. These classes
are sufficient for a control that is not data aware.
The bindDataSet()
method specifies an instance of the VectorDataSetManager
class as the model and view manager. VectorDataSetManager
is a data-aware
class that allows vector JavaBeans to access the data set. Here is the
beginning of the bindDataSet()
method:
private boolean bindDataSet() { Column column; if (dataSet != null && (column = dataSet.hasColumn(columnName)) != null) { setBatchMode(true); VectorDataSetManager cursorManager = new VectorDataSetManager(dataSet, column, this); super.setModel(cursorManager); super.setViewManager(cursorManager); navigateDataSet = true; . . .The last line shown sets a
navigateDataSet
variable to true. It is used by the navigateWithDataSet
property. This is how navigateWithDataSet
is defined:
public boolean isNavigateWithDataset() { return userSetNavigate; } public void setNavigateWithDataSet(boolean navigate) { userSetNavigate = navigate; if (userSetNavigate && navigateDataSet && dataSet != null && dataSet.isOpen()) setSubfocus(dataSet.row()); }
bindDataSet()
sets navigateDataSet
to true after the model and view
manager are set. When navigateWithDataSet
is set to true and
a data set has been specified and the specified data set is open, setSubfocus()
is called. setSubfocus()
makes the current row of the data set column the subfocus item of the ListControl
.
The next lines of code in bindDataSet()
look like this:
resetSelection(); bindProperties(column); if (topIndex > -1) { super.setTopIndex(topIndex); topIndex = -1; } if (isShowing() && !isBatchMode()) doLayout(); ...
resetSelection()
makes the new subfocus item the selected item in
the list control, or, if the multiSelect
property is true,
adds all selected items to the selection pool. It also repaints the control.
Here is the resetSelection()
method:
private void resetSelection() { if (multiSelect) { int[] selections = getSelection().getAll(); setSelection(new BasicVectorSelection()); getSelection().add(selections); } else { setSelection(new SingleVectorSelection()); getSelection().add(getSubfocus()); } repaint(50); // repaints in 50 milliseconds }Writing a composite component describes what happens when
ListControl
repaints. Briefly, the data is fetched
from the model, passed to an item painter managed by the view manager, and the
item painter paints the control. When ListControl
displays the data in
a data set, however, a different model and view manager are used. When
ListControl
is being used as a data-aware control, an instance of
the VectorDataSetManager
class gets the data from the model and
specifies the item painter to use for painting the data in the list control.
The bindProperties()
method gives the column displayed in the list control the same color, font, and alignment attributes as the column it accesses in the data set.
AccessListener
interface has one method to implement: accessChange()
. This is the implementation of accessChange()
in ListControl
:
public void accessChange(AccessEvent event) { switch(event.getID()) { case AccessEvent.OPEN: try { openDataSet(dataSet); } catch (Exception ex) { event.appendException(ex); } break; case AccessEvent.CLOSE: safeEndEdit(false); buildStringList(null); break; default: break; } }
accessChange()
is called whenever an access event occurs. An access
event occurs whenever an attempt is made to open, close, or change the
data set. ListControl's accessChange()
method processes open and
close access events. If an open access event occurs, the method tries to
open the data set. If a close access event occurs, the accessChange()
ends any edit session and fills the control with null values.
DataChangeListener
interface
has one method to implement: dataChanged()
. This is the implementation
of dataChanged()
in ListControl
:
public void dataChanged(DataChangeEvent e) {}
dataChanged()
is called whenever a data change event occurs. A data
change event occurs whenever one or more rows in the data set are added,
changed, or deleted. ListControl's
dataChanged()
method does nothing.
NavigationListener
interface has one method to implement: navigated()
. This is the
implementation of navigated()
in ListControl
:
public void navigated(NavigationEvent e) { if (!dsNavigating && userSetNavigate && navigateDataSet && getSubfocus() != dataSet.row()) setSubfocus(dataSet.row()); }
navigated()
is called whenever a navigation event occurs. A navigation
event occurs when an attempt to change the subfocus item in ListControl
is made. For example, if the user presses the down-arrow key to move
to the next item in the list control and, therefore, the next row in the
data set, a navigation event occurs.
navigated()
sets the subfocus on the column specified as the value
of columnName
in the current row of the data set.
When setSubfocus()
is called, a subfocus event occurs. ListControl
overrides the subfocusChanging()
method, which is called in
response to a changing of the focus. Here is the subfocusChanging()
method of ListControl
:
public void subfocusChanging(VectorSubfocusEvent e) throws VetoException ) { if (dataset != null && dataSet.isOpen() && navigateDataSet) { if (dataSet.row() != e.getLocation()) { try { dsNavigation = true; if (!dataSet.goToRow(e.getLocation())) { dsNavigating = false; throw new VetoException(); } catch (DataSetException ex) { DataSetException.handleException(dataSet, this, ex); dsNavigating = false; throw new VetoException(); } } dsNavigating = false; } }