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.
The AgfForm control is the basic building block of your interactive user interface. You can use a form control as:
agDialog.showForm()
or agDialog.showFormDialog()
from a container form. As shown in the following hierarchy diagram, SilverStream forms extend the java.awt.Container class.
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:
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.
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:
The form's methods allow you to change the control's properties and perform other functions at run time. For example, you can:
getBackgroundColor()
, setBackgroundColor()
, getBackgroundImage()
, setBackgroundImage()
, getBackgroundImageMode()
, and setBackgroundImageMode()
).addControl()
, addControlAt()
, removeControl()
).getParentForm()
, closeDialog()
, fireEvent()
).getUser()
, userInGroup()
).debugPrint()
).
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.
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).
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.
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()
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:
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.
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.
You can supply client-side validation in these ways:
When a control's value changes, these things happen:
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.
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.
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:
agData.gotoFirst()
)agData.insertAfter()
)agData.updateRows()
) When updating occurs via a menu item, you can make sure the control currently being edited has processed its new value by calling acceptValues()
, a method of PVBaseContainer.
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.
This is the form-level validation process:
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.
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.
In the validationTest event, you can approve, change, or reject the value:
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:
evt.getNewValue()
evt.getOldValue()
evt.getSource()
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");
In the validationFailed event, you can approve, change, or reject the value:
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()
:
In the globalValidationFailed event, you can approve or reject the value:
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.");
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.
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.
The Form Designer generates a .JAVA file, then compiles a .CLASS file the same way it does when you choose File>Check Syntax or File>Save.
While testing, there are several ways to examine the behavior of your forms:
println
method is written to the console. You can open the console and view the output from the Main Designer by selecting View>Java Console.
The form package includes several methods that are useful for debugging:
debugPrint()
This method prints a string to the console when you run a form in debug mode. When you run a form in a browser, the debug messages go to the browser's console window. If you do not specify Debug mode, the debugPrint()
method does nothing, so you can leave these method calls in your code. debugPrint()
is a method in the AgfForm class.showStatus()
This method displays a message in the browser's status bar. You do not have to save the form in Debug mode to use this method. The showStatus()
method is available on the form's agBrowser instance variable. showMessage()
This method displays an informational message to the user in a modal dialog. You can use this method for your own testing in Test mode or in a production environment. showMessage()
is an overloaded method that allows you to customize the message and the dialog box in which it is displayed. You do not have to save the form in Debug mode to use this method. showMessage()
is available on the form's agDialog instance variable. displayError()
This method displays messages from a Java exception in a dialog box. Use this method in either a Test mode or a production environment. You do not have to save the form in Debug mode to use this method. displayError()
is available on the form's agDialog instance variable. You can use the showMessage()
and showStatus()
methods in Simple Actions mode, as well as in Java mode.
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.
SilverStream runs SilverJRunner and loads your form. You can navigate to other forms via menus or buttons on the starting form.