I havea TableView, and I access properties of the list's objects as follows. This works just fine.
<TableColumn fx:id="dateColumn" editable="false" prefWidth="135.0" text="Date">
<cellValueFactory>
<PropertyValueFactory property="date" />
</cellValueFactory>
</TableColumn>
However, I'd like to access a nested property of the object, for example:
<TableColumn prefWidth="100.0" text="Course">
<cellValueFactory>
<PropertyValueFactory property="house.bathroom"/>
</cellValueFactory>
</TableColumn>
where my list object has a getHouse(), and House has a getBathroom(). Unfortunately, this doesn't work. I've tried a few spelling variations, but no luck.
I don't believe you can do this via FXML (though I could be wrong).
Assuming you're using proper JavaFX properties, you'd just setup your own CellValueFactory in the controller like so:
bathroomColumn.setCellValueFactory(tf -> tf.getValue().getHouse().bathroomProperty());
Since it's unclear what you want to display in the bathroom column, this would return the BathroomProperty.
If, however, you wanted to return a property within the Bathroom object, you'd simply call getBathroom().yourProperty() as well:
bathroomColumn.setCellValueFactory(tf -> tf.getValue().getHouse().getBathroom().myStringProperty());
Perhaps an example might help to demonstrate the concept:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableViewValues extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Simple TableView
TableView<Person> personTableView = new TableView<>();
TableColumn<Person, String> colName = new TableColumn<>("Name");
TableColumn<Person, String> colCar = new TableColumn<>("Car");
// Setup the CellValueFactories
colName.setCellValueFactory(tf -> tf.getValue().nameProperty());
colCar.setCellValueFactory(tf -> tf.getValue().getCar().modelProperty());
personTableView.getColumns().addAll(colName, colCar);
root.getChildren().add(personTableView);
// Sample Data
personTableView.getItems().addAll(
new Person("Jack", new Car("Accord")),
new Person("John", new Car("Mustang")),
new Person("Sally", new Car("Yugo"))
);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
class Person {
private final StringProperty name = new SimpleStringProperty();
private final ObjectProperty<Car> car = new SimpleObjectProperty<>();
public Person(String name, Car car) {
this.name.set(name);
this.car.set(car);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
public Car getCar() {
return car.get();
}
public void setCar(Car car) {
this.car.set(car);
}
public ObjectProperty<Car> carProperty() {
return car;
}
}
class Car {
private final StringProperty model = new SimpleStringProperty();
public Car(String model) {
this.model.set(model);
}
public String getModel() {
return model.get();
}
public void setModel(String model) {
this.model.set(model);
}
public StringProperty modelProperty() {
return model;
}
}
The Result:
If you're using plain Java beans, the process is similar. But instead of setting the CellValueProperty to a Property within your data model classes, you'd create a new StringProperty inline and pass it the bean value you need.
So, in the Car example above, you'd do this instead:
colName.setCellValueFactory(tf -> new SimpleStringProperty(tf.getValue().getName()));
colCar.setCellValueFactory(tf -> new SimpleStringProperty(tf.getValue().getCar().getModel()));
Side Note: The tf you see referenced above is just a variable I use to refer to the CellDataFeatures object in Java which we can use to get a reference to that row's data model object (using the getValue() method). Perhaps cdf would be a better choice, but habits are hard to break.
Related
[Full Source Code Attached]
I have a javafx TableView with 3 columns ("Name", "Gender", "Country"). I'm attaching cell factories to all my columns like below.
nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));
I didn't implement any commit logic on my own. Now i change the value in all the 3 columns and then print the data. I'm able to see the changed value on all columns except the genderCol. The only difference about that column is it is using an enum.
When I change the value in the countryCol(that also ComoboBoxTableCell) the countryProperty is called twice first with old value then with new value but for genderCol, genderProperty is called only once.
You can copy and execute the code, print the data first without changing anything and then change the values on all 3 columns then print the data. Check now except the genderCol other columns have the changed value.
Am I missing anything or doing anything wrong with the enum
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application
{
#Override
public void start(final Stage primaryStage)
{
try
{
final VBox root = FXMLLoader.load(this.getClass().getResource("MainView.fxml"));
final Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
catch(final Exception e)
{
e.printStackTrace();
}
}
public static void main(final String[] args)
{
launch(args);
}
}
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<VBox spacing="10.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<TableView fx:id="table" editable="true">
<columns>
<TableColumn prefWidth="100.0" text="Name" fx:id="nameCol">
<cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="Gender" fx:id="genderCol">
<cellValueFactory><PropertyValueFactory property="gender" /></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="Country" fx:id="countryCol">
<cellValueFactory><PropertyValueFactory property="country" /></cellValueFactory>
</TableColumn>
</columns>
</TableView>
<Button text="Print Data" onAction="#printData"/>
</children>
</VBox>
MainController.java
package application;
import java.net.URL;
import java.util.ResourceBundle;
import application.Person.GenderEnum;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
/**
* #author Saravana Kumar M
*
*/
public class MainController implements Initializable
{
#FXML
TableView<Person> table;
#FXML
TableColumn<Person, String> nameCol;
#FXML
TableColumn<Person, GenderEnum> genderCol;
#FXML
TableColumn<Person, String> countryCol;
#Override
public void initialize(final URL location, final ResourceBundle resources)
{
this.nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
this.genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
this.countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));
final Person p1 = new Person("A", "M", "India");
final Person p2 = new Person("B", "F", "USA");
final Person p3 = new Person("C", "N", "India");
final Person p4 = new Person("D", null, "USA");
final Person p5 = new Person("E", "M", "India");
final Person p6 = new Person("F", "F", "USA");
final Person p7 = new Person("G", "N", "India");
final Person p8 = new Person("H", null, "USA");
this.table.setItems(FXCollections.observableArrayList(p1, p2, p3, p4, p5, p6, p7, p8));
this.genderCol.setOnEditCommit((event) -> this.doThis(event));
}
private void doThis(final CellEditEvent<Person, GenderEnum> event)
{
System.out.println(event);
System.out.println(event.getOldValue());
System.out.println(event.getNewValue());
System.out.println(event.getRowValue());
}
#FXML
public void printData()
{
this.table.getItems().forEach(System.out::println);
}
}
Person.java
package application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* #author Saravana Kumar M
*
*/
public class Person
{
private final StringProperty name;
private final ObjectProperty<GenderEnum> gender;
private final StringProperty country;
public Person(final String name, final String gender, final String country)
{
this.name = new SimpleStringProperty(name);
if(gender == null)
{
this.gender = new SimpleObjectProperty<>();
}
else
{
this.gender = new SimpleObjectProperty<>(GenderEnum.valueOf(gender));
}
this.country = new SimpleStringProperty(country);
}
public String getName()
{
return this.name.get();
}
public void setName(final String name)
{
this.name.set(name);
}
public StringProperty nameProperty()
{
return this.name;
}
public GenderEnum getGender()
{
return this.gender.get();
}
public void setGender(final GenderEnum gender)
{
this.gender.set(gender);
}
public ObjectProperty<GenderEnum> genderProperty()
{
return this.gender;
}
public String getCountry()
{
return this.country.get();
}
public void setCountry(final String country)
{
this.country.set(country);
}
public StringProperty countryProperty()
{
return this.country;
}
#Override
public String toString()
{
return "name : " + this.name + " name.get : " + this.name.get() + " gender : " + this.gender + " gender.get : " + this.gender.get() + " country : " + this.country.get() + " country.get : " + this.country.get();
}
public enum GenderEnum
{
M("Male"), F("Female"), N("None");
private final String label;
private GenderEnum(final String label)
{
this.label = label;
}
#Override
public String toString()
{
return this.label;
}
}
}
Thanks in Advance.
From the TableView documentation:
By default the TableColumn edit commit handler is non-null, with a
default handler that attempts to overwrite the property value for the
item in the currently-being-edited row. It is able to do this as the
Cell.commitEdit(Object) method is passed in the new value, and this is
passed along to the edit commit handler via the CellEditEvent that is
fired. It is simply a matter of calling
TableColumn.CellEditEvent.getNewValue() to retrieve this value.
It is very important to note that if you call
TableColumn.setOnEditCommit(javafx.event.EventHandler) with your own
EventHandler, then you will be removing the default handler. Unless
you then handle the writeback to the property (or the relevant data
source), nothing will happen. You can work around this by using the
TableColumnBase.addEventHandler(javafx.event.EventType, javafx.event.EventHandler) method to add a
TableColumn.EDIT_COMMIT_EVENT EventType with your desired EventHandler
as the second argument. Using this method, you will not replace the
default implementation, but you will be notified when an edit commit
has occurred.
Since you have
this.genderCol.setOnEditCommit((event) -> this.doThis(event));
in your controller class, you remove the default handler that updates the property. If you replace this with
this.genderCol.addEventHandler(TableColumn.<Person, GenderEnum>editCommitEvent(),
event -> this.doThis(event));
it will work as expected. (Note the documentation is incorrect in telling you how to get the event type. Also note your handler will be invoked before the default one, so the output from getRowValue() will show the values before updating, but pressing the button to display the values will give the new ones.)
With JavaFX, what is the best way to bind ChoiceBox to properties of a collection?
In example below I try to bind ChoiceBox elements to name of an ObservableList beans. This works fine when items are added/removed but not when the property value name change.
I was hoping there is a clean and simple solution to this but haven't yet found any example of it...
The class ExampleBean2 in deliberately not implemented with properties since that object may correspond to a external model class out of my control.
package com.playground;
import org.controlsfx.control.PropertySheet;
import org.controlsfx.property.BeanPropertyUtils;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class BindingPlayGround extends Application{
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("FXPlayGround");
Parent content = createContentPane();
Scene scene = new Scene(content, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
protected Parent createContentPane() {
ObservableList<BeanExample2> beans = FXCollections.observableArrayList();
ObservableList<PropertySheet> sheets = FXCollections.observableArrayList();
ListView<PropertySheet> listView = new ListView<PropertySheet>(sheets);
Button addBeanButton = new Button("Add Bean");
addBeanButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
BeanExample2 e = new BeanExample2();
e.setName("Name-not-set");
PropertySheet propertySheet = new PropertySheet(BeanPropertyUtils.getProperties(e));
sheets.add(propertySheet);
beans.add(e);
}
});
VBox vBar = new VBox();
vBar.getChildren().add(listView);
vBar.getChildren().add(addBeanButton);
ObservableList<BeanExample2> names = FXCollections.observableArrayList(new Callback<BeanExample2, Observable[]>() {
#Override
public Observable[] call(BeanExample2 param) {
return new Observable[]{new SimpleStringProperty(param, "name")};
}
});
Bindings.bindContent(names, beans);
Button addChoiceBoxButton = new Button("Add ChoiceBox");
addChoiceBoxButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
ChoiceBox<BeanExample2> choiceBox = new ChoiceBox<BeanExample2>(names);
vBar.getChildren().add(choiceBox);
}
});
vBar.getChildren().add(addChoiceBoxButton);
return vBar;
}
static class BeanExample2 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "BeanExample2{" +
"name='" + name + '\'' +
'}';
}
}
}
Here
ObservableList<BeanExample2> names = FXCollections.observableArrayList(new Callback<BeanExample2, Observable[]>() {
#Override
public Observable[] call(BeanExample2 param) {
return new Observable[]{new SimpleStringProperty(param, "name")};
}
});
you're creating a new property to listen to for updates that cannot be referenced except from the value returned by the call method. The only relationship between the BeanExample2 instance and the SimpleStringProperty is that the BeanExample2 instance is used as bean for the property, which has no effect besides being available via the getBean() method of the property. The value of the property is never assigned let alone modified on a change of the BeanExample2 instance.
To properly trigger updates in the ObservableList, you need to make sure the element in the array returned by the above method is actually notified of updates. Usually you add the property to the class itself:
public static class BeanExample2 {
public final String getName() {
return this.name.get();
}
private final StringProperty name = new SimpleStringProperty();
public final void setName(String value) {
this.name.set(value);
}
#Override
public String toString() {
return "BeanExample2{"
+ "name='" + name.get() + '\''
+ '}';
}
public final StringProperty nameProperty() {
return this.name;
}
}
And return an array containing the property from the Callback
ObservableList<BeanExample2> names = FXCollections.observableArrayList(new Callback<BeanExample2, Observable[]>() {
#Override
public Observable[] call(BeanExample2 param) {
return new Observable[]{param.nameProperty()};
}
});
Note that currently there seems to be a bug in ChoiceBox that adds entries for every intermediate value to the ChoiceBox.
ComboBox does not have this issue and could be used instead of a ChoiceBox.
For what I am doing, I need to set the members of a javafx.scene.control.ComboBox to the members of a Map#keySet() Set.
The options available in the ComboBox need to update as the keys of the Map are updated. (Basically, I want to use the ComboBox to select members from the Map, which WILL be updated at runtime.)
Unfortunately, neither ComboBox#itemsProperty().bind(ObservableValue<>) nor ComboBox#itemsProperty().set(ObservableList<>) will take a Set<>, so the direct route of connecting the itemsProperty to the Map#keySet doesn't work.
Basically:
How can I make it so the ComboBox's items are the members of a Map's keySet?
Again, the behavior that I need is that the ComboBox's items will reflect the KeySet of my Map. (The Map can be any implementation of Map.)
EDIT: The problem seems to be creating an ObservableList out of a Collection -- in this case a Set -- without making it just a copy, but rather a reference to the Set, in order that the ObservableList will reflect the Set's contents.
If you have an ObservableMap, you can add a listener to it. For example:
ComboBox<String> comboBox = ... ;
ObservableMap<String, Something> map = ... ;
map.addListener((MapChangeListener.Change<? extends String, ? extends Something> c) ->
comboBox.getItems().setAll(map.keySet()));
You can also do this with a binding, though I don't think it's any cleaner:
comboBox.itemsProperty().bind(Bindings.createObjectBinding(() ->
FXCollections.observableArrayList(map.keySet()),
map);
Here's a SSCCE, demonstrating both techniques:
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BindItemsToKeySet extends Application {
#Override
public void start(Stage primaryStage) {
ObservableMap<Integer, Item> itemLookupById = FXCollections.observableHashMap();
ComboBox<Integer> listenerCombo = new ComboBox<>();
ComboBox<Integer> bindingCombo = new ComboBox<>();
itemLookupById.addListener((MapChangeListener.Change<? extends Integer, ? extends Item> c) ->
listenerCombo.getItems().setAll(itemLookupById.keySet())
);
bindingCombo.itemsProperty().bind(Bindings.createObjectBinding(() ->
FXCollections.observableArrayList(itemLookupById.keySet()),
itemLookupById));
TextField textField = new TextField();
textField.setOnAction(e -> {
if (textField.getText().isEmpty()) {
return ;
}
Item item = new Item(textField.getText());
itemLookupById.put(item.getId(), item);
textField.clear();
});
textField.setTooltip(new Tooltip("Type an item name and press enter"));
VBox root = new VBox(10,
textField,
listenerCombo,
bindingCombo);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 250, 350));
primaryStage.show();
}
public static class Item {
private final int id ;
private final String name ;
private final static AtomicInteger nextID = new AtomicInteger(1000);
public Item(String name) {
this.id = nextID.incrementAndGet();
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
launch(args);
}
}
I am working on a java/Javafx project for the first time and i have a TableView with multiple column (name, prename, age...) to present my data and I need the user to be able to select a single row and give me everytime all anformation about the person(Other columns) even when he click at another column but I haven't been able to find the right way to do it.
When i select a row my code give everytime the value of the cell i click on, but i need other informations to search with in my SQLite data base and work on it (Delete/edit this person..)
Here is the code that i use:
...//rest of code
#Override
public void initialize(URL location, ResourceBundle resources) {
private TableView<Student> tbl_elev=new TableView<Student>();
...
tbl_elev.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observableValue, Object oldValue, Object newValue) {
//Check whether item is selected and set value of selected item to Label
if (tbl_elev.getSelectionModel().getSelectedItem() != null) {
TableViewSelectionModel<Student> selectionModel = tbl_elev.getSelectionModel();
ObservableList<?> selectedCells = selectionModel.getSelectedCells();
#SuppressWarnings("unchecked")
TablePosition<Object, ?> tablePosition = (TablePosition<Object, ?>) selectedCells.get(0);
Object val = tablePosition.getTableColumn().getCellData(newValue);
System.out.println("Selected Value " + val);
}
}
});
}
... //rest of code
I am waiting for your suggestions and ideas, i dont mind if you suggest another approach because this may be uncompatible (taken from internet) Please if you need any other part of the code just comment, i don't put it all because it is too long to read.. (Sorry of my bad english)
If you specify that the ChangeListener parameters are of type Student you can get use the instance methods from that object:
Here's a Minimal, Complete, and Verifiable example:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SSCCE extends Application {
#Override
public void start(Stage stage) {
VBox root = new VBox();
TableView<Student> studentsTable = new TableView<Student>();
HBox studentBox = new HBox();
Label studentHeader = new Label("Student: ");
Label studentInfo = new Label("");
studentBox.getChildren().addAll(studentHeader, studentInfo);
root.getChildren().addAll(studentsTable, studentBox);
// Prepare the columns
TableColumn<Student, String> firstNameCol = new TableColumn<Student, String>(
"First name");
firstNameCol.setCellValueFactory(cellData -> cellData.getValue()
.firstNameProperty());
TableColumn<Student, String> lastNameCol = new TableColumn<Student, String>(
"Last name");
lastNameCol.setCellValueFactory(cellData -> cellData.getValue()
.lastNameProperty());
studentsTable.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<Student>() {
// Here's the key part. See how I specify that the
// parameters are of type student. Now you can use the
// instance methods from Student.
#Override
public void changed(
ObservableValue<? extends Student> observable,
Student oldValue, Student newValue ) {
studentInfo.setText(newValue.getFirstName() + " "
+ newValue.getLastName());
// If you want to get the value of a selected student cell at
// anytime, even if it hasn't changed. Just do e.g.
// studentsTable.getSelectionModel().getSelectedItem().getFirstName()
}
});
studentsTable.getColumns().setAll(firstNameCol, lastNameCol);
// Some mock Student objects
Student student1 = new Student("Eric", "Smith");
Student student2 = new Student("Brad", "Jones");
Student student3 = new Student("Logan", "Thorpe");
// Fill the table with students.
studentsTable.getItems().addAll(student1, student2, student3);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
// The student class. In this case an inner class to simplify the example. But generally you should never use inner classes.
class Student {
private StringProperty firstName;
private StringProperty lastName;
public Student(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
}
}
After too many failed attempts and thanks to #Jonatan 's answer the code after i compelete some missing words should be like this:
...//rest of code
#Override
public void initialize(URL location, ResourceBundle resources) {
private TableView<Student> tbl_elev=new TableView<Student>();
...
tbl_elev.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Student>() {
// Here's the key part. See how I specify that the
// parameters are of type student. Now you can use the
// instance methods from Student.
#Override
public void changed(ObservableValue<? extends Student> observable,Student oldValue, Student newValue){
if(newValue!=null){
System.out.println(newValue.getName() + " "+ newValue.getPrename()+" "+newValue.getNaiss());
}
//you can add any other value from Student class via getter(getAdr,getMail,...)
}
});
}
... //rest of code
Output example:
Jonatan stenbacka 2015-09-11
Those value are ready for use to fetch the data base and specify the needed row in it to work on.
Hope that this help someone one day.
thanks...
I get a nullpointerxception when following this tutorial:
Populate a tableview using database in JavaFX .
I modified it to make it simpler and fit my needs:
Instead of Usermaster, I have Person object.
while(rs.next()){
Person per = new Person();
per.ClientID.set(rs.getInt(1));
per.FirstName.set(rs.getString(2));
per.LastName.set(rs.getString(3));
The code stops at per.ClientID.set(rs.getInt(1)); due to nullpointerxception.
If I make system.out.println(rs.getInt(1)) (or any other column), I get the value... But it appears that I can't pass it to my object per.
All Person object vars are SimpleString/IntergerProperty type, as shown in the tutorial.
Can someone help me to identify the mistake I made in coding this?
Thank you
**Answer: need to initialize values.
Now I have no errors, but my table is not populating...
Full code:
a) 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();
}
}
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;
}
}
Controller Class:
package tableview.view;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
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");
}
}
}
ClientID is null. You didn't initialize it.
If it's a property, you should create the proper getter and setters for it and not use the property directly. Besides you should never use 1, 2, etc in the ResultSet's getter. It's better practice to use the column names.