Question

I have a problem with centering my dialog boxes (that are basically AnchorPanes that have caption in them) in their parent node (that is also AnchorPane, but I beleive this is irrelevant).

Code goes as follows:

public void showDialog(final Dialog dialog) {
    dialogStack.add(dialog);

    dialogCanvas.toFront();
    dialog.toFront();

    dialogCanvas.setVisible(true);
    dialog.setVisible(true);

    getChildren().add(dialog);
    dialog.widthProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            if(newValue.doubleValue() > 0) {
                centerDialog(dialog);
            }
        }
    });
}

public void centerDialog(final Dialog dialog) {
    double width = getWidth();
    double height = getHeight();

    dialog.setLayoutX(width / 2 - dialog.getWidth() / 2);
    dialog.setLayoutY(height / 2 - dialog.getHeight() / 2);
}   

I use the widthProperty change listener because you can only center the node when it gets rendered and only then it gets the width/height set.

When I debug the code at point when I have set the dialog's layout x/y the values are calculated fine but the dialog node at the end gets positioned at top/left corner (X:0/Y:0).

Why???

Can I somehow trigger the redraw of a parent node so that the element gets positioned righty? What am I doing wrong?

Kind regards, Stjepan

Was it helpful?

Solution 2

The problem was that this was all done in the single thread and the information about layout was not yet known... But using the Platform.runLater(...) I was able to solve the problem.

At the end it is pretty elegant when you know how to solve it...

public void showDialog(final Dialog dialog) {
dialogStack.add(dialog);

dialogCanvas.toFront();
dialog.toFront();

dialogCanvas.setVisible(true);
dialog.setVisible(true);

getChildren().add(dialog);
dialog.widthProperty().addListener(new ChangeListener<Number>() {
    @Override
    public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
        if(newValue.doubleValue() > 0) {
            centerDialog(dialog);
        }
    }
});
}

public void centerDialog(final Dialog dialog) {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            double width = getWidth();
            double height = getHeight();

            dialog.setLayoutX(width / 2 - dialog.getWidth() / 2);
            dialog.setLayoutY(height / 2 - dialog.getHeight() / 2);
        }
    });

}

OTHER TIPS

Your options:

  • Continue using AnchorPane as a parent but center the child by using anchor constraints as

    AnchorPane.setTopAnchor(dialog, height / 2 - dialog.getHeight() / 2);
    AnchorPane.setLeftAnchor(dialog, width / 2 - dialog.getWidth() / 2);
    

    on every change of width and height values of parent pane. You need add event listener for both width and height properties if you want the child pane to stay centered on window resizing.

  • Use Pane instead of AnchorPane as parent and center the child pane by setLayoutX() / setLayoutY(), on every change of parent's width and height values (again using event listeners). The Pane, in opposite to AnchorPane, will not layout its children other than resizing them to their preferred sizes.

  • Use StackPane instead of AnchorPane as parent and no need to center the child pane since StackPane will do it for you automatically.

  • Use the ControlsFX Dialogs.



A demo code for the first option:

public class AnchorDemo extends Application {

    private final AnchorPane anchorPaneParent = new AnchorPane();
    private final AnchorPane anchorPaneChild = new AnchorPane(new Label("TEXT TEXT TEXT TEXT TEXT"));

    @Override
    public void start(Stage primaryStage) {
        anchorPaneParent.setStyle("-fx-border-color:red");
        anchorPaneChild.setStyle("-fx-border-color:green");
        anchorPaneParent.getChildren().add(anchorPaneChild);

        anchorPaneParent.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
            @Override
            public void changed(ObservableValue<? extends Bounds> arg0, Bounds oldBound, Bounds newBound) {
                System.out.println("oldBound = " + oldBound);
                System.out.println("newBound = " + newBound);
                System.out.println("");
                if (oldBound.getHeight() != newBound.getHeight() || oldBound.getWidth() != newBound.getWidth()) {
                    updateChildAnchorConstraint();
                }
            }
        });

        Scene scene = new Scene(anchorPaneParent, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();
        updateChildAnchorConstraint();

    }

    private void updateChildAnchorConstraint() {
        AnchorPane.setTopAnchor(anchorPaneChild, anchorPaneParent.getHeight() / 2 - anchorPaneChild.getHeight() / 2);
        AnchorPane.setLeftAnchor(anchorPaneChild, anchorPaneParent.getWidth() / 2 - anchorPaneChild.getWidth() / 2);
    }

    public static void main(String[] args) {
        launch(args);
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top