JavaFX: привязка не работает с включенным FXML - PullRequest
0 голосов
/ 21 июня 2020

Я хочу, чтобы мои включенные / встроенные элементы управления на основе f xml информировали содержащиеся в них объекты (родительские представления) об изменениях в их состояниях / свойствах. Как вы можете видеть ниже, я написал Container.f xml, включая View.f xml плюс соответствующие классы контроллеров с именем «Container. java» и «View. java».

При изменении textField ViewController. java его StringProperty 'textProperty_View' обновляется. Кажется, это работает правильно, поскольку обработчик OnAction вызывается, как ожидалось.

Но слушатель в родительском ContainerController. java не срабатывает, хотя я привязал его StringProperty к StringProperty of ViewController. java и добавил ChangeListener для этого свойства.

Что я делаю не так?

PS:

  • I упростил пример, сделав то же самое без fx:include, чтобы проверить, работает ли привязка. Оно работает. Но не тогда, когда я встраиваю представление, как в описанной проблеме (см. Код ниже)
  • Я использую javafx-15-ea + 3 и java11

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.f xml

<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

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.f xml

<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

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;
    }
}

1 Ответ

1 голос
/ 21 июня 2020

Проблема, которую вы видите, действительно разочаровывает: привязки JavaFX используют WeakListener s для реализации привязок. Это означает, что если привязки go выходят за рамки, слушатели имеют право на сборку мусора и, следовательно, могут перестать работать. (В моей настройке ваш код действительно работает, но если я где-то вызываю System.gc(), он сразу же перестает работать.)

Единственное исправление, которое я могу найти для этого, - это явно сохранить ссылку на ContainerController в классе приложения (поскольку ContainerController имеет ссылку на ViewController, а ViewController имеет ссылку на текстовое поле, это создает путь к слушателю привязки через слушателей в текстовом поле, et c.):

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();
    }
}
...