JavaFX multiple stages - java

I'm new to using Java FX and was wondering if anyone could provide some answers for me on creating multiple independent stages. I'm also using Scene Builder for ActionEvents.
An example of multiple stages could be something like this:
Login page -> main stage -> other sub stages.
The way I understand is that you need a FXML loader on each controller to bring up each stage. So on my Main java class, under START method, I'll bring up a Login stage. And on my Login Controller, I'll have a method to bring up a Main Stage (i.e. once the Login button is pressed), and finally/similarly, I'll have a method under my Main Controller to bring up other sub stages, once a button to that sub stage is pressed from the Main Stage.
Is this the right way of opening new stages? Or do I have to have all the methods in one main class to open all the stages?
Thank you for any help.

no you don't need to have to have all the methods in one main class to open all the stages. you can create your new stages in other controller when they need to be built. for example in your login page, in the controller when a button is clicked And you want to go to main stage, code for the event be like:
FXMLLoader main = new FXMLLoader();
main.setLocation(getClass().getResource("adress"));
Parent mainParent = main.load();
Scene mainScene = new Scene(mainParent);
Scene currentScene = anItemOfProgram.getScene();
Stage stage = (Stage) currentScene.getWindow();
stage.setScene(mainScene);
and this is right way to create multiple stages

Related

View related commands in MVVM application

I was inspired by article Decouple the View and its behavior to create a testable UI and wanna rework my JavaFX application with MVVM pattern.
(source: s-msft.com)
.
Ideally View Model should be View-independent and be testable as usual Java class. Also MVVM uses Command conseption to change View Model from View. So Command implementation is a part of View Model implementation and may be simply tested.
The questions is how Command should be implemented if it's result is a View changing? E.g. in View_1 I have a button after pressing on it a new View (e.g. View_2) should be created and shown. Should such Command be a part of a View and not be testable at all?
I think command could be testable. I'm not sure how exactly are you going to switch views, but AFAIK in JavaFX there is something like a Scene and Stage which is parent for scenes, is that right?
So in your command you pass the Stage as a dependency and on execution you set it a new scene.
And you can easily test it in unit tests - you can mock Stage and check if command passes correct scene to it.
I don't remember exact details of JavaFX but I would implement it like this:
class SwitchSceneCommand {
protected Stage stage;
protected Scene originalScene; // you can keep original scene if you want to have some undo-redo functionality
protected Scene newScene;
public SwitchSceneCommand(Stage stage, Scene originalScene, Scene newScene)
{
this.stage = stage;
this.originalScene = originalScene;
this.newScene = newScene;
}
public void execute()
{
this.stage.setScene(this.newScene);
}
}

FXMLLoader cannot find running controller instance and creates new one

I'm a newcomer when it comes to JavaFX and I recently encountered a problem which really confuses me alot. I'm using a class called "MainController" which controlls an FXML-File containing a TabPane. Each tab is controlled by another controller. But there is one situation in which a tab needs to be deleted, so I need access to the MainController instance to remove the currently active tab from the pane.
Whenever I'm using this code to get an instance of the currently running MainController, I instead get a completely new instance with all of its components set to their default values.
The code is:
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
loader.load();
MainController controller = loader.getController();
controller.closeCurrentTab();
 
protected void closeCurrentTab() {
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedIndex());
}
I'm currently using a static reference to the controller to access it since it is the only solution that works for me. But I know that this is highly unprofessional and I really want to avoid that.
I hope somebody knows what's wrong here.
You should ensure that you have a reference for your main controller at the point where you want to use it. I guess it is one of the "child" controllers (most probably the controller of the current tab).
Therefore if you would have a property in this class that stores the reference for your main controller, your problem would be solved.
I guess you initialize this "child" controller from the main controller like:
FXMLLoader loader = new FXMLLoader(getClass().getResource("TabController1.fxml"));
loader.load();
So here you could do:
TabController controller = loader.getController();
controller.mainControllerProperty.set(this);
Where mainControllerProperty is defined in TabController like:
ObjectProperty<MainController> mainControllerProperty = new SimpleObjectProperty();

JavaFX Render stage below parent

According to the JavaFX documentation, a child stage is defined by
A stage will always be on top of its parent window.
The issue is I'd like to render them like normal windows according to each other (if you click on one, it will render above the other). Can I do this without a hacky workaround?
The only other functionality you get from making a stage a child of another stage is that the child stage is automatically closed when the parent stage is closed. You can emulate this with a listener:
Stage firstStage = ... ;
Stage secondStage = new Stage();
// secondStage.initOwner(firstStage);
firstStage.addEventHandler(WindowEvent.WINDOW_HIDDEN, evt -> secondStage.hide());
// ...
If you are relying on using getOwner() anywhere you would have to find a way around that.
(I'm not sure if this qualifies as a "hacky workaround", but it should work...).

JavaFX Scene Builder - How can I access the components

I've build a Gui using the scene builder application. I've loaded it into my application but I want to add components to a VBox buried in the design. It seems that all i have access to use is the AnchorPanel that is returned from FXMLLoader.load.
Is there any way more elegant then drilling down the children tree's to get to the component i want?
Thanks.
If you need to add to the VBox from some random class:
give an fx:id to the VBox, say "vbox"
create a Controller for the view and associate it to the view in the FXML
in the Controller, add a #FXML VBox vbox; (where vbox it the same as the fx:id)
retrieve the controller from the FXMLLoader and access the VBox: controller.vbox;.
If you just need to add something to the VBox when your view is loaded, follow 1 to 3 above and add the relevant code in the initialize method of the Controller.

Creating a JavaFX TreeView using Scene Builder

I'm starting working with JavaFX and wish to use the new tree view (as you can use multiple icons to represent your data - which is what I wish to take advantage of).
I have created a basic form/scene that has a tree view and one button on it. When this button is pressed I wish to populate the treeview.
Now, all the examples ive looked at are where the form/scene is generated in code and the treeview is bound to that control....how do I have a pre designed form with Scene builder and populate it from external code?
You could use the following code in a controller class. Inside the FXML file you will need to set the FXID to selectionTreeView. Tested in JDK 8u5 and it worked.
#FXML
TreeView selectionTreeView;
#FXML
private void handleButtonAction(ActionEvent event) {
createTree();
}
public void createTree(String... rootItems) {
//create root
TreeItem<String> root = new TreeItem<>("Root");
//root.setExpanded(true);
//create child
TreeItem<String> itemChild = new TreeItem<>("Child");
itemChild.setExpanded(false);
//root is the parent of itemChild
root.getChildren().add(itemChild);
selectionTreeView.setRoot(root);
}
Set the class name (including package) on the root node of your control in scene builder. If you click on, then go to the code tab on the right it is the top field.
Now set an ID on the TreeView in your control.
Now in the controller object add a TreeView field, the variable name should be the same as what you set the TreeView ID as in scene builder. Annotate with field with #FXML.
Now when the FXML is loaded, the controller is created and the TreeView field is set.

Categories

Resources