Tornadofx tableview using comboBox & adding FXEvent on buttons

1.1k views Asked by At

I need help on the following basic issues :

Requirement:
I have a tableview which can be edited (inline) and rows can be deleted too and also if required some complex operations on click of a button.

My Take, below are the 3 small issues:

  1. I created a tableview with isEditable = true and made column name as editable. But when I edit, it doesnt bind to the model. (Must be missing a very simple thing)
  2. When editing, the comboBox shows the options but onselecting a value, throws an exception.

java.lang.ClassCastException: javafx.beans.property.SimpleStringProperty cannot be cast to javafx.beans.property.ObjectProperty

Then, I have added a delete button which works fine when I uncomment tableView.items.removeAt(index)
But since I want some additional functionalities, I decided to go with the FXEvent fire. But how should I use that here.

class MyView : View() {
    val warriorModel : WarriorModel by inject()
    val persons = FXCollections.observableArrayList<Warrior>(
            Warrior(1,"Tyrion Lannister", "M"),
            Warrior(2,"Ned Stark", "M"),
            Warrior(3,"Daenerys Targaryen", "F"),
            Warrior(4,"Arya Stark", "F")
    )
    override val root = vbox {
        tableview(persons) {
            isEditable = true
            column("ID", Warrior::idProperty)
            column("Name", Warrior::nameProperty).makeEditable()
            column("Gender", Warrior::genderProperty).useComboBox(FXCollections.observableArrayList("M", "F"))
            column("Action", Warrior::dummyProperty).setCellFactory { DeleteButton<Warrior>() }
            bindSelected(warriorModel)
            subscribe<DeleteEvent> { event ->
                items.removeAt(event.index)
            }
        }
    }
}
class DeleteButton<Warrior>() : TableCell<Warrior, String?>() {
    internal val btn = Button("Delete")
    override fun updateItem(item: String?, empty: Boolean) {
        super.updateItem(item, empty)
        if (empty) {
            graphic = null
            text = null
        } else {
            btn.setOnAction { event: ActionEvent ->
                //tableView.items.removeAt(index)
                fire(DeleteEvent(index))
            }
            graphic = btn
            text = null
        }
    }
}
class Warrior(id: Int, name: String, gender: String) {

    val idProperty = SimpleIntegerProperty(id)
    var id by idProperty

    val nameProperty = SimpleStringProperty(name)
    var name by nameProperty

    val genderProperty = SimpleStringProperty(gender)
    var gender by genderProperty

    val dummyProperty = SimpleStringProperty("")
}
class WarriorModel : ItemViewModel<Warrior>() {
    val id = bind { item?.idProperty }
    val name = bind { item?.nameProperty }
    val gender = bind { item?.genderProperty }
}
class DeleteEvent(val index: Int) : FXEvent()
1

There are 1 answers

1
Edvin Syse On BEST ANSWER

To access the EventBus from outside of a Component, use the FX.eventbus variable, and fire the event on that:

FX.eventbus.fire(DeleteEvent(index))

The reason you get the error when you change the value in the combobox is a bug in the framework. I have just committed a fix for it, but you can work around it by adding this extension function to your app until the 1.5.10 release:

fun <S, T> TableColumn<S, T?>.useComboBoxWorking(items: ObservableList<T>, afterCommit: ((TableColumn.CellEditEvent<S, T?>) -> Unit)? = null): TableColumn<S, T?> {
    cellFactory = ComboBoxTableCell.forTableColumn(items)
    setOnEditCommit {
        val property = it.tableColumn.getCellObservableValue(it.rowValue) as Property<T?>
        property.value = it.newValue
        afterCommit?.invoke(it)
    }
    return this
}

Just call .useComboBoxWorking() instead for now, and you should be golden :)

If you don't want to use the workaround you can temporarily change the genderProperty of your Warrior to be of type SimpleObjectProperty<String> instead of SimpleStringProperty as an alternative.