Kotlin 如何将自定义属性双向绑定到textfield?

Kotlin 如何将自定义属性双向绑定到textfield?,kotlin,tornadofx,Kotlin,Tornadofx,我想在文本字段中显示一个复杂的对象。这在使用stringBinding时运行良好。但我不知道如何使其双向,以便文本字段是可编辑的 package com.example.demo.view import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty import tornadofx.* class MainView : View("Hello Torna

我想在文本字段中显示一个复杂的对象。这在使用
stringBinding
时运行良好。但我不知道如何使其双向,以便文本字段是可编辑的

package com.example.demo.view

import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.*

class MainView : View("Hello TornadoFX") {
    val complexThing: Int = 1
    val complexProperty = SimpleObjectProperty<Int>(complexThing)

    val complexString = complexProperty.stringBinding { complexProperty.toString() }

    val plainString = "asdf"
    val plainProperty = SimpleStringProperty(plainString)

    override val root = vbox {
        textfield(complexString)
        label(plainProperty)
        textfield(plainProperty)
    }
}
package com.example.demo.view
导入javafx.beans.property.SimpleObject属性
导入javafx.beans.property.SimpleStringProperty
导入tornadofx*
类MainView:View(“Hello TornadoFX”){
val complexThing:Int=1
val complexProperty=SimpleObjectProperty(complexThing)
val complexString=complexProperty.stringBinding{complexProperty.toString()}
val plainString=“asdf”
val plainProperty=SimpleStringProperty(纯字符串)
覆盖val root=vbox{
textfield(complexString)
标签(普通属性)
textfield(纯属性)
}
}
当我运行此命令时,
纯字符串
是可编辑的,我看到标签更改,因为编辑将返回到属性中


如何编写自定义处理程序,或者需要使用什么类来读写stringBinding?我浏览了大量的属性和绑定文档,但并没有看到任何明显的内容。

这可能可以更清晰地完成。我打赌有办法绕过这片额外的土地。这个例子很脆弱,因为为了保持简单,它没有进行输入检查。但它可以证明解决方案:

class Point(x: Int, y: Int) {
    val x: Int = x
    val y: Int = y
}

class PointConverter: StringConverter<Point?>() {
    override fun fromString(string: String?): Point? {
        val xy = string?.split(",")
        return Point(xy[0].toInt(), xy[1].toInt())
    }

    override fun toString(point: Point?): String {
        return "${point?.x},${point?.y}"
    }
}

class MainView : View("Hello TornadoFX") {
    val point = Point(5, 6)
    val pointProperty = SimpleObjectProperty<Point>(point)
    val pointDisplayProperty = SimpleStringProperty()
    val pointStringProperty = SimpleStringProperty()
    val pc = PointConverter()

    init {
        pointDisplayProperty.set(pc.toString(pointProperty.value))
        pointStringProperty.set(pc.toString(pointProperty.value))
        pointStringProperty.addListener { observable, oldValue, newValue ->
            pointProperty.set(pc.fromString(newValue))
            pointDisplayProperty.set(pc.toString(pointProperty.value))
        }
    }

    override val root = vbox {
        label(pointDisplayProperty)
        textfield(pointStringProperty)
    }
}
类点(x:Int,y:Int){
val x:Int=x
val y:Int=y
}
类PointConverter:StringConverter(){
覆盖趣味fromString(string:string?):点{
val xy=字符串?.split(“,”)
返回点(xy[0].toInt(),xy[1].toInt())
}
重写funtostring(点:点?):字符串{
返回“${point?.x},${point?.y}”
}
}
类MainView:View(“Hello TornadoFX”){
val点=点(5,6)
val pointProperty=SimpleObject属性(点)
val pointDisplayProperty=SimpleStringProperty()
val pointStringProperty=SimpleStringProperty()
val pc=PointConverter()
初始化{
pointDisplayProperty.set(pc.toString(pointProperty.value))
pointStringProperty.set(pc.toString(pointProperty.value))
pointStringProperty.addListener{可观察,oldValue,newValue->
pointProperty.set(pc.fromString(newValue))
pointDisplayProperty.set(pc.toString(pointProperty.value))
}
}
覆盖val root=vbox{
标签(pointDisplayProperty)
textfield(pointStringProperty)
}
}
Ta Da

class Point(val x: Int, val y: Int) //You can put properties in constructor

class PointConverter: StringConverter<Point?>() {
    override fun fromString(string: String?): Point? {
        if(string.isNullOrBlank()) return null //Empty strings aren't valid
        val xy = string.split(",", limit = 2) //Only using 2 coordinate values so max is 2
        if(xy.size < 2) return null //Min values is also 2
        val x = xy[0].trim().toIntOrNull() //Trim white space, try to convert
        val y = xy[1].trim().toIntOrNull()
        return if(x == null || y == null) null //If either conversion fails, count as invalid
        else Point(x, y)
    }

    override fun toString(point: Point?): String {
        return "${point?.x},${point?.y}"
    }
}

class MainView : View("Hello TornadoFX") {
    val point = Point(5, 6) //Probably doesn't need to be its own member
    val pointProperty = SimpleObjectProperty<Point>(point)
    val pc = PointConverter()

    override val root = vbox {
        label(pointProperty, converter = pc) //Avoid extra properties, put converter in construction
        textfield(pointProperty, pc)
    }
}
类点(val x:Int,val y:Int)//您可以将属性放入构造函数中
类PointConverter:StringConverter(){
覆盖趣味fromString(string:string?):点{
if(string.isNullOrBlank())返回null//空字符串无效
val xy=string.split(“,”,limit=2)//仅使用2个坐标值,因此max为2
如果(xy.size<2)返回null//Min值也是2
val x=xy[0]。trim().toIntOrNull()//修剪空白,尝试转换
val y=xy[1]。修剪().toIntOrNull()
如果(x==null | | y==null)null//返回如果任一转换失败,则计为无效
else点(x,y)
}
重写funtostring(点:点?):字符串{
返回“${point?.x},${point?.y}”
}
}
类MainView:View(“Hello TornadoFX”){
val point=point(5,6)//可能不需要是它自己的成员
val pointProperty=SimpleObject属性(点)
val pc=PointConverter()
覆盖val root=vbox{
label(pointProperty,converter=pc)//避免额外的属性,将转换器放入构造中
textfield(pointProperty,pc)
}
}
我只是返回null,对转换为“account”的无效输入进行了编辑。这只是一个简单的创可贴解决方案,它不强制输入正确,但是它拒绝在你的属性中放置坏的值。