How to dynamically create bound controls when the data source is not known until the form is running.
You can run this technique code from:
NOTE First make sure that database is running on your localhost SilverStream Server | |
See Instantiating SilverStream form controls in the chapter on advanced form techniques in the Programmer's Guide For more about the operations of the setDataSource DSOs used in the example, see Using a setDataSource DSO in Application Techniques |
When frmInstantiateControls opens, it has no data entry fields. When the user chooses a data source, the example creates data-bound controls for the properties (columns) of the selected data source. Buttons for navigating the result set continue to work when the data source changes because they call methods of the AgcData control, not the underlying data source.
The valueChanged event for cboxChooseSource handles the three choices in the list: No data, Employees, and Companies.
private void handle_cmbxChooseSource_valueChanged(AgoPropertyChangeEvent evt) { String newValue = (String) evt.getNewValue(); // Only recreate controls if user's choice is different if (newValue != evt.getOldValue() ) { deleteOldControls(); if (newValue.equals("No data") || newValue == null) return; if (newValue.equals("Employees") ) { dataSource.setDataSource("dsoRDBEmployees"); } else if (newValue.equals( "Companies") ) { dataSource.setDataSource("dsoRDBCompanies"); } getData(); instantiateControls(); } }
deleteOldControls()
removes all the old label and data-entry controls from the form.
dataSource
AgcData control. dsoRDBEmployees
and dsoRDBCompanies
are setDataSource DSOs, a type of triggered business object. For more information, see the chapter on using data source business objects in the Programmer's Guide
getData()
retrieves data for the selected data source (see
Retrieving the data).
instantiateControls()
creates data-entry controls for the columns of the data source (see
Adding controls dynamically).
When the AgcData control is assigned a different data source object, you need to call invokeQuery()
to retrieve data.
public void getData() { // Retrieve data for the selected data source try { String sOrderBy = ""; String sQuery = ""; Object[] objParms = new Object[2]; objParms[0] = sOrderBy; objParms[1] = sQuery; dataSource.invokeQuery(objParms); dataSource.gotoFirst(); } catch (Exception e) { e.printStackTrace(); agDialog.showMessage("Failed to get data: \n\n" + e.toString()); } }
invokeQuery()
. The two empty strings are passed in an array of type Object.
gotoFirst()
succeeds then at least one row of data was successfully retrieved. Otherwise, it throws an exception.
The instantiateControls()
method creates a label and a data entry field for each of the properties of the data source. agDataMgr.bind()
creates the data-binding between the field and the property. Local variables keep track of the position of the controls on the form. Embedded comments explain how the code works.
public void instantiateControls() { // Instantiate controls to match the property list for // the selected data source. // First, set up the initial coordinates. //Start 10 pixels below the combobox Rectangle rectSource = cmbxChooseSource.getBounds(); int iTopMargin = rectSource.y + rectSource.height + 10; // Line it up on the left with the combobox int iLeftMargin = rectSource.x; // Keep them above the navigation buttons int iBottomY = btnFirst.getBounds().y; // Don't go beyond the right edge of the form. int iRightMostX = this.getBounds().width - CONTROL_WIDTH; int iCurrentY = iTopMargin; int iCurrentX = iLeftMargin; // For each property in data source, create a label // and a textfield. for (int i = 0; i < dataSource.getPropertyCount(); i++) { // Create a label using the property name and add it to // the form and control array AgcJLabel lblControl = new AgcJLabel(); lblControl.setName("lbl" + i); lblControl.setBounds(iCurrentX, iCurrentY, CONTROL_WIDTH, CONTROL_HEIGHT - 4); lblControl.setText( dataSource.getPropertyName(i) ); // add it to the form this.add(lblControl); // Keep track of the created control in the array dataControlsArray[i*2] = lblControl; // Create a text field to display the data and add it to // the form and control array AgcJTextField fldControl = new AgcJTextField(); fldControl.setName("fld" + dataSource.getPropertyName(i) ); fldControl.setBounds(iCurrentX, iCurrentY + CONTROL_HEIGHT, CONTROL_WIDTH, CONTROL_HEIGHT); // Bind the field to the property agDataMgr.bind(fldControl, "Text", "getText", "setText", dataSource, dataSource.getPropertyName(i) ); // add it to the form this.add(fldControl); // Keep track of the control in the array dataControlsArray[i*2+1] = fldControl; // Increment coordinates for next label and text field iCurrentY += CONTROL_HEIGHT * 2; // If we are down to the buttons, start a new column. if ( ( iCurrentY + (CONTROL_HEIGHT * 2)) > iBottomY) { // back to the top iCurrentY = iTopMargin; // Add 10 pixels between columns of fields iCurrentX = iCurrentX + CONTROL_WIDTH + 10; } // If we are beyond the right edge of the form, stop! if (iCurrentX > iRightMostX) break; } }
The dataControlsArray
instance variable is populated with references to all the controls created in the instantiateControls()
method.
public void deleteOldControls() { // // Delete the objects contained in the dataControlsArray // from the form; then set the entry in the array to null. // for (int i = 0; i < dataControlsArray.length; i++) { if (dataControlsArray[i] != null) { this.remove( dataControlsArray[i] ); dataControlsArray[i] = null; } } }
this.remove()
removes the control from the form.