简体   繁体   English

JavaFX:绑定不适用于包含的 FXML

[英]JavaFX: Binding does not work with included FXML

I want my included/embedded fxml-based controls to inform their containing objects (parent views) about changes in their states/properties.我希望我包含/嵌入的基于 fxml 的控件通知它们的包含对象(父视图)关于它们的状态/属性的变化。 As you can see below I wrote a Container.fxml including a View.fxml plus the according to controller classes, named "Container.java" and "View.java".正如您在下面看到的,我编写了一个Container.fxml ,其中包括一个View.fxml以及根据 controller 命名的“Container.java”和“View.java”类。

When the textField of ViewController.java changes, its StringProperty 'textProperty_View' is updated.ViewController.java的 textField 发生变化时,其 StringProperty 'textProperty_View' 会更新。 That seems to be working properly as the OnAction-handler is invoked as expected.这似乎工作正常,因为 OnAction 处理程序按预期调用。

But the listener in the parent ContainerController.java does not fire though I bound its StringProperty to the StringProperty of ViewController.java and added a ChangeListener for this property.但是,尽管我将其 StringProperty 绑定到ViewController.java的 StringProperty 并为此属性添加了 ChangeListener,但父ContainerController.java中的侦听器并未触发。

What am I doing wrong?我究竟做错了什么?

PS: PS:

  • I simplified the example by doing the same thing without fx:include to see if the binding works.我通过在没有fx:include的情况下执行相同的操作来简化示例,以查看绑定是否有效。 It works.有用。 But not when I embed the view like in the described problem (see code below)但不是当我像描述的问题那样嵌入视图时(见下面的代码)
  • Am using javafx-15-ea+3 and java11我正在使用 javafx-15-ea+3 和 java11

AppStarter.java AppStarter.java


public class AppStarter extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("/fxml/Container.fxml"));
        Scene scene = new Scene(root, 300, 275);
        stage.setScene(scene);
        stage.show();
    }
}

ContainerController.fxml容器控制器.fxml

<HBox xmlns="http://javafx.com/javafx/10.0.2-internal" 
      xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="gui.ContainerController">

      <fx:include source="View.fxml" fx:id="view"/>

<!-- <TextField fx:id="textFieldMain" prefWidth="200" onAction="#onActionMain"/>  -->

</HBox>

ContainerController.java ContainerController.java

public class ContainerController implements Initializable {

    private final StringProperty textProperty_Main = new SimpleStringProperty();

    @FXML
    private ViewController viewController;

//    @FXML
//    void onActionMain(ActionEvent event) {
//        System.out.println("onActionMain " + viewController.getTextProperty_View());
//    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        textProperty_Main.bind(viewController.textProperty_ViewProperty());
        textProperty_MainProperty().addListener((observable, oldValue, newValue) ->
                System.out.println("textProperty of Container changed to: " + getTextProperty_Main())
        );
    }

    public String getTextProperty_Main() {
        return textProperty_Main.get();
    }

    public StringProperty textProperty_MainProperty() {
        return textProperty_Main;
    }
}

View.fxml查看.fxml

<HBox xmlns="http://javafx.com/javafx" 
      xmlns:fx="http://javafx.com/fxml" 
      fx:controller="gui.ViewController">

     <TextField fx:id="textField" prefWidth="200" onAction="#onAction">Unset</TextField>

</HBox>

ViewController.java ViewController.java

public class ViewController {

    private final StringProperty textProperty_View = new SimpleStringProperty();

    @FXML
    private TextField textField;

    @FXML
    void onAction(ActionEvent event) {
        textProperty_ViewProperty().setValue(textField.getText());
        System.out.println("textProperty of View changed to: " + textProperty_ViewProperty().getValue());
    }

    public String getTextProperty_View() {
        return textProperty_View.get();
    }

    public StringProperty textProperty_ViewProperty() {
        return textProperty_View;
    }
}

The issue you are seeing is a really frustrating one: JavaFX bindings use WeakListener s to implement the bindings.您看到的问题非常令人沮丧: JavaFX 绑定使用 Wea WeakListener来实现绑定。 This means that if the bindings go out of scope, the listeners are eligible for garbage collection, and consequently may stop working.这意味着如果绑定 go 超出 scope,则侦听器有资格进行垃圾收集,因此可能会停止工作。 (In my setup, your code actually works, but if I invoke System.gc() somewhere, it immediately stops working.) (在我的设置中,您的代码实际上可以工作,但是如果我在某处调用System.gc() ,它会立即停止工作。)

The only fix I can find for this is to explicitly retain a reference to the ContainerController in the application class (since the ContainerController has a reference to the ViewController , and the ViewController has a reference to the text field, this creates a path to the binding's listener via the listeners on the text field, etc.):我能找到的唯一解决方法是在应用程序 class 中显式保留对ContainerController的引用(因为ContainerController有对ViewController的引用,而ViewController有对文本字段的引用,这会创建绑定的路径侦听器通过文本字段上的侦听器等):

public class AppStarter extends Application {
    
    private ContainerController controller ;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Container.fxml"));
        Parent root = loader.load();
        this.controller = loader.getController();
        Scene scene = new Scene(root, 300, 275);
        stage.setScene(scene);
        stage.show();
    }
}

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

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