为什么在 TornadoFx 中在 root 之后声明的变量会自动添加到 UI?

Why are variables declared after root automatically added to UI in TornadoFx?

我偶然发现了 TornadoFx 中的一个行为,它似乎在任何地方都没有被提及(我已经搜索了很多),我对此感到疑惑。

如果我使用 TornadoFx 构建器为标签定义这样的视图:

class ExampleView: View() {

    override
    val root = vbox{ label("first label") }

    val secondLabel = label("second label")
}

结果是:

即单纯定义secondLabel自动添加到场景root中。

但是,如果我将此定义放在 root...

的定义之前
class ExampleView: View() {

    val secondLabel = Label("second label")

    override
    val root = vbox{ label("first label") }
}

... 或者如果我使用 JavaFx Labelclass 而不是 TornadoFx 构建器...

class ExampleView: View() {

    override
    val root = vbox{ label("first label") }

    val secondLabel = Label("second label")
}

...然后它按我预期的那样工作:

当然,我可以在定义 root 元素之前简单地定义视图中的所有变量,但我仍然很好奇为什么会存在这种行为;也许我遗漏了一些通用的设计规则或设置。

TornadoFX 中的 builder 会自动将自身附加到调用它们的范围内的当前父级。因此,如果您在视图本身上调用 builder 函数,生成的ui 组件自动添加到该视图的根。这就是您所看到的。

如果你真的有一个有效的用例来创建一个 ui 组件在层次结构之外,它应该被安置在里面,你不应该调用 builder 函数,而是实例化元素及其构造函数,就像您对 Label() 所做的那样。但是,这种行为的用例很少 none。

最佳做法是在视图或视图模型中存储值属性,并使用 bui 连接器将 属性 绑定到 ui 元素。然后在需要时操作值 属性,更改将自动更新到 ui。因此,您很少需要在稍后阶段访问特定的 ui 元素。示例:

val myProperty = SimpleStringProperty("Hello world")

override val root = hbox {
    label(myProperty)
}

当您想更改标签值时,只需更新 属性。 (属性 应该在真实世界应用程序中的注入视图模型中)。

如果你真的需要引用ui元素,你应该先声明ui 属性,然后在实际b[=29=时赋值给它]ld ui 元素。使用 singleAssign() 委托定义 ui 属性 以确保只分配给它一次。

var myLabel: Label by singleAssign()

override val root = hbox {
    label("My label) {
        myLabel = this
    }
}

我想再次强调,这很少需要,如果你觉得你需要它,你应该考虑重组你的 ui 代码以更多地由数据驱动。

另一种避免存储对 ui 元素的引用的技术是利用 EventBus 来侦听事件。有很多这样的例子。