BerndGit
BerndGit

Reputation: 1660

JavaFX: Trigger on Pane not visible / destroyed

I want to fill a Pane varPane with variable content. The implementation looks similar to this

Pane varPane = new Pane();
// ..

someProperty.addListener(( obsv, oldV, newV) -> {
    varPane.getChildren().clear();    // Remove old Property Pane Content
    Pane propPane = getNewPane(newV);     // Get new content
    varPane.getChildren().add(propPane);  // add Content
});

The Pane is generated like:

public Pane getNewPane(Object newV){
       Pane myPane = new Pane();

       // Add dummy Content
        myPane.getChildren().add(new Label(newV.toString()))

        // Here I need some listener
        // somthing like [not working]:
        myPane.setOnClosed( System.out.println("Pane closed: " + newV.toString() );

        return myPane;
}

As indicated above, I need to perform some cleanup after the pane is not used anymore. However I couldn't find to correct way to implement such a listener.

The listener shall be triggered if:

I could require that on these events an function is called for the cleanup. However I think it would be safer, if the pane detects such events on its own.

Upvotes: 0

Views: 545

Answers (1)

James_D
James_D

Reputation: 209330

First note that varPane.getChildren().removeAll() doesn't do what you think. You need

varPane.getChildren().clear();

You can check if the pane's parentProperty() changes to null:

myPane.parentProperty().addListener((obs, oldParent, newParent) -> {
    if (newParent == null) {
         System.out.println("Pane closed: " + newV);
    }
});

Checking if the window containing the pane is hidden (closed) is considerably harder. You can create the following chain of listeners (basically listening to the pane's sceneProperty() in order to listen to the scene's windowProperty() in order to listener to the window's showingProperty()):

ChangeListener<Boolean> showingListener = (obs, wasShowing, isNowShowing) -> {
    if (! isNowShowing) {
        System.out.println("Window containing "+newV+" hidden");
    }
};
ChangeListener<Window> windowListener = (obs, oldWin, newWin) -> {
    if (oldWin != null) {
        oldWin.showingProperty().removeListener(showingListener);
    }
    if (newWin != null) {
        newWin.showingProperty().addListener(showingListener);
    }
};
ChangeListener<Scene> sceneListener = (obs, oldScene, newScene) -> {
    if (oldScene != null) {
        oldScene.windowProperty().removeListener(windowListener);
        if (oldScene.getWindow() != null) {
            oldScene.getWindow().showingProperty().removeListener(showingListener);
        }
    }
    if (newScene != null) {
        newScene.windowProperty().addListener(windowListener);
        if (newScene.getWindow() != null) {
            newScene.getWindow().showingProperty().addListener(showingListener);
        }
    }
};
myPane.sceneProperty().addListener(sceneListener);

You may prefer to use a bindings framework, such as the one that is part of ReactFX. Using ReactFX, you can cover both cases with

EventStreams.changesOf(Val
    .flatMap(myPane.sceneProperty(), Scene::windowProperty)
    .flatMap(Window::showingProperty)
    .orElseConst(false))
    .filter(c -> ! c.getNewValue())
    .observe(v -> System.out.println("Pane closed: "+newV));

Upvotes: 1

Related Questions