I have a JavaFx application in which I display an alert box using:
alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Title");
alert.setHeaderText("Some Text");
alert.setContentText("Choose your option.");
buttonTypeOne = new ButtonType("Yes");
buttonTypeCancel = new ButtonType("No", ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeCancel);
Optional<ButtonType> result = alert.showAndWait();
Because the alert executesshowAndWait, so it will be displayed till the user either presses 'Yes' or 'No' or 'Close' the dialog.
Problem: I need to close this dialog programatically from somewhere else. To elaborate, if, let's say the user does not chose any option for 20 sec, or let's say this alert box was shown for some background process which just got over, and now I wish to close this alert box by setting result to be buttonTypeCancel, instead of the user pressing any button.
(Like dispose method in Swing)
How can I do this? I tried Event.fireevent (https://stackoverflow.com/a/22459308/3155986) but I am not able to write the correct event associated.
Thanks in advance!
Edit: Including sample code-
MainApp.java - Java class responsible for handling the application
Controller.java - Corresponding controller file
Design.fxml - FXML file for the application which is loaded via MainApp.java and controlled by Controller.java
Compute.java - Another java class to perform computations.
public class Compute{
Alert alert;
public void function1{
Platform.runLater(new Runnable(){
public void run(){
alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Title");
alert.setHeaderText("Some Text");
alert.setContentText("Choose your option.");
buttonTypeOne = new ButtonType("Yes");
buttonTypeCancel = new ButtonType("No", ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeCancel);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeOne){
// ... user chose "One"
} else {
// ... user chose CANCEL or closed the dialog
}
}
});
}
public void function2{
//......Does some long computation here
//.... If the computation finishes before the user chooses 'Yes' or 'No' on alert box
//...Then close the alertbox here and execute the code corresponding to buttonTypeCancel
//..I tried alert.close(); and alert.hide(); but doesn't work.
}
}
Also, is there any alternate solution to do this? Originally, I wanted to keep the Compute.java clean of any javafx code but couldn't figure out how.
Try this
public void function2 {
Button cancelButton = ( Button ) alert.getDialogPane().lookupButton( buttonTypeCancel );
cancelButton.fire();
}
Or for more general
public void function2 {
for ( ButtonType bt : alert.getDialogPane().getButtonTypes() )
{
if ( bt.getButtonData() == ButtonBar.ButtonData.CANCEL_CLOSE )
{
Button cancelButton = ( Button ) alert.getDialogPane().lookupButton( bt );
cancelButton.fire();
break;
}
}
}
Full example:
#Override
public void start( final Stage primaryStage )
{
Alert alert = new Alert( Alert.AlertType.CONFIRMATION );
alert.setTitle( "Title" );
alert.setHeaderText( "Some Text" );
alert.setContentText( "Choose your option." );
ButtonType buttonTypeOne = new ButtonType( "Yes" );
alert.initModality( Modality.NONE );
ButtonType buttonTypeCancel = new ButtonType( "No", ButtonBar.ButtonData.CANCEL_CLOSE );
alert.getButtonTypes().setAll( buttonTypeOne, buttonTypeCancel );
Button b = new Button( "close alert" );
b.setOnAction(( ActionEvent event ) ->
{
for ( ButtonType bt : alert.getDialogPane().getButtonTypes() )
{
System.out.println( "bt = " + bt );
if ( bt.getButtonData() == ButtonBar.ButtonData.CANCEL_CLOSE )
{
Button cancelButton = ( Button ) alert.getDialogPane().lookupButton( bt );
cancelButton.fire();
break;
}
}
});
final Scene scene = new Scene( new Group( b ), 400, 300 );
primaryStage.setScene( scene );
primaryStage.show();
Optional<ButtonType> result = alert.showAndWait();
if ( result.get() == buttonTypeOne )
{
System.out.println( "one " );
}
else if( result.get() == buttonTypeCancel )
{
System.out.println( "cancel " );
}
}
You can invoke close() method on the Alert or any Dialog.
Here is a simple example which waits for 5 secs, and if the Alert is still showing, it closes it.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.stage.Stage;
import java.util.Optional;
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Title");
alert.setHeaderText("Some Text");
alert.setContentText("Choose your option.");
ButtonType buttonTypeOne = new ButtonType("Yes");
ButtonType buttonTypeCancel = new ButtonType("No", ButtonBar.ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeCancel);
Thread thread = new Thread(() -> {
try {
// Wait for 5 secs
Thread.sleep(5000);
if (alert.isShowing()) {
Platform.runLater(() -> alert.close());
}
} catch (Exception exp) {
exp.printStackTrace();
}
});
thread.setDaemon(true);
thread.start();
Optional<ButtonType> result = alert.showAndWait();
}
}
It's closing opened dialog in 2 second.
dialog = new Dialog<>();
dialog.setTitle("It's a dialog!");
dialog.show();
Thread newThread = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
Platform.runLater(new Runnable() {
#Override
public void run() {
dialog.close();
}
});
}
});
newThread.start();
Related
I might be missing something very obvious, but I can't find out how to set the Icon for a Dialog component (ProgressDialog to be more precise). I know how to do that for a Stage:
this.primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("/icon/Logo.png")));
But I don't find anything for the Dialog family. And somehow, setting the Stage Icon does not influence the Dialog Icon.
Thanks
There's an excellent tutorial here by Marco Jakob, where you can find not only how to use dialogs, but also how to solve your problem.
Both for the new dialogs (in JDK8u40 early versions or with openjfx-dialogs with JDK 8u25), or for those in ControlsFX, in order to set the icon of your dialog, you can use this solution:
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
stage.getIcons().add(
new Image(this.getClass().getResource("<image>.png").toString()));
This code snippet shows how to use a ProgressDialog, from ControlsFX, and set an icon for the dialog:
#Override
public void start(Stage primaryStage) {
Service<Void> service = new Service<Void>() {
#Override protected Task<Void> createTask() {
return new Task<Void>() {
#Override protected Void call() throws InterruptedException {
updateMessage("Message . . .");
updateProgress(0, 10);
for (int i = 0; i < 10; i++) {
Thread.sleep(300);
updateProgress(i + 1, 10);
updateMessage("Progress " + (i + 1) + " of 10");
}
updateMessage("End task");
return null;
}
};
}
};
Button btn = new Button("Start Service");
btn.setOnAction(e -> {
ProgressDialog dialog = new ProgressDialog(service);
dialog.setTitle("Progress Dialog");
dialog.setHeaderText("Header message");
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
stage.getIcons().add(new Image(this.getClass().getResource("<image>.png").toString()));
service.start();
});
Scene scene = new Scene(new StackPane(btn), 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
Just Do like this:
Alert(AlertType.ERROR, "Erreur de connexion! Verifiez vos Identifiants",FINISH); //Cancel..
setTitle("XNotes FX Erreur");
stage = (Stage) alert.getDialogPane().getScene().getWindow();
stage.getIcons().add(new Image("indiza/XnotesErrorIdz.png")); // To add an icon
showAndWait();
Here is the result
**My friends, is it computer science that we do? : No, we do crafts
**
You can easily use the icon of your application for the alert-icon by setting your application-window as owner of the alert box:
#FXML
Button buShow;
...
Alert alert = new Alert(AlertType.INFORMATION, "Nice Box.", ButtonType.CLOSE);
alert.initOwner(buShow.getScene().getWindow()); // Alert uses the Windows Icon
alert.show();
This is a method that I include in my JavaFX projects, simply calling this method and passing the Alert as a parameter will set both the title bar icon and the header graphic.
public class Msg {
public void showInfo(String title, String header, String message) {
Alert alertShowInfo = new Alert(Alert.AlertType.INFORMATION);
addDialogIconTo(alertShowInfo); //add icon and header graphic
alertShowInfo.setTitle(title);
alertShowInfo.setHeaderText(header);
alertShowInfo.setContentText(message);
alertShowInfo.showAndWait();
}
//this adds images to Alert
public void addDialogIconTo(Alert alert) {
// Add custom Image to Dialog's title bar
final Image APPLICATION_ICON = new Image("icon.png");
Stage dialogStage = (Stage) alert.getDialogPane().getScene().getWindow();
dialogStage.getIcons().add(APPLICATION_ICON);
// Add custom ImageView to Dialog's header pane.
final ImageView DIALOG_HEADER_ICON = new ImageView("icon.png");
DIALOG_HEADER_ICON.setFitHeight(48); // Set size to API recommendation.
DIALOG_HEADER_ICON.setFitWidth(48);
alert.getDialogPane().setGraphic(DIALOG_HEADER_ICON);
}
}
Then, in whatever class I wish to use the Alert, it will already have the customized icon and header graphic.
public static void main(String[] args){
Msg msg = new Msg();
// Alert will now include custom icon and header graphic.
msg.showInfo("Sucess!", "Program succeeded", "Now exiting program");
}
Just similar to any dialog, instead this is inside a button handler.
Alert alert = new Alert(
AlertType.WARNING,
"Alert message here.",
ButtonType.OK
);
alert.initOwner(((Button)event.getSource()).getScene().getWindow());
alert.setTitle("Alert window title");
alert.showAndWait();
I have created this methode for Email validation
The issue is that the Alert displayed goes on the top of the previous fxml instead of the same and the user have to fulfill all the fields again
I have this method
public boolean validateEmail() {
Pattern p = Pattern.compile("[a-zA-Z0-9][a-zA-Z0-9._]*#[a-zA-Z0-9]+([.][a-zA-Z]+)+");
Matcher m = p.matcher(emailField.getText());
if (m.find() && m.group().equals(emailField.getText())) {
return true;
} else {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Validation of Email");
alert.setHeaderText(null);
alert.setContentText("Please enter a valid Email");
alert.showAndWait();
return false;
}
}
I have a button which onAction calls the method below
public void showSubscription() throws IOException {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setTitle("New Subscription");
dialog.setHeaderText("Please Fulfill your information to subscribe");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("Registration.fxml"));
try {
dialog.getDialogPane().setContent(fxmlLoader.load());
} catch (IOException e) {
System.out.println("Couldn't load the Dialog");
e.printStackTrace();
return;
}
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
Optional<ButtonType> result = dialog.showAndWait();
if (result.isPresent() && result.get()==ButtonType.OK) {
RegistrationController controller = fxmlLoader.getController();
if (controller.validateEmail()) {
controller.loadRegistration();
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Information");
alert.setHeaderText(null);
alert.setContentText("Subscription Done Correctly");
alert.showAndWait();
}
}
else {
System.out.println("CANCEL pressed");
}
}
I don't know what to add to make this Alert popup in the same Registration.fxml and not go back to the previous one.
Alert inherits an initOwner(Window) method from Dialog. So you can call initOwner(...) and pass in the window responsible for showing the dialog. There's no direct reference to this, but you can get it from the scene containing the dialog's dialog pane:
alert.initOwner(dialog.getDialogPane().getScene().getWindow());
This line just needs to be called sometime before alert.showAndWait().
If you need the Alert created in the validateEmail() method to have the same owner, just pass a reference to the appropriate window to that method:
public boolean validateEmail(Window mainWindow) {
// ...
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.initOwner(mainWindow);
// ...
alert.showAndWait();
// ...
}
and
RegistrationController controller = fxmlLoader.getController();
if (controller.validateEmail(dialog.getDialogPane().getScene().getWindow())) {
// ...
}
I have this class that generates a Alert Dialog with a field to enter a password, and want to activate the OK button when pressing Enter on the password field.
public class PasswordDialog extends Dialog<String> {
private PasswordField passwordField;
public PasswordDialog(boolean usuario) {
setTitle("Senha");
if (usuario == true){
setHeaderText("Por favor insira a senha do usuário.");
}else{
setHeaderText("Por favor insira a senha do administrador.");
}
ButtonType passwordButtonType = new ButtonType("OK", ButtonData.OK_DONE);
getDialogPane().getButtonTypes().addAll(passwordButtonType, ButtonType.CANCEL);
passwordField = new PasswordField();
passwordField.setPromptText("Password");
HBox hBox = new HBox();
hBox.getChildren().add(passwordField);
hBox.setPadding(new Insets(20));
HBox.setHgrow(passwordField, Priority.ALWAYS);
getDialogPane().setContent(hBox);
Platform.runLater(() -> passwordField.requestFocus());
setResultConverter(dialogButton -> {
if (dialogButton == passwordButtonType) {
return passwordField.getText();
}
return null;
});
}
public PasswordField getPasswordField() {
return passwordField;
}
}
Actually this should happen by default (at least that's the behaviour on JavaFX 11/Win 10), but you can also close the Dialog yourself by calling setResult and close.
Example closing on arrow keys:
// in constructor
passwordField.setOnKeyPressed(evt -> {
if (evt.getCode().isArrowKey()) {
setResult(passwordField.getText());
close();
}
});
For closing on pressing enter, use the onAction event of the PasswordField:
// in constructor
passwordField.setOnAction(evt -> {
setResult(passwordField.getText());
close();
});
For more complicated behaviour of the resultConverter, you could also use it for setting the result to avoid duplicate code:
setResult(getResultConverter().call(passwordButtonType));
How do you disable the OK button until certain conditions are met?
Here im itializing my DialogPane with OK and CANCEL buttons.
Dialog<ButtonType> testDialog = new Dialog<>();
testDialog.initOwner(mainBorderPane.getScene().getWindow());
testDialog.setTitle("Test Dialog");
testDialog.setHeaderText(null);
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("testDialog.fxml"));
try{
testDialog.getDialogPane().setContent(fxmlLoader.load());
} catch(IOException e){
System.out.println("Couldn't load the dialog");
e.printStackTrace();
return;
}`
testDialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
testDialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
Optional<ButtonType> result = testDialog.showAndWait();
Then I am trying to bind the conditions to the OK button.
Conditions being: TextField inputs must match time format (0-7hours, 0-59minuts)
#FXML
private TextField hoursField;
#FXML
private TextField minutesField;
public void initialize(){
testDialogPane.lookupButton(ButtonType.OK).disableProperty()
.bind(Bindings.createBooleanBinding(
() -> !hoursField.getText().matches("[0-7]") ||
!minutesField.getText().matches("^[0-5]?[0-9]$"),
hoursField.textProperty(),
minutesField.textProperty()
));
}
Ofc, I'm getting a java.lang.NullPointerException because buttons or not existing at the initialize runtime.
Is there any other way around?
You can do the binding after you add the OK button to the dialog.
You have to add a getter for each TextField in the controller, then after you
load the testDialog.fxml you can assign the its controller to a reference like: DialogController controller = fxmlLoader.getController(); (swap DialogController with your controller)
Then after testDialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
do the binding:
testDialogPane.lookupButton(ButtonType.OK).disableProperty()
.bind(Bindings.createBooleanBinding(
() -> !controller.getHoursField.getText().matches("[0-7]") ||
!controller.getMinutesField.getText().matches("^[0-5]?[0-9]$"),
controller.getHoursField.textProperty(),
controller.getMinutesField.textProperty()
));
Create a method in your controller class that returns a BooleanExpression for valid/invalid input.
Controller
private BooleanExpression invalidInput;
public BooleanExpression invalidInputProperty() {
if (invalidInput == null) {
invalidInput = Bindings.createBooleanBinding(
() -> !hoursField.getText().matches("[0-7]") ||
!minutesField.getText().matches("^[0-5]?[0-9]$"),
hoursField.textProperty(),
minutesField.textProperty()
);
}
return invalidInput;
}
Dialog creation:
fxmlLoader.setLocation(getClass().getResource("testDialog.fxml"));
testDialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
try{
testDialog.getDialogPane().setContent(fxmlLoader.load());
Controller controller = fxmlLoader.getController(); // depending on the name of the controller class you may need to adjust this
testDialog.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty()
.bind(controller.invalidInputProperty());
} catch(IOException e){
System.out.println("Couldn't load the dialog");
e.printStackTrace();
return;
}
When I start my mediaplayer, and click the next-song button, it starts at the first song and goes down the list as intended. But if I start the program, double-click an mp3 file to play it, then click the next-song button, the first song in the list plays, not the mp3 after the mp3 I double clicked.
btn.setOnAction((ActionEvent e) ->
{
if(doubleClicked)
{
player.stop();
media = new Media(rowData.toURI().toString()); // needs to go to next song
player = new MediaPlayer(media);
player.play();
return;
}
if(music.hasNext())
{
try
{
player.stop();
media = new Media(music.next());
player = new MediaPlayer(media);
player.play();
lbl.setText(media.getSource());
}
catch(MediaException a)
{
System.out.println("Unsupported Format");
}
}
});
rowData is the mp3 file when double clicked. I've tried a bunch of things but nothing seems to work. I have my own data structure for this, but its pretty much the same as the built in Iterator and ArrayList code.
You don't adjust the Iterator's position according to the new position in the playlist. Given the index of the item clicked by the user you can simply replace the Iterator by a new one using List.listIterator.
Here's an simplified example using a ListView and Strings that are displayed in a label:
private Iterator<String> songIterator;
#Override
public void start(Stage primaryStage) {
ObservableList<String> songs = FXCollections.observableArrayList("Song 1", "Song 2", "Song 3", "Song 4");
// start with first song
songIterator = songs.iterator();
Label songLabel = new Label(songIterator.next());
ListView<String> lv = new ListView(songs);
MultipleSelectionModel<String> selectionModel = lv.getSelectionModel();
lv.setCellFactory(t -> new ListCell<String>() {
{
setOnMouseClicked(evt -> {
if (evt.getButton() == MouseButton.PRIMARY && evt.getClickCount() == 2) {
evt.consume();
// update label
songLabel.setText(getItem());
// iterator should return next item next
songIterator = songs.listIterator(selectionModel.getSelectedIndex()+1);
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
}
});
Button btn = new Button();
btn.setText("Next");
btn.setOnAction((ActionEvent event) -> {
if (songIterator.hasNext()) {
songLabel.setText(songIterator.next());
}
});
Scene scene = new Scene(new VBox(songLabel, lv, btn));
primaryStage.setScene(scene);
primaryStage.show();
}