The following properties can be used with either the long or contextual binding syntax

Bindings are usually setup in one direction. If this property is specified with a value of true then a bidirectional binding will be created instead.

import groovy.beans.Bindable
import groovy.swing.SwingBuilder

class MyModel { @Bindable String value }

def model = new MyModel() def swing = new SwingBuilder() swing.edt { frame(title: 'Binding', pack: true, visible: true) { gridLayout(cols: 2, rows: 3) label 'Normal' textField(columns: 20, text: bind('value', target: model)) label 'Bidirectional' textField(columns: 20, text: bind('value', target: model, mutual: true)) label 'Model' textField(columns: 20, text: bind('value', source: model)) } }

Typing text on textfield #2 pushes the value to model, which in turns updates textfield #2 and #3, demonstrating that textfield #2 listens top model updates. Typing text on textfield #2 pushes the value to textfield #3 but not #1, demonstrating that textfield #1 is not a bidirectional binding.

Transforms the value before it is sent to event listeners.

import groovy.beans.Bindable
import groovy.swing.SwingBuilder

class MyModel { @Bindable String value }

def convertValue = { val -> '*' * val?.size() }

def model = new MyModel() def swing = new SwingBuilder() swing.edt { frame(title: 'Binding', pack: true, visible: true) { gridLayout(cols: 2, rows: 3) label 'Normal' textField(columns: 20, text: bind('value', target: model)) label 'Converter' textField(columns: 20, text: bind('value', target: model, converter: convertValue)) label 'Model' textField(columns: 20, text: bind('value', source: model)) } }

Typing text on textfield #1 pushes the value to the model as expected, which you can inspect by looking at textfield #3. Typing text on textfield #2 however transform's every keystroke into an '*' character.

Guards the trigger. Prevents the event from being sent if the return value is false or null.

import groovy.beans.Bindable
import groovy.swing.SwingBuilder

class MyModel { @Bindable String value }

def isNumber = { val -> if(!val) return true try { Double.parseDouble(val) } catch(NumberFormatException e) { false } }

def model = new MyModel() def swing = new SwingBuilder() swing.edt { frame(title: 'Binding', pack: true, visible: true) { gridLayout(cols: 2, rows: 3) label 'Normal' textField(columns: 20, text: bind('value', target: model)) label 'Converter' textField(columns: 20, text: bind('value', target: model, validator: isNumber)) label 'Model' textField(columns: 20, text: bind('value', source: model)) } }

You can type any characters on textfield #1 and see the result in textfield #3. You can only type numbers on textfield #2 and see the result in textfield #3

This type of validation is not suitable for semantic validation (a.k.a. constraints in domain classes). You would want to have a look at the Validation plugin.

Maps a different event type, instead of PropertyChangeEvent.

Specify a value that may come from a different source. Usually found in partnership with sourceevent.

import groovy.beans.Bindable
import groovy.swing.SwingBuilder

class MyModel { @Bindable String value }

def model = new MyModel() def swing = new SwingBuilder() swing.edt { frame(title: 'Binding', pack: true, visible: true) { gridLayout(cols: 2, rows: 3) label 'Text' textField(columns: 20, id: 'tf1') label 'Trigger' button('Copy Text', id: 'bt1') bind(source: bt1, sourceEvent: 'actionPerformed', sourceValue: {tf1.text}, target: model, targetProperty: 'value') label 'Model' textField(columns: 20, text: bind('value', source: model)) } }

A contrived way to copy text from one textfield to another. The copy is performed by listening to ActionEvents pumped by the button.