I have a little problem with my JavaFX Application, I want to call a method in a controller from the main Class but it does not work.
I tried this Accessing FXML controller class
and this How do I access a UI element from another controller class in JavaFX?
But it does not work.
So, in my Application I have a main window and from there I can open a second window, when I close the second window I want to call a method in the main controller for updating some elements..
My main class have this two Windows:
#Override
public void start(Stage primaryStage) throws IOException {
this.primaryStage = primaryStage;
mainWindow();
public void mainWindow() {
try {
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("/App.fxml"));
Parent root = loader.load();
AppController appController = loader.getController();
appController.setMain(this);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondWindow() throws IOException {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("SecondWindow.fxml"));
Parent root = loader.load();
Stage stage = new Stage();
SecondWindowController secondWindowController = loader.getController();
secondWindowController.setStage(stage);
stage.initOwner(primaryStage);
stage.initModality(Modality.WINDOW_MODAL);
stage.setScene(new Scene(root));
stage.show();
stage.setOnCloseRequest(event -> {
event.consume();
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setHeaderText("close?");
alert.initOwner( stage);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
// Here I want to call the method to update in the AppController
stage.close();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Is there a way to call the method there?
Your secondWindow() method is never called.
When you do call it, just pass the reference to the appController, which you already retrieved from the FXML via AppController appController = loader.getController();, to the method which creates the new window.
Change the signature of:
secondWindow()
to:
secondWindow(final AppController appController)
Related
all the questions I found around here answered how to send data from one controller to another. However, I don't know how to return another value from the second controller to the first one. Basically, send data back and forth while the first controller is still opened.
Main class:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen1.fxml"));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.setTitle("Screen 1");
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Screen 1 controller:
public class Screen1Controller {
#FXML
TextField textFieldScreen1;
#FXML
Button buttonScreen1;
#FXML
Label labelScreen1;
#FXML
private void initialize() {
buttonScreen1.setOnAction((event) -> {
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen2.fxml"));
Scene scene = new Scene(loader.load());
Stage stage = new Stage();
//Send data to Screen 2
Screen2Controller controller2 = loader.getController();
controller2.receiveTextFromScreen1(textFieldScreen1.getText());
stage.setTitle("Screen 2");
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
Screen 2 controller:
public class Screen2Controller {
#FXML
TextField textFieldScreen2;
#FXML
Button buttonScreen2;
#FXML
Label labelScreen2;
#FXML
private void initialize() {
buttonScreen2.setOnAction((event) -> {
Stage currentStage = (Stage) buttonScreen2.getScene().getWindow();
currentStage.close();
});
}
public void receiveTextFromScreen1(String stringScreen1){
labelScreen2.setText(stringScreen1);
}
}
I have tried to use the same strategy (create a method in Screen1Controller and instantiate Screen1Controller in the second controller) with the following code but it didn't work.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen2.fxml"));
//Send data to Screen 1
Screen1Controller controller1 = loader.getController();
controller1.receiveTextFromScreen2(textFieldScreen2.getText());
Also, related to this matter (I'm assuming) a second question: how would I update the second view while I change the TextField in controller 1 without creating a new window everytime I click the button (i.e using the same second view instance)?
Any help will be greatly appreciated!
Create a constructor
public Screen1Controller {
Arrow the parameters
}
and when you call the controller, you parse the parameter:
void on2players(ActionEvent event) throws IOException {
client.out.println("ACTION NEWGAME 2 4");
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/boardscreen.fxml"));
loader.setController(new Screen1Controller(parameter);
Parent root = loader.load();
Scene scene= new Scene(root);
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
public class Screen1Controller implements Initializable {
public Screen1Controller() {
//parameters
}
#Override
public void initialize(URL location, ResourceBundle resources) {
switch(numberOfPlayers){
case 2:
//TODO...creating board for 2 players
case 3:
//TODO creatinf board for 3 players
}
}
If you do this, you force the fxml loader to call the controller. NOTE: You should get the controller inside .FXML
I want to assign an existing handleModellAction method to a generated Hyperlink with the setOnAction method, but I don't know how to do this.
Here is my code example:-
#FXML
private void handleModellAction(ActionEvent event) throws IOException{
FXMLLoader load = new FXMLLoader(getClass().getResource("InEX.fxml"));
Parent root = (Parent) load.load();
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.show();
link = (Hyperlink) event.getTarget();
model = link.getId();
stage.setTitle(model);
}
public void addNeuesModell(String bauart, String modelName){
modelHyperlink = new Hyperlink();
modelHyperlink.setId(modelName);
modelHyperlink.setText(modelName);
modelHyperlink.setOnAction(#handleModellAction);
}
Does somebody know how to do this?
Thanks a lot :)
You could try to call the setOnAction method on the modelHyperlink and pass as parameter an anonymous class as a handler, where in you could transfer the logic of your handleModellAction method. Below you can find an example:
Hyperlink link = new Hyperlink();
link.setText("http://example.com");
link.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
FXMLLoader load = new
FXMLLoader(getClass().getResource("InEX.fxml"));
Parent root = (Parent) load.load();
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.show();
link = (Hyperlink) event.getTarget();
model = link.getId();
stage.setTitle(model);
}
});
instead of defining
<HyperLink fx:id="myLink" onAction="#handleModelAction"/>
you can use just:
<HyperLink fx:id="myLink"/>
Then in the code:
refactor your handleMethod like this:
private void handleModellAction() throws IOException {
FXMLLoader load = new FXMLLoader(getClass().getResource("InEX.fxml"));
Parent root = load.load();
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.show();
}
after that:
myLink.setOnAction(action -> {
try {
handleModellAction();
} catch (IOException e) {
e.printStackTrace();
}
});
Then you can use that handleModellAction() anywhere you want in any button, hyperlink and so on..
I am fairly new to JAVAFX so please bear with me. Any help is greatly appreciated. Firstly, i have a Main app that loads addItems.fxml and addItemsController.
public class UncookedApp extends Application {
private Stage stage;
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
try {
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addItems.fxml"));
AnchorPane root=loader.load();
addItemsController itemsCtrl=loader.getController();
itemsCtrl.setMainApp(this);
Scene scene=new Scene(root,1024,768);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e){
e.printStackTrace();
}
}
public Stage getStage() {
return stage;
}
public static void main(String[] args) {
launch(args);
}
}
This is part of the addItemsController:
private UncookedApp mainApp;
public UncookedApp getMainApp() {
return mainApp;
}
public void setMainApp(UncookedApp mainApp) {
this.mainApp = mainApp;
}
public void toMeat(ActionEvent event) {
Stage stage1=mainApp.getStage();
try {
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addMeat.fxml"));
Parent root=loader.load();
addMeatCtrl itemsCtrl=loader.getController();
// itemsCtrl.setMainApp(this);
Scene scene=new Scene(root,1024,768);
stage1.setScene(scene);
stage1.show();
} catch(Exception e){
e.printStackTrace();
}
}
The addItemsController will load a different page, addMeat. I got that to work but when I try to go back from addMeat to addItems with a button handle, it doesn't work. Is it something to do with retrieving the mainApp's stage? How can I solve this?
This is what I've tried.
public void handleBackFromMeat(ActionEvent event) {
try{
Stage stage=mainApp.getStage();
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addItems.fxml"));
Parent root=loader.load();
addItemsController itemsCtrl=loader.getController();
Scene scene=new Scene(root,1024,768);
stage.setScene(scene);
stage.show();
}catch (Exception e){
e.printStackTrace();
}
}
I also had posted a similar question and then i went ahead with my way
I need to create a back button functionality in javafx?
What you can do as i did for my application was make two static list at the start of the application and then fill it with the Stage and the Fxml page name if you are using fxml and then have a central back button method on each page to move the previous page by iterating on the last item added to the list .
Two static lists in the main class
public static List<Stage>stageval = new ArrayList<Stage>();
public static List<String>fxmlval = new ArrayList<String>();
When ever i switch my fxml view i added the stage and the fxml file name to their respective lists.
fxmlval.add("Instance.fxml");
stage = (Stage)validate.getScene().getWindow();
stageval.add((Stage) delete.getScene().getWindow());
And a central back button method when ever the user clicked the back button i iterated to last item of both the lists and then after changing the stage deleted the values from the list .
public class BackButton {
public void back(String instance,Stage stage) throws IOException{
Parent parent = FXMLLoader.load(getClass().getResource(instance));
Scene scene = new Scene(parent);
scene.getStylesheets().add("/css/Style.css");
stage.setResizable(false);
stage.setScene(scene);
stage.show();
}
}
#FXML
public void back() throws IOException {
BackButton bb = new BackButton();
bb.back(fxmlval.get(fxmlval.size() - 1),
stageval.get(stageval.size() - 1));
fxmlval.remove(fxmlval.size() - 1);
stageval.remove(stageval.size() - 1);
}
Hope this helps its a work around but the solution works.
I guess it's a simple question but it was a long day at work and I cant figure the right way out right now.
I have 3 FXML files.
Menge.fxml
Start.fxml
Suche.fxml
I'm starting from Main.java as usual
#Override
public void start(Stage primaryStage) {
try {
AnchorPane root = (AnchorPane)FXMLLoader.load(getClass().getResource("Start.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
Inside the MainController I've got the following ActionListener
#FXML
void actionListenerMenge(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Menge.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
So I'm opening the Suche.fxml via the FXMLLoader.
Now there is a button inside my Suche-View which opens another scene for where the user should search the specific data record.
#FXML
void actionListenerSuche(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Suche.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
SucheController where I'm loading the MengeController again via FXMLLoader
FXMLLoader loader = new FXMLLoader(getClass().getResource("Menge.fxml"));
try {
Parent root = loader.load();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MengeController controller = (MengeController)loader.getController();
I want to edit the value inside a TextField # the MengeController to update the View from my Menge.fxml. But if I do
controller.setTextFieldValue("abc") it's not updating. I know it must be because I've loaded the MengeController twice and so there are two instances. But I don't figure out how to load it only once or inject it to update my TextField inside MengeController.
Option 1: Observe a property in the SucheController:
Define a property in the SucheController. Instead of trying to get a reference back to the MengeController, just set the property:
public class SucheController {
private final ObjectProperty<Foo> foo = new SimpleObjectProperty<>();
public ObjectProperty<Foo> fooProperty() {
return foo() ;
}
public final Foo getFoo() {
return fooProperty().get();
}
public final void setFoo(Foo foo) {
fooProperty().set(foo);
}
// ...
#FXML
private void someHandlerMethod() {
// FXMLLoader loader = new FXMLLoader(getClass().getResource("Menge.fxml"));
// try {
// Parent root = loader.load();
// } catch (IOException e) {
// e.printStackTrace();
// }
// MengeController controller = (MengeController)loader.getController();
setFoo(...);
}
}
Obviously replace Foo with a type that represents whatever data the user is providing here, and name the property and methods accordingly...
Now when you load Suche.fxml, just get a reference to the controller and observe the property. Then you can just update the text field directly in MengeController itself:
#FXML
void actionListenerSuche(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Suche.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
SucheController sucheController = loader.getController();
sucheController.fooProperty().addListener((obs, oldFoo, newFoo) -> {
someTextField.setText(newFoo.getSomeValue());
// ...
});
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
Option 2: Pass a reference to the MengeController directly to the SucheController:
Another option, which I don't like as much as it involves more coupling between the controllers, is simply to pass a reference to the MengeController to the SucheController:
public class SucheController {
private MengeController mengeController ;
public void setMengeController(MengeController mengeController) {
this.mengeController = mengeController ;
}
// ...
#FXML
private void someHandlerMethod() {
mengeController.setTextFieldValue("abc");
}
}
and then the code in your MengeController that loads Suche.fxml looks like
#FXML
void actionListenerSuche(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Suche.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
SucheController sucheController = fxmlLoader.getController();
sucheController.setMengeController(this);
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
I'm using JavaFX and working on a Schoolproject right now.
About my program, i have a login screen and if i press on login, i come to the mainframe. The content area of the mainframe is empty, because i load the content for the AnchorPane that builds the mainframe when i use the buttons in the menubar.
When i press the login button, i want to laod the mainframe and the home pane in it. But i don't know how to load the pane into the mainframe from another class.
Here my code:
Code to load the mainframe:
#FXML
protected void login(ActionEvent event) throws IOException, URISyntaxException {
Stage stage;
Parent root = null;
stage = (Stage) btnLogin.getScene().getWindow();
try{
root = FXMLLoader.load(getClass().getResource("view/mainframe.fxml"));
} catch (IOException e){
e.printStackTrace();
}
Scene scene = new Scene(root, 1280, 900);
stage.setScene(scene);
stage.show();
}
Code to laod the home pane into the mainframe:
#FXML
protected void mHome(ActionEvent event) throws IOException, URISyntaxException {
try {
URL url = getClass().getResource("view/home.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(url);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
AnchorPane page = (AnchorPane) fxmlLoader.load(url.openStream());
aContent.getChildren().clear();
aContent.getChildren().add(page);
}
catch (IOException e) {
e.printStackTrace();
}
}
These two methods are in different classes.
If you need some more information, please let me know :)
Thank you and greets, Timo
Just invoke the mHome() method. Note that, since you never use the ActionEvent, you can omit it from the method signature (the FXMLLoader will still be able to map it as an event handler). So change it to
#FXML
protected void mHome() throws IOException, URISyntaxException {
try {
URL url = getClass().getResource("view/home.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(url);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
AnchorPane page = (AnchorPane) fxmlLoader.load(url.openStream());
aContent.getChildren().clear();
aContent.getChildren().add(page);
}
catch (IOException e) {
e.printStackTrace();
}
}
If you always want to show the "home pane" when the main pane is first shown, just add a call to mHome to the initialize() method in the same controller class:
public void initialize() {
// existing code...
mHome();
}
Alternatively, if you specifically only want to show the "home pane" when you load it from the login pane, add a call to mHome in your login handler:
#FXML
protected void login(ActionEvent event) throws IOException, URISyntaxException {
Stage stage;
Parent root = null;
stage = (Stage) btnLogin.getScene().getWindow();
try{
FXMLLoader loader = new FXMLLoader(getClass().getResource("view/mainframe.fxml"));
root = loader.load();
MainController controller = loader.getController();
controller.mHome();
} catch (Exception e){
e.printStackTrace();
}
Scene scene = new Scene(root, 1280, 900);
stage.setScene(scene);
stage.show();
}
(I'm assuming MainController is the name of the controller class for mainframe.fxml; obviously just adjust as needed.)