The following issue is occurring with JDK 1.8 (8u231), Windows 10
I have a DatePicker
setup with a listener that shows an Alert
when the value changes. However, as soon as the Alert
is displayed, the datePicker.valueProperty()
reverts back to its original value.
This does not appear to be "working as designed" and multiple other developers have confirmed the issue does not exist in later versions of Java.
Here is a MCVE to demonstrate:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DatePickerCommit extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
DatePicker datePicker = new DatePicker();
// Add listener on DatePicker
datePicker.valueProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
// Show an Alert
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setContentText("You selected " + newValue);
alert.show();
alert.setY(alert.getY()+100);
}
});
root.getChildren().add(datePicker);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
The Alert
itself does display the correct date, but the DatePicker
valueProperty()
reverts back to null
, as seen in this screenshot:
Using IntelliJ's debugger, I can confirm the datePicker.valueProperty()
is set to 'null' as soon as alert.show()
(or alert.showAndWait()
) is called.
Closest Potential Known Bugs:
I was able to find a few known bugs that seem to be related, but they have all been marked as resolved as of 8u73 (possible regression?):
I am able to reproduce this issue in the JDK version I am currently working. So on investigation, the root cause is getting focus on to new stage (or losing focus from DatePicker) before the value is committed to textField.
While debugging I noticed the below outcome:
I think we can tweak this in two ways:
Option#1: Taking @Kleopatra solution in another way,i.e, by setting the text just before showing alert. This way we are fooling the ComboBoxPopupControl->setTextFromTextFieldIntoComboBoxValue method that there is a valid value in textField and not let it to reset the value.
Option#2: Wrap the part of the showing alert in Platform.runLater, to process the the alert showing at later point in execution (by that time the commit will be already performed in DatePicker).
This worked for both manually entered dates and dates selected in popup.
Not sure if this work around is suitable for you. Can you give a try?
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.time.LocalDate;
public class DatePickerCommit extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
DatePicker datePicker = new DatePicker();
// Add listener on DatePicker
datePicker.valueProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
// Option#1
datePicker.getEditor().setText(datePicker.getConverter().toString(newValue));
showAlert(newValue);
// Option#2
//Platform.runLater(()->showAlert(newValue));
}
});
root.getChildren().add(datePicker);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
private void showAlert(LocalDate value){
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setContentText("You selected " + value);
alert.show();
alert.setY(alert.getY()+100);
}
}
Basically it's a unwanted side-effect of a bug fix to support commitOnFocusLost on ComboBox and DatePicker that was accidentally (?) reverted by another bug fix to support consistent commit-cancel api on ComboBox et al and Spinner which "forgot" to add the commit-cancel api on DatePicker leading to a regression of the original bug for DatePicker with the benefit of not having the unwanted side-effect :)
To hack around, you can add your own support for commit-on-focusLost similarly to what is done for ComboBox/Spinner, that is by adding a focusListener to the DatePicker and update the editor from the picker's value (beware: not formally tested!)
datePicker.focusedProperty().addListener((src, ov, nv) -> {
if (!nv) {
datePicker.getEditor().setText(datePicker.getConverter().toString(datePicker.getValue()));
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With