JavaFX: How to change scene (FXML) from MenuItem

user4782711 picture user4782711 · Apr 13, 2015 · Viewed 7.8k times · Source

At 76, I am trying to learn Java and have produced a number of accounting related tutorials that successfully change scenes. Now looking at a structure using menubar and menu items to cope with linking 14 chapters as one of the items on the menubar.

All examples I have seen/watched show a simple println for each MenuItem. I have this working OK, but when I try to copy my FXMLLoader from my previous applications it doesn't like it.

I get this cannot find symbol: symbol: method getScene() location: variable mbarchapter1 of type MenuItem

My code is:

package accountingsnapshots;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.MenuItem;


import javafx.stage.Stage;

public class AccountingSnapshotsController implements Initializable {

    @FXML
    private MenuItem mbarhome;
    @FXML
    private MenuItem mbarchapter1;
    @FXML
    private MenuItem mbarchapter2;
    @FXML
    private MenuItem mbarchapter3;
    @FXML
    private MenuItem mbarchapter4;
    @FXML
    private MenuItem mbarcalculator;
    @FXML
    private MenuItem mbarglossary;
    @FXML
    private MenuItem mbarhelp;


    Stage stage; 
    @FXML Parent root;

    @FXML
    private void handleButtonAction(ActionEvent e) throws IOException {

    if(e.getSource()==mbarhome){
           System.out.println("this is Home");
        }

    else if(e.getSource()==mbarchapter1){
            System.out.println("this is chap 1");  

            //get reference to the button's stage
            stage = (Stage) root.getScene().getWindow();
            //load up OTHER FXML document
            root = FXMLLoader.load(getClass().getResource("AccountingSnapshots_1.fxml"));

        //create a new scene with root and set the stage
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
        }

    else if(e.getSource()==mbarchapter2){
            System.out.println("this is chap 2");       
        }

    else if(e.getSource()==mbarchapter3){
            System.out.println("this is chap 3");       
        }

    else if(e.getSource()==mbarchapter4){
            System.out.println("this is chap 4");       
        }

    else if(e.getSource()==mbarcalculator){
            System.out.println("this is Calculator");       
        }

    else if(e.getSource()==mbarglossary){
            System.out.println("this is Glossary");       
        }

    else if(e.getSource()==mbarhelp){
            System.out.println("this is Help"); 

        }

    }


    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

}

My FXML file has fx:ids against the menuItems. Shown below:

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

<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="accountingsnapshots.AccountingSnapshotsController">
   <children>
      <MenuBar layoutY="-1.0" prefHeight="30.0" prefWidth="858.0">
        <menus>
          <Menu fx:id="mbarhome" mnemonicParsing="false" onAction="#handleButtonAction" text="Home">
            <items>
              <MenuItem mnemonicParsing="false" text="Home" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Chapters">           
            <items>
              <MenuItem fx:id="mbarchapter1" mnemonicParsing="false" onAction="#handleButtonAction" text="Ch.1 - Introduction to final accounts" />
              <MenuItem fx:id="mbarchapter2" mnemonicParsing="false" onAction="#handleButtonAction" text="Ch.2 - Double entry bookkeeping" />
              <MenuItem fx:id="mbarchapter3" mnemonicParsing="false" onAction="#handleButtonAction" text="Ch.3 - Accounting adjustments" />
              <MenuItem fx:id="mbarchapter4" mnemonicParsing="false" onAction="#handleButtonAction" text="Ch.4 - Introduction to final accounts" />
            </items>
          </Menu>
          <Menu fx:id="mbarcalculator" mnemonicParsing="false" onAction="#handleButtonAction" text="Calculator">
            <items>
              <MenuItem mnemonicParsing="false" text="Calculator" />
            </items>           
          </Menu>
          <Menu fx:id="mbarglossary" mnemonicParsing="false" onAction="#handleButtonAction" text="Glossary">
            <items>
              <MenuItem mnemonicParsing="false" text="Glossary" />
            </items>
          </Menu>
          <Menu fx:id="mbarhelp" mnemonicParsing="false" onAction="#handleButtonAction" text="Help">
            <items>
              <MenuItem mnemonicParsing="false" text="Help" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </children>
</AnchorPane>

This is the output when running the application:

Created dir: C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\build\empty
Created dir: C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\build\generated-sources\ap-source-output
Compiling 2 source files to C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\build\classes
Copying 5 files to C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\build\classes
compile:
Created dir: C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\dist
Detected JavaFX Ant API version 1.3
Launching <fx:jar> task from C:\Program Files\Java\jdk1.8.0_31\jre\..\lib\ant-javafx.jar
Warning: From JDK7u25 the Codebase manifest attribute should be used to restrict JAR repurposing.
         Please set manifest.custom.codebase property to override the current default non-secure value '*'.
Launching <fx:deploy> task from C:\Program Files\Java\jdk1.8.0_31\jre\..\lib\ant-javafx.jar
jfx-deployment-script:
jfx-deployment:
jar:
Copying 12 files to C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\dist\run53408212
jfx-project-run:
Executing C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\dist\run53408212\AccountingSnapshots.jar using platform C:\Program Files\Java\jdk1.8.0_31\jre/bin/java
this is chap 1
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1762)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1645)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1364)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$324(ContextMenuContent.java:1317)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$$Lambda$147/1926554713.handle(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3724)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3452)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1728)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2461)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:348)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:273)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:382)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:553)
    at com.sun.glass.ui.View.notifyMouse(View.java:925)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$141(WinApplication.java:102)
    at com.sun.glass.ui.win.WinApplication$$Lambda$37/96639997.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1757)
    ... 43 more
Caused by: javafx.fxml.LoadException: 
file:/C:/Users/john/Documents/NetBeansProjects/AccountingSnapshots/dist/run53408212/AccountingSnapshots.jar!/accountingsnapshots/AccountingSnapshots_1.fxml:16

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2595)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2573)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2435)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3208)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3169)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3142)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3118)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3098)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3091)
    at accountingsnapshots.AccountingSnapshotsController.handleButtonAction(AccountingSnapshotsController.java:53)
    ... 53 more
Caused by: java.lang.IllegalArgumentException: Unable to coerce MenuButton@35398aab[styleClass=menu-button]'Home' to class javafx.scene.control.MenuItem.
    at com.sun.javafx.fxml.BeanAdapter.coerce(BeanAdapter.java:495)
    at javafx.fxml.FXMLLoader$PropertyElement.add(FXMLLoader.java:1387)
    at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:784)
    at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2817)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2526)
    ... 61 more

Corrected the second .fxml file, changing MenuButton to MenuItem. This is the result in the output section:

ant -f C:\\Users\\john\\Documents\\NetBeansProjects\\AccountingSnapshots jfxsa-run
init:
Deleting: C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\build\built-jar.properties
deps-jar:
Updating property file: C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\build\built-jar.properties
compile:
Detected JavaFX Ant API version 1.3
jfx-deployment:
jar:
Copying 12 files to C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\dist\run581163156
jfx-project-run:
Executing C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\dist\run581163156\AccountingSnapshots.jar using platform C:\Program Files\Java\jdk1.8.0_31\jre/bin/java
this is chap 1
Deleting directory C:\Users\john\Documents\NetBeansProjects\AccountingSnapshots\dist\run581163156
jfxsa-run:
BUILD SUCCESSFUL (total time: 19 seconds)

Any help appreciated

Answer

James_D picture James_D · Apr 13, 2015

MenuItem is not a subclass of Node, so it doesn't inherit a getScene() method.

The simplest solution is just to call getScene() on any scene graph node which you know to be in the same scene as the menubar containing your menu item.

stage = (Stage) someNode.getScene().getWindow();

where someNode is any Node that you have injected into your controller with @FXML.

As you currently only have MenuItems injected into your controller, you need to inject some node for this purpose. The most natural one would be the root of the FXML. To do this, add a fx:id="root" attribute to your FXML's root element, and then just change the declaration of the root field in the controller so that it is injected:

@FXML
private Parent root ;

Then

stage = (Stage) root.getScene().getWindow();

should give you the current Stage, as needed.