JavaFX:FXML中的双向绑定



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

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

这意味着,usernameTextField 的文本正在"观察"控制器.userName 属性。

但这会产生单向绑定。如果控制器.userName 属性发生更改,我会在文本字段中看到更新的文本,该部分有效。但是我无法再在文本字段中插入文本,因为我已经进行了单向绑定。

我能找到的关于这个的帖子已经有四年多了,但我无法弄清楚,JavaFX是否已经更新以支持更复杂的绑定。

这样做的方法是:

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

但是此功能尚未启用(上次在 OpenJFX 13 上检查(,使用它会导致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 将属性而不是其值传递给资源库。

总结一下

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

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

会更好,但唉。我也不知道这两种提出的方法与场景生成器的配合效果如何,因为我不使用它。

这段代码完美运行:

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

要操作,控制器必须包含一个吸气剂:

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

最新更新