Javafx Nested Controller - javafx-2

I have tried this nested controller stuff over and over again, but it just doesn't work for me.
I don't know why i can't get something as easy as this to work. I follow this example
<VBox fx:controller="com.foo.MainController">
<fx:include fx:id="dialog" source="dialog.fxml"/>
...
</VBox>
public class MainController extends Controller {
#FXML private Window dialog;
#FXML private DialogController dialogController;
...
}
here is my code:
app.Main.fxml
<AnchorPane prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml" fx:controller="app.MainController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
<fx:include source="InnerFile.fxml" fx:id="innerfile"/>
</children>
</AnchorPane>
app.MainController.java
public class MainController {
#FXML
private Label label;
#FXML
private Button button;
#FXML
private InnerFileController controller;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
public void initialize() {
controller.here();
}
}
i'm calling a method of the nested controller ' controller.here(); ',
and get a NullPointerExecption.
I don't know what I have done wrong.

The name of your variable for InnerFileController is incorrect. You have:
#FXML private InnerFileController controller;
but should be:
#FXML private InnerFileController innerfileController;
This is because the name of the variable for the controller of an included file is always the fx:id value with "Controller" added on to it.

Related

JavaFX ObservableList toTableview via Task Thread

I am building a multiscreen JavaFX app with data being pulled from a SQL database to ObservableLists and displayed in the interface via Tableview. Because of the multiscreen nature of the app, I am trying to initialize the data from the ObservableList to the Tableview via the controller. The SQL pull to ObservableList is done via a Task on a new thread. When I include the sqlCSEditTbl.itemsProperty().setValue((ObservableList) csSQLList) in the Task method nothing is displayed in the TableView when I run the program. If I place the code outside the Task method,the particular screen will not display. I don't know what I am missing here to be able to get the data to display on the particular screen. I've debugged the SQL to ObservableList and the data is being properly stored in the ObservableList. The problem is getting it from the ObservableList to the Tableview interface. Any help would be greatly appreciated. Thank you!
App Edit Screen Screenshot
SQLCalcScript Array Model
package model.calcs;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* Created by jdsmith on 2/17/2016.
*/
public class SQLCalcScripts {
private final IntegerProperty calcScriptID;
private final IntegerProperty calcScriptIndex;
private final StringProperty calcScriptName;
private final StringProperty calcScriptServer;
private final StringProperty calcScriptApp;
private final StringProperty calcScriptGroup;
public SQLCalcScripts() {
this(null, null, null, null, null, null);
}
public SQLCalcScripts(Integer calcScriptID, Integer calcScriptIndex, String calcScriptName, String calcScriptServer, String calcScriptApp, String calcScriptGroup) {
this.calcScriptID = new SimpleIntegerProperty(calcScriptID);
this.calcScriptIndex = new SimpleIntegerProperty(calcScriptIndex);
this.calcScriptName = new SimpleStringProperty(calcScriptName);
this.calcScriptServer = new SimpleStringProperty(calcScriptServer);
this.calcScriptApp = new SimpleStringProperty(calcScriptApp);
this.calcScriptGroup = new SimpleStringProperty(calcScriptGroup);
this.calcScriptID.addListener((e) -> System.out.println("ID changed to " + this.calcScriptID.get()));
this.calcScriptIndex.addListener((e) -> System.out.println("ID changed to " + this.calcScriptIndex.get()));
this.calcScriptName.addListener((e) -> System.out.println("ID changed to " + this.calcScriptName.get()));
this.calcScriptServer.addListener((e) -> System.out.println("ID changed to " + this.calcScriptServer.get()));
this.calcScriptApp.addListener((e) -> System.out.println("ID changed to " + this.calcScriptApp.get()));
this.calcScriptGroup.addListener((e) -> System.out.println("ID changed to " + this.calcScriptGroup.get()));
}
// Getters and Setters for Calc Script
public Integer getCalcScriptID() {
return calcScriptID.get();
}
public void setCalcScriptID(Integer calcScriptID) {
this.calcScriptID.set(calcScriptID);
}
public IntegerProperty calcScriptIDProperty() {
return calcScriptID;
}
public Integer getCalcScriptIndex() {
return calcScriptIndex.get();
}
public void setCalcScriptIndex(Integer calcScriptIndex) {
this.calcScriptIndex.set(calcScriptIndex);
}
public IntegerProperty calcScriptIndexProperty() {
return calcScriptIndex;
}
public String getCalcScriptName() {
return calcScriptName.get();
}
public void setCalcScriptName(String calcScriptName) {
this.calcScriptName.set(calcScriptName);
}
public StringProperty calcScriptNameProperty() {
return calcScriptName;
}
public String getCalcScriptServer() {
return calcScriptServer.get();
}
public void setCalcScriptServer(String calcScriptServer) {
this.calcScriptServer.set(calcScriptServer);
}
public StringProperty calcScriptServerProperty() {
return calcScriptServer;
}
public String getCalcScriptApp() {
return calcScriptApp.get();
}
public void setCalcScriptApp(String calcScriptApp) {
this.calcScriptApp.set(calcScriptApp);
}
public StringProperty calcScriptAppProperty() {
return calcScriptApp;
}
public String getCalcScriptGroup() {
return calcScriptGroup.get();
}
public void setCalcScriptGroup(String calcScriptGroup) {
this.calcScriptGroup.set(calcScriptGroup);
}
public StringProperty calcScriptGroupProperty() {
return calcScriptGroup;
}
}
csEditInterface.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="essapp.csEditController">
<children>
<VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label alignment="CENTER" contentDisplay="CENTER" text="Calculation Script Editor">
<font>
<Font name="System Bold" size="36.0" />
</font>
</Label>
<ScrollPane fitToHeight="true" fitToWidth="true" pannable="true" prefHeight="800.0" prefWidth="717.0">
<content>
<AnchorPane prefHeight="200.0" prefWidth="717.0">
<children>
<TableView editable="true" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="csEditID" editable="false" prefWidth="100.0" sortable="false" text="ID" />
<TableColumn fx:id="csEditIndex" prefWidth="100.0" text="Index" />
<TableColumn fx:id="csEditName" prefWidth="250.0" sortable="false" text="Name" />
<TableColumn fx:id="csEditServer" editable="false" prefWidth="300.0" sortable="false" text="Server" />
<TableColumn fx:id="csEditApp" editable="false" prefWidth="250.0" sortable="false" text="Application" />
<TableColumn fx:id="csEditGroup" prefWidth="400.0" sortable="false" text="Calc Group" />
</columns>
</TableView>
</children>
</AnchorPane>
</content>
<VBox.margin>
<Insets left="50.0" right="50.0" top="50.0" />
</VBox.margin>
</ScrollPane>
<Button fx:id="csEditOkBtn" defaultButton="true" mnemonicParsing="false" onAction="#createTbl" prefHeight="25.0" prefWidth="151.0" text="Commit Changes">
<VBox.margin>
<Insets top="50.0" />
</VBox.margin>
</Button>
<Button fx:id="csEditExitBtn" cancelButton="true" mnemonicParsing="false" onAction="#goToCSInt" prefHeight="25.0" prefWidth="150.0" text="Cancel">
<VBox.margin>
<Insets top="25.0" />
</VBox.margin>
</Button>
</children>
</VBox>
</children>
</AnchorPane>
csEditController Code:
package essapp;
import com.sun.javafx.tk.Toolkit;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
import model.calcs.*;
import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;
import static javafx.scene.input.KeyCode.T;
#SuppressWarnings("Duplicates")
public class csEditController implements Initializable, ControlledScreen {
ScreensController myController;
ObservableList<SQLCalcScripts> csEditCSList = FXCollections.observableArrayList();
ObservableList<CalcScripts> csEditEssSQL = FXCollections.observableArrayList();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
//Add SQL data to TableView
Task task = new Task<Void>() {
#Override
public Void call() throws Exception {
SQL2CalcScripts csSQLList = new SQL2CalcScripts();
csSQLList.sqlCalc("http://TEST-HYPRPT01:13080/aps/JAPI","GNLESB",csEditCSList);
sqlCSEditTbl.itemsProperty().setValue((ObservableList<SQLCalcScripts>) csSQLList);
return null;
}
};
// sqlCSEditTbl.itemsProperty().bind(task.valueProperty());
// sqlCSEditTbl.setItems(csEditCSList);
new Thread(task).start();
// Initialize table with columns
csEditID.setCellValueFactory(cellData -> cellData.getValue().calcScriptIDProperty().asObject());
csEditIndex.setCellValueFactory(cellData -> cellData.getValue().calcScriptIndexProperty().asObject());
csEditName.setCellValueFactory(cellData -> cellData.getValue().calcScriptNameProperty());
csEditServer.setCellValueFactory(cellData -> cellData.getValue().calcScriptServerProperty());
csEditApp.setCellValueFactory(cellData -> cellData.getValue().calcScriptAppProperty());
csEditGroup.setCellValueFactory(cellData -> cellData.getValue().calcScriptGroupProperty());
// TableView Calc Group ComboBox
ObservableList<String> csGroupList = FXCollections.observableArrayList("Supplement", "Wrapper", "Board Book");
csEditGroup.setCellFactory(ComboBoxTableCell.forTableColumn(new DefaultStringConverter(), csGroupList));
csEditGroup.setOnEditCommit(
(TableColumn.CellEditEvent<SQLCalcScripts,String> cb) -> {
((SQLCalcScripts) cb.getTableView().getItems().get(
cb.getTablePosition().getRow()
)).setCalcScriptGroup(cb.getNewValue());
}
);
}
public void setScreenParent(ScreensController screenParent){
myController = screenParent;
}
#FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
#FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
#FXML // fx:id="sqlCSEditTbl"
private TableView<SQLCalcScripts> sqlCSEditTbl; // Value injected by FXMLLoader
#FXML // fx:id="csEditGroup"
private TableColumn<SQLCalcScripts, String> csEditGroup; // Value injected by FXMLLoader
#FXML // fx:id="csEditExitBtn"
private Button csEditExitBtn; // Value injected by FXMLLoader
#FXML // fx:id="csEditServer"
private TableColumn<SQLCalcScripts, String> csEditServer; // Value injected by FXMLLoader
#FXML // fx:id="csEditID"
private TableColumn<SQLCalcScripts, Integer> csEditID; // Value injected by FXMLLoader
#FXML // fx:id="csEditIndex"
private TableColumn<SQLCalcScripts, Integer> csEditIndex; // Value injected by FXMLLoader
#FXML // fx:id="csEditOkBtn"
private Button csEditOkBtn; // Value injected by FXMLLoader
#FXML // fx:id="csEditApp"
private TableColumn<SQLCalcScripts, String> csEditApp; // Value injected by FXMLLoader
#FXML // fx:id="csEditName"
private TableColumn<SQLCalcScripts, String> csEditName; // Value injected by FXMLLoader
#FXML
private void goToCSInt(ActionEvent event){
myController.setScreen(ScreensFramework.calcScriptInterfaceID);
}
// #FXML
// private void createTbl(ActionEvent event) {
// sqlCSEditTbl.setItems(csEditCSList);
// }
}
ControlledScreen class Code:
package sample;
/**
* Created by jdsmith on 4/21/2016.
*/
public interface ControlledScreen {
//This method will allow the injection of the Parent ScreenPane
public void setScreenParent(ScreensController screenPage);
}
ScreensController class Code:
package essapp;
import java.util.HashMap;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;
/**
* Created by jdsmith on 1/7/2016.
*/
public class ScreensController extends StackPane {
//Holds the screens to be displayed
private HashMap<String, Node> screens = new HashMap<>();
public ScreensController() {
super();
}
//Add the screen to the collection
public void addScreen(String name, Node screen) {
screens.put(name, screen);
}
//Returns the Node with the appropriate name
public Node getScreen(String name) {
return screens.get(name);
}
//Loads the fxml file, add the screen to the screens collection and
//finally injects the screenPane to the controller.
public boolean loadScreen(String name, String resource) {
try {
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
Parent loadScreen = (Parent) myLoader.load();
ControlledScreen myScreenControler = ((ControlledScreen) myLoader.getController());
myScreenControler.setScreenParent(this);
addScreen(name, loadScreen);
return true;
} catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}
//This method tries to displayed the screen with a predefined name.
//First it makes sure the screen has been already loaded. Then if there is more than
//one screen the new screen is been added second, and then the current screen is removed.
// If there isn't any screen being displayed, the new screen is just added to the root.
public boolean setScreen(final String name) {
if (screens.get(name) != null) { //screen loaded
final DoubleProperty opacity = opacityProperty();
if (!getChildren().isEmpty()) { //if there is more than one screen
Timeline fade = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1.0)),
new KeyFrame(new Duration(1000), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
getChildren().remove(0); //remove the displayed screen
getChildren().add(0, screens.get(name)); //add the screen
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(800), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
}, new KeyValue(opacity, 0.0)));
fade.play();
} else {
setOpacity(0.0);
getChildren().add(screens.get(name)); //no one else been displayed, then just show
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(2500), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
return true;
} else {
System.out.println("screen hasn't been loaded!!! \n");
return false;
}
/*Node screenToRemove;
if(screens.get(name) != null){ //screen loaded
if(!getChildren().isEmpty()){ //if there is more than one screen
getChildren().add(0, screens.get(name)); //add the screen
screenToRemove = getChildren().get(1);
getChildren().remove(1); //remove the displayed screen
}else{
getChildren().add(screens.get(name)); //no one else been displayed, then just show
}
return true;
}else {
System.out.println("screen hasn't been loaded!!! \n");
return false;
}*/
}
//This method will remove the screen with the given name from the collection of screens
public boolean unloadScreen(String name) {
if (screens.remove(name) == null) {
System.out.println("Screen didn't exist");
return false;
} else {
return true;
}
}
}
ScreensFramework Main App Code:
package essapp;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class ScreensFramework extends Application {
public static String mainInterfaceID = "main";
public static String mainInterfaceFile = "mainInterface.fxml";
public static String msaInterfaceID = "msa";
public static String msaInterfaceFile = "msaInterface.fxml";
public static String creditRatingInterfaceID = "credit";
public static String creditRatingInterfaceFile = "creditRatingInterface.fxml";
public static String calcScriptEditID = "csEdit";
public static String calcScriptEditFile = "csEditInterface.fxml";
public static String calcScriptInterfaceID = "calc";
public static String calcScriptInterfaceFile = "calcScriptInterface.fxml";
public static String subVarInterfaceID = "subvar";
public static String subVarInterfaceFile = "subVarInterface.fxml";
#Override
public void start(Stage primaryStage) {
ScreensController mainContainer = new ScreensController();
mainContainer.loadScreen(ScreensFramework.mainInterfaceID, ScreensFramework.mainInterfaceFile);
mainContainer.loadScreen(ScreensFramework.calcScriptInterfaceID, ScreensFramework.calcScriptInterfaceFile);
mainContainer.loadScreen(ScreensFramework.calcScriptEditID, ScreensFramework.calcScriptEditFile);
mainContainer.loadScreen(ScreensFramework.subVarInterfaceID, ScreensFramework.subVarInterfaceFile);
mainContainer.loadScreen(ScreensFramework.msaInterfaceID, ScreensFramework.msaInterfaceFile);
mainContainer.loadScreen(ScreensFramework.creditRatingInterfaceID, ScreensFramework.creditRatingInterfaceFile);
mainContainer.setScreen(ScreensFramework.mainInterfaceID);
mainContainer.prefHeightProperty().bind(primaryStage.heightProperty());
mainContainer.prefWidthProperty().bind(primaryStage.widthProperty());
mainContainer.setCenterShape(true);
mainContainer.setScaleShape(true);
Group root = new Group();
root.getChildren().addAll(mainContainer);
Scene scene = new Scene(root);
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
primaryStage.setWidth(bounds.getWidth());
primaryStage.setHeight(bounds.getHeight());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When you call
sqlCSEditTbl.itemsProperty().setValue(...);
you are updating the UI (by updating the items displayed in the table). Like (almost?) all UI toolkits, JavaFX is single-threaded: updates to the UI can only happen on the FX Application Thread. Doing this in the Task, which is being executed on a background thread, will throw a IllegalStateException; so your call method never completes and you never see the update to the table.
The usual way to do this is to return the data from your call method:
Task<List<SQLCalcScripts>> task = new Task<List<SQLCalcScripts>>() {
#Override
public List<SQLCalcScripts> call() throws Exception {
List<SQLCalcScripts> data = /* get data.... */ ;
return data;
}
};
when the task completes, the value property is set to the value returned from the call method, so you can now do:
task.setOnSucceeded(e -> sqlCSEditTbl.getItems().setAll(task.getValue()));
It's probably a good idea to log any exceptions that occur with
task.setOnFailed(e -> task.getException().printStackTrace());
Then as before launch the task with
new Thread(task).start();

tableCoulmn won't fire On Edit Commit

I'm trying to create an editable table using javaFX Scene builder, but I can not get it to fire the On Edit Commit event, after editing the cell (ending with enter input), the input box turns blank but still covers the cell and remains until I close it by selecting it and pressing esc.
The code for my controller as as following:
public class PlaylistsWindowController extends AbstractController implements EventHandler<WindowEvent> {
private Set<Song> libary;
private final ObservableList<SongWraper> libaryTableData = FXCollections.observableArrayList();
private final String LIBADRESS = "data\\libary.sav";
#FXML
TableColumn<SongWraper, String> titleColumn;
#FXML
TableColumn<SongWraper, String> artistColumn;
#FXML
TableColumn<SongWraper, String> genreColumn;
#FXML
TableColumn<SongWraper, String> serieColumn;
#FXML
TableColumn<SongWraper, Integer> pointGainColumn;
#FXML
TableColumn<SongWraper, Integer> pointLossColumn;
#FXML
TableView<SongWraper> libaryTable;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
/*Code creating the libaryTableData*/
configLibaryTable();
}
private void configLibaryTable(){
titleColumn.setCellValueFactory(new PropertyValueFactory<SongWraper, String>("title"));
artistColumn.setCellValueFactory(new PropertyValueFactory<SongWraper, String>("artistName"));
serieColumn.setCellValueFactory(new PropertyValueFactory<SongWraper, String>("serieName"));
genreColumn.setCellValueFactory(new PropertyValueFactory<SongWraper, String>("genre"));
pointGainColumn.setCellValueFactory(new PropertyValueFactory<SongWraper, Integer>("pointGain"));
pointLossColumn.setCellValueFactory(new PropertyValueFactory<SongWraper, Integer>("pointLossColumn"));
libaryTable.setItems(libaryTableData);
libaryTable.setEditable(true);
libaryTableEditCommit();
}
private void libaryTableEditCommit(){
titleColumn.setCellFactory(TextFieldTableCell.<SongWraper>forTableColumn());
titleColumn.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<SongWraper, String>>(){
#Override
public void handle(CellEditEvent<SongWraper, String> t){
((SongWraper) t.getTableView().getItems().get(t.getTablePosition().getRow())
).setTitle(t.getNewValue());
}
}
);
}
And the FXML:
<AnchorPane prefHeight="600.0" prefWidth="800.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="gui.PlaylistsWindowController">
<!-- TODO Add Nodes -->
-------unreleated code-------
<TableView id="table" fx:id="libaryTable" editable="true" prefHeight="272.0" prefWidth="494.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="24.0">
<columns>
<TableColumn prefWidth="75.0" text="Title" fx:id="titleColumn" />
<TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="91.0" text="Artist" fx:id="artistColumn" />
<TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="91.0" text="Serie" fx:id="serieColumn" />
<TableColumn prefWidth="75.0" text="Genre" fx:id="genreColumn" />
<TableColumn prefWidth="75.0" text="Point Gain" fx:id="pointGainColumn" />
<TableColumn prefWidth="75.0" text="Point Lost" fx:id="pointLossColumn" />
</columns>
</TableView>
-------unreleated code-------
</AnchorPane>
Anyone know the reason for this problem and how to fix it?
If your model class (SongWraper) (SongWrapper? SongRapper...? ;)) has JavaFX-style property methods (i.e.
public class SongWraper {
private final StringProperty title = new SimpleStringProperty(this, "title", "");
public final String getTitle() {
return title.get();
}
public final void setTitle(String title) {
this.title.set(title);
}
public final StringProperty titleProperty() {
return title ;
}
}
then the onEditCommit shouldn't be necessary. The TextFieldTableCell binds its text property to the appropriate property in the model class. I think, because of this, the TextFieldTableCell doesn't fire the onEditCommit, though I am not sure on that last point.

How to perform functions on the stage in JavaFX's controller file

this question contains the code for my earlier question
Main class
#Override
public void start(Stage mainStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLfile.fxml"));
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
FXMLController class
#FXML
private void getAxisLoc(ActionEvent axis) {
Stage stage;
stage = (Stage) root.getScene().getWindow();
int locX;
locX = (int) stage.getX();
int locY;
locY = (int) stage.getY();
}
The exception is fired here:
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:279)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1435)
... 48 more
Caused by: java.lang.NullPointerException
at myJavaFile.FXMLfileController.getAxisLoc(FXMLfileController.java:112)
... 58 more`
Blindly, I guess that the NullPointerExeption is fired here:
stage = (Stage) root.getScene().getWindow();
If so, make sure that you added fx:id="root" in the root pane tag .
example (FXML):
<BorderPane fx:id="root" xmlns:fx="http://javafx.com/fxml" fx:controller="YourController">
and reference it in your controller class:
#FXML
Parent root;
SSCCE
Sample.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" fx:id="root" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml" fx:controller="SampleController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
SampleController.java
public class SampleController implements Initializable {
#FXML
private Label label;
#FXML
private Pane root;
#FXML
private void handleButtonAction(ActionEvent event) {
Stage stage = (Stage) root.getScene().getWindow();
//you can use label instead of root.
//Stage stage= (Stage) label.getScence().getWindow();
stage.close();
}
#Override
public void initialize(URL url, ResourceBundle rb) { //TODO }
}
App.java
public class App extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch(args); }
}

JavaFx - How to display SimpleStringProperty value in TableView

I am busy working through a tutorial and am stuck on one point.
Basically launch new scene and display values from ObservableList of type Person in a table view.
The person class consists of 3 SimpleStringProperty’s firsName, lastName and email.
But when I run the app it seems to display the values of the SimpleStringProperty [StringProperty [value:actualvalue]] in the table and not the value of the SimpleStringProperty. If I change the Person’s class getFirstName method to return a String firstName.get() then it display the value correctly in the table.
Am I supposed to use the SimpleStringProperty or rather plain objects like String?
The person class
package co.za.chrispie.addressBook;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private SimpleStringProperty firstName;
private SimpleStringProperty lastName;
private SimpleStringProperty email;
public Person(final String firstName, final String lastName, final String email) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get(); //Changes this method to return a String
}
public SimpleStringProperty getLastName() {
return lastName;
}
public SimpleStringProperty getEmail() {
return email;
}
}
The Controller class
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package co.za.chrispie.addressBook;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
public class OracleAddressBookController implements Initializable {
#FXML
TableView<Person> table;
#FXML
TableColumn<Person, String> colFirstName;
#FXML
TableColumn<Person, String> colLastName;
#FXML
TableColumn<Person, String> colEmail;
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person("Jaco", "Pieters", "jp#me.co.za"),
new Person("Chrispie", "Pieters", "chrispie#me.co.za")
);
#Override
public void initialize(URL url, ResourceBundle rb) {
assert colFirstName != null : "fx:id=\"colFirstName\" was not injected: check your FXML file 'OracleAddressBook.fxml'.";
assert colLastName != null : "fx:id=\"colLastName\" was not injected: check your FXML file 'OracleAddressBook.fxml'.";
assert colEmail != null : "fx:id=\"colEmail\" was not injected: check your FXML file 'OracleAddressBook.fxml'.";
configureTable();
}
private void configureTable() {
colFirstName.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
colLastName.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
colEmail.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setItems(data);
assert table.getItems() == data;
}
}
and the front end FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="500.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns:fx="http://javafx.com/fxml" fx:controller="co.za.chrispie.addressBook.OracleAddressBookController">
<children>
<Label layoutX="14.0" layoutY="11.0" text="Address Book" />
<TableView fx:id="table" layoutX="14.0" layoutY="35.0" prefHeight="451.0" prefWidth="572.0">
<columns>
<TableColumn prefWidth="75.0" text="First Name" fx:id="colFirstName" />
<TableColumn prefWidth="75.0" text="Last Name" fx:id="colLastName" />
<TableColumn prefWidth="75.0" text="Email" fx:id="colEmail" />
</columns>
</TableView>
</children>
<stylesheets>
<URL value="#oracleaddressbook.css" />
</stylesheets>
</AnchorPane>
You need to change the name of your property getters to nameProperty(), where name is your property. So you would have firstNameProperty() and emailProperty().
getName() works, but the table won't observe the property's changes.
The doc for PropertyValueFactory explains this subject:
In this example, the "firstName" string is used as a reference to an assumed firstNameProperty() method in the Person class type (which is the class type of the TableView items list). Additionally, this method must return a Property instance. If a method meeting these requirements is found, then the TableCell is populated with this ObservableValue . In addition, the TableView will automatically add an observer to the returned value, such that any changes fired will be observed by the TableView, resulting in the cell immediately updating.
If no method matching this pattern exists, there is fall-through support for attempting to call get() or is() (that is, getFirstName() or isFirstName() in the example above). If a method matching this pattern exists, the value returned from this method is wrapped in a ReadOnlyObjectWrapper and returned to the TableCell. However, in this situation, this means that the TableCell will not be able to observe the ObservableValue for changes (as is the case in the first approach above).

JavaFX 2.0 Table with multiline table header

Is it possible to make javaFX 2.0 table with multiline header?
All the examples, which i have found on the web, have tables, where columnt header width = its text size, without wraping. The exaple of what i have, and what i need is shown on a screen:
Found a solution without using labels. In Scene Builder 8.3.0 (Gluon) , JavaFX 8 , a button appears on mouse hover, just right of the Text field, where a multi - line can be chosen from the menu.
This results in a following XML code:
<TableColumn fx:id="prodDisc" editable="false" prefWidth="50.0" text="Prod
Disc" />
I came up with the following function:
private void makeHeaderWrappable(TableColumn col) {
Label label = new Label(col.getText());
label.setStyle("-fx-padding: 8px;");
label.setWrapText(true);
label.setAlignment(Pos.CENTER);
label.setTextAlignment(TextAlignment.CENTER);
StackPane stack = new StackPane();
stack.getChildren().add(label);
stack.prefWidthProperty().bind(col.widthProperty().subtract(5));
label.prefWidthProperty().bind(stack.prefWidthProperty());
col.setGraphic(stack);
}
A complete executable example is in this gist (requires the 2.2 developer preview as a minimum).
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
public class TableWrappedHeaders extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
TableColumn firstNameCol = new TableColumn("First Name (which is a really long name)");
makeHeaderWrappable(firstNameCol);
firstNameCol.setPrefWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setPrefWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
TableView table = new TableView();
table.getColumns().addAll(firstNameCol, lastNameCol);
table.setItems(FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams")
));
table.setPrefSize(250, 200);
Pane layout = new VBox(10);
layout.setStyle("-fx-padding: 10;");
layout.getChildren().addAll(table);
stage.setScene(new Scene(layout));
stage.show();
}
private void makeHeaderWrappable(TableColumn col) {
Label label = new Label(col.getText());
label.setStyle("-fx-padding: 8px;");
label.setWrapText(true);
label.setAlignment(Pos.CENTER);
label.setTextAlignment(TextAlignment.CENTER);
StackPane stack = new StackPane();
stack.getChildren().add(label);
stack.prefWidthProperty().bind(col.widthProperty().subtract(5));
label.prefWidthProperty().bind(stack.prefWidthProperty());
col.setText(null);
col.setGraphic(stack);
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private Person(String fName, String lName) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
}
public String getFirstName() { return firstName.get(); }
public void setFirstName(String fName) { firstName.set(fName); }
public String getLastName() { return lastName.get(); }
public void setLastName(String fName) { lastName.set(fName); }
}
}
There is probably a better way to do this, but the function above did at least work for me in my test case.
I thought this would be achievable with a simple css style, but I could not get it to work via css alone.
Is easier with a Label in the header graphic of the column:
Add a label in the column header graphic (if the column has some text as title delete it).
Set the label to allow Wrap Text
(Scene Builder -> go to Properties of the label and select Wrap Text or in Code -> label.setWrapText(true))
Set label width and height we wish
Note: is easier to do this in a FXML (with Scene Builder) than in code.
Example below:
public class TableColumnLongTextLabel extends Application {
#Override
public void start(Stage stage) {
TableView table = new TableView();
TableColumn tableColumnData1 = new TableColumn(),
tableColumnData2 = new TableColumn("Long Title without Text Wrap");
tableColumnData1.setCellValueFactory(new PropertyValueFactory<SomeStructure, String>("data1"));
tableColumnData2.setCellValueFactory(new PropertyValueFactory<SomeStructure, String>("data2"));
table.getColumns().addAll(tableColumnData1, tableColumnData2);
Label columnTitle = new Label("Long Title with Text Wrapped");
columnTitle.setPrefWidth(125);
columnTitle.setPrefHeight(50);
columnTitle.setWrapText(true);
columnTitle.setTextAlignment(TextAlignment.CENTER);
tableColumnData1.setGraphic(columnTitle);
table.setItems(FXCollections.observableArrayList(
new SomeStructure("Java", "FX", 1),
new SomeStructure("Java", "Swing", 0),
new SomeStructure("Java", "Sample", 2),
new SomeStructure("Some", "Other", 10)
));
Pane layout = new VBox(10);
layout.getChildren().addAll(table);
stage.setScene(new Scene(layout, 350, 350));
stage.show();
}
public static class SomeStructure {
private final SimpleStringProperty data1;
private final SimpleStringProperty data2;
private final SimpleIntegerProperty data3;
private SomeStructure(String data1, String data2, Integer data3) {
this.data1 = new SimpleStringProperty(data1);
this.data2 = new SimpleStringProperty(data2);
this.data3 = new SimpleIntegerProperty(data3);
}
public String getData1() {
return data1.get();
}
public void setData1(String data1) {
this.data1.set(data1);
}
public String getData2() {
return data2.get();
}
public void setData2(String data2) {
this.data2.set(data2);
}
public Integer getData3() {
return data3.get();
}
public void setData3(Integer data3) {
this.data3.set(data3);
}
}
public static void main(String[] args) {
launch(args);
}
}
Example using FXML (No code needed :) ):
<TableView prefHeight="251.0" prefWidth="556.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<columns>
<TableColumn editable="false" maxWidth="90.0" prefWidth="70.0" sortable="false" text="Player">
<columns>
<TableColumn editable="false" maxWidth="80.0" prefWidth="50.0" text="ID" />
<TableColumn editable="false" maxWidth="200.0" prefWidth="180.0" text="Name" />
</columns>
</TableColumn>
<TableColumn maxWidth="80.0" minWidth="50.0" prefWidth="60.0" text="Game" />
<TableColumn editable="false" maxWidth="160.0" minWidth="125.0" prefWidth="135.0" sortable="false" text="Team" visible="false" />
<TableColumn editable="false" maxWidth="160.0" minWidth="107.0" prefWidth="107.0" sortable="false">
<graphic>
<Label alignment="CENTER" text="Individual Points (Big Team)" textAlignment="CENTER" wrapText="true" />
</graphic>
</TableColumn>
<TableColumn editable="false" maxWidth="160.0" minWidth="99.0" prefWidth="102.0" sortable="false">
<graphic>
<Label alignment="CENTER" text="Team Points (Small Maps)" textAlignment="CENTER" wrapText="true" />
</graphic>
</TableColumn>
<TableColumn editable="false" maxWidth="116.0" minWidth="55.0" prefWidth="55.0" sortable="false">
<graphic>
<Label text="Total Points" textAlignment="CENTER" wrapText="true" />
</graphic>
</TableColumn>
</columns>
</TableView>
The easiest way to align header text is with CSS style, like this:
.table-view .column-header .label {
-fx-text-alignment: center;
}
Use new line escape sequence "\n" in header text string to split it into multiple lines.

Resources