TornadoFX как добавить проверку при редактировании TableView

Рассмотрим следующий пример:

class Item(name: String, number: Int) {
    val nameProperty = SimpleStringProperty(name)
    var name by nameProperty

    val numberProperty by lazy { SimpleIntegerProperty(number) }
    var number by numberProperty
}

class MainView : View("Example") {
    val items = listOf(Item("One", 1), Item("Two", 2)).observable()

    override val root = vbox {
        tableview(items) {
            column("Name", Item::nameProperty).makeEditable()
            column("Number", Item::numberProperty).makeEditable(NumberStringConverter())
            enableCellEditing()
        }
    }
}

Как я могу добавить validator при редактировании ячеек? Единственный способ сделать это - добавить rowExpander с некоторым textfield и попытаться проверить модель там?


person skiey    schedule 24.07.2017    source источник


Ответы (1)


Вы можете реализовать свою собственную фабрику ячеек и вернуть ячейку, которая показывает текстовое поле, привязанное к ViewModel, в режиме редактирования и метку, если нет. В качестве альтернативы, если вас устраивает всегда отображать текстовое поле, вы можете использовать cellFormat и привязать текущий элемент к ItemModel, чтобы вы могли прикрепить проверку:

class ItemModel(item: Item) : ItemViewModel<Item>(item) {
    val name = bind(Item::nameProperty)
    val number = bind(Item::numberProperty)
}


class MainView : View("Example") {
    val items = listOf(Item("One", 1), Item("Two", 2)).observable()

    override val root = vbox {
        tableview(items) {
            column("Name", Item::nameProperty).makeEditable()
            column("Number", Item::numberProperty).cellFormat {
                val model = ItemModel(rowItem)
                graphic = textfield(model.number, NumberStringConverter()) {
                    validator {
                        if (model.number.value == 123) error("Invalid number") else null
                    }
                }
            }
        }
    }
}

Это будет выглядеть так:

Решение CellFormat

Хотя это работает, это довольно расточительно, поскольку узлы часто воссоздаются. Я бы рекомендовал подход номер один, если производительность вызывает беспокойство, пока мы не получим cellFragment поддержку TableView, как у нас есть для ListView.

РЕДАКТИРОВАТЬ: я реализовал поддержку cellFragment, поэтому можно создать более надежное решение, которое будет отображать метку, когда не находится в режиме редактирования, и проверяющее текстовое поле, когда вы входите в режим редактирования.

class ItemModel : ItemViewModel<Item>() {
    val name = bind(Item::nameProperty)
    val number = bind(Item::numberProperty)
}


class MainView : View("Example") {
    val items = listOf(Item("One", 1), Item("Two", 2)).observable()

    override val root = vbox {
        tableview(items) {
            column("Name", Item::nameProperty).makeEditable()
            column("Number", Item::numberProperty).cellFragment(NumberEditor::class)
        }
    }
}

class NumberEditor : TableCellFragment<Item, Number>() {
    // Bind our ItemModel to the rowItemProperty, which points to the current Item
    val model = ItemModel().bindToRowItem(this)

    override val root = stackpane {
        textfield(model.number, NumberStringConverter()) {
            removeWhen(editingProperty.not())
            validator {
                if (model.number.value == 123L) error("Invalid number") else null
            }
            // Call cell.commitEdit() only if validation passes
            action {
                if (model.commit()) {
                    cell?.commitEdit(model.number.value)
                }
            }
        }
        // Label is visible when not in edit mode, and always shows committed value (itemProperty)
        label(itemProperty) {
            removeWhen(editingProperty)
        }
    }

    // Make sure we rollback our model to avoid showing the last failed edit
    override fun startEdit() {
        model.rollback()
    }

}

Это будет возможно начиная с TornadoFX 1.7.9.

person Edvin Syse    schedule 24.07.2017
comment
Я обновил ответ более надежным решением с использованием TableCellFragment, представленного в TornadoFX 1.7.9 :) - person Edvin Syse; 25.07.2017
comment
Здорово! Вы можете проверить это, уже используя сборку моментального снимка :) - person Edvin Syse; 26.07.2017
comment
Я пробовал это в своем проекте, но казалось, что model.item могло быть null каким-то образом (компилятор бросил NPE здесь if (model.number.value == 123L) error("Invalid number") else null), поэтому мне пришлось добавить null дополнительную проверку validator лямбда перед доступом к свойствам модели. После этого это сработало. Еще раз спасибо, Эдвин :) - person skiey; 26.07.2017