TornadoFX 如何在编辑 TableView 时添加验证
TornadoFX how to add validation while editing 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
并尝试在那里验证模型吗?
您可以实现自己的 cellfactory 和 return 一个单元格,该单元格在处于编辑模式时显示绑定到 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
}
}
}
}
}
}
它看起来像这样:
虽然它有效,但有点浪费,因为经常重新创建节点。如果性能是一个问题,我会推荐第一种方法,直到我们像对 ListView 一样获得对 TableView 的 cellFragment
支持。
EDIT:我实现了 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 开始成为可能。
考虑以下示例:
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
并尝试在那里验证模型吗?
您可以实现自己的 cellfactory 和 return 一个单元格,该单元格在处于编辑模式时显示绑定到 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
}
}
}
}
}
}
它看起来像这样:
虽然它有效,但有点浪费,因为经常重新创建节点。如果性能是一个问题,我会推荐第一种方法,直到我们像对 ListView 一样获得对 TableView 的 cellFragment
支持。
EDIT:我实现了 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 开始成为可能。