This is a regression in Java 10, refer to the bug report for further updates: JDK-8204949
Consider the following Java FX code, where b
starts invisible:
a.disableProperty().bind(b.visibleProperty())
If an application utilizing such code is run on Java 10 VM, then from the first time b
becomes visible, a
will always be rendered as if it was disabled.
Of course, during the time while a
is not really disabled (despite being rendered with a gray overlay), you can still interact with that element. E.g. you can edit text in input controls, click buttons/links and so on, the events get propagated correctly and so on.
If the application is run on Java 8 VM, it works as expected.
I currently do not have the EOS Java 9, so I have only tested on:
- Windows 10 x64 Pro 1709 & 1803
- Java 8u172 x64 (both on JDK and JRE explicitly targeting executables)
- Java 10u1 x64 (both on JDK and JRE explicitly targeting executables)
Example of how it looks like with Java 10:
How it looks on first start, before b
had a chance to become visible:
A SSCCE (mandatory for a somewhat obscure problem; a
would be propertyProvider
, b
would be invalidRenderField
and invalidRenderButton
):
package application;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.VBox;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
System.out.println("Executing start(Stage) in " + Thread.currentThread().getName());
try {
TextField invalidRenderField = new TextField();
Button invalidRenderButton = new Button("Click to reproduce");
Label propertyProvider = new Label();
propertyProvider.setVisible(false);
VBox invalidRenderParent = new VBox(invalidRenderField, invalidRenderButton, propertyProvider);
TitledPane invalidRenderPane = new TitledPane("Incorrectly rendered elements", invalidRenderParent);
Accordion root = new Accordion(invalidRenderPane);
root.setExpandedPane(invalidRenderPane);
invalidRenderField.disableProperty().bind(propertyProvider.visibleProperty());
invalidRenderButton.disableProperty().bind(propertyProvider.visibleProperty());
invalidRenderButton.setOnAction(e -> {
System.out.println("Executing 1st onAction(ActionEvent) in " + Thread.currentThread().getName());
propertyProvider.setVisible(true);
propertyProvider.setText("At this point of time you cannot modify the field or click the button");
Task<Void> task = new Task<>() {
@Override
protected Void call() throws Exception {
System.out.println("Executing non-JFX code in " + Thread.currentThread().getName());
Thread.sleep(3_000L);
return null;
}
};
task.setOnSucceeded(e2 -> {
System.out.println("Executing onSuccess(WorkerStateEvent) in " + Thread.currentThread().getName());
propertyProvider.setVisible(false);
Label infoLabel = new Label("Either click the button below or just select the previous pane within the accordion");
Button closePlaceholderButton = new Button("View failure");
VBox placeholder = new VBox(infoLabel, closePlaceholderButton);
TitledPane placeholderPane = new TitledPane("Placeholder", placeholder);
closePlaceholderButton.setOnAction(e3 -> {
System.out.println("Executing 2nd onAction(ActionEvent) in " + Thread.currentThread().getName());
root.getPanes().remove(placeholderPane);
root.setExpandedPane(invalidRenderPane);
});
root.getPanes().add(1, placeholderPane);
root.setExpandedPane(placeholderPane);
});
new Thread(task, "WhateverThread").start();
});
Scene scene = new Scene(root, 800, 450);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("Executing main(String[]) in " + Thread.currentThread().getName());
launch(args);
}
}
SSCCE output after clicking 'Click to reproduce' once:
Executing main(String[]) in main
Executing start(Stage) in JavaFX Application Thread
Executing 1st onAction(ActionEvent) in JavaFX Application Thread
Executing non-JFX code in WhateverThread
Executing onSuccess(WorkerStateEvent) in JavaFX Application Thread
Which is exactly as expected, JavaFX code executes on the designated thread.
The question is, am I doing something wrong and/or missing something obvious? I can't seem to find anything "wrong" with this code, it seems sound, yet it malfunctions. Is this a bug (and I need to lrn2search)?
All in all, any suggestions for a workaround are welcome. I need this code (or equivalent via a workaround) to work on Java 10, no excuses.
First, I'd just like to confirm that I am observing the same behavior as you.
- Windows 10 x64 Home
- Java 1.8.0_172 x64 -- Works as expected
- Java 10.0.1 x64 -- Fails (shows as disabled but is not actually disabled)
This appears to be a bug and you should report it as such.
Possible Workaround
I found that the nodes in question will be rendered correctly if you add to following code to all affected nodes:
node.disableProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) { // if no longer disabled
node.applyCss();
}
});
This could become tedious if you have a lot of nodes that can be disabled and are subject to the same sequence of events as described in your question. It may be prudent to create some type of utility method to handle this.
Edit
I found that the order of when you make propertyProvider
invisible affects the workaround I provided. If you make propertyProvider
invisible after you display the new TitledPane
then, when you go back to the other TitledPane
, the TextField
and Button
still render as disabled when they are not. Haven't found a way around that yet.
来源:https://stackoverflow.com/questions/50800612/a-disableproperty-bindb-visibleproperty-causes-invalid-element-rendering-i