or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

borders.mdcomponents.mdcore-builder.mddata-binding.mdindex.mdlayouts.mdmenus.mdtables.md

data-binding.mddocs/

0

# Data Binding

1

2

Groovy Swing provides a comprehensive data binding system that connects UI components to data models automatically, enabling declarative data synchronization.

3

4

## Core Binding Concepts

5

6

The binding system is built around ValueModel interfaces and binding factories.

7

8

```groovy { .api }

9

// Binding factories

10

def bind(Map attributes = [:])

11

def bindGroup(Map attributes = [:])

12

def bindProxy(Map attributes = [:])

13

```

14

15

## ValueModel Interface

16

17

The foundation of the binding system for representing bindable values.

18

19

```groovy { .api }

20

interface ValueModel {

21

Object getValue()

22

void setValue(Object value)

23

Class getType()

24

boolean isEditable()

25

void addValueChangeListener(PropertyChangeListener listener)

26

void removeValueChangeListener(PropertyChangeListener listener)

27

}

28

```

29

30

## ValueModel Implementations

31

32

Concrete implementations of ValueModel for different data scenarios.

33

34

```groovy { .api }

35

// Basic value holder

36

class ValueHolder implements ValueModel {

37

ValueHolder()

38

ValueHolder(Object value)

39

ValueHolder(Object value, Class type)

40

}

41

42

// Property-based model

43

class PropertyModel implements ValueModel {

44

PropertyModel(Object bean, String propertyName)

45

}

46

47

// Nested property model

48

class NestedValueModel implements ValueModel {

49

NestedValueModel(ValueModel parentModel, String childPropertyName)

50

}

51

52

// Closure-based model

53

class ClosureModel implements ValueModel {

54

ClosureModel(Closure readClosure)

55

ClosureModel(Closure readClosure, Closure writeClosure)

56

}

57

58

// Form data model

59

class FormModel {

60

FormModel()

61

FormModel(Map initialValues)

62

63

ValueModel getValueModel(String propertyName)

64

void setProperty(String name, Object value)

65

Object getProperty(String name)

66

}

67

```

68

69

### ValueModel Examples

70

71

```groovy

72

// Basic value holder

73

def nameModel = new ValueHolder('John Doe', String)

74

nameModel.setValue('Jane Smith')

75

println nameModel.getValue() // 'Jane Smith'

76

77

// Property model for bean binding

78

class Person {

79

String name

80

int age

81

}

82

83

def person = new Person(name: 'Alice', age: 30)

84

def nameModel = new PropertyModel(person, 'name')

85

def ageModel = new PropertyModel(person, 'age')

86

87

// Nested property model

88

def addressModel = new PropertyModel(person, 'address')

89

def cityModel = new NestedValueModel(addressModel, 'city')

90

91

// Closure-based model

92

def dynamicModel = new ClosureModel(

93

{ -> person.name.toUpperCase() }, // Read closure

94

{ value -> person.name = value.toLowerCase() } // Write closure

95

)

96

97

// Form model

98

def formModel = new FormModel([

99

firstName: 'John',

100

lastName: 'Doe',

101

email: 'john@example.com'

102

])

103

```

104

105

## Binding Factories

106

107

Factory methods for creating different types of data bindings.

108

109

### Basic Binding

110

111

```groovy { .api }

112

// Simple property binding

113

def bind(Map attributes)

114

115

// Common attributes:

116

// source: Object - source object

117

// sourceProperty: String - property name on source

118

// target: Object - target object (optional, defaults to component)

119

// targetProperty: String - property name on target (optional)

120

// converter: Closure - value converter (optional)

121

// reverseConverter: Closure - reverse converter (optional)

122

// validator: Closure - validation closure (optional)

123

```

124

125

### Binding Examples

126

127

```groovy

128

def swing = new SwingBuilder()

129

def person = new Person(name: 'John', age: 25, email: 'john@example.com')

130

131

swing.frame(title: 'Data Binding Demo') {

132

panel {

133

gridBagLayout()

134

135

// Simple text field binding

136

label(text: 'Name:', constraints: gbc(gridx: 0, gridy: 0))

137

textField(

138

columns: 20,

139

text: bind(source: person, sourceProperty: 'name'),

140

constraints: gbc(gridx: 1, gridy: 0)

141

)

142

143

// Binding with converter

144

label(text: 'Age:', constraints: gbc(gridx: 0, gridy: 1))

145

textField(

146

columns: 20,

147

text: bind(

148

source: person,

149

sourceProperty: 'age',

150

converter: { it.toString() },

151

reverseConverter: { Integer.parseInt(it) }

152

),

153

constraints: gbc(gridx: 1, gridy: 1)

154

)

155

156

// Binding with validation

157

label(text: 'Email:', constraints: gbc(gridx: 0, gridy: 2))

158

textField(

159

columns: 20,

160

text: bind(

161

source: person,

162

sourceProperty: 'email',

163

validator: { it?.contains('@') }

164

),

165

constraints: gbc(gridx: 1, gridy: 2)

166

)

167

168

// Checkbox binding

169

checkBox(

170

text: 'Is Active',

171

selected: bind(source: person, sourceProperty: 'active'),

172

constraints: gbc(gridx: 1, gridy: 3)

173

)

174

}

175

}

176

```

177

178

## Binding Groups

179

180

Binding groups for managing multiple related bindings.

181

182

```groovy { .api }

183

def bindGroup(Map attributes = [:])

184

185

// BindGroup methods:

186

// bind() - Activate all bindings in group

187

// unbind() - Deactivate all bindings in group

188

// update() - Update all bindings in group

189

// reverseUpdate() - Reverse update all bindings

190

```

191

192

### Binding Group Examples

193

194

```groovy

195

swing.frame(title: 'Binding Group Demo') {

196

def person = new Person()

197

198

// Create binding group

199

def personBindings = bindGroup()

200

201

panel {

202

gridLayout(rows: 4, columns: 2)

203

204

label(text: 'First Name:')

205

textField(text: bind(group: personBindings, source: person, sourceProperty: 'firstName'))

206

207

label(text: 'Last Name:')

208

textField(text: bind(group: personBindings, source: person, sourceProperty: 'lastName'))

209

210

label(text: 'Age:')

211

spinner(

212

value: bind(group: personBindings, source: person, sourceProperty: 'age'),

213

model: spinnerNumberModel(minimum: 0, maximum: 120)

214

)

215

216

button(text: 'Update All') {

217

actionPerformed {

218

personBindings.update()

219

}

220

}

221

222

button(text: 'Reset Form') {

223

actionPerformed {

224

personBindings.reverseUpdate()

225

}

226

}

227

}

228

}

229

```

230

231

## Advanced Binding Features

232

233

### Binding Proxies

234

235

Proxy objects for advanced binding scenarios.

236

237

```groovy { .api }

238

def bindProxy(Map attributes = [:])

239

240

// BindingProxy provides:

241

// - Delayed binding resolution

242

// - Dynamic target switching

243

// - Conditional binding activation

244

```

245

246

### Complex Binding Scenarios

247

248

```groovy

249

// List binding with selection

250

swing.frame(title: 'List Binding') {

251

def items = ['Item 1', 'Item 2', 'Item 3'] as ObservableList

252

def selectedItem = new ValueHolder()

253

254

splitPane {

255

// List with items binding

256

scrollPane(constraints: 'left') {

257

list(

258

listData: bind(source: items),

259

selectedValue: bind(source: selectedItem, sourceProperty: 'value')

260

)

261

}

262

263

// Detail panel bound to selection

264

panel(constraints: 'right') {

265

label(text: bind(

266

source: selectedItem,

267

sourceProperty: 'value',

268

converter: { "Selected: $it" }

269

))

270

}

271

}

272

}

273

274

// Table binding with row selection

275

swing.frame(title: 'Table Binding') {

276

def tableData = [

277

[name: 'John', age: 30],

278

[name: 'Jane', age: 25]

279

] as ObservableList

280

281

def selectedRow = new ValueHolder()

282

283

table(

284

model: bind(source: tableData),

285

selectedElement: bind(source: selectedRow, sourceProperty: 'value')

286

) {

287

propertyColumn(header: 'Name', propertyName: 'name')

288

propertyColumn(header: 'Age', propertyName: 'age')

289

}

290

}

291

```

292

293

## Component-Specific Binding Support

294

295

Different components support different binding properties through specialized property classes.

296

297

```groovy { .api }

298

// AbstractButton binding properties

299

// - selected, text, icon, enabled, visible

300

301

// JTextField binding properties

302

// - text, enabled, editable, visible

303

304

// JList binding properties

305

// - listData, selectedIndex, selectedValue, selectedValues

306

307

// JTable binding properties

308

// - model, selectedRow, selectedColumn, selectedElement

309

310

// JComboBox binding properties

311

// - items, selectedItem, selectedIndex

312

313

// JSpinner binding properties

314

// - value, enabled

315

316

// JSlider binding properties

317

// - value, minimum, maximum, enabled

318

319

// JProgressBar binding properties

320

// - value, minimum, maximum, string, stringPainted

321

```

322

323

### Component Binding Examples

324

325

```groovy

326

def model = new FormModel([

327

items: ['A', 'B', 'C'],

328

selectedItem: 'B',

329

percentage: 50,

330

description: 'Sample text'

331

])

332

333

swing.panel {

334

vbox {

335

// Combo box binding

336

comboBox(

337

items: bind(source: model, sourceProperty: 'items'),

338

selectedItem: bind(source: model, sourceProperty: 'selectedItem')

339

)

340

341

// Slider with progress bar

342

slider(

343

minimum: 0, maximum: 100,

344

value: bind(source: model, sourceProperty: 'percentage')

345

)

346

progressBar(

347

minimum: 0, maximum: 100,

348

value: bind(source: model, sourceProperty: 'percentage'),

349

stringPainted: true

350

)

351

352

// Text area binding

353

scrollPane {

354

textArea(

355

rows: 5, columns: 30,

356

text: bind(source: model, sourceProperty: 'description')

357

)

358

}

359

}

360

}

361

```

362

363

## Binding Lifecycle

364

365

Understanding binding activation and cleanup.

366

367

```groovy { .api }

368

// Binding lifecycle methods

369

// bind() - Activate binding and start synchronization

370

// unbind() - Deactivate binding and stop synchronization

371

// update() - Force immediate source-to-target update

372

// reverseUpdate() - Force immediate target-to-source update

373

```

374

375

### Manual Binding Management

376

377

```groovy

378

// Creating and managing bindings manually

379

def nameBinding = swing.bind(

380

source: person,

381

sourceProperty: 'name',

382

target: textField,

383

targetProperty: 'text'

384

)

385

386

// Activate binding

387

nameBinding.bind()

388

389

// Update from source

390

nameBinding.update()

391

392

// Update from target

393

nameBinding.reverseUpdate()

394

395

// Deactivate when done

396

nameBinding.unbind()

397

```