JavaFX TabPane - One controller for each tab

roneypc picture roneypc · Nov 10, 2013 · Viewed 26.5k times · Source

I'm new to Fx. I have a TabPanel with 10 Tabs. Each Tab has many controls (charts, buttons, etc.), and what I want is to assign a controller for each Tab. The SceneBuilder only let me assign a controller for the whole view, I mean, only the top panel (the root) has the "Controller class" option, so I have to write the code for all the tabs in one class and this, as entail, resulting in a very large class and difficult to understand and maintain. Perhaps the solution is very simple, but as I say, I have very little experience with FX and I have not been able to find something similar on the web.

Any idea? Thank you.

Answer

Benjamin Gale picture Benjamin Gale · Nov 10, 2013

One approach is to encapsulate each of your tab pages into a separate FXML file with it's own associated controller class.

Then in your FXML file for the main tab control you can do something like this:

<TabPane fx:controller="com.foo.MainController">
    <tabs>
        <Tab text="Untitled Tab 1">
            <content>
                <fx:include fx:id="fooTabPage" source="fooTabPage.fxml"/>
            </content>
        </Tab>
        <Tab text="Untitled Tab 2">
            <content>
                <fx:include fx:id="barTabPage" source="barTabPage.fxml"/>
            </content>
        </Tab>
    </tabs>
</TabPane>

Notice that instead of embedding the content directly, I am using the fx:include directive which tells the FXMLLoader to load the FXML file that is being referenced. The individual FXML files used for the page content will all have their own controller so that the logic is nicely separated.

If you need to interact with the sub-pages or sub-controllers from the main controller then you can reference them like you do with any other FXML control to have them injected.

public class MainController {
    // Inject tab content.
    @FXML private FooTabPage fooTabPage;
    // Inject controller
    @FXML private FooTabController fooTabPageController;

    // Inject tab content.
    @FXML private BarTabPage barTabPage;
    // Inject controller
    @FXML private BarTabController barTabPageController;
}

If you have a large number of pages (each with a large number of their own controls) another approach is to leave each tab empty, and once the main view is loaded, load the relevant page into your control.

You would need to listen for tab changes to switch the content and add relevant code to load / unload the views that are being used for the content of the tab pages.

I would recommend starting with the first approach and refactoring to use the second approach if you discover performance problems.