Access TabPane from inside a dynamically added Tab - javafx

I'm new in FXML and I searched a lot to find a solution for my troubles with TabPanes. In the below example (based on the example in JavaFX: Adding a new tab from a tab controller) I want to add some tabs to a TapPane - adding a new tab from inside an "included"-tab (Tab 1) isn't a problem, but when I try to add a tab from inside a dynamically created tab (e.g. Tab 2), I always get a NullPointerException.
Could anyone please tell me how I get access to the FirstTab-Controller from inside the dynamically created tab (Tab 2)?
TestingTabPane.java:
package testingtabs;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TestingTabPane extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FirstTab.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
FirstTab.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<TabPane fx:id="myTabPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testingtabs.FirstTabController">
<tabs>
<Tab fx:id="FirstTab" text="Tab 1">
<fx:include source="NewTab.fxml" fx:id="newTab"/>
</Tab>
</tabs>
</TabPane>
FirstTabController.java:
package testingtabs;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
public class FirstTabController {
#FXML
public TabPane myTabPane;
#FXML
public NewTabController newTabController;
public void initialize() {
newTabController.setMainWindowController(this);
}
public void createTab() throws IOException {
int numTabs = myTabPane.getTabs().size();
Tab tab = new Tab("Tab " + (numTabs + 1));
Parent root = FXMLLoader.load(getClass().getResource("NewTab.fxml"));
tab.setContent(root);
myTabPane.getTabs().add(tab);
}
}
NewTab.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="testingtabs.NewTabController">
<children>
<Button fx:id="addTabBtn" layoutX="268.0" layoutY="187.0" mnemonicParsing="false" onAction="#onAddTabBtnClicked" text="add tab" />
</children>
</AnchorPane>
NewTabController.java:
package testingtabs;
import java.io.IOException;
import javafx.fxml.FXML;
public class NewTabController {
private FirstTabController firstTabController ;
public void setMainWindowController(FirstTabController mainWindowController) {
this.firstTabController = mainWindowController ;
}
#FXML
public void onAddTabBtnClicked() throws IOException {
firstTabController.createTab();
}
}

When you load a tab "dynamically" via the createTab() method, you never call setMainWindowController(...) on the controller for that newly-created tab content. So in that controller, firstTabController is still null, and you get a Null Pointer Exception in the onAddTabBtnClicked method.
You can do
public void createTab() throws IOException {
int numTabs = myTabPane.getTabs().size();
Tab tab = new Tab("Tab " + (numTabs + 1));
FXMLLoader loader = new FXMLLoader(getClass().getResource("NewTab.fxml"));
Parent root = loader.load();
NewTabController newTabController = loader.getController();
newTabController.setMainWindowController(this);
tab.setContent(root);
myTabPane.getTabs().add(tab);
}
You don't really need to treat the first tab differently to the others; you could simply call the createTab() method from initialize() to make the first one, reducing the code a little (and using consistent techniques throughout):
FirstTab.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<TabPane fx:id="myTabPane" maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
prefWidth="600.0" tabClosingPolicy="UNAVAILABLE"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="testingtabs.FirstTabController" />
FirstTabController:
package testingtabs;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
public class FirstTabController {
#FXML
public TabPane myTabPane;
public void initialize() {
createTab();
}
public void createTab() throws IOException {
int numTabs = myTabPane.getTabs().size();
Tab tab = new Tab("Tab " + (numTabs + 1));
FXMLLoader loader = new FXMLLoader(getClass().getResource("NewTab.fxml"));
Parent root = loader.load();
NewTabController newTabController = loader.getController();
newTabController.setMainWindowController(this);
tab.setContent(root);
myTabPane.getTabs().add(tab);
}
}
NewTab.fxml and NewTabController remain the same.

Related

pane.getChildren().addAll(); not working in a scene javafx

This code will not allow the line to draw in my window... All I have in the fxml file is a simple pane with the fx:id of hi to test things out. There is no error, the line simply doesn't appear. I've also tried this with a box and circle. I really need help, this is an important project.
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
public class PlotSceneController implements Initializable {
#FXML
Pane hi;
#Override
public void initialize(URL url, ResourceBundle rb) {
Line line = new Line(0,0,10,110);
line.setStroke(Color.BLACK);
line.setStrokeWidth(10);
hi.getChildren().addAll(line);
}
}
FXML File
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.shape.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="hi" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-
Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
</children>
</Pane>
Main Class, leads to another page with a button that leads to the page I'm having trouble with.
public class Main extends Application {
Stage firstStage;
Scene loginButton;
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
firstStage = primaryStage;
loginButton = new Scene(root, 900, 700);
primaryStage.setTitle("Treatment Data");
primaryStage.setScene(loginButton);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) { //Main class
launch(args); //Launches application/window
}
}
You are missed to set a controller class PlotSceneController.java. Set controller class in different two way like using main class setController() method or set controller class in left bottom side controller pane in scene Builder screen.
Using Main
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
loader.setController(new PlotSceneController());
Parent root = (Parent) loader.load();
Or Using FXML
Set Controller class with full package path like below way

Copy entite fxml data to different container

I am having two vbox(es).
First vbox fx:id is vbox1
Second vbox fx:id is vbox2
In vbox1 I am having textbox, combobox, buttons and everything else.
I am having one button which want to copy(onclick) entire source/fxml from vbox1 to vbox2.
Is there anyway to do that?
Define the content of the VBoxes in a separate FXML file. You can include the content in the first VBox directly in your "main" fxml with a <fx:include>:
<VBox fx:id="vbox1">
<fx:include source="Content.fxml"/>
</VBox>
and then you can load another copy in the button's handler with
#FXML
public void handleButtonAction(ActionEvent e) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Content.fxml"));
vbox2.getChildren().add(loader.load());
}
Complete example (everything in a package called application):
Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<center>
<HBox spacing="5">
<VBox fx:id="vbox1">
<fx:include source="Content.fxml"/>
</VBox>
<VBox fx:id="vbox2"/>
</HBox>
</center>
<bottom>
<Button text="Load" onAction="#load" BorderPane.alignment="CENTER"/>
</bottom>
</BorderPane>
MainController.java:
package application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.VBox;
public class MainController {
#FXML
private VBox vbox1 ;
#FXML
private VBox vbox2 ;
#FXML
private void load() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Content.fxml"));
vbox2.getChildren().add(loader.load());
}
}
Content.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.collections.FXCollections?>
<?import java.lang.String?>
<?import javafx.scene.control.Button?>
<VBox xmlns:fx="http://javafx.com/fxml/1">
<TextField promptText="Text Field"/>
<ComboBox>
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="One"/>
<String fx:value="Two"/>
<String fx:value="Three"/>
</FXCollections>
</items>
</ComboBox>
<Button text="Click me"/>
</VBox>
Main.java:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("Main.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

How customize a tooltip with css?

I am using the version javafx 2 with kepler and I tried to customized a tooltip without succeed:
.tooltip{
-fx-background-color: linear-gradient(blue,lightskyblue);
-fx-background-radius: 5;
}
.page-corner {
-fx-shape: " ";
}
It is not working. Do you have any ideas ?
Update:
here an mcve :
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainApp extends Application {
#Override
public void start(Stage stage) {
AnchorPane root;
try {
root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
Scene scene = new Scene(root, 600, 400);
stage.setTitle("FXML Welcome");
stage.setScene(scene);
stage.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
Application.launch(MainApp.class, args);
}
}
Sample.fxml file :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" stylesheets="MCVE/application.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Sample">
<children>
<TextField layoutX="66.0" layoutY="49.0">
<tooltip>
<Tooltip text="tooltip message" />
</tooltip>
</TextField>
</children>
</AnchorPane>
controller :
public class Sample {
//controller
}
application.css file :
.tooltip{
-fx-background-color: linear-gradient(blue,lightskyblue);
-fx-background-radius: 5.0;
}
.page-corner {
-fx-shape: " ";
}
I think this is because the Tooltip appears in its own window (Tooltip is a subclass of PopupControl, which eventually is a subclass of Window). Because you have applied the stylesheet to the AnchorPane, it isn't seen by the Tooltip, which is outside that hierarchy.
Instead, add the stylesheet to the Scene in your MainApp class:
scene.getStylesheets().add(getClass().getResource("/mcve/application.css").toExternalForm());

Using custom javafx label doesn't work

Ok so I've created my own customer label, for this example it's extremely basic. What I did was extend the Javafx Label in my custom class called MyLabel. I'm also using FXML to create the GUI. Now when I do this I can't seem to instantiate with my custom class as I get this error.
"Can not set net.blacksquirreldevs.tests.MyLabel field net.blacksquirreldevs.tests.SampleLabelController.sampleLabel to javafx.scene.control.Label"
Here is the code for everything
Main.java
package net.blacksquirreldevs.tests;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("SampleLabel");
AnchorPane anchorPane = (AnchorPane) FXMLLoader.load(getClass().getResource("SampleLabel.fxml"));
primaryStage.setScene(new Scene(anchorPane));
primaryStage.show();
}
public static void main(String... args) {
launch(args);
}
}
MyLabel.java
package net.blacksquirreldevs.tests;
import javafx.scene.control.Label;
public class MyLabel extends Label {
public MyLabel(String text) {
super();
setText(text);
}
}
SampleLabelController.java
package net.blacksquirreldevs.tests;
import javafx.fxml.FXML;
import java.net.URL;
import java.util.ResourceBundle;
public class SampleLabelController {
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private MyLabel sampleLabel = new MyLabel("Hello world!");
#FXML
void initialize() {
assert sampleLabel != null : "fx:id=\"sampleLabel\" was not injected: check your FXML file 'SampleLabel.fxml'.";
}
}
And finally the fxml file SampleLabel.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="net.blacksquirreldevs.tests.SampleLabelController">
<children>
<Label fx:id="sampleLabel" layoutX="153.0" layoutY="171.0" text="Sample Text">
<font>
<Font name="Arial Bold" size="50.0" />
</font>
</Label>
</children>
</AnchorPane>
So what I want to know is why I can' declare, in my SamplelabelController class, the sampleLabel as MyLabel?
Hopefully I've explained this well enough, if not let me know and I'll try to go a bit more in detail.
You are using <Label ... in your FXML instead of <MyLabel
Beside that your MyLabel has a none default constructor and in this case you NEED to provide a builder named MyLabelBuilder so that FXMLLoader can create an instance of MyLabel

JavaFx 2.0 with FXML: tabs at bottom instead of top?

Is it possible to define the TabBar to place the tabs at bottom instead of top?
To set the side of the tabs use following snippet:
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
tab1.setContent(new Label("Tab1 content"))
tabPane.getTabs().add(tab1);
tabPane.setSide(Side.BOTTOM)
Now the content is above the tabs.
Also as full FXML example:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.collections.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.web.*?>
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="example.MainViewController">
<bottom>
<HBox>
<children>
<Button fx:id="btnNewTab" mnemonicParsing="false" text="Add New Tab" onAction="#btnNewTabAction" />
</children>
</HBox>
</bottom>
<center>
<TabPane fx:id="tabPane" side="BOTTOM">
<tabs>
<Tab text="Untitled Tab 1">
<content>
<AnchorPane id="Content">
<Label>Content 1</Label>
</AnchorPane>
</content>
</Tab>
<Tab text="Untitled Tab 2">
<content>
<AnchorPane id="Content">
<Label>Content 2</Label>
</AnchorPane>
</content>
</Tab>
</tabs>
</TabPane>
</center>
<top>
<Label text="TabPane Example" />
</top>
</BorderPane>
The main class:
package example;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ExampleMain extends Application {
/**
* #param args
*/
public static void main(String[] args) {
Application.launch(ExampleMain.class, args);
}
#Override
public void start(Stage primaryStage) throws Exception {
URL location = ExampleMain.class.getResource("MainView.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
try {
Parent root = (Parent) fxmlLoader.load(location.openStream());
// in case you need access to the controller
MainViewController mainViewController = fxmlLoader.getController();
primaryStage.setScene(new Scene(root, 1024, 768));
Scene s = primaryStage.getScene();
s.setRoot(root);
primaryStage.sizeToScene();
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And the controller:
package example;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
public class MainViewController {
private static int counter = 0;
#FXML
private TabPane tabPane;
#FXML
public void btnNewTabAction() {
Tab tab = new Tab();
tab.setText("new Tab " + ++counter);
tab.setContent(new Label("Content of new tab " + counter));
tabPane.getTabs().add(tab);
}
}
as you can see in the example, fxml is an xml representation of the Java classes. If the class has an property side (getSide, setSide) then the class has in fxml an attribute side. So you can read the api documentation to find out which attributes are available in fxml as well.
Hope this helps.
Don't define the TabBar! Use a proper layout pane e.g. javafx.scene.layout.BorderPane.
Find attached examples for JavaFX 2.0 API and FXML:
JAVAFX 2.0 API
package tabtest;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TabTest extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 300, 250);
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
tabPane.getTabs().add(tab1);
root.setBottom(tabPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
For further information have a look at http://download.oracle.com/javafx/2.0/layout/jfxpub-layout.htm
FXML
package fxmltest;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class FxmlTest extends Application {
public static void main(String[] args) {
Application.launch(FxmlTest.class, args);
}
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
Scene scene = new Scene(root, 300, 250);
stage.setScene(scene);
stage.show();
}
}
Sample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane>
<bottom>
<TabPane>
<tabs>
<Tab text="Tab 1" />
</tabs>
</TabPane>
</bottom>
</BorderPane>

Resources