How to return value from a stage before closing it? - javafx

I have a "main stage" where I press a button to open a "second stage" where I have a table, the user selects one item of the the table and click on "asignar" button (which is just a confirm button), once clicked, it must return the code of the item selected in the table to the main stage and close the second stage.
Here is the code that matters.
I have an INT variable which must take the value of a function:
codigo = controller.setVista(this, usuario, password);
The "setVista" function goes like this:
public int setVista(ListHorarios vista, String usuario, String password) {
this.vista = vista;
this.usuario = usuario;
this.password = password;
this.inicializarTabla();
this.actualizarTabla(0, "%");
btnSeleccionar.setOnAction(e -> {
asignarSeleccion();
Stage stage = (Stage) btnSeleccionar.getScene().getWindow();
stage.close();
});
return codigo_horario;
}
And the "asignarSeleccion" like this:
private void asignarSeleccion() {
final HorarioTableModelo aux_horario = getTablaSeleccionada();
posicion = datos.indexOf(aux_horario);
if (aux_horario != null) {
codigo_horario = aux_horario.getCodigo();
}
}
My problem is that I can't get the "codigo_horario" value into the first variable "codigo" before the stage closes, what do I am missing?

Here is a possible example. The structure is the same as in the answer in my comment.
The second Stage is opened through a "controller" that is stores the data that should be returned even when the Stage is closed and exposes a getter to be used to retrieve the value from the outer world.
import javafx.application.Application;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
Button bSecondStage = new Button("Show second Stage");
bSecondStage.setOnAction(e -> {
WindowController wc = new WindowController();
wc.showStage();
System.out.println(wc.getData());
});
root.setCenter(bSecondStage);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
class WindowController {
private String data;
void showStage() {
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
VBox root = new VBox();
Scene scene = new Scene(root);
TextField tf = new TextField();
Button submit = new Button("Submit");
submit.setOnAction(e -> {
data = tf.getText();
stage.close();
});
root.getChildren().addAll(tf, submit);
stage.setScene(scene);
stage.showAndWait();
}
String getData() {
return data;
}
}
}

You can write your own Stage class with a return statement.
public class MyStage extends Stage {
public String showAndReturn(myFXControll controll) {
super.showAndWait();
return controll.getReturn();
}
}
After that you have to define a return function to your controller.
public class TableFilterControll implements Initializable {
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
}
public String getReturn() {
return "I'm a nice return value"; //return what you want controlled by your controller class
}
}
Now you can controll your return from the parent controller.
String retValue=myStage.showAndReturn(childControll);
System.out.println(retValue);
I think this is a good solution for clean code. And you can style your FXML with Screne Builder.

There is some error in 4baad4's example. If the method in the controller is iCanGetDataBeforeClose, then that's what should be called:
String someValue = controller.iCanGetDataBeforeClose();
But even that didn't work right for me. I actually got this to work without using setOnCloseRequest at all. In the form controller, I had a method like this:
public boolean getCompleted() {
return this.finished;
}
Then in the form calling it:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("myView.fxml"));
AnchorPane pane = (AnchorPane) loader.load();
myViewController controller = loader.getController();
Scene scene = new Scene(pane);
Stage stage = new Stage();
stage.setScene(scene);
stage.showAndWait();
if (controller.getCompleted()){
doStuff();
}
One might think that since the stage had exited that the controller would throw a null reference exception, but it didn't, and it returned the correct response.
This solution works and is simplest proposed IMHO.

In my main controller I create Stage. Load controller which I can use like any class. And by creating an event I can get data just before closing the window.
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/package/mySceneBuilderWindow.fxml"));
final Pane rootPane = (Pane)loader.load();
Scene scene = new Scene(rootPane);
Stage stage = new Stage();
stage.setTitle("Optional title");
stage.setScene(scene);
mySceneBuilderWindowController controller = loader.<mySceneBuilderWindowController>getController();
controller.iCanSendDataToCtrl("String for now"); // sending data or init textFields...
stage.show();
/* set event which is fired on close
// How to close in the other window... (pressing X is OK too)
#FXML private Button fxidSave = new Button(); // global var
#FXML private void handleSaveButton() {
Stage stage = (Stage) fxidSave.getScene().getWindow();
stage.close(); // closes window
}
*/
stage.setOnCloseRequest((EventHandler<WindowEvent>) new EventHandler<WindowEvent>() {
public void handle(WindowEvent we) {
String iCanGetDataBeforeClose = controller.getData();
System.out.println(iCanGetDataBeforeClose);
// static class can be used aswell -> System.out.println(Context.getMyString());
}
});
} catch (IOException e) {
System.out.println("something wrong with .fxml - name wrong? path wrong? building error?");
}
My other mySceneBuilderWindowController methods:
public void iCanSendDataToCtrl(String giveMe) {
// do something ex. myTextBox.setText(giveMe);
}
public String iCanGetDataBeforeClose() {
// do something ex. return myTextBox.getText();
}

Related

how to add a textfield from one popup to the main screen with its own controllers

How to add a double from one textfield popup to another textfield on the main screen. I have a cinema system where there is a main page which shows the total price and I have a popup for drinks as well. how do I get the value from drinks popup window to add to the textfield in the main window. I have assigned both classes with the variable completetotal which should add together and close the window when I click the button 'finish'. However it doesn't.
Controller for drinkspopup
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import static sample.FilmsController.completetotal;
public class DrinksController {
public TextField watertf;
public TextField coketf;
public Button totalbut;
public TextField totaltf;
public Button finishdrinks2;
private FilmsController fc = new FilmsController();
/*changed button to private */
/*public TextField gettotaltf() {
return totaltf;
}
private FilmsController parent;*/
private Drinks drink = new Drinks();
/* public void setParent(FilmsController fc) {
// Remember the reference to the parent GUI, then we can call its methods
parent = fc;
}*/
/*added action event*/
public void addrinks(ActionEvent actionEvent) {
try {
int water = new Integer(watertf.getText());
int coke = new Integer(coketf.getText());
double result = water * (1.75) + coke * (1.25);
totaltf.setText("" + result);
completetotal += result;
} catch (NumberFormatException e) {
totaltf.setText("Not a Number");
}
}
/*This is supposed to add the drinks then add it to the variable complete total*/
public void finishdrinksclose2(ActionEvent actionEvent) {
int water = new Integer(watertf.getText());
int coke = new Integer(coketf.getText());
double result = water * (1.75) + coke * (1.25);
completetotal += result;
totaltf.setText("" + result);
Stage stage = (Stage) finishdrinks2.getScene().getWindow();
stage.close();
}
}
Mainscreen Filmscontroller
public void comptotal(ActionEvent actionEvent) {
try {
int adult = new Integer(adulttf.getText());
int child = new Integer(childtf.getText());
double filmprice= new Double(filmpricetf.getText());
double result = adult * (filmprice) + child*(filmprice);
completetotal+= result;
totalpricetf.setText("" + result);
}
catch (NumberFormatException e) {
totalpricetf.setText("Not a Number");
}}
}
I have only added part of the code which does the procedure on filmscontroller.
Lets imagine this your MainScreen when you try to call popUp window
click.setOnAction(event -> {
FXMLLoader loader = new FXMLLoader();
try {
Parent parent = loader.load(getClass().getClassLoader().getResource("second.fxml").openStream());
Stage stage = new Stage();
stage.setScene(new Scene(parent));
stage.initModality(Modality.APPLICATION_MODAL);
Children children = loader.getController();
children.setController(this);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
});
Let imagin this your popUp window
#FXML
private JFXTextField field;
private Controller controller;
#FXML private Button cls;
#Override
public void initialize(URL location, ResourceBundle resources)
{
cls.setOnAction(event -> {
this.controller.setTextIntoTextField(field.getText());
Stage stage = (Stage)((Button)(event).getSource()).getScene().getWindow();
stage.close();
});
}
public void setController(Controller controller) {
this.controller = controller;
}
this is a type of callback method can help you to load all the data that should appear in mainscreen, more precisely from children to parent controller class

JavaFX change ComboBox items (outside of initialize() method)

I am playing around with SceneBuilder and come across a few questions about the intialize() method and how to change ComboBox items after it's already been initialized in said method. So basically, after I set the items in initialize, I am not able to change them anymore from another method in the controller.
Here is my code:
public class AppController implements Initializable {
private ObservableList<String> list = FXCollections.observableArrayList();
private MainModel model;
#FXML
private ComboBox<String> cobUsers = new ComboBox<String>();
#Override
public void initialize(URL url, ResourceBundle rb) {
list.add("name1");
list.add("name2");
cobUsers.setItems(list); // this works!
}
public void initModel(MainModel model) {
this.model = model;
}
public void addItems(){
list.add("name3");
list.add("name4");
cobUsers.setItems(list); // this does not work. ComboBox items remain "name1" and "name2"
}
}
public class App extends Application {
private Stage primaryStage;
private AnchorPane rootLayout;
private AppController appController = new AppController();
MainModel model = new MainModel();
#Override
public void start(Stage primaryStage) {
appController.initModel(model);
this.primaryStage = primaryStage;
this.primaryStage.setTitle("App");
initRootLayout();
appController.addItems();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
So guess my question is, how can I access/change my ComboBox later on, after it's been initialized in intialize()?
Thanks! :)
UPDATE 1:
I have changed the initRootLayout() in the App class (see below) and it WORKS now. list now contains 4 items and all of them show up in the ComboBox after calling addItems(). Thanks everyone!
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
rootLayout = (AnchorPane) loader.load();
AppController controller = loader.<AppController>getController();
controller.addItems();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}

Save/Load editable Fall tree in javafx

i have a editable fall tree which i want to save and load here is my code
public class Muddassir extends Application
{
private BorderPane root;
TreeView<String> tree;
TreeItem<String> module,unite,translateA ,translateB,rotate;
#Override
public void start(Stage primaryStage) throws Exception
{
root = new BorderPane();
root.setLeft(getLeftHBox());
root.setTop(getTop());
Scene scene = new Scene(root, 900, 500);
primaryStage.setTitle("BorderPane");
primaryStage.setScene(scene);
primaryStage.show();
}
private VBox getLeftHBox()
{
TreeItem<String> rootnode = new TreeItem<String>("Library");
rootnode.setExpanded(true);
module = makeBranch("module",rootnode);
makeBranch("Parameter X",module);
unite = makeBranch("unite",module);
makeBranch("Parameter uX",unite);
translateA = makeBranch("translateA",unite);
makeBranch("Parameter taX",translateA);
translateB = makeBranch("translateB",unite);
makeBranch("Parameter tbX",translateB);
rotate = makeBranch("rotate",translateB);
makeBranch("Parameter RX",rotate);
tree= new TreeView<>(rootnode);
tree.setEditable(true);
tree.setCellFactory(TextFieldTreeCell.forTreeView());
VBox vbox =new VBox(10);
vbox.setPadding(new Insets(5));
Text text= new Text("Fall tree");
text.setFont(Font.font("I", FontWeight.BOLD,16));
vbox.getChildren().addAll(text,tree);
return vbox;
}
private TreeItem<String> makeBranch(String title, TreeItem<String> parent) {
TreeItem<String>item = new TreeItem<>(title);
item.setExpanded(true);
parent.getChildren().add(item);
return item;
}
private Parent getTop() {
TextField fieldName = new TextField();
Button btSave = new Button("Save");
btSave.setOnAction(event -> {
SaveData data = new SaveData();
// data.name = ((TreeView<String>) tree).getText();
data.name = fieldName.getText();
try {
Function.save(data, "Javafx.save");
}
catch (Exception e) {
e.printStackTrace();
}
});
Button btLoad = new Button("Load");
btLoad.setOnAction(event -> {
try {
SaveData data = (SaveData) Function.load("Javafx.save");
//((TreeItems<String>)tree).setText(data.name);
fieldName.setText(data.name);
}
catch (Exception e) {
e.printStackTrace();
}
});
HBox hb= new HBox(5,fieldName,btSave,btLoad);
hb.setPadding(new Insets(5));
return hb;
}
public static void main(String[] args)
{
Application.launch(args);
}
}
And Function class is
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Function {
public static void save(Serializable data, String fileName) throws Exception {
try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(fileName)))) {
oos.writeObject(data);
}
}
public static Object load(String fileName) throws Exception {
try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(fileName)))) {
return ois.readObject();
}
}
}
And SaveData Class is
import javafx.scene.control.TreeView;
public class SaveData implements java.io.Serializable{
private static final long serialVersionUID = 1L;
public String name;
}
I wanted to edit the Tree and make changes, save that changes and after next execution, i should have that changed result. Since i don't know much about database so i came up with this method to save changes in a file and load that file.
Problem is that i can save and load text in textfield but i cannot save and load changes in tree.
I want to save changes in tree and load these changes on my next execution. I tried but failed.
Thank you.

Multiple independent stages in JavaFX

Is there a way to launch multiple independent stages in JavaFX? By independent I mean that the stages are all created from the main thread.
At the moment my application is more or less an algorithm where I would like to plot some charts and tables during execution (mainly to check whether the results are correct/ to debug).
The problem is that I cannot figure out how to create and show multiple stages independently, i.e. I would like to do something like this
public static void main(){
double[] x = subfunction.dosomething();
PlotUtil.plot(x); //creates a new window and shows some chart/table etc.
double[] y = subfunction.dosomethingelse();
PlotUtil.plot(y); //creates a new window and shows some chart/table etc.
.....
}
which would allow to use PlotUtil as one would use the plotting functions in other scripting languages (like Matlab or R).
So the main question is how to "design" PlotUtils? So far I tried two things
PlotUtils uses Application.launch for each plot call (creating a new stage with a single scene every time) --> does not work as Application.launch can only be invoked once.
Create some kind of "Main Stage" during the first call to PlotUtils, get a reference to the created Application and start subsequent stages from there --> does not work as using Application.launch(SomeClass.class) I am not able to get a reference to the created Application instance.
What kind structure/design would allow me to implement such a PlotUtils function?
Update 1:
I came up with the following idea and was wondering whether there are any major mistakes in this solution.
Interface to be implemented by all "Plots"
public abstract class QPMApplication implements StageCreator {
#Override
public abstract Stage createStage();
}
Plotting functionality:
public class PlotStage {
public static boolean toolkitInialized = false;
public static void plotStage(String title, QPMApplication stageCreator) {
if (!toolkitInialized) {
Thread appThread = new Thread(new Runnable() {
#Override
public void run() {
Application.launch(InitApp.class);
}
});
appThread.start();
}
while (!toolkitInialized) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Platform.runLater(new Runnable() {
#Override
public void run() {
Stage stage = stageCreator.createStage();
stage.show();
}
});
}
public static class InitApp extends Application {
#Override
public void start(final Stage primaryStage) {
toolkitInialized = true;
}
}
}
Using it:
public class PlotStageTest {
public static void main(String[] args) {
QPMApplication qpm1 = new QPMApplication() {
#Override
public Stage createStage() {
Stage stage = new Stage();
StackPane root = new StackPane();
Label label1 = new Label("Label1");
root.getChildren().add(label1);
Scene scene = new Scene(root, 300, 300);
stage.setTitle("First Stage");
stage.setScene(scene);
return stage;
}
};
PlotStage.plotStage(qpm1);
QPMApplication qpm2 = new QPMApplication() {
#Override
public Stage createStage() {
Stage stage = new Stage();
StackPane root = new StackPane();
Label label1 = new Label("Label2");
root.getChildren().add(label1);
Scene scene = new Scene(root, 300, 200);
stage.setTitle("Second Stage");
stage.setScene(scene);
return stage;
}
};
PlotStage.plotStage(qpm2);
System.out.println("Done");
}
}
The easiest approach here would be just to refactor your application so that it is driven from the FX Application thread. For example, you could rewrite your original code block as
public class Main extends Application {
#Override
public void start(Stage primaryStageIgnored) {
double[] x = subfunction.dosomething();
PlotUtil.plot(x); //creates a new window and shows some chart/table etc.
double[] y = subfunction.dosomethingelse();
PlotUtil.plot(y); //creates a new window and shows some chart/table etc.
// .....
}
public static void main(String[] args) {
launch(args);
}
}
Now PlotUtil.plot(...) merely creates a Stage, puts a Scene in it, and show()s it.
This assumes the methods you're calling don't block, but if they do you just have to wrap them in a Task and call PlotUtils.plot(...) in the onSucceeded handler for the task.
If you really want to drive this from a non-JavaFX application, there's a fairly well-known hack to force the JavaFX Application thread to start if it's not already started, by creating a new JFXPanel. A JFXPanel should be created on the AWT event dispatch thread.
Here's a very basic example of the second technique. Start the application and type "show" into the console. (Type "exit" to exit.)
import java.util.Scanner;
import java.util.concurrent.FutureTask;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.swing.SwingUtilities;
public class Main {
private JFXPanel jfxPanel ;
public void run() throws Exception {
boolean done = false ;
try (Scanner scanner = new Scanner(System.in)) {
while (! done) {
System.out.println("Waiting for command...");
String command = scanner.nextLine();
System.out.println("Got command: "+command);
switch (command.toLowerCase()) {
case "exit":
done = true;
break ;
case "show":
showWindow();
break;
default:
System.out.println("Unknown command: commands are \"show\" or \"exit\"");
}
}
Platform.exit();
}
}
private void showWindow() throws Exception {
ensureFXApplicationThreadRunning();
Platform.runLater(this::_showWindow);
}
private void _showWindow() {
Stage stage = new Stage();
Button button = new Button("OK");
button.setOnAction(e -> stage.hide());
Scene scene = new Scene(new StackPane(button), 350, 75);
stage.setScene(scene);
stage.show();
stage.toFront();
}
private void ensureFXApplicationThreadRunning() throws Exception {
if (jfxPanel != null) return ;
FutureTask<JFXPanel> fxThreadStarter = new FutureTask<>(() -> {
return new JFXPanel();
});
SwingUtilities.invokeLater(fxThreadStarter);
jfxPanel = fxThreadStarter.get();
}
public static void main(String[] args) throws Exception {
Platform.setImplicitExit(false);
System.out.println("Starting Main....");
new Main().run();
}
}
Here is something more along the lines I would actually follow, if I wanted the user to interact via the OS terminal (i.e. using System.in). This uses the first technique, where the application is driven by an FX Application subclass. Here I create two background threads, one to read commands from System.in, and one to process them, passing them via a BlockingQueue. Even though nothing is displayed in the main FX Application Thread, it is still a very bad idea to block that thread waiting for commands. While the threading adds a small level of complexity, this avoids the "JFXPanel" hack, and doesn't rely on there being an AWT implementation present.
import java.util.Scanner;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class FXDriver extends Application {
BlockingQueue<String> commands ;
ExecutorService exec ;
#Override
public void start(Stage primaryStage) throws Exception {
exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
commands = new LinkedBlockingQueue<>();
Callable<Void> commandReadThread = () -> {
try (Scanner scanner = new Scanner(System.in)) {
while (true) {
System.out.print("Enter command: ");
commands.put(scanner.nextLine());
}
}
};
Callable<Void> commandProcessingThread = () -> {
while (true) {
processCommand(commands.take());
}
};
Platform.setImplicitExit(false);
exec.submit(commandReadThread);
exec.submit(commandProcessingThread);
}
private void processCommand(String command) {
switch (command.toLowerCase()) {
case "exit":
Platform.exit();
break ;
case "show":
Platform.runLater(this::showWindow);
break;
default:
System.out.println("Unknown command: commands are \"show\" or \"exit\"");
}
}
#Override
public void stop() {
exec.shutdown();
}
private void showWindow() {
Stage stage = new Stage();
Button button = new Button("OK");
button.setOnAction(e -> stage.hide());
Scene scene = new Scene(new StackPane(button), 350, 75);
stage.setScene(scene);
stage.show();
stage.toFront();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX - Unique scene per stage

I have a class that extends Application and calls the primary stage. This primary stage has a Next Button, that will call another stage (options stage). The options stage has a Previous Button.
I'd like to get the instance of the primary stage, in the state it was before the user clicked Next Button, for example: a textfield with input data or combobox with selected item.
How can I do that?
Main class:
public class MainClass extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final FXMLLoader loader = new FXMLLoader(getClass().getResource("interfaceOne.fxml"));
final Parent root = (Parent)loader.load();
final MyController controller = loader.<MyController>getController();
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png")));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
Platform.setImplicitExit(false);
controller.setStage(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MyController:
public class MyController{
// Some declarations ...
Stage stage = null;
public void setStage(Stage stage) {
this.stage = stage;
}
// Next button's action
#FXML
public void handleNextAction(ActionEvent event) {
try {
Parent root = FXMLLoader.load(getClass().getResource("optionInterface.fxml"));
stage.initStyle(StageStyle.TRANSPARENT);
stage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png")));
stage.setScene(new Scene(root));
stage.show();
// Hide the current screen
((Node)(event.getSource())).getScene().getWindow().hide();
} catch (Exception exc) {
System.out.println("Error: " + exc.getMessage());
}
}
}
Options Controller:
public class OptionsController implements Initializable {
public void handlePreviousAction(ActionEvent event) {
try {
Parent root = FXMLLoader.load(getClass().getResource("interfaceOne.fxml"));;
MyController controller = MyController.getInstance();
stage.initStyle(StageStyle.TRANSPARENT);
stage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png")));
stage.setScene(new Scene(root));
controller.setStage(stage);
controller.isLocationLoaded(false);
stage.show();
// Hide the current screen
((Node)(event.getSource())).getScene().getWindow().hide();
} catch (IOException exc) {
System.out.println("Error: " + exc.getMessage());
}
}
}
Recommended Approach
Don't use multiple stages for this, instead use a single stage and multiple scenes or layered Panes.
Sample References
Angela Caicedo's sophisticated Scene switching tutorial.
A wizard style configuration.
Background
Read over a discussion of the theater metaphor behind JavaFX to help understand the difference between a Stage and a Scene and why you want to probably be changing scenes in and out of your application rather than stages.
Simple Sample
I created a simple sample based upon your application description which just switches back and forth between a main scene and an options scene. As you switch back and forth between the scenes, you can see that the scene state is preserved for both the main scene and the options scene.
For the sample, there is just a single stage reference, which is passed to the application in it's start method and the stage reference is saved in the application. The application creates a scene for the main screen and another for the options screen, saving both scene references switches the currently displayed scene back and forth between these references as required using stage.setScene.
The demo is deliberately simple to make it easy to understand and does not persist any of the data used or make use of a MVC style architecture or FXML as might be done in a more realistic demo.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class RoomReservationNavigator extends Application {
public static void main(String[] args) { Application.launch(args); }
private Scene mainScene;
private Scene optionsScene;
private Stage stage;
#Override public void start(Stage stage) {
this.stage = stage;
mainScene = createMainScene();
optionsScene = createOptionsScene();
stage.setScene(mainScene);
stage.show();
}
private Scene createMainScene() {
VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
layout.getChildren().setAll(
LabelBuilder.create()
.text("Room Reservation System")
.style("-fx-font-weight: bold;")
.build(),
HBoxBuilder.create()
.spacing(5)
.children(
new Label("First Name:"),
new TextField("Peter")
)
.build(),
HBoxBuilder.create()
.spacing(5)
.children(
new Label("Last Name:"),
new TextField("Parker")
)
.build(),
new Label("Property:"),
ChoiceBoxBuilder.<String>create()
.items(FXCollections.observableArrayList(
"The Waldorf-Astoria",
"The Plaza",
"The Algonquin Hotel"
))
.build(),
ButtonBuilder.create()
.text("Reservation Options >>")
.onAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent t) {
stage.setScene(optionsScene);
}
})
.build(),
ButtonBuilder.create()
.text("Reserve")
.defaultButton(true)
.onAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent t) {
stage.hide();
}
})
.build()
);
return new Scene(layout);
}
private Scene createOptionsScene() {
VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: azure; -fx-padding: 10;");
layout.getChildren().setAll(
new CheckBox("Breakfast"),
new Label("Paper:"),
ChoiceBoxBuilder.<String>create()
.items(FXCollections.observableArrayList(
"New York Times",
"Wall Street Journal",
"The Daily Bugle"
))
.build(),
ButtonBuilder.create()
.text("Confirm Options")
.defaultButton(true)
.onAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent t) {
stage.setScene(mainScene);
}
})
.build()
);
return new Scene(layout);
}
}

Resources