I have trouble when closing a window in JavaFX.
I define my setOnCloseRequest as I wanted and it works when I click the x in the window. However, I also need a button to close the window and this onCloseRequest has to work, the problem is it does not. The event does not fire at all.
I am using JavaFX 2.2 (Java 7) and I notice that the reference for setOnCloseRequest says close the window on external request
Solution
Fire an event from your internal close request (on the button push), so that the application thinks it received an external close request. Then your close request logic can be identical whether the request came from an external event or an internal one.
private EventHandler<WindowEvent> confirmCloseEventHandler = event -> {
// close event handling logic.
// consume the event if you wish to cancel the close operation.
}
...
stage.setOnCloseRequest(confirmCloseEventHandler);
Button closeButton = new Button("Close Application");
closeButton.setOnAction(event ->
stage.fireEvent(
new WindowEvent(
stage,
WindowEvent.WINDOW_CLOSE_REQUEST
)
)
);
Note
This is a Java 8+ solution, for JavaFX 2, you will need to convert the lambda functions in anonymous inner classes and will be unable to use the Alert dialog box but will need to provide your own alert dialog system as JavaFX 2 does not feature an in-built one. I strongly recommend upgrading to Java 8+ rather than staying with JavaFX 2.
Sample UI
Sample Code
The sample code will show the user a close confirmation alert and cancel the close request if the user does not confirm the close.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.*;
import javafx.stage.WindowEvent;
import java.util.Optional;
public class CloseConfirm extends Application {
private Stage mainStage;
#Override
public void start(Stage stage) throws Exception {
this.mainStage = stage;
stage.setOnCloseRequest(confirmCloseEventHandler);
Button closeButton = new Button("Close Application");
closeButton.setOnAction(event ->
stage.fireEvent(
new WindowEvent(
stage,
WindowEvent.WINDOW_CLOSE_REQUEST
)
)
);
StackPane layout = new StackPane(closeButton);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
private EventHandler<WindowEvent> confirmCloseEventHandler = event -> {
Alert closeConfirmation = new Alert(
Alert.AlertType.CONFIRMATION,
"Are you sure you want to exit?"
);
Button exitButton = (Button) closeConfirmation.getDialogPane().lookupButton(
ButtonType.OK
);
exitButton.setText("Exit");
closeConfirmation.setHeaderText("Confirm Exit");
closeConfirmation.initModality(Modality.APPLICATION_MODAL);
closeConfirmation.initOwner(mainStage);
// normally, you would just use the default alert positioning,
// but for this simple sample the main stage is small,
// so explicitly position the alert so that the main window can still be seen.
closeConfirmation.setX(mainStage.getX());
closeConfirmation.setY(mainStage.getY() + mainStage.getHeight());
Optional<ButtonType> closeResponse = closeConfirmation.showAndWait();
if (!ButtonType.OK.equals(closeResponse.get())) {
event.consume();
}
};
public static void main(String[] args) {
launch(args);
}
}
Related
I have a JavaFX application and I need simple to avoid user from opening the same window inside the application more than once.
I tried to find some solution, but nothing get applicable.
As a sample... I have a window that give me payments options, its not a modal, it's a new stage. While I click the button to open that window, it's open, doesn't matter if there is another instance of this same stage running, simple open new windows every click. I want to avoid this. Like switch to the already opened stage window when click the button, or simply miss the click if that window is already opened.
You just need to keep track of the stage and only open a new one if its not already shown. You could also choose to disable the Button if the new window is showing, but I prefer to have the new window simply brought back in front so the user knows it's there.
You can do this by creating a reference to your Stage and then checking if it is null or showing within the button's event handler.
Here is an MCVE to demonstrate:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
/**
* Reference to the new Window that will allow only one instance at a time.
*/
private Stage newWindowStage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(5);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
Button openWindow = new Button("Open Window");
// **********************************************************************************************
// Set the button to open the new Window Stage
// **********************************************************************************************
openWindow.setOnAction(event -> {
// **********************************************************************************************
// Check if the Stage is already showing.
// **********************************************************************************************
if (newWindowStage == null || !newWindowStage.isShowing()) {
// **********************************************************************************************
// The new window is not currently open, so create/show it
// **********************************************************************************************
newWindowStage = new Stage();
newWindowStage.setWidth(300);
newWindowStage.setHeight(300);
newWindowStage.setScene(new Scene(
new VBox(
new Label("New Window!")
)
));
newWindowStage.show();
} else {
// **********************************************************************************************
// The window is already open, so bring it to the front of focus
// **********************************************************************************************
newWindowStage.toFront();
}
});
root.getChildren().add(openWindow);
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(200);
primaryStage.setHeight(200);
primaryStage.setTitle("Test Application");
primaryStage.show();
}
}
By PrinterJob of JavaFx can call the Print Dialog. My problem is that the dialog when calling does not come to the fore.
Here is my example:
import javafx.application.Application;
import javafx.print.Printer;
import javafx.print.PrinterJob;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Printexample extends Application
{
#Override
public void start( final Stage primaryStage )
{
final PrinterJob job = PrinterJob.createPrinterJob( Printer.getDefaultPrinter() );
final Button b = new Button( "Print Dialog" );
b.setOnAction( event -> job.showPrintDialog( primaryStage ) );
final BorderPane pane = new BorderPane( b );
primaryStage.setMinWidth( 400 );
primaryStage.setMinHeight( 300 );
primaryStage.setTitle( "Print" );
final Scene scene = new Scene( pane );
primaryStage.setScene( scene );
primaryStage.centerOnScreen();
primaryStage.addEventFilter( KeyEvent.KEY_PRESSED, event ->
{
if ( event.getCode().equals( KeyCode.ESCAPE ) )
{
primaryStage.close();
}
} );
primaryStage.show();
}
public static void main( final String[] args )
{
launch( args );
}
}
The second problem: The frame is not modal, therefore it can lead to errors.
Information: I use Java 8_92.
Probably a current limitation of JavaFX as described by JDK-8088395.
So you have these options:
Wait this to eventually be fixed in an update or JavaFX 9.
Write yourself a custom dialog and then communicate with the print APIs to populate it, as suggested in JDK-8098009.
Block your scene using a overlay, show the print dialog and then remove the overlay. You'll also need to prevent the window closing while the scene is blocked.
Use AWT Print Dialog (kludge, you've been warned), e.g.:
java.awt.print.PrinterJob printJob = PrinterJob.getPrinterJob();
Button b = new Button("Print Dialog");
b.setOnAction(event -> {
JFrame f = new JFrame();
printJob.printDialog();
// Stage will be blocked(non responsive) until the printDialog returns
});
These Problem happen to me today with a spring boot application and the same Code under Windows 10.
I write here the solution, that for me works, because it was the same issue.
Spring is setting by default the java.awt.headless Property with true.
In the Implementation of showPrintDialog Method there is a bug checking about headless returning true but not showing the dialog.
The solution for me was setting the headless property in Spring as false as described Run Spring Boot Headless by Efe Kahraman
I think you might be missing a peice of code to send the stage to the front.
Try adding: stage.toFront();
Source: http://www.java2s.com/Code/Java/JavaFX/Movingstagewindowtofront.htm
So this problem is a bit of a tricky one. This class is for when, in my main program, I can produce a warning window for potentially dangerous actions that the user might do. The nice thing about this window is that if the user clicks OK, then it will return true to my main class. This is done through the following:
private boolean showWarningWindow(String message)
{
ConfirmationBox warning = new ConfirmationBox(message);
warning.showAndWait();
if (warning.isSelected())
{
return true;
}
return false;
}
This method is in my main GUI class. The problem is below, within ConfirmationBox. The line initModality(Modality.APPLICATION_MODAL); doesn't work properly. If you accidentally click back to your original GUI window, you're screwed, because now your ConfirmationBox window is trapped under your main window AND because of Modality.APPLICATION_MODAL, you won't be able to click anything to get the window back. There isn't a separate application on your taskbar to get the window back in focus and you can't even alt-tab to try and fix it.
Clearly the Modality.APPLICATION_MODAL works but somehow it doesn't make the connection that it needs to interrupt the main window.
Try it in your own applications. Add the showWarningWindow method to your application and add the ConfirmationWindow class and you will see what I mean. I'm not quite sure how to solve this.
package application;
import javafx.beans.property.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class ConfirmationBox extends Stage
{
private VBox layout = new VBox();
private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
public boolean isSelected()
{
return selected.get();
}
public ReadOnlyBooleanProperty selectedProperty()
{
return selected.getReadOnlyProperty();
}
public ConfirmationBox(String question)
{
// Core functionality of the ConfirmationBox.
setTitle("Warning");
initStyle(StageStyle.UTILITY);
initModality(Modality.APPLICATION_MODAL);
setResizable(false);
layout.setSpacing(10);
layout.setPadding(new Insets(10));
createControls();
// Add the Label and Buttons to the Confirmation Box.
layout.getChildren().addAll(new Label(question + "\n\n\n"), createControls());
java.awt.Toolkit.getDefaultToolkit().beep();
setScene(new Scene(layout));
sizeToScene(); // workaround because utility stages aren't automatically sized correctly to their scene.
}
private HBox createControls()
{
final Button ok = new Button("OK");
ok.setOnAction(e -> {
selected.set(true);
close();
});
final Button cancel = new Button("Cancel");
cancel.setOnAction(e -> {
selected.set(false);
close();
});
final HBox controls = new HBox(10, ok, cancel);
controls.setAlignment(Pos.CENTER_RIGHT);
return controls;
}
}
All I wanted is to update a label as my program is running. I am reading some files and I wanted it to display the name of the file is was reading.
However, it only displays the last file using the code below (basically GUI doesn't respond until the whole process is completed):
static Text m_status_update = new Text(); //I declared this outside the function so dont worry
m_status_update.setText("Currently reading " + file.getName());
I got around 4-5 files and I just want to display the name.
I saw a similar question Displaying changing values in JavaFx Label
, the best answer recommended the following:
Label myLabel = new Label("Start"); //I declared this outside the function so dont worry
myLabel.textProperty().bind(valueProperty);
However the valueProperty is a StringProperty and I am stuck converting a string into a string property.
Also, I saw this Refresh label in JAVAFX , but the OP could update the label based on action. I really dont have any action going on?
If you run the entire process on the FX Application thread, then that is (effectively) the same thread that is being used to display the UI. If both the display of the UI, and your file iteration process are running in the same thread, only one can happen at once. So you prevent the UI from updating until the process is complete.
Here's a simple example where I just pause for 250 milliseconds between each iteration (simulating reading a reasonably large file). One button launches this in the FX Application thread (notice how the UI is unresponsive while this runs - you can't type in the text field). The other button uses a Task to run it in the background, properly scheduling updates to the UI on the FX Application thread.
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class UpdateTaskDemo extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label();
Button runOnFXThreadButton = new Button("Update on FX Thread");
Button runInTaskButton = new Button("Update in background Task");
HBox buttons = new HBox(10, runOnFXThreadButton, runInTaskButton);
buttons.setPadding(new Insets(10));
VBox root = new VBox(10, label, buttons, new TextField());
root.setPadding(new Insets(10));
runOnFXThreadButton.setOnAction(event -> {
for (int i=1; i<=10; i++) {
label.setText("Count: "+i);
try {
Thread.sleep(250);
} catch (InterruptedException exc) {
throw new Error("Unexpected interruption");
}
}
});
runInTaskButton.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
for (int i=1; i<=10; i++) {
updateMessage("Count: "+i);
Thread.sleep(250);
}
return null ;
}
};
task.messageProperty().addListener((obs, oldMessage, newMessage) -> label.setText(newMessage));
new Thread(task).start();
});
primaryStage.setScene(new Scene(root, 400, 225));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I want to send E-Mail when I click on a menu.
MenuItem sendmail= new MenuItem("Send E-Mail");
sendmail.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
// Call E-mail Client
}
});
Can you tell me how I can call e-mail client installed to the user PC from this code?
This is undocumented, but appears to work, and is a "pure FX" solution rather than relying on java.awt APIs or knowing the location of external executables. Call the Application.getHostServices().showDocument(...) method and pass in a mailto: url as the url:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class OpenDefaultBrowser extends Application {
#Override
public void start(Stage primaryStage) {
final HBox root = new HBox(5);
final TextField textField = new TextField("help#example.com");
final Button goButton = new Button("Mail");
EventHandler<ActionEvent> goHandler = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
getHostServices().showDocument("mailto:"+textField.getText());
}
};
textField.setOnAction(goHandler);
goButton.setOnAction(goHandler);
root.getChildren().addAll(textField, goButton);
final Scene scene = new Scene(root, 250, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You can use the Desktop class. I believe the code would look something like:
import java.awt.Desktop;
if (Desktop.isDesktopSupported()) {
Desktop desktop = Desktop.getDesktop();
if (desktop.isSupported(Desktop.Action.MAIL)) {
URI mailto = new URI("mailto:john#example.com?subject=Hello%20World");
desktop.mail(mailto);
}
}
I learned from #James_D example. In my case, I want users to email me in case there are issues with this app I developed. Because the email does not change, I thought it was best, for this specific case, to include a JavaFX Hyperlink to a "mail to" email address. The code is as follows:
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Report an issue");
alert.setHeaderText(null);
Text msg = new Text("For issues, email ");
Hyperlink emailLink = new Hyperlink("hector#foo.com");
emailLink.setOnAction(event -> {
getHostServices().showDocument("mailto:" + emailLink.getText());
alert.close(); // to dimiss the alert if user clicks hyperlink
});
TextFlow flow = new TextFlow(msg, emailLink);
DialogPane dialogPane = alert.getDialogPane();
dialogPane.setContent(flow);
alert.showAndWait();
This works fine if you have an email client like Outlook. We use Google Suite, which means we use Gmail. For Windows, if an email client is not installed, it will default to the Windows Mail app. I needed to open Gmail on the browser. So, I needed to change this on the Windows settings.
If you just want to start a separate process which represents the e-mail client, use java.lang.Runtime.exec (see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html) or if you want the client to be embedded, use Java Mail API (http://www.oracle.com/technetwork/java/javamail/index.html)