tableview not showing data from database - java

I am unable to populate my tableview. I believe the problem is in the controller, in the way my data is being sent to the FXML file, because a system out print (see below) shows exactly what I have in my database.
Please let me know where I did a mistake. I went over all tutorials that exist on that, but nothing fits my problem.
Thanks
Main App:
package tableview;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainApp extends Application {
public static void main(String[] args) {
// TODO Auto-generated method stub
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("view/FXMLTable.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Controller:
import tableview.model.Person;
public class FXMLTableController{
#FXML
public TableView<Person> tableview ;
#FXML
private TableColumn<Person, Number> clientIdColumn;
#FXML
private TableColumn<Person, String> firstNameColumn;
#FXML
private TableColumn<Person, String> lastNameColumn;
#FXML
private void initialize() {
assert tableview != null : "fx:id=\"tableview\" was not injected: check your FXML file 'UserMaster.fxml'.";
clientIdColumn.setCellValueFactory(cellData -> cellData.getValue().
clientIDProperty());
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue()
.firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue()
.lastNameProperty());
buildData();
}
private ObservableList<Person> data;
public void buildData(){
data = FXCollections.observableArrayList();
Connection con = null;
try {
Class.forName("org.sqlite.JDBC");
con = DriverManager.getConnection("jdbc:sqlite:tableviewdb.db");
String SQL = "Select * from INFO";
ResultSet rs = con.createStatement().executeQuery(SQL);
while(rs.next()){
Person per = new Person();
per.ClientID.set(rs.getInt("CLIENTID"));
per.FirstName.set(rs.getString("FIRSTNAME"));
per.LastName.set(rs.getString("LASTNAME"));
data.add(per);
}
tableview = new TableView<Person>();
tableview.setItems(data);
System.out.println(tableview.getItems().get(1).ClientID);
}
catch(Exception e){
e.printStackTrace();
System.out.println("Error on Building Data");
}
}
}
Model Class:
package tableview.model;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
public SimpleIntegerProperty ClientID = new SimpleIntegerProperty();
public SimpleStringProperty FirstName = new SimpleStringProperty();
public SimpleStringProperty LastName = new SimpleStringProperty();
public SimpleIntegerProperty getClientID() {
return ClientID;
}
public SimpleStringProperty getFirstname() {
return FirstName;
}
public SimpleStringProperty getLastName() {
return LastName;
}
public IntegerProperty clientIDProperty(){
return ClientID;
}
public StringProperty firstNameProperty(){
return FirstName;
}
public StringProperty lastNameProperty(){
return LastName;
}
}
FXML file:
(disregard the save button for now...)
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<children>
<SplitPane prefHeight="400.0" prefWidth="600.0">
<items>
<SplitPane dividerPositions="0.5" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<TextField layoutX="93.0" layoutY="34.0" />
<TextField layoutX="93.0" layoutY="85.0" />
<Label layoutX="35.0" layoutY="39.0" text="name" />
<Label layoutX="35.0" layoutY="90.0" text="email" />
<Button layoutX="204.0" layoutY="140.0" mnemonicParsing="false" text="save" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<TableView layoutY="-2.0" prefHeight="200.0" prefWidth="598.0">
<columns>
<TableColumn prefWidth="302.0" text="name" />
<TableColumn prefWidth="295.0" text="email" />
</columns>
</TableView>
</children>
</AnchorPane>
</items>
</SplitPane>
</items>
</SplitPane>
</children>
</AnchorPane>

You're creating a new TableView and setting its items, instead of setting the items on the table that the FXML file defined. Remove the
tableView = new TableView<Person>();
from the controller.
To get the #FXML-annotated fields in the controller to be populated with the appropriate elements from the FXML file, you need to add fx:id attributes to those elements:
<TableView fx:id="tableview" ... >
<columns>
<TableColumn fx:id="firstNameColumn" ... />
...

Related

How would one persist a single instance of a User model across multiple Controller with Java/JavaFX MVC?

Overview
I am working on a simple JavaFX application with multiple scenes using the MVC Architectural pattern. I have the design in place but I am having trouble persisting the User model across the different scenes. At the moment I do not have any backend. I am looking to just instantiate the User model when the application starts and display that single instance across my application. Any changes to that user model will up updated across the scenes. I am not looking to persist after the application closes.
I believe the issue arises from a design choice I had early on in development. I use a Controller named SceneNavigatorControl that calls the class Navigator which handles the presentation of different scenes in the application. These scenes are .fxml files.
SceneNavigatorControl
public class SceneNavigatorControl implements Initializable {
#FXML
private BorderPane mainStage;
#FXML
private void displayHomeScene() {
Navigator object = new Navigator();
Pane view = object.getScene("Home");
handleButtonChange("Home");
mainStage.setCenter(view);
}
#FXML
private void displayProfileScene() {
Navigator object = new Navigator();
Pane view = object.getScene("Profile");
handleButtonChange("Profile");
mainStage.setCenter(view);
}
#Override
public void initialize(URL location, ResourceBundle resources) {
// Called when the application starts, should this be where I instantiate User?
//User user = new User();
//user.setName("Bob");
displayHomeScene();
}
}
The above code handles which scene to take the user to. Depending on which button is tapped. One of the above functions is called and the scene is displayed by using the Navigator Class. I would think this is where I instantiate User and pass it to all other controllers
Navigator Class
public class Navigator {
private Pane view;
public Pane getScene(String fileName) {
try {
URL fileUrl = ActivityTracker.class.getResource("/ActivityTracker/Views/" + fileName + ".fxml");
if (fileUrl == null) {
throw new java.io.FileNotFoundException("FXML File cannot be found");
}
view = new FXMLLoader().load(fileUrl);
} catch (Exception e) {
System.out.print("No page " + fileName + " please check FXMLLoader");
}
return view;
}
}
The navigator class simple checks if a .fxml file with a given name exists. If it does, it replaces the current scene on the stage with the one given.
HomeView
<AnchorPane fx:id="Home" prefHeight="500.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ActivityTracker.Controllers.HomeController">
<children>
<Label fx:id="clockLabel" layoutX="90.0" layoutY="32.0" textFill="WHITE" AnchorPane.leftAnchor="90.0" AnchorPane.topAnchor="32.0">
<font>
<Font name="Lucida Grande" size="100.0" />
</font></Label>
<Pane layoutX="410.0" layoutY="-1.0" prefHeight="500.0" prefWidth="90.0" style="-fx-background-color: #1d1d1d;" />
</children>
This is our view which is created using an .fxml file. On the first line is where we declare what view is associated with what controller.
HomeController
public class HomeController implements Initializable {
#FXML
private Pane Home;
#Override
public void initialize(URL location, ResourceBundle resources) {
// Print users details
}
}
ActivityTracker (Main)
public class ActivityTracker extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(ActivityTracker.class.getResource("Views/Stage.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
} catch (Exception ex) {
Logger.getLogger(ActivityTracker.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
User Model
public class User {
// Define all user stat variables for the model
private String userName;
/**
* Method that returns the users name
* #return users name of type String
*/
public String getUsersName() {
return userName;
}
/**
* Method that takes in a string value and sets it as the current users name
* #param name the current users weight
*/
public void setUsersName(String name) {
this.userName = name;
}
}
The following is an mre of setting one model instance to two controllers.
Define a simple model to be used by the two controllers:
class User {
private final String fName, lName;
public User(String fName, String lName) {
this.fName = fName;
this.lName = lName;
}
public String getFirstName() {
return fName;
}
public String getLastName() {
return lName;
}
}
Define two very basic fxml views, each with a controller:
FirstName.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<HBox alignment="CENTER_LEFT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="30.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="fx_tests.FNameController">
<children>
<Label text="First Name: " HBox.hgrow="NEVER">
<font>
<Font size="16.0" />
</font>
</Label>
<Label fx:id="fName" text=""-"">
<font>
<Font size="16.0" />
</font>
</Label>
</children>
</HBox>
LastName.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<HBox alignment="CENTER_LEFT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="30.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="fx_tests.LNameController">
<children>
<Label text="Last Name: " HBox.hgrow="NEVER">
<font>
<Font size="16.0" />
</font>
</Label>
<Label fx:id="lName" text=""-"">
<font>
<Font size="16.0" />
</font>
</Label>
</children>
</HBox>
Define a simple interface:
interface Controller {
void setModel(User model);
}
And have the two controllers implement it:
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class FNameController implements Controller{
#FXML
private Label fName;
#Override
public void setModel(User model) {
fName.setText(model.getFirstName());
}
}
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class LNameController implements Controller{
#FXML
private Label lName;
#Override
public void setModel(User model) {
lName.setText(model.getLastName());
}
}
Define the main view that is the parent of the FirstName.fxml and LastName.fxml:
Main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<BorderPane xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx_tests.MainController">
<top>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="User Info" textAlignment="CENTER" wrappingWidth="250" BorderPane.alignment="CENTER">
<font>
<Font size="20.0" />
</font>
</Text>
</top>
<center>
<VBox fx:id="infoPane" alignment="CENTER" spacing="10.0" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets left="20.0" />
</BorderPane.margin>
</VBox>
</center>
</BorderPane>
The controller of the main view sets the model to the two controllers:
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
public class MainController {
#FXML
private VBox infoPane;
#FXML
void initialize() throws IOException{
try {
User model = new User("Ann","Davis"); //initialize model
FXMLLoader loader = new FXMLLoader(getClass().getResource("FirstName.fxml"));
Pane fName = loader.load();
Controller controller = loader.getController();
controller.setModel(model); //set model to first name controller
loader = new FXMLLoader(getClass().getResource("LastName.fxml"));
Pane lName = loader.load();
controller = loader.getController();
controller.setModel(model); //set model to last name controller
infoPane.getChildren().addAll(fName, lName);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Test it all using:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class FxmlTest extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Pane root = FXMLLoader.load(getClass().getResource("Main.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}

JavaFX TableView - Unable to display rows

Working on a Simple Invoice Software for self. Got stuck at the first point.
Unable to display rows in Tableview. I have tried every possible solution, gone through many tutorial videos, guides etc. Still not able to find the problem
Please help
NewInvoice.fxml
<TabPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="720.0" prefWidth="1280.0" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
<tabs>
<Tab text="CASH">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<Button fx:id="additembutton" layoutX="412.0" layoutY="41.0" mnemonicParsing="false" text="ADD" />
<TableView fx:id="invoiceitemtableview" layoutX="190.0" layoutY="180.0" prefHeight="200.0" prefWidth="481.0">
<columns>
<TableColumn fx:id="amountcol" prefWidth="75.0" text="Amount" />
<TableColumn fx:id="gstcol" prefWidth="75.0" text="GST" />
<TableColumn fx:id="subtotalcol" prefWidth="75.0" text="Subtotal" />
<TableColumn fx:id="cgstcol" prefWidth="75.0" text="CGST" />
<TableColumn fx:id="sgstcol" prefWidth="75.0" text="SGST" />
</columns>
</TableView>
<TextField fx:id="itemcodetextbox" layoutX="216.0" layoutY="41.0" />
</children></AnchorPane>
</content>
</Tab>
<Tab text="BOBCARD">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
NewInvoiceController.java
public class NewInvoiceController implements Initializable {
#FXML
TableView<InvoiceItemBean> invoiceitemtableview;
#FXML
TableColumn<InvoiceItemBean,String> amountcol, gstcol, subtotalcol, cgstcol, sgstcol;
final ObservableList<InvoiceItemBean> data = FXCollections.observableArrayList(
new InvoiceItemBean("1049","5","999.10","22.5","22.5"),
new InvoiceItemBean("1800","12","1180.10","305.00","305.00")
);
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
amountcol.setCellValueFactory(new PropertyValueFactory<>("amount"));
gstcol.setCellValueFactory(new PropertyValueFactory<>("gst"));
subtotalcol.setCellValueFactory(new PropertyValueFactory<>("subtotal"));
cgstcol.setCellValueFactory(new PropertyValueFactory<>("cgst"));
sgstcol.setCellValueFactory(new PropertyValueFactory<>("sgst"));
invoiceitemtableview.setItems(data);}
}
InvoiceItemBean.java
public class InvoiceItemBean {
private final SimpleStringProperty amount;
private final SimpleStringProperty gst;
private final SimpleStringProperty subtotal;
private final SimpleStringProperty cgst;
private final SimpleStringProperty sgst;
public InvoiceItemBean( String amount, String gst, String subtotal, String cgst, String sgst) {
this.amount = new SimpleStringProperty(amount);
this.gst = new SimpleStringProperty(gst);
this.subtotal = new SimpleStringProperty(subtotal);
this.cgst = new SimpleStringProperty(cgst);
this.sgst = new SimpleStringProperty(sgst);
}
public String getAmount() {
return amount.get();
}
public String getGst() {
return gst.get();
}
public String getSubtotal() {
return subtotal.get();
}
public String getCgst() {
return cgst.get();
}
public String getSgst() {
return sgst.get();
}
public void setAmount(String amount_value) {
amount.set(amount_value);
}
public void setGst(String gst_value) {
gst.set(gst_value);
}
public void setSubtotal(String subtotal_value) {
subtotal.set(subtotal_value);
}
public void setCgst(String cgst_value) {
cgst.set(cgst_value);
}
public void setSgst(String sgst_value) {
sgst.set(sgst_value);
}
}
Main.java
public class ClimaxInvoice extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("NewInvoice.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Unable to get anything
Output Table view showing no content
PLEASE HELP!!!
Your problem is really simple, you haven't specified the controller in the fxml.
Just add fx:controller="packageName.NewInvoiceController"
at the end of the first line in your fxml file.
So the whole line should be:
<TabPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="720.0" prefWidth="1280.0" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="packageName.NewInvoiceController">
Of course, replace packageName with the name of your package.

Bind TableView items with ObservableList in FXML - Java FX

I'm unable to bind TableView items with ObservableList in FXML.
Everything works fine when I set materialTable.setItems(materialDataObservableList); in button click event.
But I don't want button to know about TableView so I wanted to bind materialTable.items to materialDataObservableList property.
What am I doing wrong?
Or maybe I don't understand how binding works...
Thanks for help!
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="464.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.xxx.sm.frontend.fx.MainSceneController">
/*
*/
<children>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<Button fx:id="getMaterialsButton" mnemonicParsing="false" onAction="#getMaterialsButton" text="Get materials" />
<TableView fx:id="materialTable" editable="true" prefHeight="413.0" prefWidth="600.0" items="${materialDataObservableList}" >
//Columns here
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</VBox>
</children>
public class MainSceneController {
private ObservableList<MaterialData> materialDataObservableList;
public TableView materialTable;
public Button getMaterialsButton;
public void getMaterialsButton() {
getMaterialsFromRESTController();
}
private void getMaterialsFromRESTController() {
MaterialClient controller = new MaterialClient();
try {
materialDataObservableList = FXCollections.observableArrayList(controller.getMaterias());
} catch (IOException e) {
System.out.println("Failed to connect to RESTController");
}
//materialTable.setItems(materialDataObservableList);
}
public ObservableList<MaterialData> getMaterialDataObservableList() {
return materialDataObservableList;
}
}
If you are calling getMaterialsFromRESTController() from MainSceneController's constructor (so it is initialized as soon as the controller is available to the FXMLLoader), then
<TableView fx:id="materialTable" editable="true" prefHeight="413.0" prefWidth="600.0"
items="${controller.materialDataObservableList}" >
will work. (Note you access a property of the controller with ${controller.property}.)
If not, you can modify the controller as follows to make it work:
public class MainSceneController {
private final ObservableList<MaterialData> materialDataObservableList
= FXCollections.observableArrayList();
public TableView materialTable;
public Button getMaterialsButton;
public void getMaterialsButton() {
getMaterialsFromRESTController();
}
private void getMaterialsFromRESTController() {
MaterialClient controller = new MaterialClient();
try {
materialDataObservableList.setAll(controller.getMaterias());
} catch (IOException e) {
System.out.println("Failed to connect to RESTController");
}
}
public ObservableList<MaterialData> getMaterialDataObservableList() {
return materialDataObservableList;
}
}
and then the above FXML should work.
Here is a SSCCE:
BindTableItemsExample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="TableController">
<center>
<TableView items="${controller.items}">
<columns>
<TableColumn text="Item">
<cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
</TableColumn>
</columns>
</TableView>
</center>
<bottom>
<Button text="Load" onAction="#loadItems" BorderPane.alignment="center" >
<BorderPane.margin>
<Insets top="5" left="5" right="5" bottom="5"/>
</BorderPane.margin>
</Button>
</bottom>
</BorderPane>
TableController.java:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
public class TableController {
private final ObservableList<Item> items = FXCollections.observableArrayList();
#FXML
private void loadItems() {
items.setAll(createItems());
}
private List<Item> createItems() {
return IntStream.rangeClosed(1, 100)
.mapToObj(i -> "Item "+i)
.map(Item::new)
.collect(Collectors.toList());
}
public ObservableList<Item> getItems() {
return items ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
setName(name);
}
public final StringProperty nameProperty() {
return this.name;
}
public final java.lang.String getName() {
return this.nameProperty().get();
}
public final void setName(final java.lang.String name) {
this.nameProperty().set(name);
}
}
}
BindTableItemsTest.java (application class):
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class BindTableItemsTest extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("BindTableItemsExample.fxml"));
primaryStage.setScene(new Scene(loader.load(), 600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Missing code to display data inside of the table

Goal:
Display data, below, inside of the table
"Jacob", "Smith", "jacob.smith#example.com"
"Isabella", "Johnson", "isabella.johnson#example.com"
"Ethan", "Williams", "ethan.williams#example.com"
"Emma", "Jones", "emma.jones#example.com"
"Michael", "Brown", "michael.brown#example.com"
Problem:
What code am I missing in order to display the data inside of the table?
Info:
I'm new in JavaFX.
I'm using netbeans
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="787.0" prefWidth="1086.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<TableView id="ttt" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn id="firstNameCol" prefWidth="124.0" text="First name" />
<TableColumn id="lastNameCol" prefWidth="139.0" text="Last name" />
<TableColumn id="emailCol" minWidth="7.0" prefWidth="197.0" text="Email" />
<TableColumn id="addressCol" prefWidth="105.0" text="Address" />
<TableColumn id="zipcodeCol" prefWidth="100.0" text="Zipcode" />
<TableColumn id="cityCol" prefWidth="204.0" text="City" />
</columns>
</TableView>
</center>
</BorderPane>
package dreamcrm;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DreamCRM extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.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);
}
}
package dreamcrm;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
package dreamcrm;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
public Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
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);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
You will want to use fx:id instead of id for this. fx:id will still work with CSS selectors, so no worries there.
As #fabian pointed out, and that I missed, you also need to add the fx:controller in your fxml file so that it knows what is going to control it.
In your document controller, you will need to let it know it is getting some info from your fxml document using #FXML
So like this,
#FXML
private TableView ttt;
#FXML
private TableColumn<Person, String> firstNameCol, lastNameCol, emailCol,addressCol, zipCol, cityCol;
Here, you use your fx:id as the variable name.
Then, in your initialize function, you will need to set the cellValueFactory, like so.
#FXML
public void initialize(){
firstNamecol.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
//etc..
//Don't forget to add your list that you made to the tableview
ttt.setItems(list);
}
After you add the ObservableList you can now add and remove items and it should update accordingly.
On a related note, you need to have SimpleStringProperty as a returnable value, i.e.
public StringProperty firstNameProperty(){
return firstName;
}
This should get you started.

Edit the values of the TableView from TextField

Hi I am trying in the spirit of the example in http://code.makery.ch/java/javafx-2-tutorial-intro/ to make a similar application where the TableView values can be edited by a series of TextFields instead of a popup form. The reason why I would wannt do it like that is that I am having many fields in a similar application I want to develop and I would like to avoid the user editing them on the TableView
The TableView shows a list of Persons where for each I record name, surname and country. I have tried to create a bidirectionalBinding between the name property of the Person bean and the textProperty() of the TextField but this doesn't work.
I also tried to add to the textProperty a ChangeListener so when it changes to update the ObservableList of propoerties and this also didn't work
Apparently I am doing something wrong and so far I have the following code:
FXDocumentController.java
public class FXMLDocumentController implements Initializable {
private static final Logger logger = Logger.getLogger(FXMLDocumentController.class.getName());
private ObservableList<Person> data;
#FXML
private TableView<Person> tableview;
#FXML
private TableColumn<Person, String> colName;
#FXML
private TableColumn<Person, String> colSurname;
#FXML
private TableColumn<Person, String> colCountry;
#FXML
private TextField name;
#Override
public void initialize(URL url, ResourceBundle rb) {
assert tableview != null : "fx:id=\"tableview\" was not injected: check your FXML file 'UserMaster.fxml'.";
colName.setCellValueFactory(
new PropertyValueFactory<Person,String>("name"));
colSurname.setCellValueFactory(
new PropertyValueFactory<Person,String>("surname"));
colCountry.setCellValueFactory(
new PropertyValueFactory<Person,String>("country"));
DBClass objDbClass = new DBClass();
try{
con = objDbClass.getConnection();
buildData();
tableview.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
if (mouseEvent.getClickCount() == 1) {
Person p = tableview.getSelectionModel().getSelectedItem();
//name.textProperty().setValue(p.getName());
name.textProperty().bindBidirectional(p.name);
//name.textProperty().bind(p.name);
}
}
}
});
name.textProperty().addListener(new ChangeListener(){
#Override
public void changed(ObservableValue ov, Object t, Object t1) {
System.out.println("Value changed!");
/** Tried also this but this wont work
name.textProperty().setValue((String)t1);
int index = tableview.getSelectionModel().getSelectedIndex();
data.get(index).name.setValue( (String) t1);
tableview.setItems(data);
* */
}
}
);
}
catch(ClassNotFoundException ce){
logger.info(ce.toString());
}
catch(SQLException ce){
logger.info(ce.toString());
}
} //initialize
private void buildData() {
data = FXCollections.observableArrayList();
try{
data.add(new Person("Jon", "Doe", "USA"));
data.add(new Person("Lars", "Andersson", "Sweden"));
tableview.setItems(data);
}
catch(Exception e){
e.printStackTrace();
System.out.println("Error on Building Data");
}
}
}
Person.java
public class Person {
public SimpleStringProperty name = new SimpleStringProperty();
public SimpleStringProperty surname = new SimpleStringProperty();
public SimpleStringProperty country = new SimpleStringProperty();
public Person(String name, String surname, String country){
this.name.set(name);
this.surname.set(surname);
this.country.set(country);
}
public String getName(){
return name.get();
}
public String getSurname(){
return surname.get();
}
public String getCountry(){
return country.get();
}
}
And for the sake of completeness I am giving the fxml file (very ugly but I am experimenting with the functionality;)) and the launcher
FXMLDocument.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" blendMode="SRC_OVER" cache="false" disable="false" focusTraversable="false" prefHeight="576.0" prefWidth="1024.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="javafxdemoproject.FXMLDocumentController">
<children>
<SplitPane dividerPositions="0.3336594911937378" focusTraversable="true" layoutX="0.0" layoutY="55.0" prefHeight="521.0" prefWidth="1024.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="410.0" prefWidth="297.0">
<children>
<TableView fx:id="tableview" editable="true" prefHeight="520.0" prefWidth="338.0" tableMenuButtonVisible="true" AnchorPane.bottomAnchor="178.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn prefWidth="75.0" text="Name" fx:id="colName" />
<TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="154.0" text="Surname" fx:id="colSurname" />
<TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="112.0" text="Country" fx:id="colCountry" />
</columns>
</TableView>
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="404.0" prefWidth="460.0">
<children>
<Accordion layoutX="0.0" layoutY="0.0" prefHeight="520.0" prefWidth="262.0">
<expandedPane>
<TitledPane fx:id="personalTp" animated="false" text="Personal Details">
<content>
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<TextField fx:id="name" layoutX="44.0" layoutY="27.0" prefWidth="200.0" />
</children>
</AnchorPane>
</content>
</TitledPane>
</expandedPane>
<panes>
<fx:reference source="personalTp" />
<TitledPane fx:id="x2" animated="false" text="Positions held">
<content>
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<ListView prefHeight="621.0" prefWidth="454.0" AnchorPane.bottomAnchor="-2.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="74.0" />
</children>
</AnchorPane>
</content>
</TitledPane>
</panes>
</Accordion>
</children>
</AnchorPane>
</items>
</SplitPane>
<MenuBar layoutY="0.0" AnchorPane.rightAnchor="908.0">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<ToolBar layoutY="24.0" AnchorPane.leftAnchor="0.0">
<items>
<Button mnemonicParsing="false" text="Button" />
</items>
</ToolBar>
</children>
</AnchorPane>
JavaFXDemoProject.java
public class JavaFXDemoProject extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.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);
}
}
Questions
1) Is it at all possible what I am trying to do?
2) Why isn't this working with bidirectionalBinding
Any help appreciated
I changed your controller class like this
public void initialize(URL url, ResourceBundle rb) {
assert tableview != null : "fx:id=\"tableview\" was not injected: check your FXML file 'UserMaster.fxml'.";
colName.setCellValueFactory(
new PropertyValueFactory<Person,String>("name"));
colSurname.setCellValueFactory(
new PropertyValueFactory<Person,String>("surname"));
colCountry.setCellValueFactory(
new PropertyValueFactory<Person,String>("country"));
buildData();
tableview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Person>() {
#Override
public void changed(ObservableValue<? extends Person> observable, Person oldValue, Person newValue) {
if (oldValue !=null) name.textProperty().unbindBidirectional(oldValue.nameProperty());
if (newValue !=null) name.textProperty().bindBidirectional(newValue.nameProperty());
}
});
} //initialize
and your person class a bit
public class Person {
private StringProperty name = new SimpleStringProperty();
private StringProperty surname = new SimpleStringProperty();
private StringProperty country = new SimpleStringProperty();
public Person(String name, String surname, String country){
this.name.set(name);
this.surname.set(surname);
this.country.set(country);
}
public StringProperty nameProperty(){return name;}
public StringProperty surnameProperty(){return surname;}
public StringProperty countryProperty(){return country;}
}
Now the TextField can bind to the property in Person

Categories