I am new to JavaFX. I programmed a tableview with a list of persons with their address. Everything worked fine. Then I wanted to write it in the MVC or even better the MVP Design pattern. I am working with FXML and scenebuilder to layout the scenes. Now with the MVC I have the problem that I get a NUllpointerException when i.e. I wanna close the confirm dialog box. And I also can't change the labeltext for the confirmdialog window scene. I think I know what the problem is but don't know how to solve this properly. The controller gets instantiated twice and therefore the first values of the variables form the fxml are NUll.
So this is how I wrote my little app:
MiniTest.java - my main App
package AddressBook;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MiniTest extends Application {
private static Stage window;
@Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("Adresslist of Clients");
Model model = new Model();
try {
FXMLLoader mainViewloader = new FXMLLoader(getClass().getResource("View.fxml"));
Parent root = (Parent) mainViewloader.load();
MainController mainController = mainViewloader.<MainController>getController();
window.setScene(new Scene(root));
window.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Stage getPrimaryStage() {
return window;
}
public static void main(String[] args) {
launch(args);
}
}
Model.java - this is my model, not sure if the content has to be in here. For the next step I wanna collect the data from a mysql db and save it to the db. For now I have the persons like this.
package AddressBook;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class Model {
public ObservableList<Person> getPerson() {
ObservableList<Person> personList = FXCollections.observableArrayList();
Address addressPerson1 = new Address("Jaguarweg 12", "23454", "Bensheim");
Address addressPerson2 = new Address("Friedrich-Ebert-Str. 134", "82635", "Berlin");
Address addressPerson3 = new Address("Adam-Opel-Str. 1", "92364", "Bamberg");
Address addressPerson4 = new Address("Power-Shell-Pfad 21", "10083", "Hamburg");
Address addressPerson5 = new Address("Schwertstr. 76", "749236", "Stuttgart");
Address addressPerson6 = new Address("Hans-Jacob-Weg 4", "66396", "Wiesbaden");
Address addressPerson7 = new Address("Georg-Lucas-Str. 110", "53421", "Wien");
Address addressPerson8 = new Address("Andalusienweg 17", "723612", "Ostfildern");
Address addressPerson9 = new Address("Mercedes-Benz-Str. 9", "883621", "Wolfsburg");
Address addressPerson10 = new Address("Heinrich-Schwein-Str. 43", "134923", "Frankfurt");
Address addressPerson11 = new Address("Engel-Teufel-Str. 66", "083273", "Hildesheim");
personList.add(new Person("Georg Sorresto", addressPerson1));
personList.add(new Person("Flynn Bozzen", addressPerson2));
personList.add(new Person("Bill Klang", addressPerson3));
personList.add(new Person("Wilhelm Busch", addressPerson4));
personList.add(new Person("Gertrud Raven", addressPerson5));
personList.add(new Person("Markus Berg", addressPerson6));
personList.add(new Person("Juergen Schmidt", addressPerson7));
personList.add(new Person("Fritz Titz", addressPerson8));
personList.add(new Person("Bodo Bambino", addressPerson9));
personList.add(new Person("Ortrun Giner", addressPerson10));
personList.add(new Person("Jakob Huber", addressPerson11));
return personList;
}
}
Person.java - my Person class
package AddressBook;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private SimpleStringProperty name;
private Address address;
public Person() {
this.name = new SimpleStringProperty("");
this.address = new Address("", "", "");
}
public Person(String name, Address address) {
this.name = new SimpleStringProperty(name);
;
this.address = address;
}
public String getName() {
return name.get();
}
public void setName(String name2) {
name.set(name2);
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
In MainController.java - for certain things like closing the window or for deleting a person I call a method like this as my own confirmdialogwindow with corresponding String for the Label.
boolean answer = confirmDialogBoxController.display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
and here is the full MainController.java - this is the mainController for the mainView(View.fxml)
package AddressBook;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class MainController implements Initializable {
@FXML
private MenuItem exitMenu;
@FXML
private TableView<Person> personTable;
@FXML
private TextField zipInput;
@FXML
private TextField cityInput;
@FXML
private TextField streetInput;
@FXML
private TextField nameInput;
@FXML
private TableColumn<Person, String> cityColumn;
@FXML
private TableColumn<Person, String> zipColumn;
@FXML
private TableColumn<Person, String> streetColumn;
@FXML
private TableColumn<Person, String> nameColumn;
@FXML
private Button addButton;
@FXML
private Button deleteButton;
@FXML
private SimpleStringProperty message;
private Stage window;
private ConfirmDialogBoxController confirmDialogBoxController;
private AlertEmptyNameController alertEmptyNameController;
private Model model;
public MainController(){
model = new Model();
System.out.println("+1 MainController()");
}
public MainController(Model model){
this.model = model;
System.out.println("+1 MainController(model)");
}
@Override
public void initialize(URL location, ResourceBundle resources) {
Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = (TableColumn<Person, String> p) -> new MainController.EditingCell();
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
nameColumn.setCellFactory(cellFactory);
nameColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setName(t.getNewValue());
}
);
streetColumn.setCellValueFactory(person -> new
SimpleStringProperty(person.getValue().
getAddress().
getStreet()));
streetColumn.setCellFactory(cellFactory);
streetColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).getAddress().setStreet(t.getNewValue());
});
zipColumn.setCellValueFactory(person -> new
SimpleStringProperty(person.getValue().
getAddress().
getZip()));
zipColumn.setCellFactory(cellFactory);
zipColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).getAddress().setZip(t.getNewValue());
});
cityColumn.setCellValueFactory(person -> new
SimpleStringProperty(person.getValue().
getAddress().
getCity()));
cityColumn.setCellFactory(cellFactory);
cityColumn.setOnEditCommit(
(
TableColumn.CellEditEvent<Person, String> t) ->
{
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).getAddress().setCity(t.getNewValue());
});
personTable.setItems(model.getPerson());
personTable.setEditable(true);
MiniTest.getPrimaryStage().setOnCloseRequest(e -> {
try {
e.consume();
closeWindow(e);
} catch (IOException ex) {
ex.printStackTrace();
}
});
}
public SimpleStringProperty getMessageProperty(){
return message;
}
public void setMessage(String message) {
this.message.setValue(message);
}
public String getMessage() {
return this.message.getValue();
}
@FXML
public void closeWindow(WindowEvent e) throws IOException {
boolean answer = confirmDialogBoxController.display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
@FXML
public void closeWindowFromMenu(ActionEvent e) throws IOException {
boolean answer = confirmDialogBoxController.display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
public Stage getPrimaryStage() {
return window;
}
@FXML
public void addNewPerson() throws IOException {
alertEmptyNameController = new AlertEmptyNameController();
Person person = new Person();
person.setName(nameInput.getText());
if(nameInput.getText().isEmpty()){
alertEmptyNameController.alertEmptyName();
}else{
person.getAddress().setStreet(streetInput.getText());
person.getAddress().setZip(zipInput.getText());
person.getAddress().setCity(cityInput.getText());
personTable.getItems().add(person);
}
nameInput.clear();
streetInput.clear();
zipInput.clear();
cityInput.clear();
}
@FXML
public void deletePerson() throws IOException {
boolean answer = confirmDialogBoxController.display("Deleting Entry", "Are you sure you want to delete this entry?");
if (answer) {
ObservableList<Person> personSelected, allPersons;
allPersons = personTable.getItems();
personSelected = personTable.getSelectionModel().getSelectedItems();
personSelected.forEach(allPersons::remove);
}
}
static class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.focusedProperty().addListener((ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) -> {
if (!arg2) {
commitEdit(textField.getText());
}
});
}
private String getString() {
return getItem() == null ? "" : getItem();
}
}
}
View.fxml - this is the main View
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane fx:id="borderPane" stylesheets="@MyStyle.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="AddressBook.MainController">
<bottom>
<HBox fx:id="bottomLayout" alignment="CENTER" style="-fx-background-color: #292929;" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets />
</BorderPane.margin>
<children>
<TextField fx:id="nameInput" promptText="Name" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="8.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<TextField fx:id="streetInput" layoutX="15.0" layoutY="15.0" promptText="Street" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<TextField fx:id="zipInput" layoutX="163.0" layoutY="15.0" promptText="Zip" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<TextField fx:id="cityInput" layoutX="312.0" layoutY="15.0" promptText="City" style="-fx-background-radius: 0;">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</TextField>
<Button fx:id="addButton" mnemonicParsing="false" onAction="#addNewPerson" style="-fx-background-color: #5ebcff; -fx-background-radius: 0; -fx-text-fill: white;" text="Add">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="5.0" top="10.0" />
</HBox.margin>
</Button>
<Button fx:id="deleteButton" layoutX="474.0" layoutY="15.0" mnemonicParsing="false" onAction="#deletePerson" style="-fx-background-radius: 0; -fx-background-color: #5ebcff; -fx-text-fill: white;" text="Delete">
<HBox.margin>
<Insets bottom="8.0" left="5.0" right="8.0" top="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
<center>
<TableView fx:id="personTable" editable="true" style="-fx-background-color: #5e5e5e;" stylesheets="@MyStyle.css" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="nameColumn" prefWidth="152.0" text="Name" />
<TableColumn fx:id="streetColumn" prefWidth="196.0" text="Street" />
<TableColumn fx:id="zipColumn" prefWidth="88.0" text="Zip" />
<TableColumn fx:id="cityColumn" prefWidth="163.0" text="City" />
</columns>
</TableView>
</center>
<top>
<HBox fx:id="menuLayout" style="-fx-background-color: #292929;" stylesheets="@MyStyle.css" BorderPane.alignment="CENTER">
<children>
<MenuBar fx:id="menuBar" stylesheets="@MyStyle.css">
<menus>
<Menu fx:id="fileMenu" mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="exitMenu" mnemonicParsing="false" onAction="#closeWindowFromMenu" text="Exit" />
</items>
</Menu>
</menus>
</MenuBar>
</children>
</HBox>
</top>
</BorderPane>
ConfirmDialogBoxController.java - this is the controller for the confirm dialog box for closing window with the "X" or in the menu File->Exit and for asking the user if he really wants to delete the entry in the tableview
package AddressBook;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class ConfirmDialogBoxController implements Initializable {
private static Boolean answer;
@FXML
private Button yesButton;
@FXML
private Button noButton;
@FXML
private Label confirmLabel;
@FXML
private Stage stage;
@FXML
private Model model;
public ConfirmDialogBoxController(){
System.out.println("+1 ConfirmDialogBoxController() object instantiated");
}
public ConfirmDialogBoxController(Model model){
this.model = model;
System.out.println("+1 ConfirmDialogBoxController(model) object instantiated");
}
public boolean display(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.confirmLabel.setText(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
return answer;
}
public Label getLabel(){
return confirmLabel;
}
@FXML
public boolean yesButtonClicked(ActionEvent actionEvent) {
answer = true;
stage = (Stage) yesButton.getScene().getWindow();
stage.close();
return answer;
}
@FXML
public boolean noButtonClicked(ActionEvent actionEvent) {
answer = false;
Stage confirmWindow = (Stage) noButton.getScene().getWindow();
confirmWindow.close();
return answer;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
// confirmLabel.textProperty().bindBidirectional(messageProperty);
// System.out.println("messageProperty.getValue(): "+ messageProperty.getValue());
// System.out.println("DEBUG: message is = " + messageProperty.get());
}
}
ConfirmDialogBoxView.fxml - this is the ConfirmDialogBox View
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefWidth="450.0" stylesheets="@MyStyle.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="AddressBook.ConfirmDialogBoxController">
<children>
<VBox fx:id="vBox" alignment="CENTER" spacing="20.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="confirmLabel" textAlignment="CENTER" />
<HBox fx:id="hBox" alignment="CENTER" spacing="10.0">
<children>
<Button fx:id="yesButton" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#yesButtonClicked" text="Yes" textAlignment="CENTER">
<HBox.margin>
<Insets />
</HBox.margin>
</Button>
<Button fx:id="noButton" alignment="CENTER" contentDisplay="CENTER" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#noButtonClicked" text="No" textAlignment="CENTER">
<HBox.margin>
<Insets />
</HBox.margin>
</Button>
</children>
<VBox.margin>
<Insets />
</VBox.margin>
</HBox>
</children>
<padding>
<Insets bottom="50.0" top="30.0" />
</padding>
</VBox>
</children>
</AnchorPane>
AlertEmptyNameController.java - this is the controller for the view when the name field is empty and you click on add person
package AddressBook;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
public class AlertEmptyNameController {
@FXML
private Button okButton;
@FXML
private Label emptyNameLabel;
private Stage alertEmptyNamestage;
private Model model;
private AlertEmptyNameController alertEmptyNameController;
public AlertEmptyNameController() {
System.out.println("+1 AlertEmptyNameControlle() object instantiated");
}
public AlertEmptyNameController(Model model){
this.model = model;
System.out.println("+1 AlertEmptyNameController(model) object instantiated");
}
public void alertEmptyName() throws IOException {
FXMLLoader alertEmptyNameLoader = new FXMLLoader(getClass().getResource("AlertEmptyNameView.fxml"));
alertEmptyNameLoader.load();
AlertEmptyNameController alertEmptyNameController = alertEmptyNameLoader.getController();
Parent root = alertEmptyNameLoader.getRoot();
alertEmptyNamestage = new Stage();
alertEmptyNamestage.setScene(new Scene(root));
alertEmptyNamestage.setTitle("The Namefield is empty!");
alertEmptyNamestage.initModality(Modality.APPLICATION_MODAL);
alertEmptyNamestage.showAndWait();
}
@FXML
public boolean okButtonClicked(ActionEvent actionEvent) {
boolean answer = true;
alertEmptyNamestage = (Stage) okButton.getScene().getWindow();
alertEmptyNamestage.close();
return answer;
}
}
AlertEmptyNameView.fxml - the view for AlertEmptyName
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<AnchorPane prefHeight="250.0" prefWidth="450.0" stylesheets="@MyStyle.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="AddressBook.AlertEmptyNameController">
<children>
<VBox fx:id="vBox" alignment="CENTER" spacing="20.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="emptyNameLabel" text="The Namefield cannot be empty!" textAlignment="CENTER" />
<HBox fx:id="hBox" alignment="CENTER" spacing="10.0">
<children>
<Button fx:id="okButton" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#okButtonClicked" text="Ok" textAlignment="CENTER">
<HBox.margin>
<Insets />
</HBox.margin>
</Button>
</children>
<VBox.margin>
<Insets />
</VBox.margin>
</HBox>
</children>
<padding>
<Insets bottom="50.0" top="30.0" />
</padding>
</VBox>
</children>
</AnchorPane>
And this is for example the NullPointerException when I wanna close the window:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at AddressBook.MainController.closeWindow(MainController.java:163)
at AddressBook.MainController.lambda$initialize$8(MainController.java:137)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at com.sun.javafx.stage.WindowPeerListener.closing(WindowPeerListener.java:88)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:122)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$3(GlassWindowEventHandler.java:151)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:410)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:149)
at com.sun.glass.ui.Window.handleWindowEvent(Window.java:1273)
at com.sun.glass.ui.Window.notifyClose(Window.java:1177)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$4(WinApplication.java:186)
at java.lang.Thread.run(Thread.java:748)
I hope that someone can help me.
Cheers
I deleted now the display() method in confirmDialogBoxController.java and put it in mainController.java
...
public boolean display(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.getLabel().setText(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
return answer;
}
@FXML
public void closeWindow(WindowEvent e) throws IOException {
boolean answer = display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
...
Your confirmDialogBoxController
in the main controller is null: it is never initialized. The way you have set this up is problematic: the ConfirmDialogBoxController
is only initialized when the corresponding FXML is loaded (common to any FXML-controller pair), but the code for loading the FXML is in the ConfirmDialogBoxController
class, so you can't load the FXML until you have a controller instance.
It's not really the responsibility of the ConfirmDialogBoxController
to display the UI defined by its FXML. You should move the display()
method to your MainController
class:
public class MainController implements Initializable {
// Existing code omitted...
// updated close method:
@FXML
public void closeWindow(WindowEvent e) throws IOException {
boolean answer = display("Exiting Window", "Are you sure you want to close the window?");
if (answer) {
MiniTest.getPrimaryStage().close();
}
}
public boolean displayConfirmDialog(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.setConfirmMessage(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
// Get the user response (true = Yes button clicked) from controller:
return confirmDialogBoxController.getResponse() ;
}
}
And then update the ConfirmDialogBoxController
:
public class ConfirmDialogBoxController implements Initializable {
// existing code omitted...
// This should not be static, and should be a primitive type:
// private static Boolean answer ;
private boolean answer ;
// this method removed:
/*
public boolean display(String title, String message) throws IOException {
FXMLLoader confirmViewLoader = new FXMLLoader(getClass().getResource("ConfirmDialogBoxView.fxml"));
confirmViewLoader.load();
ConfirmDialogBoxController confirmDialogBoxController = confirmViewLoader.getController();
confirmDialogBoxController.confirmLabel.setText(message);
Parent root = confirmViewLoader.getRoot();
Stage stage = new Stage();
stage.setTitle(title);
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
System.out.println("MessageVariable in DisplayMethod is:"+message);
stage.showAndWait();
return answer;
}
*/
// This added for convenience:
public void setConfirmMessage(String message) {
confirmLabel.setText(message);
}
// This added to return the response (answer):
public boolean getResponse() {
return answer ;
}
// Note also your event handlers should be void:
@FXML
public void yesButtonClicked(ActionEvent actionEvent) {
answer = true;
stage = (Stage) yesButton.getScene().getWindow();
stage.close();
// return answer;
}
@FXML
public void noButtonClicked(ActionEvent actionEvent) {
answer = false;
Stage confirmWindow = (Stage) noButton.getScene().getWindow();
confirmWindow.close();
// return answer;
}
}
You probably want a similar refactoring for your other dialog.