Programmer's Guide



Chapter 14   Form Basics

The SilverStream form is the basis for a visually rich and functionally powerful front end to a database application. This chapter introduces the SilverStream form, its built-in features, and its uses. Specifically, it covers these topics:

This chapter assumes that you understand and can navigate the Form Designer. If you are not familiar with the SilverStream design environment, see Form Designer in the online Tools Guide or the Tutorial for more information.

About forms   Top of page

The AgfForm control is the basic building block of your interactive user interface. You can use a form control as:

Each time that you create a form using the SilverStream Form Designer, SilverStream creates a class that extends com.sssw.rt.form.AgfForm. The class declaration automatically includes four instance variables, or helper objects, that provide methods to manage various aspects of the form. The instance variables are:

The Form API   Top of page

In addition to its primary job of providing an interactive user interface, a form is also a control, and as such you can program it the way you would any other SilverStream control. That means that forms have properties, methods, and events. The following sections describe some of the form's basic properties, methods, and events.

Form properties   Top of page

The form contains properties that allow you to control its look and feel. For example, you can:

Other form properties allow you to control the form's access to data. For example, you can set properties that:

Form methods   Top of page

The form's methods allow you to change the control's properties and perform other functions at run time. For example, you can:

Form events   Top of page

The form's events allow you to control program execution by coding responses to certain actions. The form can:

In addition, the AgfForm class by default implements all of the following standard Java listener interfaces: java.awt.event.ActionListener, java.awt.event.ItemListener, and java.awt.event.WindowListener.

Forms and data   Top of page

Many of the SilverStream controls are data-aware (that is, they can be bound to a field in a database table). However, it is most important to understand the relationship between the SilverStream form control and the database, because the form control manages data access and transaction scope.

The SilverStream form control provides access to database data through an instance variable called agData. The agData variable is available on every form, whether the form is displayed as a subform, a stand-alone form, or a dialog box. You use agData's methods to navigate (such as agData.gotoNext()) and manipulate the data (such as agData.insertAfter()). The contents of agData are obtained from the server and are a copy of the database's contents.

The agData instance variable provides access to the form's primary table. The concept of a primary table is extremely important in the SilverStream environment because you can only update the data from the form's primary database table. Consequently, controls that you add to the form that access data from a table other than the primary table can display data, but in most cases, cannot update it.

If you have an application that requires you to update multiple database tables on a single form, you have several options. See Working with data from multiple tables for information on these options.

When you create the form, you have the option to bind it to a table. If you bind the form to a table, you can specify form properties to control what data is bound and how it is bound (for example, as read-only).

    For information on:

Working with data from multiple tables   Top of page

In many cases, you will want to access data from multiple database tables within a single form. SilverStream restricts database update to a single primary database table for forms, but provides other mechanisms to update multiple database tables. The other mechanisms include using subforms or AgcData controls.

Using subforms to update multiple databases

SilverStream subforms are regular AgfForm control objects that are added to another form in the design environment. Because they are instances of AgfForm, subforms have the same properties and can perform the same functions as any other form. As a result, the subform has a primary dataset associated with it. It has an agData object, and is populated and manipulated in the same way as a stand-alone form object.

If you call agData.updateRows() on the container (or parent) form, then all changes made to the form and any objects on that form (including subforms, views, and AgcData controls) are sent to the server. The same rules apply for these transactions as for any others: if any one of the changes fails, then the entire transaction fails and all changes are rolled back.

If you call agData.updateRows() on the subform, then only changes made to the subform and its controls are submitted to the server. To explicitly call the updateRows() method on the subform control, you must specify the subform name. Otherwise, SilverStream assumes that you are referring to the container form. This is how to specify an update on a subform.

  subformname.agData.updateRows() 

Using the AgcData control to update multiple database tables

The AgcData control provides access to database tables other than the primary table managed by agData. To support access to multiple datasets on a single form, simply add as many AgcData controls to your form as there are datasets that you want to access.

For more information on AgcData controls, see:

Controls and data   Top of page

SilverStream supports data binding for most controls. Data binding associates the control's current value with a database column. In addition to data binding, list-type controls can have lists loaded from data. It's important to understand the distinction so that you use the right techniques to get the result you want.

Data binding

Most SilverStream controls can be bound to data, meaning that the "value" of a control is linked to a database column. For example, when you go to the next record in the table, a text field's value is set to the value in the record's field. If you change the value in the text field, the data in the column changes and will be changed in the database if you update the data.

You data-bind a control using the Data Column property in the inspector, or by using agDataMgr.bind(). (The agDataMgr instance variable on the form is an instance of rt.event.AgoBindingManager.)

Loading a list control with data

List-type controls can be loaded from data, as well as bound to data. List-type controls include AgcJList, AgcJComboBox, AgcJTree, AgcJTable, AgcList, AgcChoice, AgcComboBox, and AgcTreeControl. For example, the choices in an AgcJList can be loaded from a database table. You can also data-bind the AgcJList as well, so that the selection in the list is set according to the data in the column, and if you change the selection, the data in the column changes too.

You load a control with data by setting the Load Choices property to From Table, by using the loadFromRowCursor() method, or adding data to a data model and associating it with the control.

    For procedures, see binding data to controls and providing a list of values for list, combo box, and tree controls in the Form Designer section of the Tools Guide.

Forms and data validation   Top of page

SilverStream provides several ways to check data that the user enters in a form. You can check the data as the user enters data in each field and you can check whether the data in several fields are correctly coordinated when the user leaves that data record. This control-level and form-level validation is all done on the client. SilverStream also provides table-level validation on the server. For more information about table-level validation, see the section about creating validation rules for tables in the Table Designer section in the Help system.

Where and how to check for valid data   Top of page

You can supply client-side validation in these ways:

How control-level validation is performed   Top of page

When a control's value changes, these things happen:

  1. SilverStream tests the user's entry against the validation expressions entered in the Property Inspector.

  2. If the data causes the validation expression to be:

  3. In the validationTest event, if the data fails your programmatic tests, your code throws an AgoValidationException to trigger the validationFailed event.

  4. The validationFailed event occurs.

  5. If code in the validationFailed event doesn't throw an AgoValidationException with a disposition of success, the globalValidationFailed event fires.

  6. If code in the globalValidationFailed event doesn't throw an AgoValidationException with a disposition of success and Show Validation Messages is true for the form, then SilverStream displays the error message in a dialog box.

The error message is the last message provided for the error, in this order:

The diagram illustrates the process for validating a control. For each of the events that SilverStream fires, you write code that evaluates the data and throws the exception that passes or fails the data.

How form-level validation is performed   Top of page

You can validate data at the form level in the events globalValidationTest and globalValidationFailed. Global validation allows you to test the relationships between data in different fields and prevent the database from being updated if the data is not correct. For global validation, you can check any field on the form. No specific control or newly changed value is associated with global validation so you can't access old and new control values or the event source or call the correction() method of AgoValidationException as you can in the validationTest event.

When validation occurs

You can explicitly trigger form-level validation by calling agDataMgr.validateAll(), a method of AgoBindingManager. The globalValidationTest event also fires when the user tries to "leave" the current record. The user leaves a record by:

The globalValidationTest event does not fire when the user would not have modified the record. For example, these methods do not fire a globalValidationTest event: agData.delete(), agData.doFind(), agData.refreshRows()

In addition, the user can navigate to an invalid record but not away from it, even if they have not changed the data, because the bad data fails validation.

Validation process

This is the form-level validation process:

  1. Control-level validation is performed for all the controls (described above). It can proceed to globalValidationFailed if invalid data is found .

  2. If control-level validation succeeds, the form fires the globalValidationTest event.

  3. In the globalValidationTest event, if the data fails your programmatic tests, your code throws an AgoValidationException with a disposition of failure to trigger the globalValidationFailed event and the user is prevented from navigating to another record.

  4. If code in the globalValidationFailed event doesn't throw an AgoValidationException with a disposition of success and Show Validation Messages is true for the form, then SilverStream displays the error message in a dialog box.

    The error message is either a generic error message provided by SilverStream or the message specified in the most recently thrown exception.

The diagram illustrates the process for form-level validation.

Responding to good and bad values with AgoValidationException   Top of page

To specify what happens after each validation event, you throw an AgoValidationException using one of its static factory methods to create an object with the appropriate disposition. The method you use determines what happens next. You can supply a new value, trigger a failure event, or change the message that will be displayed. The following sections discuss each of the validation events and how to respond to good and bad values.

Coding in the validationTest event

In the validationTest event, you can approve, change, or reject the value:

If the value is...

Code in the event should...

Valid

Return.

Incorrect but the control is a bound control and the application can supply a correct value

Create the exception object with the correction() method. SilverStream sets the correct value in the control and the data cache. validationFailed is not fired.

  throw AgoValidationException.correction( 
   correctValue)

Not valid with no additional information

Throw the exception with a failure disposition. SilverStream fires validationFailed.

  throw AgoValidationException.failure( ) 

Not valid, but the application wants to supply a message

Throw the exception specifying a message. This message is substituted for any previous message associated with this validation. SilverStream fires validationFailed.

  throw AgoValidationException.failure(msg) 

Not valid, but the application has a suggested new value

Throw the exception specifying a message and a suggested value. This message is substituted for any previous message associated with this validation. SilverStream sets the supplied value in the control, but not in the data cache. SilverStream fires validationFailed.

  throw AgoValidationException.failure( 
   msg, correctValue)

Additional coding in the validationTest event

In the validationTest event for a control, the AgoPropertyChangeEvent object contains information about the changed data. You can get:

NOTE   In versions of SilverStream before 3.0, you could use the getChangeSource() method to determine if the control's value changed by user action. This was not reliable and does not work at all with Swing controls.

What caused the data to change?

In Version 3.0, if you need to know whether the change was caused by user action or in your program code, you can set a flag each time you change the value programmatically and check it in the validationTest event. This example illustrates how to do this and where to put the code.

In the declarations section of the form, declare this variable:

  // Flag declared in form declarations section 
boolean bTextFieldProgramChange = false;

When the form's code changes the control's value programmatically, set the flag:

  bTextFieldProgramChange = true; 
fldMyData.setText("Autofill");
bTextFieldProgramChange = false;

In the validationTest event for the control, check whether bTextFieldProgramChange is true:

  // In the validationTest event for the field, don't allow the user 
// to leave the value empty or specify the value "Autofill"
if (bTextFieldProgramChange == false && (evt.getNewValue() == null       || evt.getNewValue().equals("Autofill"))) {
   throw AgoValidationException.failure( );

In the validationFailed event, respond to the validation failure, which is triggered only when the user changed the value:

  // In the validationFailed event, substitute a value 
throw AgoValidationException.failure(
   "Please change the data to a something valid.", "Error");

Coding in the validationFailed event

In the validationFailed event, you can approve, change, or reject the value:

If the value is...

Code in the event should...

Valid

Throw the exception with a disposition of success. globalValidationFailed is not fired.

throw AgoValidationException.success( )

Incorrect but the control is a bound control and the application can supply a correct value

Create the exception, using the correction() method, which supplies the correct value. SilverStream sets the correct value in the control and the data cache. globalValidationFailed is not fired.

  throw AgoValidationException.correction( 
   correctValue)

Not valid with no additional information

Return.

SilverStream automatically fires globalValidationFailed.

Not valid, but the application is supplying a message

Throw the exception specifying a message. This message is substituted for any previous message associated with this validation. SilverStream fires globalValidationFailed.

  throw AgoValidationException.failure(msg) 

Not valid, but the control is a bound control and the application is supplying a message and a suggested new value

Throw the exception specifying a message and a suggested value. This message is substituted for any previous message associated with this validation. SilverStream sets the supplied value in the control, but not in the data cache. SilverStream fires globalValidationFailed.

  throw AgoValidationException.failure( 
   msg, correctValue)

Coding in the globalValidationTest event

In the globalValidationTest event, you can compare values in multiple controls and throw a validation failure if the comparisons reveal invalid values. Since globalValidationTest is associated with the whole form, not individual controls, you cannot supply corrected values or call evt.getSource():

If the values are...

Code in the event should...

Valid

Return.

Not valid with no additional information

Throw the exception with a failure disposition. SilverStream fires globalValidationFailed.

  throw AgoValidationException.failure( ) 

Not valid, but the application is supplying a message

Throw the exception specifying a message. This message is substituted for any previous message associated with this validation. SilverStream fires globalValidationFailed.

  throw AgoValidationException.failure(msg) 

Coding in the globalValidationFailed event

In the globalValidationFailed event, you can approve or reject the value:

If the value is...

Code in the event should...

Valid

Throw the exception with a disposition of success. SilverStream does not display any error message.

  throw AgoValidationException.success( ) 

Not valid with no additional information

Return.

If the Show Validation Errors property is true, SilverStream automatically displays the current validation message.

Not valid, but the application is supplying a message

Throw the exception specifying a message. This message is substituted for any previous message associated with this validation. If the Show Validation Errors property is true, SilverStream automatically displays the current validation message.

  throw AgoValidationException.failure(msg) 

Displaying validation messages to the user   Top of page

By default, when the validation process gets to the globalValidationFailed event, SilverStream displays a validation failure message in a dialog box.

The default message for validation rules is "Validation failed", but for each validation rule, you can specify your own message. For properties that specify limits on the control's value, SilverStream generates a message describing the limits to the user.

You can modify the message or supply a new message in any of the validation events. When you specify a message for AgoValidationException, it replaces previous messages that SilverStream or you have created in response to the event. The last message specified for the exception is the one that is displayed to the user.

If you don't want the dialog box, you can turn it off by clearing the Show Validation Errors check box in the Data section on the form's property sheet. In any of the events, you can write code that displays the message currently associated with the AgoValidationException object, your version of the message, or any other message.

If you turn off the default dialog box, you will want to write code for the validationFailed event to let the user know what to do. Your code might display a validation error message in a label control, in the browser status bar, or in a message box:

  lblStatus.setText( 
   "Bad product name: please enter more than one character.");

Compiling, debugging, and testing forms   Top of page

You use the SilverStream Form Designer to design a SilverStream form object. When you start the SilverStream Designer, SilverStream creates a visualization of the form (showing what it will look like when instantiated); however, at design time, it is not an actual instance of the form object.

SilverStream compiles the form, the controls and the event code, when you select File>Check Syntax in the Programming Editor or File>Save from the Form Designer. For compile operations, SilverStream first generates a .JAVA file, which it then compiles to a .CLASS file on the local machine. By default the .JAVA and .CLASS files are saved to the following directories on the local file system:

  SilverStream/compilecache/servername/databasename/source/com/sssw/gen/forms 
SilverStream/compilecache/servername/databasename/classes/com/sssw/gen/forms

You can specify a directory other than SilverStream through the Preferences dialog. Note that the servername and databasename correspond to the server and database names where the forms that you are testing reside. Thus, if you maintain multiple servers or databases on a single machine, there will be multiple directories.

In addition, the source and compiled files are also written to the server. The files on the server are the ones used at run time. The files saved to the local machine are cached for compiling only.

Each time a compile occurs, SilverStream regenerates the .JAVA file, so you should not make changes directly to the file. You should only use the Programming Editor to write or modify your SilverStream applications.

Testing and running forms   Top of page

The Form Designer supplies a Test Mode that you can use to run a form at any point during the application development cycle. In Test Mode, you interact with a single form; you cannot navigate to other forms. To do that, see Testing a multiform application.

NOTE   Test Mode simulates running a Java application and behaves as if you are running the form in SilverJRunner. However, agGeneral.getEnvironment() returns a different result in Test Mode and in SilverJRunner.

To test a form:

While testing, there are several ways to examine the behavior of your forms:

Methods for debugging

The form package includes several methods that are useful for debugging:

Testing a multiform application

Test Mode lets you work with a single form and its subforms. However, you can't navigate to other forms. To test a form in the context of your whole application, you can use the Run action button in the Main Designer.

The Run button starts SilverJRunner and loads the selected form. You can interact with the form and navigate to other forms.

To test your application:

  1. In the Main Designer, select the starting form for the set of forms that you want to test.

  2. Click the Run action icon at the bottom of the Designer window.

SilverStream runs SilverJRunner and loads your form. You can navigate to other forms via menus or buttons on the starting form.






Copyright © 2000, SilverStream Software, Inc. All rights reserved.