JavaFX TabPane - One controller for each tab
Asked Answered
M

1

31

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.

Moreno answered 10/11, 2013 at 12:49 Comment(0)
B
89

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.

Besom answered 10/11, 2013 at 12:59 Comment(9)
Thank you for your quick response. I'll get to work with your solution.Moreno
No problem. Check out the FXML reference if you need further details.Besom
please select this as an answer. I think it is very well explained, and deserves to be upvotedWeekender
Note the field names for the controllers for the tab content in this answer are incorrect; to get the controllers injected (as opposed to the tab content itself) you need @FXML private FooTabController fooTabPageController ; ("Controller" is appended to the fx:id of the include). See docs.Lyns
Thanks for pointing out my error. I have updated the code sample above.Besom
Anyone can provide fooTabPage.fxml example? Does it need to have an fx:controller attribute?Fechter
@Fechter As far as I know yes the fooTabPage.fxml need a fx:controller attrivute on the root elementCavalla
I'm unable to gain access to the fooTabController (or barTabController). What might I be doing wrong?Kulun
Has anyone else had issues with this solution and using the View->Show Sample Controller Skeleton feature. I get a blank page and a null pointer exception viewable on the console. If i remove the fx:id's from the <fx:include fx:id="fooTabPage" source="fooTabPage.fxml"/> then it works fine.Ascensive

© 2022 - 2024 — McMap. All rights reserved.