繁体   English   中英

JavaFX:FXML 中的双向绑定

[英]JavaFX: Bidirectional Binding in FXML

我正在使用 JavaFX,并且正在研究数据绑定。 我发现我可以在 FXML 中定义单向绑定,如下所示:

<TextField fx:id="usernameTextField" text="${controller.userName}" GridPane.columnIndex="1" />

这意味着, usernameTextField 的文本正在“观察” controller.userName 属性。

但这会创建一种单向绑定。 如果 controller.userName 属性发生变化,我会在文本字段中看到更新的文本,该部分有效。 但是我不能再在文本字段中插入文本,因为我已经进行了单向绑定。

我能找到的所有关于此的帖子现在已经有四年多了,但我不知道 JavaFX 是否已更新以支持更复杂的绑定。

这样做的方法是:

<TextField fx:id="usernameTextField" text="#{controller.userName}"/>

但是此功能尚未启用(最后在 OpenJFX 13 上检查),使用它会导致FXMLLoader抛出UnsupportedOperationException("This feature is not currently enabled.") FXMLLoader UnsupportedOperationException("This feature is not currently enabled.")

正如已经说过的,没有简单的方法可以从 FXML 进行双向绑定。 然而,它可以从 FXML 完成,但不能使用 FXML 本身,可以这么说。 这是我的发现。

<fx:script>

第一种也是最明显的方法是使用<fx:script>通过 JavaScript 进行任何需要的绑定。 例如,可以使用以下代码段进行双向绑定:

<?language javascript?>
<!-- ... -->
<TextField fx:id="aControl"/>
<fx:script>
javafx.beans.binding.Bindings.bindBidirectional(
    controller.someProperty(),
    aControl.textProperty()
);
</fx:script>

这里someProperty()是一个名为some的属性的控制器方法。

自定义助手类

第二个选项是创建一个辅助类,然后在<fx:define>块中使用它(使用上面的约定):

<?import u7n.examples.BidiBind?>
<!-- ... -->
<TextField fx:id="aControl"/>
<fx:define>
<BidiBind o1="$controller" p1="some" o2="$aControl" p2="text"/>
</fx:define>

这是此类的示例实现:

package u7n.examples;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
public class BidiBind {
    private Object o1;
    private String p1;
    public Object getO1() { return o1; }
    public void setO1(Object o1) { this.o1 = o1; bind(); }
    public String getP1() { return p1; }
    public void setP1(String p1) { this.p1 = p1; bind(); }

    private Object o2;
    private String p2;
    public Object getO2() { return o2; }
    public void setO2(Object o2) { this.o2 = o2; bind(); }
    public String getP2() { return p2; }
    public void setP2(String p2) { this.p2 = p2; bind(); }

    private <T> void bind() {
        if (o1 == null || p1 == null || o2 == null || p2 == null) return;
        try {
            @SuppressWarnings("unchecked")
            Property<T> property1 = (Property<T>)o1.getClass().getMethod(p1 + "Property").invoke(o1);
            @SuppressWarnings("unchecked")
            Property<T> property2 = (Property<T>)o2.getClass().getMethod(p2 + "Property").invoke(o2);
            Bindings.bindBidirectional(property1, property2);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        o1 = o2 = null;
        p1 = p2 = null;
    }
}

它使用反射来访问属性,因为我还没有找到让 FXMLLoader 将属性而不是其值传递给 setter 的方法。

把它们加起来

两种方式都不是理想的,但是可以从 FXML 完成双向绑定,尽管不是很方便。 当然,像

<TextField fx:id="aControl" text="#{controller.some}"/>

会更好,但唉。 此外,我不知道这两种建议的方法与 Scene Builder 的配合效果如何,因为我不使用它。

此代码完美运行:

<TextField fx:id="usernameTextField" text="${controller.userName}"/>

要操作,控制器必须包含一个 getter :

public String getUserName() { return "mystringvalue"; }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM