sialcasa / mvvmFX

an Application Framework for implementing the MVVM Pattern with JavaFX
Apache License 2.0
494 stars 104 forks source link

OnViewRemoved method is not invoked on Window closed #591

Open piterso opened 5 years ago

piterso commented 5 years ago

When i open new Window from my "Main Application Window" after closing new Window method onViewRemoved in my ViewModel is not invoked. I use also SpringBoot in my project and i try using @PreDetroy on both @Component scopes (prototype and singleton) - won't help. @PreDestroy is only invoked for singleton components and after whole application is closed but i want to listen for event when one of the windows in my application is closed.

This is the code that opens new window

Parent parent = FluentViewLoader.fxmlView(TestView.class).load().getView();
Stage stage = new Stage();
stage.setTitle("Test");
stage.setScene(new Scene(parent));
stage.show();

I got ViewModel for TestView it looks like this:

@Component
@Scope("prototype")
@Slf4j
public class TestViewModel implements ViewModel, SceneLifecycle {

    public void initialize() {

    }

    @Override
    public void onViewAdded() {
        log.debug("View added to application");
    }

    @Override
    public void onViewRemoved() {
        log.debug("View removed from the application");
    }
}

After closing new Window based on TestView method onViewRemoved on TestViewModel is not invoked. It's pretty important for me to actually make it work.

I figure out that i can set on stage listener on close request like that:

stage.setOnCloseRequest(windowEvent -> {
            // .. some code
        });

I manage to make that work in that way:

ViewTuple<TestView, TestViewModel> viewTuple = FluentViewLoader.fxmlView(TestView.class).load();
TestViewModel viewModel = viewTuple.getViewModel();

Stage stage = new Stage();
stage.setTitle("Test");
stage.setScene(new Scene(viewTuple.getView()));
stage.setOnCloseRequest(windowEvent -> viewModel.stop());
stage.show();

is this the best way for doing this? onViewRemoved won't be invoked?

Do you have anny suggestions?

manuel-mauky commented 5 years ago

The onViewRemoved method is listening to when the View is removed from the JavaFX scene-graph. Basically we use a listener on the sceneProperty of Node. For some reason this property isn't automatically reset to null when the window is closed.

I managed to get it working with this code:

stage.setOnCloseRequest(windowEvent -> {
    stage.getScene().setRoot(new VBox());
});

This explicitly changes the root-node of the scene so that the old root-node (your Window view) is removed from the scene. Maybe it's a good idea to wrap the setRoot call in a Platform.runLater because I don't know if there are any timing problems when you remove the root node before the window is actually closed as the callback is on "close request".

piterso commented 5 years ago

Thanks for your answer. Is your solution better than my code? Do my code can cause some problems?

manuel-mauky commented 5 years ago

No, your code also works fine. The advantage of your code is that you don't depend on strange behavior of JavaFX (like not resetting the scene). The disadvantage is that your ViewModel has to explicitly provide the stop method and you have to invoke this method manually in your app class which introduces a dependency form the app class to this specific viewModel. If later in time you implement another ViewModel which needs the same logic then you would also have to adjust your app class again.

Other then that I don't see any disadvantages in your code. Both solutions are trade-offs.