Adding SWT Input Validation the Easy Way
Any input provided by a user in a GUI application must typically be validated in one way or another. There is a number of ways this gets done, while some applications have just ignored the matter altogether.
When crafting an Eclipse RCP application, there are some help provided by SWT and JFace. We can add ModifyListeners and VerifyListeners to certain SWT widgets. JFace also provides ControlDecorations to help us indicate to the user where a problem with a specific input value exists.
The problem is that these are at a low level, and we need to do a lot of "monkey"-coding just to add basic validation and error indication to a widget, and then we're not even touching the world of input masks. If you're like me, you want to concentrate on solving your business problem, and don't want to write lots of basic UI code over and over.
This is where the RCP Toolbox is very useful. It provides a light-weight validation framework (among other features) that makes it much easier to add validation and input masks to SWT Text, Combo and CCombo widgets.
The goal
Let us have a look at how to define a basic wizard for creating a new Booking. This wizard must capture the following fields from the user:
And of course we want indicators next to each field when an error or warning condition exists in the field, as well as a message being written to the WizardDialog's message area. For fun we want the user to be able to get a quick-fix option on the date field for setting it to the current time.
The Validation framework
The RCP Toolbox provides a number of custom widgets and a easy to use validation framework. Adding validation starts with the ValidationToolkit class. This class gets instantiated to work with a specific type of contents, and is then used to create ValidatingField instances that can handle that type of contents.
The rest of the framework deals with interfaces and default implementations to facilitate the validation of contents, definition of input masks, provision of quick-fixes, error-handling and conversion of the input text to specific class types.
The WizardPage and the ValidationToolkits
We start by first defining our BookingWizardPage class and instantiating the necessary ValidationToolkit instances.
//Not all imports are shown
import com.richclientgui.toolbox.validation.IFieldErrorMessageHandler;
import com.richclientgui.toolbox.validation.ValidationToolkit;
import com.richclientgui.toolbox.validation.converter.DateStringConverter;
import com.richclientgui.toolbox.validation.converter.IntegerStringConverter;
import com.richclientgui.toolbox.validation.string.StringValidationToolkit;
public class BookingWizardPage extends WizardPage {
private static final int DECORATOR_POSITION = SWT.TOP | SWT.LEFT;
private static final int DECORATOR_MARGIN_WIDTH = 1;
private static final int DEFAULT_WIDTH_HINT = 150;
private StringValidationToolkit strValToolkit = null;
private ValidationToolkit<Date> dateValToolkit = null;
private ValidationToolkit<Integer> intValToolkit = null;
private final IFieldErrorMessageHandler errorMessageHandler;
public BookingWizardPage() {
super("booking.pageone","New Booking Entry", null);
errorMessageHandler = new WizardPageErrorHandler();
}
public void createControl(Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(2, false));
strValToolkit = new StringValidationToolkit(DECORATOR_POSITION,
DECORATOR_MARGIN_WIDTH, true);
strValToolkit.setDefaultErrorMessageHandler(errorMessageHandler);
intValToolkit = new ValidationToolkit<Integer>(new IntegerStringConverter(),
DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true);
intValToolkit.setDefaultErrorMessageHandler(errorMessageHandler);
dateValToolkit = new ValidationToolkit<Date>(new DateStringConverter(),
DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true);
dateValToolkit.setDefaultErrorMessageHandler(errorMessageHandler);
//TODO: create ValidatingFields
setControl(composite);
}
}
The StringValidationToolkit class we instantiate in line 28 is a ValidationToolkit that deals specifically with ValidatingFields that have normal String contents. In line 32 we instantiate a typed instance of ValidationToolkit that will create ValidatingFields that only takes Integers as input.
We must provide a way that the contents of the fields are converted from a String to the correct content type. This is done with a set of coverter classes provided by the framework. In lines 32 and 36 we specify a IntegerStringConverter and a DateStringConverter to convert Integer and java.util.Date values respectively.
The framework makes use of the JFace org.eclipse.jface.fieldassist.ControlDecoration and related classes to indicate whether a field has an error or warning condition, whether it is a required field, and if there is a quick-fix available (by right-clicking on the decorator icon) for the current error or warning condition. The position of these decorator icons relative to the input widgets as well as the margin width between the decorator icon and the widget can be specified when constructing a ValidationToolkit. All the fields created by this ValidationToolkit instance will use the same settings for there decorator icons. In lines 9 - 10 we have defined some constants for the decorator position and margins, and we use this for constructing all the ValidationToolkit instances.
Handling the error messages
We also make use of an IFieldErrorMessageHandler to get feedback from the validation process. The validation framework will call these error handlers when error or warning conditions occur, and allow us to do something with those messages. By default these messages are only displayed in the tooltips of the decorator icons. A default error handler can be specified for each toolkit instance, or a separate handler can be set for each ValidatingField if so required.
The inner class WizardPageErrorHandler implements the IFieldErrorMessageHandler interface and basically just set the messages on the WizardPage's message area.
//inner class of BookingWizardPage
class WizardPageErrorHandler implements IFieldErrorMessageHandler {
public void handleErrorMessage(String message, String input) {
setMessage(null, DialogPage.WARNING);
setErrorMessage(message);
}
public void handleWarningMessage(String message, String input) {
setErrorMessage(null);
setMessage(message, DialogPage.WARNING);
}
public void clearMessage() {
setErrorMessage(null);
setMessage(null, DialogPage.WARNING);
}
}
The actual error or warning messages are generated by the various IFieldValidator implementations (we'll get to those), and can easily be customized by implementing custom validators.
Creating a simple ValidatingField
Of course just having some toolkit instances does not help us much. We need actual input widgets that are being validated. The first step is to update the createControl method.
public void createControl(Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(2, false));
strValToolkit = new StringValidationToolkit(DECORATOR_POSITION,
DECORATOR_MARGIN_WIDTH, true);
strValToolkit.setDefaultErrorMessageHandler(errorMessageHandler);
intValToolkit = new ValidationToolkit<Integer>(new IntegerStringConverter(),
DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true);
intValToolkit.setDefaultErrorMessageHandler(errorMessageHandler);
dateValToolkit = new ValidationToolkit<Date>(new DateStringConverter(),
DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true);
dateValToolkit.setDefaultErrorMessageHandler(errorMessageHandler);
createNameField(composite);
createDateField(composite);
createNumberPersonsField(composite);
createTelephoneNumberField(composite);
setControl(composite);
}
Then we can look at creating our first validated input field that makes use of a SWT Text widget to capture the name of the person doing the booking.
private void createNameField(Composite composite) {
new Label(composite, SWT.NONE).setText("Booking Name:");
final ValidatingField<String> nameField = strValToolkit.createTextField(
composite, new IFieldValidator<String>(){
public String getErrorMessage() {
return "Name may not be empty.";
}
public String getWarningMessage() {
return "That's a very short name...";
}
public boolean isValid(String contents) {
return !(contents.length()==0);
}
public boolean warningExist(String contents) {
return contents.length() < 3;
}
}, true, "");
GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false);
gd.widthHint = DEFAULT_WIDTH_HINT;
nameField.getControl().setLayoutData(gd);
}
Since this field works with String contents, we make use of the strValToolkit instance to create the field in line 4 above. We specify the parent composite that the input widget must be added to, the IFieldValidator that will be used to validate the field contents, whether this is a required field or not (thus whether the required decorator icon must be shown or not) and an initial empty string value for the field. Note that this call will also create a Text widget to be used for the field, but the API allows that you can create your own Text, Combo or CCombo instance and pass that to the toolkit to use when creating a new ValidatingField.
An anonymous inner class implementation of IFieldValidator is specified in lines 5 - 23. We're doing some very basic validation checks in this example, but it is easy to implement validators that makes use of other heavy-weight business validation frameworks. Our validator will indicate an error condition if the contents of the field is empty (line 16), in which case the error message "Name may not be empty." will be displayed (line 8). This validator will also indicate a warning condition if the name field contains less than 3 characters (line 20) with the message "That's a very short name..." (line 12).
In lines 24 - 26 we set the layout of the input widget on the composite.
- Login or register to post comments
- 8419 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)









