0

I have a certain problem with recreating a WinAPI-like class (MessageBox).

This java class provided below is supposed to deliver short messages to the user.

FXML file was generated in SceneBuilder.

Frankly, I have no clue why updating textArea field doesn't update the window with provided message ('String').

Java (not working):

    import Util.WindowURLProvider; // this is a custom class which provides URL links to FXML pages
    import javafx.event.ActionEvent;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.TextArea;
    import javafx.stage.Stage;

    import java.io.IOException;

    public class StatusScreen {
        private static Stage statusStage = new Stage();
        private static WindowURLProvider windowURLProvider = new WindowURLProvider();
        public TextArea textArea;
        public Button okButton;

        public StatusScreen() {
        }

        public void setScreen(String title, String textToShow) throws IOException {
            textArea = new TextArea();
            textArea.appendText(textToShow); // the problem area

            statusStage.setTitle(title);
            statusStage.setScene(
                    new Scene(
                            FXMLLoader.load(windowURLProvider.getStatusWindowURL())
                    )
            );

            statusStage.show();
        }

        public void onOkButtonPressed(ActionEvent actionEvent) {
            statusStage.close();
        }
    }

FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>

<AnchorPane prefHeight="150." prefWidth="200.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="GUI.Screens.StatusScreen">
   <children>
      <GridPane prefHeight="150.0" prefWidth="200.0">
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints maxHeight="112.0" minHeight="10.0" prefHeight="112.0" vgrow="SOMETIMES" />
          <RowConstraints maxHeight="70.0" minHeight="10.0" prefHeight="38.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <Button fx:id="okButton" mnemonicParsing="false" onAction="#onOkButtonPressed" prefHeight="25.0" prefWidth="213.0" text="OK" GridPane.rowIndex="1" />
            <TextArea fx:id="textArea" focusTraversable="false" prefHeight="200.0" prefWidth="200.0" promptText="SAMPLE_TEXT" text="" wrapText="true" />
         </children>
      </GridPane>
   </children>
</AnchorPane>

EDIT:

Java (working):

Better solutions are down in the comment section :)

This example uses scene's lookup method to get textArea object reference

import Util.WindowURLProvider;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

import java.io.IOException;

public class StatusScreen {
    private static Stage statusStage = new Stage();
    private static WindowURLProvider windowURLProvider = new WindowURLProvider();
    public TextArea textArea;
    public Button okButton;

    public StatusScreen() {
    }

    public void setScreen(String title, String textToShow) throws IOException {
        Scene scene = new Scene(FXMLLoader.load(windowURLProvider.getStatusWindowURL()));

        textArea = (TextArea) scene.lookup("#textArea");
        textArea.appendText(textToShow);

        statusStage.setTitle(title);
        statusStage.setScene(scene);

        statusStage.show();
    }

    public void onOkButtonPressed(ActionEvent actionEvent) {
        statusStage.close();
    }
}
  • Note that your `lookup` is not guaranteed to work: lookups will only succeed once CSS has been applied to the scene. This typically happens when the scene is first rendered, or you can force it via `applyCSS()`. If you want to grab controls defined in FXML this way, I recommend using the `FXMLLoader`'s `namespace`, instead of CSS lookups. – James_D Nov 29 '17 at 14:15

2 Answers2

0

This is an example of how to realize:

public class StatusScreen {
    private static Stage statusStage = new Stage();
    private static WindowURLProvider windowURLProvider = new WindowURLProvider();

    @FXML
    public TextArea textArea;

    public StatusScreen() {
    }

    public void setScreen(String title, String textToShow) throws IOException {
        FXMLLoader loader = new FXMLLoader(windowURLProvider.getStatusWindowURL());
        loader.setController(this);
        Parent root = loader.load();

        textArea.appendText(textToShow);

        statusStage.setTitle(title);
        statusStage.setScene(new Scene(root));
        statusStage.show();
    }

    public void onOkButtonPressed(ActionEvent actionEvent) {
        statusStage.close();
    }
}

Note that the statement in the view has removed the explicit reference to a controller

<AnchorPane prefHeight="150." prefWidth="200.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <children>
        <GridPane prefHeight="150.0" prefWidth="200.0">
            <columnConstraints>
                <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            </columnConstraints>
            <rowConstraints>
                <RowConstraints maxHeight="112.0" minHeight="10.0" prefHeight="112.0" vgrow="SOMETIMES" />
                <RowConstraints maxHeight="70.0" minHeight="10.0" prefHeight="38.0" vgrow="SOMETIMES" />
            </rowConstraints>
            <children>
                <Button fx:id="okButton" mnemonicParsing="false" onAction="#onOkButtonPressed" prefHeight="25.0" prefWidth="213.0" text="OK" GridPane.rowIndex="1" />
                <TextArea fx:id="textArea" focusTraversable="false" prefHeight="200.0" prefWidth="200.0" promptText="SAMPLE_TEXT" text="" wrapText="true" />
            </children>
        </GridPane>
    </children>
</AnchorPane>
mr mcwolf
  • 2,574
  • 2
  • 14
  • 27
0

The code you posted creates two text fields: one in the FXML file, and one in Java. You set the text on the text field you create in Java, but the only text field that is displayed is the one created in FXML. Consequently, you never see the text you set.

A "quick and dirty" fix is just to retrieve the text field from the FXMLLoader directly. Remove the fx:controller attribute from the FXML file, and do

public class StatusScreen {

    private static WindowURLProvider windowURLProvider = new WindowURLProvider();

    public void setScreen(String title, String textToShow) throws IOException {

        FXMLLoader loader = new FXMLLoader(windowURLProvider.getStatusWindowURL());
        Stage statusStage = new Stage();
        statusStage.setTitle(title);
        statusStage.setScene(
                new Scene(loader.load())
        );
        TextArea textArea = (TextArea) loader.getNamespace().get("textArea");
        textArea.appendText(textToShow); 

        Button okButton = (Button) loader.getNamespace().get("okButton");
        okButton.setOnAction(this::onOkButtonPressed);

        statusStage.show();
    }

    public void onOkButtonPressed(ActionEvent actionEvent) {
        statusStage.close();
    }
}

A better fix is to use a separate controller class:

public class StatusScreenController {

    @FXML
    private TextArea textArea ;

    public void setText(String text) {
        textArea.setText(text);
    }

    public String getText() {
        return textArea.getText();
    }

    @FXML
    private void onOkButtonPressed() {
        textArea.getScene().getWindow().hide();
    }
}

Change the fx:controller attribute in the FXML to point to this new controller, and then just do:

public class StatusScreen {
    private static WindowURLProvider windowURLProvider = new WindowURLProvider();

    public void setScreen(String title, String textToShow) throws IOException {
        Stage statusStage = new Stage();
        FXMLLoader loader = new FXMLLoader(windowURLProvider.getStatusWindowURL());
        statusStage.setTitle(title);
        statusStage.setScene(
                new Scene(loader.load())
        );
        StatusScreenController controller = loader.getController();
        controller.setText(textToShow);
        statusStage.show();
    }

}

Finally, you could use a "custom component" approach, which is already outlined in another answer.

James_D
  • 201,275
  • 16
  • 291
  • 322