JavaFX - parsing child arguments back to parent - button

I have 2 Controllers and I want to pass values from second controller to first controller, here are code sample:
FXMLController.java
private int paramAnswers;
private int paramNotificationTime;
private int paramNotificationDelay;
#FXML
private void handleMenuItemOptionsAction(ActionEvent event) throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Options.fxml")); // UNDECORATED*
Scene scene = new Scene(root, Color.TRANSPARENT);
final Stage stage = new Stage();
stage.setTitle("Options");
stage.setScene(scene);
stage.show();
}
public void setOptionsParams(int paramAnswers, int paramNotificationTime, int paramNotificationDelay){
this.paramAnswers = paramAnswers;
this.paramNotificationTime = paramNotificationTime;
this.paramNotificationDelay = paramNotificationDelay;
}
and second controller:
OptionsController.java
private FXMLController parentController;
private int paramAnswers;
private int paramNotificationTime;
private int paramNotificationDelay;
#Override
public void initialize(URL location, ResourceBundle resources) {
.... }
#FXML
private void handleButtonSaveAction(ActionEvent event) throws IOException{
/*Pass these parameteres OptionsController parameters back to the FXMLController like parentController.setOptionsParams(paramAnswers, paramNotificationTime, paramNotificationDelay);
*/
(((Button)event.getSource()).getScene().getWindow())).close();
}
Arleady tried with parsing FXMLControler as .this into OptionsController initialize method, tried making listers and bunch of other resolved problems on stackoverflow but it just don't want work :< I need to pass that atributes back to FXMLController and close child window, so my main app would change behavior depending on passed values... :X
For any help I will be grateful

you can have a function in the second controller let's say passParams() set it's parameters what every you want to pass to that controller and from the first controller when you click on a button or something
this line
FXMLLoader.load(getClass().getResource("/fxml/Options.fxml"));
need to be changed to
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/Options.fxml"));
Parent root = (Parent) loader.load();
OptionsController controller = loader.getController();
EDIT
you need to pass the first controller to the second controller
controller.setParentController(this); // this is the first controller
in Second controller
FirstController mController;
public void setParams(FirstController controller) {
this.mController = controller;
}
now in the button click function you use the mController you got from the previous step
mController.setOptionsParams(...); //send the params collected from the textfields
this function is implemented in the FirstController
Note: a more general way to do this by using call-backs it's the same but your code depends on interfaces not concrete classes by implementing a general interface in FirstController that have the setOptionParams() method

First you add a callback interface to your parent controller class:
public interface OptionCallback {
public void setOptionsParams(int paramAnswers, int paramNotificationTime, int paramNotificationDelay);
}
public class YourParentController implements Initializable, OptionCallback {
...
}
Using the FXMLLoader object you can get a hold of your child controller object and pass the parent object to it:
FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource("yourFXML.fxml"));
Parent root = (Parent) loader.load();
YourChildController yourChildController = loader.<YourChildController>getController();
yourChildConrtoller.registerCallback(this);
In the childController you save that callback:
private OptionCallback optionCallback;
public void registerCallback(OptionCallback callback) {
optionCallback = callback;
}
And whenever results are ready you use it to pass it to parent:
optionCallback.setOptionsParams(...);

Related

Javafx MVC how to signal new threaded result from one controller to another

I have a Javafx MVC Structure where Controller 1, 2, 3... are in charge of updating the UI elementes, and Super Controller executes whatever job is passed to it from them, and returns the result.
I have a Button Btn1 in Controller1 which when pressed will be disabled and show a processing symbol while the main job is done in other thread whose function is present in Super Controller. Once that thread completes its job, it'll notify the calling function that job is done, so that Btn1 will be enabled again, and processing symbol removed.
CONTROLLER1
#FXML
Button Btn1;
private ProgressIndicator loading;
private SUPERCONTROLLER con;
#Override
public void initialize(URL url, ResourceBundle rb){
loading=new javafx.scene.control.ProgressIndicator();
loading.setProgress(javafx.scene.control.ProgressIndicator.INDETERMINATE_PROGRESS);
}
#FXML
private void press(MouseEvent evt){
Btn1.setDisable(true);
Btn1.setGraphic(loading);
Task<Integer>t=this.con.Action();
new Thread(t).start();
t.setOnSuceeded(e->{
if(t.getValue()==0){
System.out.println("Success");
}else{
System.out.println("falure");
}
Btn1.setDisable(false);
Btn1.setGraphic(null);
});
}
private void inject(SUPERCONTROLLER c){
this.con=c;
}
SUPERCONTROLLER
#FXML
private VBox sidePane;
#Override
public void initialize(URL url,ResourceBundle rb){
var fxml=new FXMLLoader(getClass().getResource("CONTROLLER1.fxml");
Pane childPanel=fxml.load();
sidePane.getChildren().add(childPanel);
CONTROLLER1 childPanel.getController();
CONTROLLER1.inject(this);
}
protected Task<Integer> Action(){
Task<Integer>task=new Task<>(){
#Override protected Integer call(){
Thread.currentThread.wait(3000); //Imitate long task
return 0;
}
}
return task;
}
However, I want the thread concept to stay in Super Controller itself. i.e., Instead of setting suceeded operation in Controller1, I want to move it to Super Controller and Controller1 should just re-enable the button when Super Controller tells it.

How to access JavaFx node elements values in methods of FXML Controller class?

It seems values of nodes (textfields, checkboxes, etc...) can only be accessed via an event listener on them. Why am I saying that ?
- First I'm new to JavaFx
- Second : below is the scenario I'm using
public class FXMLDocumentController implements Initializable {
#FXML
private JFXCheckBox myCheckBox;
#FXML
private JFXButton myButton;
#Override
public void initialize(URL url, ResourceBundle rb) {
myCheckBox.addEventHandler(MouseEvent.MOUSE_CLICKED, (e)->{
if(myCheckBox.isSelected()){
System.out.Println("Printed from event handler");
});
myButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) -> {
myMethod();
});
}
public void myMethod(){
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
for(Node node : root.getChildrenUnmodifiable()){
if(node.getId().equals("myCheckBox"){
JFXCheckBox myChk = (JFXCheckBox) node;
if(myChk.isSelected()){
System.out.Println("Printed from my inner method");
}
System.out.Println(myChk.isSelected());
}
}
}
}
What happend is when I check the box, the event handler does how supposed. It prints Printed from event handler. But right afetr that, when clicking myButton, the myMethod always prints false.
When I use a class variable indicating if the checkbox is selected and read it in myMethod, it says the check box has been checked ...
What is going on ?
The same thing happens with textfields ...

JavaFX: passing data between Controllers is always null

I want to apologize in advance, as this is discussed previously in "Passing Parameters Directly From the Caller to the Controller", but I followed every possible solution I found and still I can't get it work.
I have difficulty in passing a parameter from one controller to another.
Specifically:
LoginController passes username to MainController.
When I click login button LoginController sets the username to the MainController. But, when Main.fxml is loaded username is NULL.
As trying to figure it out, I would like to ask:
When MainController initialize() method gets called? I suppose by the time LoginController calls st.show(); ?
If the previous is correct, then why MainController username is NULL, since we have already set its value in LoginController using mainController.setUsername(username) ?
Any help would be appreciated.
This is my code.
LoginController.java
public class LoginController implements Initializable {
...
#FXML TextField username;
#FXML public void actionLoginButton() {
...
Stage st = new Stage();
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
Region root = (Region) loader.load();
Scene scene = new Scene(root);
st.setScene(scene);
MainController mainController = loader.<MainController>getController();
mainController.setUsername(username.getText());
st.show();
}
...
}
MainController.java
public class MainController implements Initializable {
...
String username;
#FXML Label lblWelcomeUser;
public void setUsername(String usrname) {
this.username = usrname;
}
#Override
public void initialize(URL url, ResourceBundle rb) {
...
lblWelcomeUser.setText("Welcome, " + this.username);
...
}
...
}
The issue is the timing of your call to set username.
The initialize() method of MainController is called during the execution of the following statement:
Region root = (Region) loader.load();
At this time, the username field in MainController is null and so its value is reported as null in the welcome message. Your call to setUsername() occurs after the MainController.initialize() method has completed.
In general, the initialize() method of controller classes loaded with the FXML loader should never try to do anything with instance fields whose values have not been injected by the FXML loader. These instance fields will not have been initialized at the time that the initialize() method is invoked.

JavaFX window setTitle

I have a main class which is the following:
public class Window extends Application {
#Override
public void start(Stage foablak) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Foablak.fxml"));
Scene scene = new Scene(root);
foablak.setScene(scene);
foablak.setWidth(900);
foablak.setHeight(700);
foablak.setResizable(false);
foablak.setTitle("Window");
foablak.show();
}
public static void main(String[] args) {
launch(args);
}
}
How can I update the Title of the from another .java class without closing the window and open a new one?
Exposing properties using static, just for the sake of exposing, may be considered as a bad design. You have different ways to achieve the same, for example, expose a method from the Window class which sets the stage title.
public class Window extends Application {
private Stage stage;
#Override
public void start(Stage foablak) throws Exception {
stage = foablak;
Parent root = FXMLLoader.load(getClass().getResource("Foablak.fxml"));
Scene scene = new Scene(root);
foablak.setScene(scene);
foablak.setWidth(900);
foablak.setHeight(700);
foablak.setResizable(false);
foablak.setTitle("Window");
foablak.show();
}
public static void main(String[] args) {
launch(args);
}
public void setStageTitle(String newTitle) {
stage.setTitle(newTitle);
}
}
Yes, you can. Inside of your Application.start() method, save a reference to your primary Stage that you can access elsewhere, and then call Stage.setTitle().
class MyApplication extends Application {
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) {
MyApplication.primaryStage = primaryStage;
// ...
}
}
MyApplication.primaryStage.setTitle("New Title");
As an aside, I would avoid calling your class Window, as that is the name of one of the JavaFX classes.
The following may not be the solution you were looking for, but it might be useful for some of the developers:
Scenario: There is only 1 JavaFX app; The app needs to be run
multiple times; You want to differentiate who is running the app
Attention: Pay attention of the running order of the Main and
Controller class
Solution: i. In the Controller Class, declare a private static variable, e.g., private static String strWho; ii. Expose strWho by providing a getter method. e.g.: public static String getWho(){
return strWho;
}; iii. Implement the initialize method for the Controller, and based on your need, assign a distinct value each time you run the JavaFX app. eg., #FXML
public void initialize() {
strWho = "you need to have logic here, to have a distinct value each time you run the app";
}
In the Main start method, right before you call the stage.show, set the title. eg:
primaryStage.setTitle(Controller.getWho()));
primaryStage.show();
One way to implement the logic for distinct value of the strWho each time you run the app: You can have a TextInputDialog in the Controller's initialize method, to accept user input, by asking for a name etc.

JavaFX load FXML inside parent controller

I have a borderpane where I'm loading an fxml (alias FirstPanel) with relative controller inside of it and positioned in the center. The fxml contains a TableView and a button which should load another fxml (alias SecondPanel) and relative controller instead the first panel. Basically, the SecondPanel needs to show some details about the data selected in the table.
Is it possible to do it? How can I get the parent of my FirstPanel and use it for the SecondPanel instead of the first?
UPDATE
I've tried many solutions but without reach my goal. The application load the UsersMainPageController which contains only an AnchorPane like parent control, so this is the relative code:
[UsersMainPageController]
public class UsersMainPageController implements Initializable {
private PostOffice application;
#FXML
private AnchorPane ParentControl;
public void setApp(PostOffice application){
this.application = application;
}
public void loadPage(String pageName) {
try {
URL url = getClass().getResource(pageName);
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(url);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
AnchorPane page = (AnchorPane) fxmlLoader.load(url.openStream());
ParentControl.getChildren().clear();///name of pane where you want to put the fxml.
ParentControl.getChildren().add(page);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void loadManageUsers () {
loadPage("UsersManage.fxml");
}
public void loadListUsers () {
loadPage("UsersList.fxml");
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
loadListUsers();
}
}
loadListUsers calls UsersList.fxml with the relative controller UsersListController that contains a TableView with some records and some buttons. When I click a specific button, it should call loadManageUsers with relative controller UsersManageController which contains some fields for editing data and inserting new users. When users are edited or inserted, it should be able to return to the previous page with the TableView and clear the current page (in this case UsersManageController).
[UsersListController]
public class UsersListController implements Initializable {
private UsersMainPageController mainController;
#FXML
private void handleButtonEditAction(ActionEvent event) throws IOException {
mainController.loadManageUsers();
}
}
[UsersManageController]
public class UsersManageController implements Initializable {
private UsersMainPageController mainController;
#FXML
private void handleButtonBackAction(ActionEvent event) throws IOException {
mainController.loadListUsers();
}
}
When I click from the UsersListController the ButtonEdit to load the UsersManageController, I get this error:
Caused by: java.lang.NullPointerException
at postoffice.multiuser.UsersListController.handleButtonAggiornaAction(UsersListController.java:210)
... 50 more
you can add your child fxml into parent controller..
1.first just take a anchor pane and set its bounds where you want to put your FXML code.
now try this...
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
openNewWindow("Example.fxml");
}
});
you can pass fxml name into function...
public void openNewWindow(String FXMLFile)
{
//ChildNode child;
try {
URL url = getClass().getResource(FXMLFile);
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(url);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
AnchorPane page = (AnchorPane) fxmlLoader.load(url.openStream());
anchor.getChildren().clear();///name of pane where you want to put the fxml.
anchor.getChildren().add(page);
}
catch (IOException e) {
e.printStackTrace();
}
}

Resources