0

I'm working on JavaFX application. I want to write a notepad. Right now I'm working on my menu bar. This is my Menu.fxml file:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<MenuBar id="menuBar" fx:id="menuBar" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.menu.MenuController">
    <menus>
        <Menu id="fileMenu" fx:id="fileMenu" mnemonicParsing="false" text="File">
            <items>
                <MenuItem id="newFileItem" fx:id="newFileItem" text="New Ctrl+N" />
                <MenuItem id="openFileItem" fx:id="openFileItem" text="Open Ctrl+O" onAction="#openFile" />
                <MenuItem id="saveFileItem" fx:id="saveFileItem" text="Save Ctrl+S" />
                <MenuItem id="saveFileAsItem" fx:id="saveFileAsItem" text="Save file as... Ctrl+S"/>
            </items>
        </Menu>
        <Menu id="editionMenu" fx:id="editionMenu" mnemonicParsing="false" text="Edit">
        </Menu>
        <Menu id="settingsMenu" fx:id="settingsMenu" mnemonicParsing="false" text="Settings">
        </Menu>
        <Menu id="helpMenu" fx:id="helpMenu" mnemonicParsing="false" text="Help">
        </Menu>
    </menus>
</MenuBar>

I wanted to write one general controller and several specialized controllers for every menu so that I don't have one big controller which is responsible for file management, settings, edition etc. This is MenuController:

package controllers.menu;

import javafx.fxml.FXML;
import javafx.scene.control.MenuBar;

public class MenuController {

    @FXML
    private MenuBar menuBar;

    private FileMenuController fileMenuController;
    private EditMenuController editMenuController;
    private SettingsMenuController settingsMenuController;
    private HelpMenuController helpMenuController;

    public MenuController() {
        fileMenuController = new FileMenuController();
        fileMenuController.setMenuBar(menuBar);
    }

    public void openFile(){
        fileMenuController.openFile();
    }
}

This is FileMenuController:

package controllers.menu;

import javafx.scene.control.MenuBar;
import javafx.stage.FileChooser;

import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;

class FileMenuController {

    private MenuBar menuBar;

    public void openFile() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Open");
        fileChooser.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("*.txt", "*.txt"),
                new FileChooser.ExtensionFilter("All types", "*.*")
        );
        File file = fileChooser.showOpenDialog(menuBar.getScene().getWindow());
    }

    public void setMenuBar(MenuBar menuBar) {
        this.menuBar = menuBar;
    }

}

When I choose open option in file menu I got these exceptions:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
Caused by: java.lang.NullPointerException
    at controllers.menu.FileMenuController.openFile(FileMenuController.java:23)
    at controllers.menu.MenuController.openFile(MenuController.java:19)

Can anyone give me some piece of advice how to solve this problem? Is there better way to split menu controllers so that every single controller is responsible for only one thing?

WojciechS
  • 11
  • 2
  • The constructor is getting called before the menuBar is getting set, maybe use a reference to the MenuController and then have a method, getMenuBar? – matt Jul 11 '17 at 12:53
  • Can you add the appropriate tags to your specific menus, eg `fx:controller="controllers.menu.FileMenuController"` – matt Jul 11 '17 at 12:56
  • One fxml file can have only one controller, so I can't add a controller to every menu item unfortunately. – WojciechS Jul 11 '17 at 13:02
  • https://stackoverflow.com/questions/16879826/multiple-controller-for-one-fxml-file – matt Jul 11 '17 at 13:03
  • I forgot about Initializable interface...Thank you matt! I can create FileMenuController in initialize method instead of creating it in constructor. Then it works. – WojciechS Jul 11 '17 at 13:04
  • Maybe you should add the answer here, it seems the other answers aren't very clear to me. – matt Jul 11 '17 at 13:07

1 Answers1

1

The constructor is getting called before the menuBar is getting set, so MenuController should use initalize method from Initalizable interface. Fixed MenuController:

package controllers.menu;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.MenuBar;

import java.net.URL;
import java.util.ResourceBundle;

public class MenuController implements Initializable{

    @FXML
    private MenuBar menuBar;

    private FileMenuController fileMenuController;
    private EditMenuController editMenuController;
    private SettingsMenuController settingsMenuController;
    private HelpMenuController helpMenuController;

    public void openFile(){
        fileMenuController.openFile();
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        fileMenuController = new FileMenuController();
        fileMenuController.setMenuBar(menuBar);
    }
}
WojciechS
  • 11
  • 2