FXML parent element not injected in child controller [duplicate] - javafx-2

I am having the following problem with a program that I am currently writing, and I have searched on the internet, but I couldn't really find anything to help me understand the following problem
So inside another class I have written a method that executes this whenever the search button is clicked and the method looks like this:
public void searchButton(){
try {
new SearchController().display();
} catch (IOException e) {
e.printStackTrace();
}
}
And then the SearchController class looks something like this (I simplified it here):
public class SearchController {
#FXML
private Button cancelButton;
#FXML
private Label what;
private static Stage stage;
private static BorderPane borderPane;
#FXML
public void initialize(){
what.setText("Testing"); // this woks
cancelButton.setOnAction(e -> stage.close());
}
public void display() throws IOException {
stage = new Stage();
stage.setResizable(false);
stage.setTitle("Product search");
stage.initModality(Modality.APPLICATION_MODAL);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(SearchController.class.getResource("Search.fxml"));
borderPane = loader.load();
Scene scene = new Scene(borderPane);
stage.setScene(scene);
//what.setText("Testing") and this doesn't work
stage.showAndWait();
}
}
Can someone please tell me why it is possible to write text on the initialize method (that method gets called after the borderPane = loader.load(); line...so why doesn't it work if I try to write on the label after that line?)
Thank you in advance

The FXMLLoader creates an instance of the class specified in the fx:controller attribute of the FXML root element. It then injects the elements defined in the FXML file into the controller instance it created when the fx:id attributes match the field names. Then it calls the initialize() method on that instance.
You create an instance of the controller "by hand" with new SearchController(). This is not the same object that is created by the FXMLLoader. So now when you have loaded the fxml file you have two different instances of SearchController. So if you call what.setText(...) from the display() method, you are not calling it on the controller instance created by the FXMLLoader. Consequently, what has not been initialized in the instance on which you are calling what.setText(...), and you get a null pointer exception.
Since initialize() is invoked by the FXMLLoader on the instance it created, when you call what.setText(...) from the initialize() method, you are calling it on the instance created by the FXMLLoader, and so the FXML-injected fields for that instance have been initialized.

Related

Access an Object from the FX Controller Class

i have a FXML project and therefore a Controller class. I created an instance of a class called "Control" inside the initialize method of my Controller class.
The Problem now is: I created a second FXML Controller for a Preferences Menu and i to need access the Control object created in the main Controller class.
How do i do that?
I cannot use a simple getter, i get an error that this cannot be derefferenced from a static context.
Edit:
For the Code:
This is the main Gui Controller:
private Control control;
.
.
.
#Override
public void initialize(URL url, ResourceBundle rb) {
//Create a Controller
control = new Control(0);
.
.
.
}
The second FXML is loaded within this handler:
#FXML
public void handleSetPreferences(ActionEvent ae) throws Exception {
Group root = new Group();
Stage stage = new Stage();
BorderPane frame = FXMLLoader.load(getClass().getResource("guipreferences.fxml"));
root.getChildren().add(frame);
Scene scene = new Scene(root);
stage.initStyle(StageStyle.UNDECORATED);//disables the OS frame
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
I need to access the control object created within the first Controller from the Controller created with the handle above.

Popup window Label is not getting loaded

1.Trying to display exception message in popup window. Exception message is not appearing.
2.Eg: When i click button a popup window (Second fxml file) is to load with proper exception message in the label
3.Popup window is appearing, But the Label is not loading (Bold one --> ExceptionLabel.setText("Please enter Proper file path")) it says null pointer exception.
4.I am not sure what i missing. Same declared in FX:ID and also in second fxml file linked the main controller. Thanks in advance.
#FXML
public Label ExceptionLabel;
Stage PopupWindow = new Stage();
public void Buttonhandle(ActionEvent event) throws IOException {
try {
if(ESBOutboundFile!=null && OutputFile!=null){
String Output = SBlogpaser.Logpaser(ESBInboundFile,ESBOutboundFile,OutputFile);
System.out.println(Output);
}else{
Window(PopupWindow);
**ExceptionLabel.setText("Please enter Proper file path");**
}
} catch (Exception ex) {
System.out.println(ex);
}
}
public void Window(Stage Popup) throws Exception {
this.Popup=Popup;
final FXMLLoader fxmlLoader = new FXMLLoader();
Parent root= fxmlLoader.load(getClass().getResource("POPUPWindow.fxml"));
Scene scene1 = new Scene(root);
Popup.setScene(scene1);
Popup.show();
}
If i keep the label in "OK" handle button it is getting displayed.
From where are you expecting ExceptionLabel to be instantiated?
Assuming you are pointing the fx:controller attribute of the root of your POPUPWindow.fxml file to the current class, it will just create a new instance of that class, and inject values into that instance. The field ExceptionLabel in the current instance won't be initialized.
You could probably just about make this work by setting the controller of the FXMLLoader to the current object, something like this:
public void window(Stage popup) throws Exception {
this.popup=popup; // why?
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("POPUPWindow.fxml"));
fxmlLoader.setController(this);
Parent root= fxmlLoader.load();
Scene scene1 = new Scene(root);
popup.setScene(scene1);
popup.show();
}
and then remove the fx:controller attribute from POPUPWindow.fxml.
This seems like a really bad idea, though, because now the current object is acting as a controller for two different FXML files. This will be confusing at best, and under fairly reasonable conditions would produce weird results. It would be much better to write a different controller class for the popup:
public class PopupController {
private final String message ;
#FXML
private Label exceptionLabel ;
public PopupController(String message) {
this.message = message ;
}
public void initialize() {
exceptionLabel.setText(message);
}
}
and then use the window(...) method above, but with
fxmlLoader.setController(new PopupController("Please enter Proper file path"));
Obviously, if you're reusing the window(..) method, you might want to pass the message in as a parameter to that method.

Closing a substage

What I'm trying to do I think is exactly what Meg is talking about here: JavaFX2 : Closing a stage (substage) from within itself
When I try to implement JewelSea's answer I get the "nonstatic method getSource() cannot be referenced from a static context."
So I have my secondary window (scene) created in Scene Builder with a simple controller class that has basically one function: tie the button to a close() event handler. Here's the code I have:
public class ProductNotFoundController
implements Initializable {
#FXML // fx:id="closeButton"
private Button closeButton; // Value injected by FXMLLoader
#Override // This method is called by the FXMLLoader when initialization is complete
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
closeButton.setOnAction(new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent t) {
// ProductNotFound.avisoClose();
Node source;
source = (Node) ActionEvent.getSource();
Stage stage = (Stage) source.getScene().getWindow();
stage.close();
}
});
}
}
Can someone please tell me what I'm doing wrong? And/or where should I put the close() method?
Replace ActionEvent by t, getSource is a non-static method.
jewelsea was using actionEvent the instance of the ActionEvent class.

JavaFX "already set as root of another scene" exception when launching modal dialog a second time

I am building a simple JavaFX application that requires me to launch a modal window in front of my main application window. Using the code below, I am able to launch the modal window 1 time and close it. If I attempt to launch it again, I receive:
java.lang.IllegalArgumentException: BorderPane[id=root, styleClass=root]is already set as root of another scene
I am using the Spring Controller/FXML View dependency injection method described here:
http://www.zenjava.com/2012/02/20/porting-first-contact-to-spring/
I am able to programatically create a scene and hide/display a simple dialog without using FXML / Spring Controller injection. This works fine.
I am unable to explain the 'already set as root' exception, as I am creating a new Scene() each time the startButton is clicked. The 1st Scene should have been destroyed whenever the modal window was closed the 1st time.
Relevant files are below.
MainTabPanel.java - The main view of my application. This contains the 'startButton' that is clicked to launch the modal window.
The ActivePresentation Controller/View is injected as:
#Inject private ActivePresentation activePresentation;
Below is the initialize() method that attempts to launch the modal when startButton is clicked.
#FXML
public void initialize()
{
availableReceiversIdColumn.setCellValueFactory(new PropertyValueFactory("id"));
availableReceiversFirmwareVersionColumn.setCellValueFactory(new PropertyValueFactory("firmwareVersion"));
availableReceiversModelColumn.setCellValueFactory(new PropertyValueFactory("model"));
availableReceiversChannelColumn.setCellValueFactory(new PropertyValueFactory("channel"));
ObservableList<String> responseTypes = FXCollections.observableArrayList();
responseTypes.add("Single Response Alpha");
responseTypes.add("Single Response Numeric");
responseTypeChoiceBox.setItems(responseTypes);
startButton.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
presentationResponseService.startPresentation();
activePresentation.populateResponses(null);
activePresentation.populateResults(null);
Scene activePresentationScene = new Scene(activePresentation.getView());
activePresentationScene.getStylesheets().add("styles.css");
stage.setScene(activePresentationScene);
stage.setTitle("Active Presentation");
stage.showAndWait();
}
});
}
The closeButton is defined in the modal dialog as follows.
closeButton.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
presentationResponseService.closePresentation();
Stage stage = (Stage) root.getScene().getWindow();
stage.close();
}
});
The Java based Spring configuration for the ActivePresentation bean and FXML loader are as follows.
#Bean
public ActivePresentation activePresentation()
{
return loadPresenter("/fxml/ActivePresentation.fxml");
}
FXML Loader
private <T> T loadPresenter(String fxmlFile)
{
try
{
FXMLLoader loader = new FXMLLoader();
loader.load(getClass().getResourceAsStream(fxmlFile));
return (T) loader.getController();
} catch (IOException e)
{
throw new RuntimeException(String.format("Unable to load FXML file '%s'", fxmlFile), e);
}
}
I had the issue when I had a pop-up window autowired. it was working the first time but when I close the pop-up window the second time, I was getting this error.
I managed by checking if the pop-up window had already a scene, see the below code:
if (window.getScene() == null) {
Scene scene = new Scene(window);
stage.setScene(scene);
} else {
stage.setScene(window.getScene());
}
Seems, the stage is still under control of JFX (logic : you can try yo open it again).
And you create a stage each time while previous is still alive, so, could you try to add stage.setScene(null) near stage.close().
Or use the same scene or the same stage each time?
I don't see any bug in this part of JFX : node is the same, stages (and scenes in it) are different. So there are 2 ways : use only 1 stage+scene, or create different border pane instances each time.

Pass object from one scene to another

As I'm learning the new world of JavaFX2 I stumbled on another annoying problem. I'm developing a program with multiple scenes (~10 scenes). For that I created a small class like this:
public class SceneSelector {
...
public void setScene(Stage stage, String fxmlfilename, ObservableList ol) throws Exception{
String s = "../" + fxmlfilename;
Parent root = FXMLLoader.load(getClass().getResource(s));
root.setUserData(ol);
Scene scene = new Scene(root);
stage.setScene(scene);
//show the stage
stage.show();
}
}
This class works good enough for switching between the scenes.
Now the problem is that I sometimes need to pass data from Scene1 to Scene2. I'm trying to do this by setting the setUserData() for the new scene which basicly works exept for one thing. How can I get the userdata when the new Scene is beeing initialized? (because the Nodes are still null at that time)
Code at scene1:
//Code connected to a button that opens the new Scene
private void openLabID(ActionEvent event) throws Exception {
final Stage primaryStage = (Stage) btnNewScene.getScene().getWindow();
ObservableList<Koe> olAfTeWerkenKoeien = DA_Koe.getAfTeWerkenKoeien();
ss.setScene(primaryStage, "GUI/scenes/koe/Koe.fxml", olAfTeWerkenKoeien);
}
Code at scene2:
public void initialize(URL url, ResourceBundle rb) {
Scene s = lbl.getScene();
ObservableList<Koe> olAfTeWerkenKoeien = (ObservableList<Koe>) s.getRoot().getUserData();
System.out.println(olAfTeWerkenKoeien.size());
}
Of course Scene s gives a null value at this point (because lbl is null at this point), so I wonder, is there a method that is beeing fired right after initialize?
When I attach this code to a button on Scene2, it works like a charm, but it should be loaded automatically.
EDIT:
The setting of the data with the setMyData() method is not a problem, however retrieving it is:
public ObservableList<Koe> getMyData() {
return this.myData;
}
How can I get the CustomScene object when a controller initializes? Because doing this below will result in a NullPointerException (because btnSluiten is not initialized just yet):
#Override
public void initialize(URL url, ResourceBundle rb) {
...
Stage stage = (Stage) btnSluiten.getScene().getWindow();
CustomScene cs = (CustomScene) stage.getScene();
ObservableList<Koe> olKoe = cs.getMyData();
System.out.println(olKoe.size());
}
I believe you missed the point within the Scene object. From the Scene class documentation we can see that:
The JavaFX Scene class is the container for all content in a scene graph.
Which means that the Scene object is just a container and as such it's not supposed to hold any data.
With that in mind, you can make another static object with a field such as
private static Label lbl;
...
public static Label getLbl()
{
return MyStaticObject.Lbl;
}
...
and use it to store your lbl (or whatever object suits your information) and then statically retrieve it.
I'm doing that to set the owner of my other Stage objects from my application. I hope it helps. Cheers
If you really want your scene to be meaningful (aka store specific user data) you can extend it:
public class FooScene extends Scene {
private ObservableList myData;
public setMyData(ObservableList data) {
this.myData = data;
//handle data
}
}
The easiest way to make sure setup code is called after scene initialized it to call it by yourself:
public class SceneSelector {
...
public void setScene(Stage stage, String fxmlfilename, ObservableList ol) throws Exception{
String s = "../" + fxmlfilename;
Parent root = FXMLLoader.load(getClass().getResource(s));
// first: add root to scene
FooScene scene = new FooScene(root);
// second: apply data to scene (or root)
scene.setMyData(ol);
stage.setScene(scene);
//show the stage
stage.show();
}
}
You can use controller for scenes and pass the data through controller:
String filePath1 = "../" + fxmlfilename;
URL location1 = YourController1.class.getResource(filePath1);
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location1);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
Parent root = (Node) fxmlLoader.load(location1.openStream());
YourController1 ctrl1 = (YourController1) fxmlLoader.getController();
Then you can assign data to the controller:
ctrl1.setUserData();
Finally, just show the scene as you want:
Scene scene = new Scene(root);
stage.setScene(scene);
//show the stage
stage.show();
In the initialize() method in the controller, just get data from controller as usual data object.
Some Addition of #Sergey Grinev :
Create A custom Scene :
package sample;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class DataPassingScene extends Scene {
public DataPassingScene(Parent parent) {
super(parent);
}
String tafsir;
public String getTafsir() {
return tafsir;
}
public void setTafsir(String tafsir) {
this.tafsir = tafsir;
}
}
Suppose your Main Class Name is App.java, Then Create a method to show new Stage :
public static void showLayout (Stage primaryStage, String fxmlFileName, String stringData) throws IOException {
Parent root = FXMLLoader.load(Objects.requireNonNull(App.class.getClassLoader().getResource(fxmlFileName)));
DataPassingScene scene = new DataPassingScene(root);
scene.setTafsir(stringData); // Here we pass the data
primaryStage.setScene(scene);
primaryStage.show();
}
Now When you want to pass the Data, Call the above method from any Where / any class in your app with some Data :
String tafsir = "This My Data" ;
try {
App.showLayout(new Stage(), "showTafsirFxml.fxml",tafsir);
} catch (IOException e) {
e.printStackTrace();
}
Then Get the data in your controller. To get the scene you have to get the stage, and to get the stage you have use one of your elements of FXML, suppose here your element is a button, called closeButton, So :
#FXML
private Button closeButton;
#Override
public void initialize(URL url, ResourceBundle rb) {
Platform.runLater(new Runnable() {
#Override public void run() {
Stage stage = (Stage) closeButton.getScene().getWindow();
DataPassingScene scene = (DataPassingScene) stage.getScene();
String s = scene.getTafsir(); // Here we get the Data
if(s!=null)
System.out.println("This is Tafsir From Highlight Table: "+s);
else
System.out.println("Data Passing Null");
}});
}
because, You have to wait some Times in above runLater, because for initializing scene take some time. Other wise scene will be null.

Resources