Reputation: 5931
In my application I have a BorderPane
which contains a ScrollPane
in the center. The ScrollPane
has two logical sections top
and bottom
.
When I create a stage to show the BorderPane
I want the stage to be sized so that only the top
section of ScrollPane
is shown and the user can scroll down to see the bottom
section if they need it.
So I try to bind the prefHeight
property of the ScrollPane
to the prefHeight
property of the top
section. Hoping that the ScrollPane
will get the right height and the stage will look "Just Right™". For some reason this fails miserably. But when I attach a listener to the prefHeight
property it does indeed return the correct height. So I'm flabbergasted as to what is going on.
Interestingly, if I remove the BorderPane
it works in the SSCCE but this is not an option in the real case.
EDIT: SSCCE updated
Here is a SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SSCCE extends Application {
@Override
public void start(Stage aStage) throws Exception {
VBox top = new VBox(new ComboBox<>(), new ComboBox<>(), new ComboBox<>());
top.setStyle("-fx-background-color: pink");
top.heightProperty().addListener((aObs, aOld, aNew)->{
System.out.println(aNew.doubleValue());
});
Region regionBottom = new Region();
regionBottom.setPrefHeight(100);
regionBottom.setPrefWidth(200);
regionBottom.setStyle("-fx-background-color: red");
VBox content = new VBox(top, regionBottom);
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setFitToWidth(true);
scrollPane.prefHeightProperty().bind(top.prefHeightProperty());
BorderPane root = new BorderPane(scrollPane);
root.setTop(new MenuBar(new Menu("Foo")));
Scene scene = new Scene(root);
aStage.setScene(scene);
aStage.sizeToScene();
aStage.show();
aStage.toFront();
}
public static void main(String[] args) {
launch();
}
}
Actual result:
Expected result:
Upvotes: 0
Views: 1344
Reputation: 209225
The prefHeightProperty
defaults to a sentinel value, indicating that the pref height should be computed. (Region.USE_COMPUTED_SIZE
). Thus when you bind the prefHeight
of the scroll pane, you effectively instruct it to do the same, which basically means it will get its default size.
Persuading the layout to be performed and measured in order to get the correct size for the scroll pane's preferred height is a bit of a hack. The correct layout sizes depend on CSS, so you must first force the CSS to be applied, and then you can ask for the computed pref height of the top area. You can do the latter with a call to
top.prefHeight(-1);
So the following works:
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setFitToWidth(true);
// scrollPane.prefHeightProperty().bind(top.heightProperty());
BorderPane root = new BorderPane(scrollPane);
root.setTop(new MenuBar(new Menu("Foo")));
Scene scene = new Scene(root);
aStage.setScene(scene);
root.applyCss();
scrollPane.setPrefHeight(top.prefHeight(-1));
root.requestLayout();
aStage.sizeToScene();
aStage.show();
aStage.toFront();
(Note the call to aStage.sizeToScene();
is not needed: this is the default behavior anyway.)
Upvotes: 1