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();
}
}
Related
I'm trying to open a stage by clicking a button, but before opening it, I want to check if the stage is already opened, then popup the opened stage to the front instead of opening a new one(no multi open of the same Stage).
#FXML
private void btn_Validate(ActionEvent event) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/scontrols/students/StudentManagement.fxml"));
Parent root = (Parent) loader.load();
StudentManagementController sendTo = loader.getController();
sendTo.receiveFromCamera(txtPictureName.getText());
Stage stage = new Stage();
stage.setScene(new Scene(root));
if(!stage.isShowing())
{
stage.show();}
} catch (IOException ex) {
Logger.getLogger(WebCamController.class.getName()).log(Level.SEVERE, null, ex);
}
}
You're checking !stage.isShowing() on the newly created Stage. This will never do what you want. You need to keep a reference to the other Stage and keep using that reference.
public class Controller {
private Stage otherStage;
#FXML
private void btn_Validate(ActionEvent event) {
if (otherStage == null) {
Parent root = ...;
otherStage = new Stage();
otherStage.setScene(new Scene(root));
otherStage.show();
} else if (otherStage.isShowing()) {
otherStage.toFront();
} else {
otherStage.show();
}
}
}
If you don't want to keep the Stage in memory when it's closed, then you can alter the above slightly.
public class Controller {
private Stage otherStage;
#FXML
private void btn_Validate(ActionEvent event) {
if (otherStage == null) {
Parent root = ...;
otherStage = new Stage();
otherStage.setOnHiding(we -> otherStage = null);
otherStage.setScene(new Scene(root));
otherStage.show();
} else {
otherStage.toFront();
}
}
}
You may want to store a reference to the loaded controller as well, depending on your needs.
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..
Working with SceneBuilder. I have 2 stages, each one with a controller: stage1Controller,stage2Controller.
Stage1Controller :
public class Stage1Controller {
#FXML
private MenuItem translate;
#FXML
private Menu file;
#FXML
private Menu edit;
#FXML
private Menu help;
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
//HOW TO ACCESS THE stage2Controller setLabel()??
}
private void translateFirstStage(){
file.setText("Fichier");
edit.setText("Modifier");
help.setText("Aide");
}
}
Stage2Controller:
public class Stage2Controller {
#FXML
private Label lb;
private void setLabel(String string){
lb.setText("string");
}
}
Here is how both fxml files are loaded in Main.java class using 2 methods (called in Start(Stage primaryStage) method):
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
The handleTranslate(ActionEvent event) method is used as an OnAction method for the MenuItem translate in the first Stage, it translates the view in both stages.
How Can i put setLabel in handleTranslate Method ? Thanks
The "quick and dirty" way is to give the Stage1Controller a reference to the Stage2Controller:
public class Stage1Controller {
private final Stage2Controller stage2Controller ;
public void setStage2Controller(Stage2Controller stage2Controller) {
this.stage2Controller = stage2Controller ;
}
// ...
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
stage2Controller.setLabel(...);
}
// other code as before ...
}
Now in your main app:
public class MainApp extends Application {
private Stage1Controller stage1Controller ;
private Stage2Controller stage2Controller ;
#Override
public void start(Stage primaryStage) {
firstStage();
secondStage();
stage1Controller.setStage2Controller(stage2Controller);
// ...
}
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
stage1Controller = loader.getController();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
stage2Controller = loader.getController();
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
// ...
}
A more robust approach to this, though, is to let both controllers access a shared model class, storing the data. If you represent the data using JavaFX observable properties, the controllers can listen for changes on the properties they care about. For example:
public class Model {
private final StringProperty text = new SimpleStringProperty("Initial text...");
public StringProperty textProperty() {
return text ;
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
// other properties as needed...
}
Now your controllers will look like this:
public class Stage1Controller {
private Model model ;
public void setModel(Model model) {
this.model = model ;
}
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
model.setText(...); // value will appear in stage2 controller's label!
}
// ...
}
and
public class Stage2Controller {
#FXML
private Label lb ;
private Model model ;
public void setModel(Model model) {
lb.textProperty().unbind();
this.model = model ;
lb.textProperty().bind(model.textProperty());
}
// ...
}
And in this case your main app looks like:
public class MainApp extends Application {
private final Model = new Model();
#Override
public void start(Stage primaryStage) {
// ...
}
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
Stage1Controller controller = loader.getController();
controller.setModel(model);
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
Stage2Controller controller = loader.getController();
controller.setModel(model);
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
This is the function. The error is in stage.initStyle(StageStyle.UNDECORATED); which says style cannot be set after the stage is visible. Can you help me out here where i should declare this in the function and suggest some code editing needed if any.
#FXML
public void Showmain() {
// Handle Button event.
myButton3.setOnAction((e) -> {
System.out.println("Project screen");
// FXMLLoader fxmlLoader1= new FXMLLoader(getClass().getResource("Projectscreen.fxml"));
try {
Node node = (Node) e.getSource();
Stage stage = (Stage) node.getScene().getWindow();
//
scene = stage.getScene();
stage.setWidth(1280);
stage.setHeight(968);
stage.centerOnScreen();
stage.setTitle("Welcome to Kdaf");
// stage.initStyle(StageStyle.UNDECORATED);
//stage.setResizable(false);
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Projectscreen.fxml"));
Parent root = (Parent) fxmlLoader.load();
scene.setRoot(root);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
});
}