bekwam courses

TableView Binding in Kotlin

September 16, 2016

This article describes how to create JavaFX TableView selection expressions using Kotlin. A TableView is loaded with three inventory Items. The app has two Buttons which are enabled and disabled based on selections in the TableView. The TableView / Button interactions are set up using JavaFX Binding.

JavaFX Binding gives programmers the ability to express relationships between UI controls without having to write extra objects and methods. Moreover, a functional approach to relating controls requires an initial case. Binding is a one-liner that will cover the initial case and the behavior which helps developers write more code that can be maintained in a single place.

Functionality

The following series of screenshots highlights the behavior of the application. Initially, the TableView is displayed with no rows selected. Neither the Inventory nor the Tax Button is enabled.

TableView With No Items Selected
Buttons Disabled When No Selection

When row is selected, the Inventory Button is enabled. This screenshot shows the first item selected.

TableView With First Items Selected
Inventory Enabled On Selection

The previous screenshot showed the Tax Button also enabled. This was because that particular item had a taxable value of "true". This next screenshot shows what happens when the selected item is taxable "false".

TableView With Taxable Item Selected
Tax Button Disabled If Field Is False

TornadoFX

The TornadoFX JavaFX / Kotlin framework gives this example a convenient App subclass and a set of Type Safe Builder for constructing a UI using declarations. The following class declaration typically involves multiple lines of code to create a Scene, Stage, and a main. (TableSelectView contains the root Node of the Scene and will be presented later.)

class TableSelectApp : App(TableSelectView::class)

Kotlin Data Classes simplify creating JavaBeans. This code defines a four-field tuple of an inventory Item. This single line would typically be written with multiple fields, getters and setters, and extra overridden methods like equals(), hashCode(), and toString().

data class Item(val sku : String, val descr : String, val price : Float, val taxable : Boolean)

View

TableSelectView is a TornadoFX construct called a View. This is an object containing the root Node of a Scene as well as a reference to the Stage. The root Node is created using the Type Safe Builders of TornadoFX. While FXML is supported in TornadoFX, Type Safe Builders are encouraged to take advantage of the numerous shortcuts in the framework. See the upcoming columns() declaration in the root assignment as an example.

This View overrides the superclass and supplies a title "TableSelectApp" to the primaryStage. The class then overrides the root Node property and procedes to build out a nested UI control hierarchy using the Type Safe Builders.

class TableSelectView : View("TableSelectApp") {

  override val root = vbox {
    tblItems = tableview(items) {

        column("SKU", Item::sku)
        column("Item", Item::descr)
        column("Price", Item::price)
        column("Taxable", Item::taxable)

        prefWidth = 667.0
        prefHeight = 376.0

        columnResizePolicy = CONSTRAINED_RESIZE_POLICY

        vboxConstraints {
            vGrow = Priority.ALWAYS
        }
    }
    hbox {
        btnInventory = button("Inventory")
        btnCalcTax = button("Tax")

        spacing = 8.0
    }

    padding = Insets(10.0)
    spacing = 10.0
  }

A VBox, the root Node, contains the TableView and an HBox of Buttons. At the end of the code block, the VBox is assigned a padding of 10 and a spacing of 10.

The TableView is created on a data structure of items which will be presented later. The following four columns() declarations produce TableColumn / PropertyValueFactory mappings which relate the fields TableView model element -- an Item -- to specific TableColumns. The width, height, and columnResizePolicy of the TableView are set. Finally, vboxConstraints are set which is the technique TornadoFX uses to invoke the static VBox.setVGrow() method on the TableView Node.

Two Buttons are added to the HBox and a spacing is set.

Type Safe Builder References

Several of the objects created in a Type Safe (the VBox, the HBox) can be encapsulated in the declaration. The TableView and the two Buttons need to be exposed to the rest of the program in order to apply the Binding expressions. In TornadoFX, this is done with an assignment in the Type Safe Builder paired with a declaration in the class. The class declaration is supplemented with the singleAssign() directive. These are the class declarations.

  var tblItems : TableView<Item> by singleAssign()
  var btnInventory : Button by singleAssign()
  var btnCalcTax : Button by singleAssign()

Binding

Two JavaFX Binding expressions are used to relate the TableView to each of the two Buttons. The first Binding expression disables the Inventory Button when there is no selection made in the TableView. The expression is written in the View class init() method which is a constructor.

  init {
    btnInventory.disableProperty().bind(
        tblItems.selectionModel.selectedItemProperty().isNull
    )

The Tax Button incorporates the same condition -- disabled when no selection -- and adds a condition which considers a data field "taxable" in the selected item. The second condition is joined using the or() method. Bindings.select() is a method that navigates the Item data structure to pull out the taxable value.

    btnCalcTax.disableProperty().bind(
        tblItems.selectionModel.selectedItemProperty().isNull().or(
            Bindings.select<Boolean>(
                tblItems.selectionModel.selectedItemProperty(),
                "taxable"
            ).isEqualTo( false )
        )
    )

This example highlighted the power of JavaFX Binding by using a single pair of declarative statements to relate a TableView to Buttons. Traditionally, one might set up an initial case and then add listener objects to the TableView to manipulate the Buttons. JavaFX Binding consolidates this code into a single place. The TornadoFX framework gave this example a convenient App class which eleminated the need to write main, Stage, and Scene handling code.

Using Kotlin and TornadoFX can significantly reduce the amount of code in your JavaFX application. I wrote this same program in Java for the FXDocs project and that program was roughly 150 lines of Java code compared with the 75 line of Kotlin code presented here. Most of the savings can be attributed to the Kotlin Data Class construct and the TornadoFX App class.

Resources

The source code presented in this video series is an IntelliJ IDEA project found in the zip file below.

TableSelectAppKotlin Source Zip (4Kb)
Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc