JavaFx actions between controllers - java

How i can do button action for editing TableView. I need to put text from TextArea to table when i touch button. And if put System.out.println in inputToTable() it is work.
public class InputController {
public TextArea inputArea;
public Button inputButton;
private TableController tableController;
public void initialize() {
tableControllerInit();
}
public void inputToTable() {
if(inputArea.getText() != "") {
tableController.tableInfo.setItems(FXCollections.observableArrayList(new InputObject(inputArea.getText())));
}
}
private void tableControllerInit() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("table.fxml"));
fxmlLoader.load();
tableController = fxmlLoader.getController();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class TableController {
#FXML TableView<InputObject> tableInfo;
#FXML TableColumn<InputObject, String> col1;
public void initialize() {
col1.setCellValueFactory(new PropertyValueFactory<>("text"));
}
}
public class Controller implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
public class InputObject {
String text;
public InputObject(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
<BorderPane fx:controller="sample.Controller" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<left>
<fx:include source="table.fxml"/>
</left>
<center>
<fx:include source="input.fxml"/>
</center>
</BorderPane>
<TableView fx:controller="sample.TableController" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:id="tableInfo" prefHeight="400.0" prefWidth="330.0">
<columns>
<TableColumn fx:id="col1" prefWidth="75.0" text="Output" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<VBox fx:controller="sample.InputController" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<TextArea fx:id="inputArea" prefHeight="188.0" prefWidth="270.0" />
<Button fx:id="inputButton" onAction="#inputToTable" mnemonicParsing="false" text="Input">
<VBox.margin>
<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
</VBox.margin>
</Button>
</children>
</VBox>

You load table.fxml twice: once via the fx:include in the main FXML file, and once in InputController, via the FXMLLoader you create in the tableControllerInit() method. Consequently, two instances of TableController are created, one associated with the first UI you load from table.fxml, and one associated with the second UI you load from table.fxml.
The UI you load via the fx:include is displayed in the VBox defined in the main FXML file. The UI you load with the FXMLLoader is never displayed (in fact, you never even keep a reference to it, you just call loader.load() and discard the result). When you try to update the table's items (do you really intend to replace all the existing items, by the way?), you refer to the second controller instance, which is associated with the UI which is never displayed. Consequently, you are updating a table that is not displayed, and you never see any results.
What you really need to do is share the same data between the two controllers associated with the two fx:includes. You can do this simply by injecting those two controllers into the main controller, as described in the "Nested Controllers" section in the documentation.
First, give the fx:include elements fx:id attributes:
<BorderPane fx:controller="sample.Controller" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<left>
<fx:include fx:id="table" source="table.fxml"/>
</left>
<center>
<fx:include fx:id="input" source="input.fxml"/>
</center>
</BorderPane>
Then you can inject the controllers into the main controller by creating fields with the word "Controller" appended to the fx:id. Create a single observable list, which will represent the list of items displayed in the table, and pass it to each controller:
public class Controller implements Initializable {
#FXML
private TableController tableController ;
#FXML
private InputController inputController ;
#Override
public void initialize(URL location, ResourceBundle resources) {
ObservableList<InputObject> items = FXCollections.observableArrayList();
tableController.setTableItems(items);
inputController.setTableItems(items);
}
}
Finally, just define the obvious methods in each of the other two controllers:
public class TableController {
#FXML
private TableView<InputObject> tableInfo;
#FXML
private TableColumn<InputObject, String> col1;
public void initialize() {
col1.setCellValueFactory(new PropertyValueFactory<>("text"));
}
public void setTableItems(ObservableList<InputObject> tableItems) {
tableInfo.setItems(tableItems);
}
}
Now the table is displaying the contents of the items list created in the main controller's initalize() method, and the InputController has a reference to the same list. So all you need to do is update that list in the InputController. I assume you just want to add items to the table (not replace them all):
public class InputController {
#FXML
private TextArea inputArea;
#FXML
private Button inputButton;
private ObservableList<InputObject> tableItems ;
public void setTableItems(ObservableList<InputObject> tableItems) {
this.tableItems = tableItems ;
}
public void inputToTable() {
if(! inputArea.getText().isEmpty()) {
tableItems.add(new InputObject(inputArea.getText()));
}
}
}
More generally, if you have more data to share among the different controllers, you would create one or more "model" classes and share a model instance with the controllers. Then you can observe the properties of the model and update them. See Applying MVC With JavaFx for a more comprehensive example.

Related

javafx TreeTableView string array columns

All existing answers were using a class object to display multiple columns. Do I have to use a class? Can I just use a string array like C#'s ListViewItem? If I can, how?
For example, display "hello" in the first column and "world" in the second column.
public class HelloController {
#FXML
private TreeTableView mytree;
#FXML
private TreeTableColumn colFirst;
#FXML
private TreeTableColumn colSecond;
#FXML
void initialize()
{
TreeItem<String[]> item = new TreeItem<String[]>(new String[]{"hello", "world"});
colFirst.setCellValueFactory((CellDataFeatures<Object, String[]> p)
-> new ReadOnlyStringWrapper(p.getValue().toString()));
mytree.setRoot(item);
}
}
fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.fx2.HelloController">
<TreeTableView fx:id="mytree" prefHeight="200.0" prefWidth="200.0">
<columns>
<TreeTableColumn id="colFirst" prefWidth="75.0" text="First" />
<TreeTableColumn id="colSecond" prefWidth="75.0" text="Second" />
</columns>
</TreeTableView>
</VBox>
Never use raw types: parameterize your types properly:
public class HelloController {
#FXML
private TreeTableView<String[]> mytree;
#FXML
private TreeTableColumn<String[], String> colFirst;
#FXML
private TreeTableColumn<String[], String> colSecond;
// ...
}
Then in the lambda expression, p is a TreeTableColumn.CellDataFeatures<String[], String>, so p.getValue() is a TreeItem<String[]> and p.getValue().getValue() is the String[] representing the row.
So you can do
#FXML
void initialize() {
TreeItem<String[]> item = new TreeItem<String[]>(new String[]{"hello", "world"});
colFirst.setCellValueFactory(p
-> new ReadOnlyStringWrapper(p.getValue().getValue()[0]));
mytree.setRoot(item);
}

Interact between several FXMLs

I'm trying to write my first application with javaFX and FXML files, but I got stuck with accesing variables of the FXMLs.
I am able to start the program with the first scene (first FXML). There is a button I'm able to interact with, and the first scene hides and the second scene appears. In that scene, the user has to insert a value in a text field. I'm able to save this into a variable by confirming this input over another button.
This button click hides the second scene and opens the first scene again. Now I want to set the value of a label in that first scene to the value of the variable I saved the user's input in. But this throws an error message.
It seems like this label value is not accessable, even if this label is part of the currently loaded FXML, but I am able to get the value of a label of the second FXML (user interaction field), which isn't visible anymore.
Can anyone help me, how to make this Label of the first FXML readable and changable?
My code is quite long, but I tired to give you the most important parts:
#FXML
private Label RollsMax;
#FXML
private TextField roll_input;
#FXML
private String amountRolls;
... // several more declarations
#FXML
void btn_confirmInput_onClick(ActionEvent event){ //btn_confirmInput is part of scene2.fxml
event.consume();
amountRolls=roll_input.getText();
System.out.println(amountRolls); // here I get the result of the user's input
try{
root=FXMLLoader.load(getClass().getResource("project/scene1.fxml"));
stage=(Stage)((Node)event.getSource()).getScene().getWindow();
scene=new Scene(root);
stage.setScene(scene);
stage.show();
} //end try
catch (IOException e){
e.printStackTrace();
} //end catch
RollsMax.setText(amountRolls); // THIS LINE IS NOT WORKING AS RollsMax SEEMS NOT AVAILABLE, EVEN IF IT IS PART OF SCENE1.fxml
} // end btn_confirmInput_onClick()
Any ideas? The error I get is 'java.lang.RuntimeException: java.lang.reflect.InvocationtargetException' as well as a'java.lang.NullPointerException'.
Thanks a lot!
Endcoder
To share data between the scenes, we'll introduce a Model class that holds the shared information:
public class Model {
private SimpleStringProperty valueProperty = new SimpleStringProperty("N/A");
public SimpleStringProperty getValueProperty() {
return valueProperty;
}
}
And an interface to be implemented by the controllers of the two scenes (actually the controllers of the two roots of the two scenes).
The interface adds the functionality of injection a Model into the controller:
public interface Controller {
void setModel(Model model);
}
sceneOne.fxml with a button to switch scene and a label to display user's input:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/16"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="SceneOneController">
<children>
<Label layoutX="159.0" layoutY="40.0" text="Scene 1">
<font>
<Font size="24.0" />
</font>
</Label>
<Button layoutX="264.0" layoutY="246.0" mnemonicParsing="false" onAction="#changeScene" prefHeight="26.0" prefWidth="102.0" text="Get Input" />
<Label fx:id="inputValue" layoutX="187.0" layoutY="142.0" text="Label" />
</children>
</AnchorPane>
And its controller which implements the Controller interface:
public class SceneOneController implements Controller {
#FXML Label inputValue;
private Model model;
public void changeScene(ActionEvent e) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sceneTwo.fxml"));
Parent root = loader.load();
Controller controller = loader.getController(); //get a reference to sceneTwo controller
controller.setModel(model);
Stage stage = (Stage)((Node)e.getSource()).getScene().getWindow();
stage.setScene(new Scene(root));
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void setModel(Model model) {
this.model = model;
if(model != null){
inputValue.textProperty().unbind();
inputValue.textProperty().bind(model.valueProperty);
}
}
}
sceneTwo.fxml with a button to switch scene and a TextField for the user's input:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane fx:id="main" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="SceneTwoController">
<children>
<Label layoutX="150.0" layoutY="41.0" text="Scene 2">
<font>
<Font size="24.0" />
</font>
</Label>
<Button layoutX="264.0" layoutY="246.0" mnemonicParsing="false" onAction="#changeScene" prefHeight="26.0" prefWidth="102.0" text="Update" />
<TextField fx:id="inputValue" layoutX="121.0" layoutY="138.0" prefHeight="25.0" prefWidth="162.0" />
</children>
</AnchorPane>
And its controller:
public class SceneTwoController implements Controller {
#FXML TextField inputValue;
private Model model;
public void changeScene(ActionEvent e) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sceneOne.fxml"));
Parent root = loader.load();
Controller controller = loader.getController(); //get a reference to sceneOne controller
controller.setModel(model);
Stage stage = (Stage)((Node)e.getSource()).getScene().getWindow();
stage.setScene(new Scene(root));
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void setModel(Model model) {
this.model = model;
if(model != null){
inputValue.textProperty().unbind();
model.valueProperty.bind(inputValue.textProperty());
}
}
}
And finally the application to test it all:
public class SwitchSceneMVC extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("sceneOne.fxml"));
Parent root = loader.load();
Model model = new Model();
Controller controller = loader.getController();
controller.setModel(model);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}
Use a controller class for every FXML. Then you should be able to interact between them with public methods.
Especially when just hidinng the first scene you should then be able to just write from one scene to the other one.
Alternatively: while starting the second scene make a parameter where you bring the instance of the first scene. Than you should be able to use the needed methods on the first scenes instance.
Like
Code Scene one
#FXML
private void startSceneTwoButton(){
new SceneTwo(this);
}
Code Scene two
FirstScene scene;
public SecondScene(FirstScene scene){
this.scene = scene;
//some code
}
#FXML
private void button(){
scene.enterValue(value);
}

JavaFX - RadioButtons in TableCell with Add Data Button

I'm currently working on an application where I use a TableView including RadioButtons in one TableCell. For that I created an own RadioButtonCell class. I also have a "Add new Row"-Button to let the user add some additional rows. After clicking the "Add" button a third time, I get more RadioButtonCells than rows. I don't find the mistake in my code.
Here a screenshot after clicking the Button a third time:
RadioButtonCell:
import java.util.EnumSet;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
public class RadioButtonCell<S,T extends Enum<T>> extends TableCell<S,T>{
private EnumSet<T> enumeration;
public RadioButtonCell(EnumSet<T> enumeration) {
this.enumeration = enumeration;
}
#Override
protected void updateItem(T item, boolean empty)
{
super.updateItem(item, empty);
if (!empty)
{
// GUI
HBox hb = new HBox(7);
hb.setAlignment(Pos.CENTER);
final ToggleGroup group = new ToggleGroup();
// create a radio button for each 'element' of the enumeration
for (Enum<T> enumElement : enumeration) {
RadioButton radioButton = new RadioButton(enumElement.toString());
radioButton.setUserData(enumElement);
radioButton.setToggleGroup(group);
hb.getChildren().add(radioButton);
if (enumElement.equals(item)) {
radioButton.setSelected(true);
}
}
// issue events on change of the selected radio button
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
#SuppressWarnings("unchecked")
#Override
public void changed(ObservableValue<? extends Toggle> observable,
Toggle oldValue, Toggle newValue) {
getTableView().edit(getIndex(), getTableColumn());
RadioButtonCell.this.commitEdit((T) newValue.getUserData());
}
});
setGraphic(hb);
}
}
}
Model:
public class Points {
private final SimpleObjectProperty<Participation> participation = new SimpleObjectProperty<Participation>(); // radio buttons
public static enum Participation {
NONE, HALO, BORDER;
public String toString() {
return super.toString().toLowerCase();
};
}
/**
* Constructor.
* #param <Participation>
*/
public Points(Participation p) {
this.participation.setValue(p);
public void setParticipation(Participation p){
participation.set(p);
}
public Participation getParticipation(){
return participation.get();
}
public SimpleObjectProperty<Participation> ParticipationProperty() {
return participation;
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox prefHeight="900.0" prefWidth="1250.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="address.view.DataOverviewController">
<children>
<SplitPane fx:id="splitPaneVertical" prefHeight="776.0" prefWidth="1200.0" VBox.vgrow="ALWAYS">
<items>
<TabPane fx:id="tabPane" stylesheets="#Theme.css">
<tabs>
<Tab text="Points">
<content>
<SplitPane fx:id="splitPaneHorizontal" dividerPositions="0.949874686716792" orientation="VERTICAL" stylesheets="#Theme.css">
<items>
<TableView fx:id="pointsDataTable">
<columns>
<TableColumn fx:id="pointsBackgroundColumn" prefWidth="200.0" resizable="false" text="Background" />
</columns>
</TableView>
<AnchorPane SplitPane.resizableWithParent="false">
<children>
<HBox fx:id="pointsHBoxButton" alignment="CENTER" prefHeight="35.0" prefWidth="1016.0" spacing="20.0" stylesheets="#Theme.css" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Button fx:id="pointsAddButton" mnemonicParsing="false" onAction="#handleAddPoints" text="Add" />
</children>
</HBox>
</children>
</AnchorPane>
</items>
</SplitPane>
</content>
</Tab>
</tabs>
</TabPane>
</items>
</SplitPane>
Controller:
public class DataOverviewController {
#FXML
private TableView<Points> pointsDataTable;
#FXML
private TableColumn<Points, Participation> pointsBackgroundColumn;
#FXML
private Button pointsButtonAdd;
public DataOverviewController() {
}
#FXML
private void initialize() {
// select multipe rows, make rows editable
pointsDataTable.setEditable(true);
pointsDataTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
linesDataTable.setEditable(true);
pointsBackgroundColumn.setCellFactory((param) -> new RadioButtonCell<Points, Participation>(EnumSet.allOf(Participation.class)));
pointsBackgroundColumn.setCellValueFactory(new PropertyValueFactory<Points, Participation>("Participation"));
pointsBackgroundColumn.setOnEditCommit(
new EventHandler<CellEditEvent<Points, Participation>>() {
#Override
public void handle(CellEditEvent<Points, Participation> t) {
((Points) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setParticipation(t.getNewValue());
}
}
);
public void setMainConfig(MainConfig mainConfig) {
// Add observable list data to the table
pointsDataTable.setItems(mainConfig.getTableDataPoints());
}
#FXML
private void handleAddPoints() {
// create new record and add it to the tableview
Points dataPoints = new Points(0, "Points", "TabFileName.tab",Participation.NONE, false, false, 18, "new");
pointsDataTable.getItems().add(dataPoints);
}
}
}
I reduced my code to the important parts. Maybe someone can help?
Thanks.
Items can be added as well as removed from TableCells. This means you need to handle the case where the TableCell becomes empty by undoing any changes done to the cell when a item was added. Otherwise there may be TableCells that look as if they contain a item although they are actually empty.
A updateItem method should look like this:
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
// modifications to restore the empty state
} else {
// customized way of displaying the item
}
}
Furthermore I do not recommend recreating the Nodes for displaying the item in the cell every time the item is changed, since this means you'll have a lot of unnecessary node creations which is the thing TableView is trying to avoid by reusing cells. Store them in fields and keep them for later use instead.

Anchorpane not supporting my controller on javafx

My program is refusing to run because the "anchorpane" is refusing to support my controller on all my fxml files, hence my fxml page is not loading. Also, it is also giving me error message that my main class could not be found. I would welcome any help or suggestion on the stated.
My fxml file
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" pickOnBounds="false" prefHeight="614.0" prefWidth="1015.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="winnaccs.WinSecHomeController">
The Controller
public class WinSecHomeController implements Initializable, ControlledScreen {
ScreensController myController;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
public void setScreenParent (ScreensController screenParent){
myController = screenParent;
}
#FXML
private void goToLogin(ActionEvent event) {
myController.setScreen(Winnaccs.screen2);
}
}
just try to change this one
fx:controller="winnaccs.WinSecHomeController"
with this other
fx:controller="winnaccs/WinSecHomeController"

JavaFX Thread with progressindicator not spinning, work done in non FXThread

I would like to indicate a loading process with the JavaFX progressindicator.
The problem is that the indicator isn't rotating when the code is executed the first time.
On the second time it is rotating that means the bindings are working, both the disable and the visible-property.
I also change the state in the FX thread and do the work on a seperate thread so i can
see no error here.
Does anyone see the problem?
Controller:
//vars
#FXML private ProgressIndicator loadingIndicator;
private BooleanProperty isSaving = new SimpleBooleanProperty(false);
#Override
public void initialize(URL location, ResourceBundle resources) {
parentToDisable.disableProperty().bind(isSaving);
loadingIndicator.visibleProperty().bind(isSaving);
}
#FXML
void onSave(ActionEvent event) {
isSaving.set(true); //<<<<<<<<<problem
// separate non-FX thread
new Thread() {
// runnable for that thread
public void run() {
//++++++++++//long running task......+++++++++++++++
// update ProgressIndicator on FX thread
Platform.runLater(new Runnable() {
public void run() {
isSaving.set(false); //<<<<<<<<<problem
}
});
}
}.start();
}
Fxml:
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="600.0"
prefWidth="500.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com
/fxml/1">
<content>
<StackPane>
<children>
<VBox fx:id="parentToDisable">
<!-- shortened -->
<Button fx:id="btnSave" mnemonicParsing="false" onAction="#onSave"
text="Speichern" />
<!-- shortened -->
</VBox>
<ProgressIndicator fx:id="loadingIndicator"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="60.0" prefWidth="60.0"
visible="false" />
</children>
</StackPane>
</content>
</ScrollPane>
That looks like a bug: RT-33261. It seems to be fixed in the latest pre-release of JDK 8u20.
My guess is it's to do with the progress indicator not being visible when the scene graph is first constructed. As a workaround, remove the visible="false" attribute from the ProgressIndicator in the fxml file, and wrap the binding in a Platform.runLater(...) call:
public void initialize(...) {
Platform.runLater(() -> loadingIndicator.visibleProperty().bind(isSaving));
}

Categories

Resources