I want to build a wizard-style application in JavaFX. Therefore I have got a wizard.fxml
with navigation buttons and an empty AnchorPane for each step/task which is in a seperate fxml file respectively to embed them in the (main) wizard during runtime.
Now when I want to call the first step like that in the WizardController:
FXMLLoader loader = SpringFXMLLoader.getLoader(this.getClass().getClassLoader().getResource("logon.fxml")); //creates a loader with the current application context
loader.setController(this);
loader.setRoot(this.taskPane /*The AnchorPane*/);
loader.load();
it gets loaded as expected but I get this problem: Now whenever I hover over things like a button or a TextField, the cursor starts flickering between a "hand"-cursor and a "default"-cursor. Buttons in the wizard.fxml
are flickering but still working, but I can't click and/or type into the TextFields of the embedded logon.fxml
. I thought maybe it has something to do with overlapping objects, so I disabled PickOnBounds
for every node but the problem remains. Also running the program consumes a hell lot of CPU and RAM resources, I cannot even drag the program windows smoothly.
Using this code (without fx:root in logon.fxml):
FXMLLoader loader = SpringFXMLLoader.getLoader(this.getClass().getClassLoader().getResource("logon.fxml")); //creates a loader with the current application context
loader.setController(this);
Node task = loader.load();
taskPane.getChildren().clear();
taskPane.getChildren().add(task);
AnchorPane.setTopAnchor(task, 0d);
AnchorPane.setRightAnchor(task, 0d);
AnchorPane.setBottomAnchor(task, 0d);
AnchorPane.setLeftAnchor(task, 0d);
gives me the exact same output and problem. When I comment out the single line loader.load();
the problem does not occur, but obviously I need to load.
wizard.fxml (without imports)
<BorderPane stylesheets="@application.css" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="path.to.my.controller.WizardController">
<bottom>
<ButtonBar id="buttonBar" BorderPane.alignment="CENTER">
<buttons>
<Button id="btnPrevious" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" pickOnBounds="false" text="< Zurück" />
<Button id="btnNext" alignment="CENTER" contentDisplay="CENTER" defaultButton="true" mnemonicParsing="false" pickOnBounds="false" text="Weiter >" />
<Button id="btnFinish" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" pickOnBounds="false" text="Fertigstellen" />
<Button id="btnCancel" alignment="CENTER" cancelButton="true" contentDisplay="CENTER" mnemonicParsing="false" pickOnBounds="false" text="Abbrechen" />
</buttons>
</ButtonBar>
</bottom>
<center>
<AnchorPane fx:id="taskPane" pickOnBounds="false" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
logon.fxml (with fx:root)
<fx:root type="AnchorPane" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TitledPane alignment="TOP_LEFT" animated="false" collapsible="false" contentDisplay="CENTER" pickOnBounds="false" styleClass="taskPane" stylesheets="@application.css" text="Logon" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<font>
<Font name="System Bold" size="18.0" />
</font>
<content>
<GridPane maxHeight="150.0" maxWidth="250.0" pickOnBounds="false" styleClass="inputGrid">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="94.0" minWidth="10.0" prefWidth="58.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="142.0" minWidth="10.0" prefWidth="140.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label pickOnBounds="false" text="Mandant" />
<Label pickOnBounds="false" text="Benutzer" GridPane.rowIndex="1" />
<Label pickOnBounds="false" text="Kennwort" GridPane.rowIndex="2" />
<TextField id="fldMandant" fx:id="fldMandant" pickOnBounds="false" GridPane.columnIndex="1" />
<TextField fx:id="fldBenutzer" pickOnBounds="false" prefWidth="110.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<TextField fx:id="fldKennwort" pickOnBounds="false" GridPane.columnIndex="1" GridPane.rowIndex="2" />
</children>
</GridPane>
</content>
</TitledPane>
</children>
</fx:root>
Alright, so the problem was accidental recursion/loop. The code I executed to load a Node from a FXML was in the initialize(URL, ResourceBundle)
-method. Now, setting the controller of the new loaded fxml to the same controller-object from where it is created from (loader.setController(this);
) causes the initialize(URL, ResourceBundle)
-method to run again and therefore load the same fxml again. This never stops.
My solution was to remove the loader.setController(this);
call and (more importantly) set the controller in the fxml itself to some other controller. To keep connection between the controllers I created an interface called SubController
. All controllers which represent a step/task and not the wizard itself implement it. The interface specifies the four onButtonClick-methods I want to forward to the SubController. Actual handling of the event now happens in the SubController.
@FXML
void onCancel(ActionEvent event)
{
this.currentSub.onCancel(event);
}
@FXML
void onFinish(ActionEvent event)
{
this.currentSub.onFinish(event);
}
@FXML
void onNext(ActionEvent event)
{
this.currentSub.onNext(event);
}
@FXML
void onPrevious(ActionEvent event)
{
this.currentSub.onPrevious(event);
}