从列表属性到任意对象属性的javafx绑定

从列表属性到任意对象属性的javafx绑定,javafx,kotlin,tornadofx,Javafx,Kotlin,Tornadofx,我试图让一个类将一个属性绑定到另一个类的list属性,其中第一个属性是通过对列表中的对象进行汇总计算得到的。下面的代码是我的生产代码的简化版本。(生产代码正在对DateTime对象进行汇总——下面代码的基本部分是列表和对象属性之间的绑定(为了简单起见,这里是字符串)。) 我试过各种各样的东西。一种方法是在下面的Summary类的列表中使用addListener,但是我遇到了奇怪的错误,侦听器回调在Summary对象上进行更新。在做了大量阅读之后,我认为摘要字符串和列表之间的绑定更合适,但我不知道

我试图让一个类将一个属性绑定到另一个类的list属性,其中第一个属性是通过对列表中的对象进行汇总计算得到的。下面的代码是我的生产代码的简化版本。(生产代码正在对DateTime对象进行汇总——下面代码的基本部分是列表和对象属性之间的绑定(为了简单起见,这里是字符串)。)

我试过各种各样的东西。一种方法是在下面的Summary类的列表中使用
addListener
,但是我遇到了奇怪的错误,侦听器回调在Summary对象上进行更新。在做了大量阅读之后,我认为摘要字符串和列表之间的绑定更合适,但我不知道如何将绑定连接到属性

package com.example.demo.view

import javafx.beans.Observable
import javafx.beans.binding.StringBinding
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections
import tornadofx.View
import tornadofx.button
import tornadofx.label
import tornadofx.vbox

class Thing(x: Int) {
    val xProperty = SimpleIntegerProperty(x)
    val yProperty = SimpleStringProperty("xyz")
}

class Collection {
    private var things = FXCollections.observableList(mutableListOf<Thing>()) {
        arrayOf<Observable>(it.xProperty)
    }

    val thingsProperty = SimpleListProperty<Thing>(things)

    fun addThing(thing: Thing) {
        things.add(thing)
    }
}

class Summary(var collection: Collection) {
    val summaryBinding = object : StringBinding() {
        // The real code is more practical but
        // this is just a minimal example.
        override fun computeValue(): String {
            val sum = collection.thingsProperty.value
                .map { it.xProperty.value }
                .fold(0, { total, next -> total + next })
            return "There are $sum things."
        }
    }

    // How to make this property update when collection changes?
    val summaryProperty = SimpleStringProperty("There are ? things.")
}

class MainView : View() {
    val summary = Summary(Collection())

    override val root = vbox {
        label(summary.summaryProperty)
        button("Add Thing") {
            summary.collection.addThing(Thing(5))
        }
    }
}
package com.example.demo.view
导入javafx.beans.Observable
导入javafx.beans.binding.StringBinding
导入javafx.beans.property.SimpleIntegerProperty
导入javafx.beans.property.SimpleListProperty
导入javafx.beans.property.SimpleStringProperty
导入javafx.collections.FXCollections
导入fx.View
“导入”按钮
导入fx.label
导入fx.vbox
类事物(x:Int){
val xProperty=SimpleIntegerProperty(x)
val yProperty=SimpleStringProperty(“xyz”)
}
类集合{
private var things=FXCollections.observableList(mutableListOf()){
arrayOf(it.xProperty)
}
val thingsProperty=简单属性(事物)
有趣的事物(事物:事物){
添加(东西)
}
}
类摘要(变量集合:集合){
val summaryBinding=对象:StringBinding(){
//真正的代码更实用,但是
//这只是一个很小的例子。
重写fun computeValue():字符串{
val sum=collection.thingsProperty.value
.map{it.xProperty.value}
.fold(0,{total,next->total+next})
return“有$sum项。”
}
}
//如何在集合更改时更新此属性?
val summaryProperty=SimpleStringProperty(“有?东西”)
}
类MainView:View(){
val summary=摘要(集合())
覆盖val root=vbox{
标签(summary.summaryProperty)
按钮(“添加内容”){
汇总.收集.添加内容(内容(5))
}
}
}

请记住,我是根据您最简单的例子来回答这个问题的:

class Thing(x: Int) {
    val xProperty = SimpleIntegerProperty(x)
    var x by xProperty

    val yProperty = SimpleStringProperty("xyz")
    var y by yProperty
}


class MainView : View() {
    val things = FXCollections.observableList(mutableListOf<Thing>()) {
        arrayOf<Observable>(it.xProperty)
    }

    val thingsProperty = SimpleListProperty<Thing>(things)

    val totalBinding = integerBinding(listProperty) {
        value.map { it.x }.fold(0, { total, next -> total + next })
    }

    val phraseBinding = stringBinding(totalBinding) { "There are $value things." }

    override val root = vbox {
        label(phraseBinding)
        button("Add Thing") {
            action {
                list.add(Thing(5))
            }
        }
    }
}
类事物(x:Int){
val xProperty=SimpleIntegerProperty(x)
按xProperty计算的var x
val yProperty=SimpleStringProperty(“xyz”)
按属性的变量y
}
类MainView:View(){
val things=FXCollections.observableList(mutableListOf()){
arrayOf(it.xProperty)
}
val thingsProperty=简单属性(事物)
val totalBinding=整数绑定(listProperty){
value.map{it.x}.fold(0,{total,next->total+next})
}
val phraseBinding=stringBinding(totalBinding){“有$value的东西。”}
覆盖val root=vbox{
标签(短语绑定)
按钮(“添加内容”){
行动{
添加(事物(5))
}
}
}
}
我删除了您的其他类,因为根据示例,我看不出它们的原因。如果collection类的功能比在实际项目中保存list属性的功能更多,那么只需添加它就可以了。如果不是,那么就没有理由给它自己的类一个列表。summary类实际上只是两个绑定(如果不需要将总计与短语分开,则为一个)。我不认为有必要给他们自己的类,除非你计划在多个视图中使用它们

我认为您最大的问题是您没有将按钮的操作包装在
action{}
中。因此,您的代码只是在init上添加了一个
东西(5)
,并且没有操作集


另外,只有当您为该文件导入TornadFX.

时,xProperty提供的
var x
功能才会起作用。

我认为TornadFX提供了
stringBinding
integerBinding
,这在某种程度上简化了工作。我可以将生成的绑定对象当作大多数地方的属性来使用吗?您还说过“最后,列表绑定只对列表更改(添加/删除项)作出反应。它不会侦听项属性的更改。”--这不是真的,这是我代码中的
arrayOf(It.xProperty)
语法所做的。(我通过在示例中添加另一个按钮来验证这一点,该按钮更改了列表中第一个元素的xProperty.value,我可以看到全部更改。)感谢您让我知道这一点!我测试过了,你是对的。我已相应地调整了我的答案。是的,绑定有很多只读属性的功能。我经常将它们用于标签和绑定组件的禁用属性。我还想提到的是,在实际项目中,list和list属性可能应该放在控制器或Presenter类中。