I have a parent Window which displays people i.e:
FirsName:John
LastName:Brown
Age:18
I click the Edit button, child window opens, change first name and then click accept, child window closes, the changes are in mysql database saved but the parent window still shows the old values. I do have a refresh method which works, but how can I call it from the child window or set it to be called after closing the child window?
Both Parent and Child Windows have controller and fxml. Any help would be appreciated.
James_D's Answer (better)
Thanks to #James_D, you should use setOnHidden instead of setOnCloseRequest, that way you can again just call .close() and the EventHandler will be called.
My Answer
You could make use of setOnCloseRequest:
childStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(WindowEvent we) {
// Refresh the parent window here
}
});
To invoke this close request on a child form you can do:
childStage.getOnCloseRequest().handle(new WindowEvent(childStage, WindowEvent.WINDOW_CLOSE_REQUEST));
// or
childStage.fireEvent(new WindowEvent(childStage, WindowEvent.WINDOW_CLOSE_REQUEST));
So you could implement the close button on the child stage like so:
Button closeButton = new Button("Close");
closeButton.setOnAction(event -> childStage.fireEvent(new WindowEvent(childStage, WindowEvent.WINDOW_CLOSE_REQUEST)));
I think the best way to have automatic field updates in JavaFX is by the use of properties. If the name field in the parent window is bound to a SimpleStringProperty you only need to set the property's value when the child window is closed to notify the parent window of the update.
I made a small parent / child example to test the concept and the relevant piece of code is this, placed on the child creation method:
FXMLLoader loader = new FXMLLoader(getClass().getResource("Child.fxml"));
Parent root = loader.load();
ChildController controller = loader.getController();
name.textProperty().bind(controller.name.textProperty());
The field name is a Label in the parent and a TextField in the child. Any change in the child's text is immediately reflected in the parent.
This is an interesting topic. I will make a full example and post ir in my 'a cup of java' blog as soon as possible.
If you use popup, you can set the method to showandwait instead of just show from the Stage object, and after run the method that update the GUI. Because showandwait method pause the main frame while the popup is running, once the popup closes it will run your update code.
stage.showAndWait();
updateGUI();
we think we have two stage(window),1-parent stage,2-child window(pop up window)
first send the stage object of parent stage to child stage
in child stage set :
childStage.setOnHidden(event->{
Parent root=FXMLLoader.load(getClass().getResource(parent fxml));
Scene scene=new Scene(root);
parentStage.SetScene(scene);
parentStage.show();
`
Related
I am familiarizing with JavaFX. I have created a loginStage and a mainStage. When pressing the 'Login' Button on the loginStage, I want to switch to the mainStage. The loginStage is a lot smaller than the mainStage. I want the mainStage to be located such that the former loginStage was positioned right in the middle of the mainStage (if we hadn't closed it).
I managed to switch from the loginStage to the mainStage. But for some reason the mainStage does not position itself correctly; it seems to me that its X- and Y-coordinate are not set at all!
Here is my code:
public void switchWindow() {
FXMLLoader loader = new FXMLLoader();
try {
Stage loginStage = (Stage) btnLogin.getScene().getWindow();
loginStage.setScene(null);
loginStage.hide();
Pane mainPane= (Pane) loader.load(getClass().getResource("/mainStage.fxml"));
Stage mainStage= new Stage();
mainStage.initStyle(StageStyle.TRANSPARENT);
Scene mainScene= new Scene(mainPane);
mainStage.setScene(mainScene);
mainStage.setX(loginStage.getX() + loginStage.getWidth()/2 - mainStage.getWidth()/2);
mainStage.setY(loginStage.getY() + loginStage.getHeight()/2 - mainStage.getHeight()/2);
mainStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
Also: I will be creating more stages. Hence, I figured it might be a good idea to create a switchWindow() function inside a separate package. I might just call the function with parameters oldStage and newPane. I wanted to ask you: What is the best practice of switching between stages? So that I do not reproduce the same code over and over again?
I figured it out - and yes, I am sorry, this seems to have been answered before.
The problem is that .getWidth() or .getHeight() methods can only be invoked AFTER the .show() method. Therefore, either change the order of calling the functions. This works fine - the mainStage seems to be at the correct x,y-coordinates from begin on. But to be sure that it does not get moved there after showing the stage, we could also just look up the width and the height of the mainStage inside the .fxml file and then substract these values in .setX() and .setY() respectively.
I'm currently writing an HexEditor Application with JavaFX 8 for various reasons. (Important is, that I have to do this I can't just use another HexEditor)
My problem is that when I want to update my UI for example with
textarea.setText(line);
table.setItems(getListForTable());
I get a NullPointerException because textarea(TextArea) and table (TableView) are null, but if I have data before I intialize my stage and set that to the components it works.
In other questions I read that Platfrom.runLater() should solve that but somehow it doesn`t work for me, I still get the NullPointerException. Also I'm sure I'm in the applications main thread, because my application only uses one thread.
Here is the full code from the method:
public void openFile() //this is called when the user presses the "Open"-MenuItem
{
FileChooser fc = new FileChooser();
fc.getExtensionFilters().addAll(
new ExtensionFilter("Text Files", "*.txt"),
new ExtensionFilter("All Files", "*.*"));
file = fc.showOpenDialog(primaryStage);
if(file != null)
{
create();
Platform.runLater(() -> textarea.setText(line)); //NPE
}
}
Also I tried using a button to update the UI but that works only if the user presses the button and also not with the Button.fire() Method, but thats not a good workaround if you have to press that button everytime you did something to see the changes.
EDIT: here are the methods initializing the code in the start() Method. And as I said: I`m sure there getting assigned correctly, because I can use them for setting up listeners and such while starting the program.
private TextArea textarea;
private TableView<Row> table; //Row is my own class used to get the contents to the table
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
initRootLayout();
showContentScene();
}
private void initRootLayout() { //initializes root layout as frame for Content
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Editor.class.getResource("RootLayout.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();
}
}
private void showContentScene() { //initializes Content View
try {
// Load Content (panel).
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Editor.class.getResource("Content.fxml"));
ContentPane = (AnchorPane) loader.load();
table = (TableView<Row>) loader.getNamespace().get("table");
textarea = (TextArea) loader.getNamespace().get("textarea");
// Set Content into the center of root layout.
rootLayout.setCenter(ContentPane);
} catch (IOException e) {
e.printStackTrace();
}
}
EDIT: I overworked the code structure but now I have another exception from the loader telling me "Root cannot be null".
EDIT: For everyone viewing this: It was mostly a structure problem, make sure that every .fxml-file you use has its own Controller.
Your structure is very unusual. Usually you either construct nodes in your code and don't use and FXMLLoader OR you let the FXMLLoader construct nodes for you. In your case you are doing both, which can work, but is tricky to get right (as you have discovered).
So, given you are using FXML, just use it for constructing all of your nodes and place references to the constructed nodes in a controller, with the references annotated with #FXML so that the FXMLLoader initializes them. Then, never assign the references to new nodes you create.
Ensure that the references and controller are in different classes from your Application class, because you never really want to make the main application class a controller, all the main application class should do is invoke the FXMLLoader, it does not need any node references.
You never need to use Platform.runLater unless you are running on a non-JavaFX application thread and you want to execute some code on the JavaFX application thread. For your example this is clearly not the case, because the comment for openFile is this is called when the user presses the "Open"-MenuItem. The JavaFX framework handles the input event for the user pressing the open menu item and ensures that an event handler for it is invoked on the JavaFX application thread, so trying to use Platform.runLater in such a situation is both confusing and redundant.
Even if you address some of the issues mentioned in this answer, your program may still not work as you expect as it may have other issues, which cannot be verified with the current code in your question. For instance, the question code includes neither FXML or controller code. Those would be required to truly find all the issues with code and address this.
I have two good working windows (main window, first window) in seperate .java files, at the same package.
The program handles, if I call the the First window (from the main), the Main stays in the background. The only problem is that I still can use the main window if I click there, and create infinity first windows.
One solution is to shut down the Main window, to avoid clicking there again, but I would like to keep there. In order to make the solution I need a code which makes the background window somehow disabled until I click something in the First Window.
But to do that I need to give the rights to the "handler.java" to operate with the main.
Anybody has any idea how to do that?
Let me show you the 2 java file's methods:
Main.java:
public class program extends Application {
#Override
public void start(Stage main) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("mainwindow.fxml"));
Scene scene = new Scene(root);
main.setScene(scene);
main.setTitle("Main);
main.setWidth(800);
main.setHeight(600);
main.setResizable(false);
main.show();
First.java
public void first() throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("first.fxml"));
Scene scene = new Scene(root);
Stage first = new Stage();
first.setScene (scene);
first.setTitle("Köszöntjük az Útiokosban!");
first.setWidth(400);
first.setHeight(200);
first.setResizable(false);
first.show();
}
Use Stage.initModality to make one of your Stages modal (blocking).
Note: You need to set the modality before displaying the Stage.
Try using the advisory in the chapter "Modality" in this link:
http://docs.oracle.com/javase/8/javafx/api/javafx/stage/Stage.html
Playing around with my first JavaFX application. Running it on Java8 but that shouldn't be an issue regarding this question.
My problem:
I have a scene (FXML) in which a menu and menu items exist. When one presses a menu item a new window or popup should show. This works just fine, but I want to disable the parent window while the new window is active. Figured out this is possible with modality.
My real problem is: Determining the parent window from the action event I receive. Because the event comes from an menu item it seems a bit problematic. Probably a really stupid question.
My code snippet:
Stage stage = new Stage();
Parent root = FXMLLoader.load(EbooksdownloaderController.class.getResource("about.fxml"));
stage.setScene(new Scene(root));
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(((Node)event.getSource()).getScene().getWindow());
stage.show();
Casting the source to a Node gives a class casting exception. But I don't have a clue which other path to follow.
Thanks.
Have been fiddling around a while without any success.
As a final resort I finished it using the following code:
#FXML
private AnchorPane ap;
Stage stage = new Stage();
Parent root = FXMLLoader.load(EbooksdownloaderController.class.getResource("about.fxml"));
stage.setScene(new Scene(root));
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(ap.getScene().getWindow());
stage.show();
Not really the way I would prefer. But it works.
I've added an accordion layout container with a tree in its first content pane to my layout. No, a requirement calls to expand and select one node when the application is loaded (it's a mockup).
Then I've added this to the constructor of the class, that corresponds with the uibinder layout:
widget = uiBinder.createAndBindUi(this); // everything's bound
accordionLayoutContainer.setActiveWidget(firstPanel); // OK, expands first pane
tree.getSelectionModel().select(mynode, true); // no visible effect
tree.setExpanded(mynode, false); // no visible effect
What's missing here? Do I have to force the layout of "something" after setting the states or is it the wrong place to select and expand nodes?
Found the solution. The call to setExpand has to be deferred until the tree has been attached. So I added an AttachEvent.Handler to a parent widget - adding it to directly to the tree doesn't work, because the handler is called to early, before the models are registered.
widget = uiBinder.createAndBindUi(this); // everything's bound
accordionLayoutContainer.setActiveWidget(firstPanel); // OK, expands first pane
accordionLayoutContainer.addAttachHandler(new AttachEvent.Handler() {
#Override
public void onAttachOrDetach(AttachEvent event) {
clientsTree.getSelectionModel().select(mynode, true);
clientsTree.setExpanded(mynode, true);
}
});